diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index b46910cd65..4cd7703869 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,5 +1,6 @@ import '@renderer/databases' +import NotesPage from '@renderer/pages/notes/NotesPage' import store, { persistor } from '@renderer/store' import { Provider } from 'react-redux' import { HashRouter, Route, Routes } from 'react-router-dom' @@ -43,6 +44,7 @@ function App(): React.ReactElement { } /> } /> } /> + } /> } /> diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx index 4e03cee5ff..95f2509183 100644 --- a/src/renderer/src/components/app/Sidebar.tsx +++ b/src/renderer/src/components/app/Sidebar.tsx @@ -22,6 +22,7 @@ import { LayoutGrid, MessageSquare, Moon, + NotepadText, Palette, Settings, Sparkle, @@ -155,7 +156,8 @@ const MainMenus: FC = () => { translate: , minapp: , knowledge: , - files: + files: , + notes: } const pathMap = { @@ -165,7 +167,8 @@ const MainMenus: FC = () => { translate: '/translate', minapp: '/apps', knowledge: '/knowledge', - files: '/files' + files: '/files', + notes: '/notes' } return sidebarIcons.visible.map((icon) => { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index f520aee3d7..1d1fe0242b 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -2558,6 +2558,9 @@ "select_embedding_model_placeholder": "选择嵌入模型", "embedding_dimensions": "嵌入维度", "stored_memories": "已存储记忆" + }, + "notes": { + "title": "笔记" } } } diff --git a/src/renderer/src/pages/notes/NotesPage.tsx b/src/renderer/src/pages/notes/NotesPage.tsx new file mode 100644 index 0000000000..075c1be69d --- /dev/null +++ b/src/renderer/src/pages/notes/NotesPage.tsx @@ -0,0 +1,30 @@ +import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar' +import { FC } from 'react' +import styled from 'styled-components' + +const NotesPage: FC = () => { + return ( + + + 笔记 + + +

笔记页面

+

这里是笔记功能的内容区域。

+
+
+ ) +} + +const Container = styled.div` + flex: 1; +` + +const ContentContainer = styled.div` + height: calc(100vh - var(--navbar-height)); + display: flex; + flex-direction: column; + padding: 20px; +` + +export default NotesPage diff --git a/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx b/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx index 89aa11ca4b..b87fe13506 100644 --- a/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx +++ b/src/renderer/src/pages/settings/DisplaySettings/SidebarIconsManager.tsx @@ -10,7 +10,16 @@ import { import { useAppDispatch } from '@renderer/store' import { setSidebarIcons } from '@renderer/store/settings' import { message } from 'antd' -import { FileSearch, Folder, Languages, LayoutGrid, MessageSquareQuote, Palette, Sparkle } from 'lucide-react' +import { + FileSearch, + Folder, + Languages, + LayoutGrid, + MessageSquareQuote, + NotepadText, + Palette, + Sparkle +} from 'lucide-react' import { FC, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -115,7 +124,8 @@ const SidebarIconsManager: FC = ({ translate: , minapp: , knowledge: , - files: + files: , + notes: }), [] ) @@ -213,7 +223,7 @@ const IconList = styled.div` border: 1px solid var(--color-border); display: flex; flex-direction: column; - overflow-y: hidden; + overflow-y: auto; ` const IconItem = styled.div` diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index bbb2c7378d..5f8c1d84eb 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -56,7 +56,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 121, + version: 122, blacklist: ['runtime', 'messages', 'messageBlocks'], migrate }, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 71990b05b5..76668c547a 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -1812,6 +1812,15 @@ const migrateConfig = { } catch (error) { return state } + }, + '122': (state: RootState) => { + if (state.settings && state.settings.sidebarIcons) { + // Check if 'notes' is not already in visible icons + if (!state.settings.sidebarIcons.visible.includes('notes' as any)) { + state.settings.sidebarIcons.visible = [...state.settings.sidebarIcons.visible, 'notes' as any] + } + } + return state } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 1d30d9d9fa..607a83ca43 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -19,7 +19,15 @@ import { RemoteSyncState } from './backup' export type SendMessageShortcut = 'Enter' | 'Shift+Enter' | 'Ctrl+Enter' | 'Command+Enter' | 'Alt+Enter' -export type SidebarIcon = 'assistants' | 'agents' | 'paintings' | 'translate' | 'minapp' | 'knowledge' | 'files' +export type SidebarIcon = + | 'assistants' + | 'agents' + | 'paintings' + | 'translate' + | 'minapp' + | 'knowledge' + | 'files' + | 'notes' export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [ 'assistants', @@ -28,7 +36,8 @@ export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [ 'translate', 'minapp', 'knowledge', - 'files' + 'files', + 'notes' ] export interface NutstoreSyncRuntime extends RemoteSyncState {} diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 633a17dd1d..9783a5a184 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -525,7 +525,15 @@ export interface TranslateHistory { createdAt: string } -export type SidebarIcon = 'assistants' | 'agents' | 'paintings' | 'translate' | 'minapp' | 'knowledge' | 'files' +export type SidebarIcon = + | 'assistants' + | 'agents' + | 'paintings' + | 'translate' + | 'minapp' + | 'knowledge' + | 'files' + | 'notes' export type ExternalToolResult = { mcpTools?: MCPTool[] diff --git a/src/renderer/src/types/note.ts b/src/renderer/src/types/note.ts new file mode 100644 index 0000000000..54c75f1571 --- /dev/null +++ b/src/renderer/src/types/note.ts @@ -0,0 +1,93 @@ +export type NoteType = 'note' | 'folder' + +export interface Note { + id: string + title: string + content: string + type: NoteType + parentId?: string // For folder hierarchy + tags?: string[] + created_at: number + updated_at: number + isStarred?: boolean + isArchived?: boolean + wordCount?: number + readingTime?: number // in minutes + metadata?: { + lastCursorPosition?: number + isFullscreen?: boolean + fontSize?: number + theme?: string + } +} + +export interface NoteFolder { + id: string + name: string + type: 'folder' + parentId?: string + created_at: number + updated_at: number + isExpanded?: boolean + color?: string +} + +export interface NoteFilter { + search?: string + tags?: string[] + isStarred?: boolean + isArchived?: boolean + parentId?: string + dateRange?: { + start: number + end: number + } +} + +export interface NoteSortOption { + field: 'title' | 'created_at' | 'updated_at' | 'wordCount' + order: 'asc' | 'desc' +} + +export interface NoteExportOptions { + format: 'markdown' | 'html' | 'pdf' | 'json' + includeMetadata?: boolean + includeTags?: boolean + includeArchived?: boolean +} + +export interface NoteImportOptions { + format: 'markdown' | 'html' | 'json' + targetFolderId?: string + preserveStructure?: boolean + mergeTags?: boolean +} + +export interface NoteStats { + totalNotes: number + totalFolders: number + totalWords: number + totalReadingTime: number + recentActivity: { + date: string + count: number + }[] +} + +export interface NoteEditorConfig { + theme: 'light' | 'dark' | 'auto' + fontSize: number + fontFamily: string + lineHeight: number + tabSize: number + wordWrap: boolean + showLineNumbers: boolean + enableVim: boolean + enableEmmet: boolean + autoSave: boolean + autoSaveInterval: number // in milliseconds + spellCheck: boolean + typewriterMode: boolean + focusMode: boolean + previewMode: 'split' | 'preview' | 'source' +} \ No newline at end of file