diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 6747d8d..33efd2f 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -15,7 +15,8 @@ "Bash(npx electron-vite *)", "WebSearch", "WebFetch(domain:primevue.org)", - "Bash(curl -s http://localhost:5173)" + "Bash(curl -s http://localhost:5173)", + "Bash(curl -s http://localhost:5173/)" ] } } diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index fffe277..27952fb 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -61,6 +61,8 @@ async function onNavigate(id) { if (id === 'trash') { await notesStore.loadNotes({ isTrashed: 1 }) + } else if (id === 'favorites') { + await notesStore.loadNotes({ isTrashed: 0, isFavorited: 1 }) } else { await notesStore.loadNotes({ isTrashed: 0 }) } @@ -71,6 +73,15 @@ function onSelectNote(note) { notesStore.getNote(note.id) } +async function onToggleFavorite(note) { + await notesStore.toggleFavorite(note.id) + // If we're in the favorites view, reload to remove unfavorited notes + if (currentNav.value === 'favorites') { + await notesStore.loadNotes({ isTrashed: 0, isFavorited: 1 }) + computeOverviewNotes() + } +} + function onBackToOverview() { notesStore.clearCurrentNote() // Reload notes to reflect any changes @@ -241,6 +252,7 @@ async function onWorkspaceConfirm(path) { :notes="overviewNotes" :title="getOverviewTitle()" @select-note="onSelectNote" + @toggle-favorite="onToggleFavorite" /> diff --git a/src/renderer/src/components/NotesOverview.vue b/src/renderer/src/components/NotesOverview.vue index b3519a3..34a3901 100644 --- a/src/renderer/src/components/NotesOverview.vue +++ b/src/renderer/src/components/NotesOverview.vue @@ -3,14 +3,14 @@ import { ref, computed } from 'vue' import Button from 'primevue/button' import Tag from 'primevue/tag' import ScrollPanel from 'primevue/scrollpanel' -import { Search, LayoutGrid, List, FileText, Clock, Calendar, Folder } from '@lucide/vue' +import { Search, LayoutGrid, List, FileText, Clock, Calendar, Folder, Star } from '@lucide/vue' const props = defineProps({ notes: { type: Array, required: true }, title: { type: String, default: '所有笔记' } }) -const emit = defineEmits(['select-note']) +const emit = defineEmits(['select-note', 'toggle-favorite']) const viewMode = ref('card') // 'card' | 'list' @@ -18,6 +18,11 @@ function selectNote(note) { emit('select-note', note) } +function toggleFavorite(note, event) { + event.stopPropagation() + emit('toggle-favorite', note) +} + function formatDate(dateStr) { if (!dateStr) return '' const d = new Date(dateStr) @@ -104,9 +109,16 @@ function getPreview(note) {
-

+

{{ note.title || '无标题' }}

+
@@ -150,9 +162,18 @@ function getPreview(note) {
-

- {{ note.title || '无标题' }} -

+
+

+ {{ note.title || '无标题' }} +

+ +

{{ getPreview(note) }}

diff --git a/src/renderer/src/components/SideBar.vue b/src/renderer/src/components/SideBar.vue index ecb1c32..907b2ad 100644 --- a/src/renderer/src/components/SideBar.vue +++ b/src/renderer/src/components/SideBar.vue @@ -17,13 +17,19 @@ import { import {useThemeStore} from '../stores/theme' import {useNotesStore} from '../stores/notes' import {useNotebooksStore} from '../stores/notebooks' -import {useNotebookTree} from '../composables/useNotebookTree' +import {useSidebarTree} from '../composables/useSidebarTree' import ButtonIcon from "./ButtonIcon.vue"; const themeStore = useThemeStore() const notesStore = useNotesStore() const notebooksStore = useNotebooksStore() -const {notebookTree, flatNotebooks, expandedKeys, loadNotebooks, loadNotes} = useNotebookTree() + +const props = defineProps({ + notebooks: {type: Array, default: () => []}, + tags: {type: Array, default: () => []} +}) + +const {sidebarTree, flatNotebooks, expandedKeys, toggleNodeExpanded, loadNotebooks, loadNotes} = useSidebarTree(computed(() => props.tags)) const theme = computed(() => themeStore.theme) const toggleTheme = themeStore.toggleTheme @@ -34,11 +40,6 @@ const newNotebookParentName = computed(() => { return nb ? nb.name : '' }) -const props = defineProps({ - notebooks: {type: Array, default: () => []}, - tags: {type: Array, default: () => []} -}) - const emit = defineEmits(['navigate', 'create-note', 'select-notebook', 'select-tag', 'select-note']) // Selected tree node @@ -60,17 +61,6 @@ function toggleCollapsed() { // Navigation state const activeNav = ref('all') -// Section expand state -const expandedSections = ref({ - notes: true, - notebooks: true, - tags: true -}) - -function toggleSection(section) { - expandedSections.value[section] = !expandedSections.value[section] -} - // Search const searchQuery = ref('') const searchResults = ref([]) @@ -88,18 +78,6 @@ async function onSearchInput() { }, 200) } -// Navigation items -const navSections = [ - { - id: 'notes', - items: [ - {id: 'all', icon: File, label: '所有笔记'}, - {id: 'favorites', icon: Star, label: '收藏夹'}, - {id: 'trash', icon: Trash2, label: '回收站'}, - ] - } -] - // Methods function handleNav(id) { activeNav.value = id @@ -116,14 +94,23 @@ function selectNotebook(notebook) { * PrimeVue v4 passes node directly (not wrapped in event object) */ function onTreeSelect(node) { - if (node.data.type === 'notebook') { + const { type, id } = node.data + + if (type === 'nav') { selectedTreeKey.value = node.key - activeNav.value = `notebook-${node.data.id}` + handleNav(id) + } else if (type === 'notebook') { + selectedTreeKey.value = node.key + activeNav.value = `notebook-${id}` emit('select-notebook', node.data) - } else if (node.data.type === 'note') { + } else if (type === 'note') { selectedTreeKey.value = node.key - activeNav.value = `note-${node.data.id}` + activeNav.value = `note-${id}` emit('select-note', node.data) + } else if (type === 'tag') { + selectedTreeKey.value = node.key + activeNav.value = `tag-${id}` + emit('select-tag', node.data) } } @@ -218,6 +205,29 @@ function getNotebookName(note) { const nb = props.notebooks.find(n => n.id === note.notebook_id) return nb ? nb.name : '' } + +/** + * Get the active style class based on node type + */ +function getNodeActiveClass(node) { + const { type, id } = node.data + if (type === 'nav') { + return activeNav.value === id + ? 'bg-surface-200/70 text-primary-700 font-medium shadow-sm' + : 'text-surface-600 hover:bg-surface-100' + } + if (type === 'notebook') { + return activeNav.value === `notebook-${id}` + ? 'bg-emerald-50 text-emerald-700 font-medium shadow-sm' + : 'text-surface-600 hover:bg-surface-100' + } + if (type === 'tag') { + return activeNav.value === `tag-${id}` + ? 'bg-amber-50 text-amber-700 font-medium shadow-sm' + : 'text-surface-600 hover:bg-surface-100' + } + return '' +}