diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index fb9c22d8..f36b7f16 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -2,7 +2,7 @@ use tauri::LogicalSize; #[tauri::command] fn resize_for_route(window: tauri::Window, route: String) { - let is_login = route == "/login" || route == "/"; + let is_login = route == "/login"; if is_login { let _ = window.set_min_size(None::); let _ = window.set_size(tauri::Size::Logical(LogicalSize::new(480.0, 700.0))); diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index be5060be..6435a070 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -13,8 +13,8 @@ "windows": [ { "title": "Memoh", - "width": 480, - "height": 700 + "width": 1280, + "height": 800 } ], "security": { diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md index 132ef13e..2f2d7c0d 100644 --- a/apps/web/AGENTS.md +++ b/apps/web/AGENTS.md @@ -2,7 +2,7 @@ ## Overview -`@memohai/web` is the management UI for Memoh, built with Vue 3 + Vite. It provides visual configuration for bots, models, channels, memory, and more. +`@memohai/web` is the management UI for Memoh, built with Vue 3 + Vite. It provides a chat interface for interacting with bots, plus visual configuration for bots, models, channels, memory, and more. ## Tech Stack @@ -16,7 +16,7 @@ | Data Fetching | Pinia Colada (`@pinia/colada`) + `@memohai/sdk` | | Forms | vee-validate + `@vee-validate/zod` + Zod | | i18n | vue-i18n (en / zh) | -| Icons | FontAwesome (primary) + lucide-vue-next (secondary) | +| Icons | FontAwesome (primary: fas/far/fab) + lucide-vue-next (secondary) | | Toast | vue-sonner | | Tables | @tanstack/vue-table | | Markdown | markstream-vue + Shiki + Mermaid + KaTeX | @@ -27,38 +27,66 @@ ``` src/ -├── App.vue # Root component (RouterView + Toaster) +├── App.vue # Root component (RouterView + Toaster + settings init) ├── main.ts # App entry (plugins, global components, API client setup) -├── router.ts # Route definitions and auth guard -├── style.css # Tailwind imports, CSS variables, theme tokens +├── router.ts # Route definitions, auth guard, chunk error recovery +├── style.css # Tailwind imports (delegates to @memohai/ui/style.css) ├── i18n.ts # vue-i18n configuration -├── assets/ # Static assets +├── assets/ # Static assets (logo.png, vue.svg) ├── components/ # Shared components -│ ├── sidebar/ # App sidebar navigation -│ ├── main-container/ # Main content area (header + breadcrumb + content) +│ ├── sidebar/ # Bot list sidebar (collapsible, bot items, settings link) +│ │ ├── index.vue # Sidebar with bot list + settings gear footer +│ │ └── bot-item.vue # Individual bot entry in sidebar +│ ├── settings-sidebar/ # Settings section sidebar (back-to-chat + nav items) +│ ├── main-container/ # Main content area (KeepAlive RouterView) │ ├── master-detail-sidebar-layout/ # Master-detail layout pattern +│ ├── chat-list/ # Chat list helpers +│ │ └── channel-badge/ # Channel badge component +│ ├── chat/ # Chat UI sub-components +│ │ ├── chat-status/ # Chat connection status indicator +│ │ └── chat-step/ # Chat processing step indicator │ ├── data-table/ # TanStack table wrapper +│ ├── file-manager/ # File browser (list + viewer) +│ ├── monaco-editor/ # Monaco code editor wrapper │ ├── form-dialog-shell/ # Dialog wrapper for forms │ ├── confirm-popover/ # Confirmation popover │ ├── loading-button/ # Button with loading state │ ├── status-dot/ # Status indicator dot │ ├── warning-banner/ # Warning banner -│ ├── search-provider-logo/ # Search provider icons +│ ├── channel-icon/ # Channel platform icon +│ ├── provider-icon/ # LLM provider icon (icons.ts + index.vue) +│ ├── search-provider-logo/ # Search provider icons (custom-icons.ts + index.vue) │ ├── searchable-select-popover/ # Searchable dropdown +│ ├── timezone-select/ # Timezone selector +│ ├── key-value-editor/ # Key-value pair editor +│ ├── import-models-dialog/ # Bulk model import dialog │ ├── add-platform/ # Add platform dialog │ ├── add-provider/ # Add LLM provider dialog -│ ├── create-model/ # Create model dialog -│ └── chat-list/ # Chat list helpers +│ └── create-model/ # Create model dialog ├── composables/ # Reusable composition functions -│ ├── api/ # API-related composables (chat, SSE, platform) +│ ├── api/ # API-related composables +│ │ ├── useChat.ts # Aggregated re-export of chat composables +│ │ ├── useChat.types.ts # Bot, Session, Message, StreamEvent types +│ │ ├── useChat.chat-api.ts # Bot/session CRUD (fetchBots, fetchSessions, etc.) +│ │ ├── useChat.message-api.ts # Message fetch, SSE streaming, local channel +│ │ ├── useChat.sse.ts # SSE stream reader and parser +│ │ ├── useChat.ws.ts # WebSocket connection (send, abort, reconnect) +│ │ ├── useChat.content.ts # Message content parsing (tool calls, text, reasoning) +│ │ ├── useChat.sse.test.ts # SSE parser tests +│ │ ├── useContainerStream.ts # Container creation SSE stream +│ │ └── usePlatform.ts # Platform list query + create mutation │ ├── useDialogMutation.ts # Mutation wrapper with toast error handling │ ├── useRetryingStream.ts # SSE retry with exponential backoff │ ├── useSyncedQueryParam.ts # URL query param sync │ ├── useBotStatusMeta.ts # Bot status metadata │ ├── useAvatarInitials.ts # Avatar initial generation │ ├── useClipboard.ts # Clipboard utilities -│ └── useKeyValueTags.ts # Tag management -├── constants/ # Constants (client types, etc.) +│ ├── useKeyValueTags.ts # Tag management +│ ├── useShikiHighlighter.ts # Shiki syntax highlighter singleton +│ └── useTerminalCache.ts # Terminal output cache +├── constants/ # Constants +│ ├── client-types.ts # LLM client type definitions +│ └── compatibilities.ts # Feature compatibility flags ├── i18n/locales/ # Translation files (en.json, zh.json) ├── layout/ │ └── main-layout/ # Top-level layout (SidebarProvider) @@ -66,105 +94,169 @@ src/ │ └── api-client.ts # SDK client setup (base URL, auth interceptor) ├── pages/ # Route page components │ ├── login/ # Login page -│ ├── main-section/ # Authenticated layout wrapper -│ ├── home/ # Home page -│ ├── chat/ # Chat interface (SSE streaming) +│ ├── main-section/ # Chat section layout (bot sidebar + main container) +│ ├── settings-section/ # Settings section layout (settings sidebar + KeepAlive) +│ ├── home/ # Chat interface (used by both `/` and `/chat/:botId?/:sessionId?`) +│ │ ├── index.vue # Route ↔ store sync, session sidebar + chat area +│ │ ├── composables/ # Page-specific composables +│ │ │ ├── useFileManagerProvider.ts # File manager context +│ │ │ └── useMediaGallery.ts # Media gallery state +│ │ └── components/ # Chat UI components (26 files) +│ │ ├── chat-area.vue # Main chat area (messages, input, attachments) +│ │ ├── session-sidebar.vue # Session list sidebar (search, filter, CRUD) +│ │ ├── bot-sidebar.vue # Alternative bot sidebar layout +│ │ ├── chat-header.vue # Chat top bar (status, step indicator) +│ │ ├── message-item.vue # Single message (user/assistant, markdown, blocks) +│ │ ├── session-item.vue # Session list row (avatar, title, timestamp) +│ │ ├── session-metadata.vue # Collapsible session metadata panel +│ │ ├── bot-item.vue # Bot item in sidebar +│ │ ├── thinking-block.vue # Collapsible thinking/reasoning block +│ │ ├── attachment-block.vue # Attachment grid (images, audio, files) +│ │ ├── media-gallery-lightbox.vue # Fullscreen media lightbox +│ │ ├── tool-call-generic.vue # Generic tool call (name, status, JSON I/O) +│ │ ├── tool-call-list.vue # File listing tool display +│ │ ├── tool-call-read.vue # File read tool display +│ │ ├── tool-call-write.vue # File write tool display +│ │ ├── tool-call-edit.vue # File edit tool display +│ │ ├── tool-call-exec.vue # Command execution tool display +│ │ ├── tool-call-web-search.vue # Web search tool display +│ │ ├── tool-call-web-fetch.vue # Web fetch tool display +│ │ ├── tool-call-browser.vue # Browser action tool display +│ │ ├── tool-call-memory.vue # Memory read/write tool display +│ │ ├── tool-call-message.vue # Send message tool display +│ │ ├── tool-call-email.vue # Email tool display +│ │ ├── tool-call-schedule.vue # Schedule tool display +│ │ ├── tool-call-contacts.vue # Contacts tool display +│ │ ├── tool-call-subagent.vue # Sub-agent tool display +│ │ └── tool-call-skill.vue # Skill activation tool display │ ├── bots/ # Bot list + detail (tabs: overview, memory, channels, etc.) +│ │ ├── index.vue # Bot grid with create dialog +│ │ ├── detail.vue # Bot detail with tabbed interface +│ │ └── components/ # Bot sub-components (25+ files) │ ├── models/ # LLM provider & model management -│ ├── search-providers/ # Search provider management +│ ├── search-providers/ # Search provider management (12+ engine configs) +│ ├── memory-providers/ # Memory provider management +│ ├── tts-providers/ # TTS provider & model management │ ├── email-providers/ # Email provider management -│ ├── settings/ # User settings (profile, password, theme, channels) -│ └── platform/ # Platform management +│ ├── browser-contexts/ # Browser context management +│ ├── usage/ # Token usage statistics +│ ├── settings/ # User settings (profile, password, bind codes) +│ ├── platform/ # Platform management +│ └── oauth/ # OAuth callback pages +│ └── mcp-callback.vue # MCP OAuth callback handler ├── store/ # Pinia stores │ ├── user.ts # User state, JWT token, login/logout │ ├── settings.ts # UI settings (theme, language) │ ├── capabilities.ts # Server capabilities (container backend) -│ └── chat-list.ts # Chat state, messages, SSE streaming +│ ├── chat-selection.ts # Current bot/session selection (localStorage persisted) +│ └── chat-list.ts # Chat messages, streaming state, SSE/WS event processing └── utils/ # Utility functions ├── api-error.ts # API error message extraction ├── date-time.ts # Date/time formatting + ├── date-time.test.ts # Date/time tests ├── channel-icons.ts # Channel platform icons - └── key-value-tags.ts # Tag ↔ Record conversion + ├── key-value-tags.ts # Tag ↔ Record conversion + ├── key-value-tags.test.ts # Tag conversion tests + ├── image-ref.ts # Image reference URL resolution + ├── image-ref.test.ts # Image ref tests + ├── timezones.ts # Timezone list and utilities + └── useControlVisibleStatus.ts # Visibility control utility ``` ## Routes +The app uses a two-section layout architecture: + +### Chat Section (`/`) + +| Path | Name | Component | Description | +|------|------|-----------|-------------| +| `/` | home | `home/index.vue` | Home — empty state when no bot selected | +| `/chat/:botId?/:sessionId?` | chat | `home/index.vue` | Chat interface with bot + session params | + +Both routes render the same `home/index.vue` component. The `home` route shows an empty state; the `chat` route auto-selects a bot and optionally a session based on URL params. URL and store state are bidirectionally synced. + +### Settings Section (`/settings`) + +| Path | Name | Component | Description | +|------|------|-----------|-------------| +| `/settings/bots` | bots | `bots/index.vue` | Bot list grid | +| `/settings/bots/:botId` | bot-detail | `bots/detail.vue` | Bot detail with tabs | +| `/settings/models` | models | `models/index.vue` | LLM provider & model management | +| `/settings/search-providers` | search-providers | `search-providers/index.vue` | Search provider management | +| `/settings/memory-providers` | memory-providers | `memory-providers/index.vue` | Memory provider management | +| `/settings/tts-providers` | tts-providers | `tts-providers/index.vue` | TTS provider & model management | +| `/settings/email-providers` | email-providers | `email-providers/index.vue` | Email provider management | +| `/settings/browser-contexts` | browser-contexts | `browser-contexts/index.vue` | Browser context management | +| `/settings/usage` | usage | `usage/index.vue` | Token usage statistics | +| `/settings/profile` | settings | `settings/index.vue` | User profile settings | +| `/settings/platform` | platform | `platform/index.vue` | Platform management | + +`/settings` redirects to `/settings/bots` by default. + +### Standalone Routes + | Path | Name | Component | Description | |------|------|-----------|-------------| | `/login` | Login | `login/index.vue` | Login form (no auth required) | -| `/chat` | chat | `chat/index.vue` | Chat interface with bot sidebar | -| `/home` | home | `home/index.vue` | Home dashboard | -| `/bots` | bots | `bots/index.vue` | Bot list grid | -| `/bots/:botId` | bot-detail | `bots/detail.vue` | Bot detail with tabs | -| `/models` | models | `models/index.vue` | LLM provider & model management | -| `/search-providers` | search-providers | `search-providers/index.vue` | Search provider management | -| `/email-providers` | email-providers | `email-providers/index.vue` | Email provider management | -| `/settings` | settings | `settings/index.vue` | User settings | -| `/platform` | platform | `platform/index.vue` | Platform management | +| `/oauth/mcp/callback` | oauth-mcp-callback | `oauth/mcp-callback.vue` | MCP OAuth callback (no auth required) | -Auth guard: all routes except `/login` require `localStorage.getItem('token')`. Logged-in users accessing `/login` are redirected to `/chat`. +### Auth Guard + +- All routes except `/login` and `/oauth/*` require `localStorage.getItem('token')`. +- Logged-in users accessing `/login` are redirected to `/`. +- Chunk load errors (dynamic import failures) trigger an automatic page reload. +- Tauri integration: `afterEach` hook calls `resize_for_route` when running inside Tauri. ## Layout System -Three-tier layout architecture: +Two-section layout architecture, both sharing the same `MainLayout` wrapper: 1. **MainLayout** (`layout/main-layout/`) — Top-level wrapper using `SidebarProvider` from `@memohai/ui`. Provides `#sidebar` and `#main` slots. -2. **Sidebar** (`components/sidebar/`) — Collapsible navigation with logo, menu items, and user avatar footer. Active route highlighting. -3. **MainContainer** (`components/main-container/`) — Header (sidebar trigger + breadcrumb) + scrollable content area with `` wrapped ``. -Several pages use **MasterDetailSidebarLayout** (`components/master-detail-sidebar-layout/`) for left-sidebar + detail-panel patterns (chat, models, search providers, email providers). +2. **Chat Section** (`pages/main-section/`) — Uses `MainLayout` with: + - **Sidebar** (`components/sidebar/`) — Bot list sidebar (collapsible). Header shows "Bots" label + create button. Body lists all bots as `BotItem` entries. Footer has a settings gear link to `/settings`. + - **MainContainer** (`components/main-container/`) — `` wrapped `` for chat pages. + +3. **Settings Section** (`pages/settings-section/`) — Uses `MainLayout` with: + - **SettingsSidebar** (`components/settings-sidebar/`) — Collapsible settings navigation. Top has a "back to chat" button that restores the last selected bot/session. Menu items: Bots, Models, Search Providers, Memory Providers, TTS Providers, Email Providers, Browser Contexts, Usage, Settings, Platform. + - **SidebarInset** — `` wrapped `` for settings pages. + +4. **Home/Chat Page** (`pages/home/`) — Internal layout: + - **SessionSidebar** — Left panel: session search, source filter, new session button, session list. + - **ChatArea** — Center panel: message list with scroll, input area with attachments. + - **SessionMetadata** — Right panel (currently disabled): collapsible session metadata. + +Several settings pages use **MasterDetailSidebarLayout** (`components/master-detail-sidebar-layout/`) for left-sidebar + detail-panel patterns (models, search providers, email providers, memory providers, tts providers, browser contexts). ## CSS & Theming +Design tokens, color palette, typography, elevation strategy, and component visual specs are defined in `packages/ui/DESIGN.md`. **Read that file before making any UI changes.** + ### Tailwind CSS 4 -CSS-based configuration in `style.css` (no `tailwind.config.*` file): +CSS-based configuration (no `tailwind.config.*` file). All design tokens (CSS variables, `@theme inline` mapping, base styles) live in `packages/ui/src/style.css`. The web app imports them via: ```css -@import "tailwindcss"; +@import "@memohai/ui/style.css"; ``` -Design tokens are CSS custom properties in `:root` / `.dark` using OKLCH color space with a unified subtle purple hue (285): -- Colors: `--background`, `--foreground`, `--primary`, `--secondary`, `--muted`, `--accent`, `--destructive`, `--border`, `--input`, `--ring` -- Sidebar: `--sidebar`, `--sidebar-foreground`, `--sidebar-primary`, etc. -- Radius: `--radius` with size variants (`--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`) -- Chart: `--chart-1` through `--chart-5` - -Tokens are mapped to Tailwind via `@theme inline` block in `style.css`. - -### Color Design Principles - -The color system avoids extreme black/white to reduce visual strain and create a layered, warm feel: - -- **No pure black or pure white**: Light mode foreground is dark charcoal (L=25%), not black. Dark mode background is dark gray (L=20.5%), not black. Dark mode foreground is soft off-white (L=88%), not pure white. -- **Subtle color temperature**: All neutral grays carry a tiny chroma (0.004–0.006) at hue 285 (purple), preventing a sterile "dead gray" feel. -- **Text hierarchy through lightness**: Three distinct levels — `text-foreground` (primary text), `text-secondary-foreground` (emphasis text on secondary surfaces), `text-muted-foreground` (secondary/helper text). -- **Surface layering**: `background` < `card`/`popover` — card surfaces are slightly lighter than the page background for visual depth. - ### Dark Mode -- CSS: `@custom-variant dark (&:is(.dark *))` in `style.css` - Runtime: `useColorMode` from `@vueuse/core` in `store/settings.ts` - Storage: theme preference persisted via `useStorage` - Toggle: Available in Settings page and login page -- Usage: semantic tokens auto-switch; no `dark:` prefix needed for themed colors +- Usage: semantic tokens auto-switch; no `dark:` prefix needed -### Styling Convention +### Styling Rules -- **Utility-first**: Tailwind classes as primary styling method. Minimal `