diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 37012f6..b30aa13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,18 @@ "@lucide/vue": "^1.17.0", "@primeuix/themes": "^2.0.3", "@tailwindcss/vite": "^4.3.0", + "chokidar": "^5.0.0", "electron": "^42.3.0", "electron-vite": "^5.0.0", + "gray-matter": "^4.0.3", + "nanoid": "^5.1.11", + "primeicons": "^7.0.0", "primevue": "^4.5.5", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", + "sql.js": "^1.14.1", "tailwindcss": "^4.3.0", "unified": "^11.0.5", "vue": "^3.5.35" @@ -2828,6 +2833,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -4162,6 +4182,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -4181,6 +4214,18 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -4577,6 +4622,49 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/gray-matter/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4843,6 +4931,15 @@ "dev": true, "license": "ISC" }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -5003,6 +5100,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/lazy-val": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", @@ -6241,9 +6347,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz", + "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==", "funding": [ { "type": "github", @@ -6252,10 +6358,10 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/node-abi": { @@ -6553,6 +6659,24 @@ "dev": true, "license": "MIT" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/postject": { "version": "1.0.0-alpha.6", "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", @@ -6583,6 +6707,12 @@ "node": "^12.20.0 || >=14" } }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", + "license": "MIT" + }, "node_modules/primevue": { "version": "4.5.5", "resolved": "https://registry.npmjs.org/primevue/-/primevue-4.5.5.tgz", @@ -6700,6 +6830,19 @@ "read-binary-file-arch": "cli.js" } }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/rehype-stringify": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", @@ -6944,6 +7087,19 @@ "node": ">=11.0.0" } }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/semver": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", @@ -7100,6 +7256,12 @@ "license": "BSD-3-Clause", "optional": true }, + "node_modules/sql.js": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz", + "integrity": "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==", + "license": "MIT" + }, "node_modules/stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", @@ -7152,6 +7314,15 @@ "node": ">=8" } }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", diff --git a/package.json b/package.json index f478b03..cbcf7c0 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,18 @@ "@lucide/vue": "^1.17.0", "@primeuix/themes": "^2.0.3", "@tailwindcss/vite": "^4.3.0", + "chokidar": "^5.0.0", "electron": "^42.3.0", "electron-vite": "^5.0.0", + "gray-matter": "^4.0.3", + "nanoid": "^5.1.11", + "primeicons": "^7.0.0", "primevue": "^4.5.5", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", + "sql.js": "^1.14.1", "tailwindcss": "^4.3.0", "unified": "^11.0.5", "vue": "^3.5.35" diff --git a/src/main/index.js b/src/main/index.js index 5a7d611..6e6c908 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,8 +1,10 @@ import { app, BrowserWindow, ipcMain, nativeTheme } from 'electron' import { join } from 'path' import { is } from '@electron-toolkit/utils' +import { StorageService } from './services/StorageService' let mainWindow = null +let storageService = null // 标题栏颜色配置 const titleBarColors = { @@ -59,9 +61,189 @@ ipcMain.handle('theme:set', (_, theme) => { }) }) -app.whenReady().then(createWindow) +// ==================== Register IPC Handlers ==================== + +function registerIpcHandlers() { + // --- Notes --- + ipcMain.handle('notes:create', async (_, data) => { + try { + return await storageService.createNote(data) + } catch (err) { + console.error('Failed to create note:', err) + throw err + } + }) + + ipcMain.handle('notes:get', async (_, id) => { + try { + return await storageService.getNote(id) + } catch (err) { + console.error('Failed to get note:', err) + throw err + } + }) + + ipcMain.handle('notes:update', async (_, id, data) => { + try { + return await storageService.updateNote(id, data) + } catch (err) { + console.error('Failed to update note:', err) + throw err + } + }) + + ipcMain.handle('notes:delete', async (_, id, permanent) => { + try { + return await storageService.deleteNote(id, permanent) + } catch (err) { + console.error('Failed to delete note:', err) + throw err + } + }) + + ipcMain.handle('notes:restore', async (_, id) => { + try { + return await storageService.restoreNote(id) + } catch (err) { + console.error('Failed to restore note:', err) + throw err + } + }) + + ipcMain.handle('notes:list', async (_, filter) => { + try { + return await storageService.listNotes(filter) + } catch (err) { + console.error('Failed to list notes:', err) + throw err + } + }) + + ipcMain.handle('notes:search', async (_, query) => { + try { + return await storageService.searchNotes(query) + } catch (err) { + console.error('Failed to search notes:', err) + throw err + } + }) + + // --- Notebooks --- + ipcMain.handle('notebooks:create', async (_, data) => { + try { + return await storageService.createNotebook(data) + } catch (err) { + console.error('Failed to create notebook:', err) + throw err + } + }) + + ipcMain.handle('notebooks:list', async () => { + try { + return await storageService.listNotebooks() + } catch (err) { + console.error('Failed to list notebooks:', err) + throw err + } + }) + + ipcMain.handle('notebooks:rename', async (_, id, name) => { + try { + return await storageService.renameNotebook(id, name) + } catch (err) { + console.error('Failed to rename notebook:', err) + throw err + } + }) + + ipcMain.handle('notebooks:delete', async (_, id) => { + try { + return await storageService.deleteNotebook(id) + } catch (err) { + console.error('Failed to delete notebook:', err) + throw err + } + }) + + // --- Tags --- + ipcMain.handle('tags:create', async (_, name) => { + try { + return await storageService.createTag(name) + } catch (err) { + console.error('Failed to create tag:', err) + throw err + } + }) + + ipcMain.handle('tags:list', async () => { + try { + return await storageService.listTags() + } catch (err) { + console.error('Failed to list tags:', err) + throw err + } + }) + + ipcMain.handle('tags:delete', async (_, id) => { + try { + return await storageService.deleteTag(id) + } catch (err) { + console.error('Failed to delete tag:', err) + throw err + } + }) + + ipcMain.handle('tags:addToNote', async (_, noteId, tagName) => { + try { + return await storageService.addTagToNote(noteId, tagName) + } catch (err) { + console.error('Failed to add tag to note:', err) + throw err + } + }) + + ipcMain.handle('tags:removeFromNote', async (_, noteId, tagName) => { + try { + return await storageService.removeTagFromNote(noteId, tagName) + } catch (err) { + console.error('Failed to remove tag from note:', err) + throw err + } + }) + + // --- Stats --- + ipcMain.handle('stats:get', async () => { + try { + return await storageService.getStats() + } catch (err) { + console.error('Failed to get stats:', err) + throw err + } + }) +} + +// ==================== App Lifecycle ==================== + +app.whenReady().then(async () => { + // Initialize storage with user data directory + const workspacePath = join(app.getPath('documents'), 'Tunji') + storageService = new StorageService() + await storageService.init(workspacePath) + + // Register IPC handlers + registerIpcHandlers() + + // Create window + createWindow() +}) app.on('window-all-closed', () => { + // Save all pending changes before quitting + if (storageService) { + storageService.flush() + storageService.close() + } + if (process.platform !== 'darwin') { app.quit() } diff --git a/src/main/indexpreload.js b/src/main/indexpreload.js index dfbcaa3..18403ba 100644 --- a/src/main/indexpreload.js +++ b/src/main/indexpreload.js @@ -3,5 +3,38 @@ import { contextBridge, ipcRenderer } from 'electron' // 安全地暴露 API 给渲染进程 contextBridge.exposeInMainWorld('electronAPI', { platform: process.platform, - setTheme: (theme) => ipcRenderer.invoke('theme:set', theme) + + // Theme + setTheme: (theme) => ipcRenderer.invoke('theme:set', theme), + + // Notes + notes: { + create: (data) => ipcRenderer.invoke('notes:create', data), + get: (id) => ipcRenderer.invoke('notes:get', id), + update: (id, data) => ipcRenderer.invoke('notes:update', id, data), + delete: (id, permanent) => ipcRenderer.invoke('notes:delete', id, permanent), + restore: (id) => ipcRenderer.invoke('notes:restore', id), + list: (filter) => ipcRenderer.invoke('notes:list', filter), + search: (query) => ipcRenderer.invoke('notes:search', query), + }, + + // Notebooks + notebooks: { + create: (data) => ipcRenderer.invoke('notebooks:create', data), + list: () => ipcRenderer.invoke('notebooks:list'), + rename: (id, name) => ipcRenderer.invoke('notebooks:rename', id, name), + delete: (id) => ipcRenderer.invoke('notebooks:delete', id), + }, + + // Tags + tags: { + create: (name) => ipcRenderer.invoke('tags:create', name), + list: () => ipcRenderer.invoke('tags:list'), + delete: (id) => ipcRenderer.invoke('tags:delete', id), + addToNote: (noteId, tagName) => ipcRenderer.invoke('tags:addToNote', noteId, tagName), + removeFromNote: (noteId, tagName) => ipcRenderer.invoke('tags:removeFromNote', noteId, tagName), + }, + + // Stats + getStats: () => ipcRenderer.invoke('stats:get'), }) diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index 7307b4d..67bae42 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -1,80 +1,127 @@ - diff --git a/src/renderer/src/assets/styles/main.css b/src/renderer/src/assets/styles/main.css index 418c1d7..18621ea 100644 --- a/src/renderer/src/assets/styles/main.css +++ b/src/renderer/src/assets/styles/main.css @@ -140,42 +140,6 @@ --color-app-ring: var(--theme-ring); } -/* ----- 统一按钮样式 ----- */ - -/* PrimeVue Button 统一 12px 圆角 */ -.p-button { - border-radius: 12px !important; -} - -/* 按钮统一交互过渡 */ -.btn-icon { - transition: background-color 0.15s ease, color 0.15s ease; -} - -/* 激活态按钮 */ -.btn-active { - background: var(--theme-bg-elevated) !important; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important; - color: var(--theme-primary-600) !important; -} - -/* 实心主色按钮 */ -.btn-filled:not(.p-button-text) { - background: #00bf66 !important; - border-color: #00bf66 !important; - color: #ffffff !important; -} - -.btn-filled:not(.p-button-text):hover { - background: #00a555 !important; - border-color: #00a555 !important; -} - -.btn-filled:not(.p-button-text):active { - background: #00914b !important; - border-color: #00914b !important; -} - /* ----- 全局基础样式 ----- */ /* 主题切换平滑过渡 */ @@ -207,3 +171,8 @@ html { background-color: rgba(129, 140, 248, 0.3); color: #e2e8f0; } + +/* Tooltip 样式 */ +.p-tooltip-text { + font-size: 0.75rem !important; +} diff --git a/src/renderer/src/components/EditorPanel.vue b/src/renderer/src/components/EditorPanel.vue index 95b3cf4..fe448d8 100644 --- a/src/renderer/src/components/EditorPanel.vue +++ b/src/renderer/src/components/EditorPanel.vue @@ -20,10 +20,12 @@ const props = defineProps({ note: { type: Object, default: null } }) -const emit = defineEmits(['back']) +const emit = defineEmits(['back', 'save']) const noteTitle = ref('') const noteContent = ref('') +const isSaving = ref(false) +const lastSavedAt = ref(null) // View mode: edit | preview | split const viewMode = ref('edit') @@ -42,14 +44,49 @@ const headings = ref([]) // Rendered HTML for preview const renderedHtml = ref('') +// Auto-save timer +let saveTimer = null +const SAVE_DELAY = 1000 // 1 second debounce + // Update local state when note prop changes watch(() => props.note, (newNote) => { if (newNote) { noteTitle.value = newNote.title || '' - noteContent.value = newNote.content || getDefaultContent(newNote.title) + noteContent.value = newNote.content || '' } }, { immediate: true }) +// Auto-save when content changes +function scheduleSave() { + if (saveTimer) clearTimeout(saveTimer) + saveTimer = setTimeout(async () => { + await doSave() + }, SAVE_DELAY) +} + +async function doSave() { + if (!props.note) return + isSaving.value = true + try { + emit('save', { + title: noteTitle.value, + content: noteContent.value + }) + lastSavedAt.value = new Date() + } finally { + isSaving.value = false + } +} + +// Watch for content changes and trigger auto-save +watch(noteContent, () => { + scheduleSave() +}) + +watch(noteTitle, () => { + scheduleSave() +}) + // Parse headings from markdown content watch(noteContent, (content) => { const result = [] @@ -83,14 +120,6 @@ watch(noteContent, async (content) => { } }, { immediate: true }) -function getDefaultContent(title) { - return `# ${title} - -在这里开始写作... - -` -} - function formatDate(dateStr) { if (!dateStr) return '' const d = new Date(dateStr) @@ -98,6 +127,9 @@ function formatDate(dateStr) { } function goBack() { + // Save before going back + if (saveTimer) clearTimeout(saveTimer) + doSave() emit('back') } @@ -125,6 +157,17 @@ const toolbarItems3 = [ { icon: Link, title: '链接' }, { icon: Image, title: '图片' }, ] + +// Tag names for display +const tagNames = computed(() => { + if (!props.note?.tags) return [] + return props.note.tags.map(t => typeof t === 'string' ? t : t.name) +}) + +// Notebook name +const notebookName = computed(() => { + return props.note?.notebook_name || props.note?.notebook || '' +}) + + diff --git a/src/renderer/src/main.js b/src/renderer/src/main.js index 6ae7b4e..c99f59e 100644 --- a/src/renderer/src/main.js +++ b/src/renderer/src/main.js @@ -4,7 +4,7 @@ import Aura from '@primeuix/themes/aura' import Tooltip from 'primevue/tooltip' import App from './App.vue' import './assets/styles/main.css' - +import 'primeicons/primeicons.css' const app = createApp(App) app.use(PrimeVue, {