Compare commits
19 Commits
cdb7baa197
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 10d95951a3 | |||
| 50d3177e9e | |||
| 86a9d54e70 | |||
| 600b50f239 | |||
| a7c9657ff1 | |||
| e47bd3bf87 | |||
| 41437e1751 | |||
| 8c70e0558f | |||
| 0bd1590681 | |||
| 26d1430d6a | |||
| bc05747f5f | |||
| 71bac12cd9 | |||
| 9da17c824d | |||
| d8cb4a768b | |||
| b9230772ed | |||
| 9b4bc63e1a | |||
| 501f226542 | |||
| 2444ca0811 | |||
| 828475ae4f |
3631
CELIA 3.8.json
Normal file
3631
CELIA 3.8.json
Normal file
File diff suppressed because one or more lines are too long
115
README.md
115
README.md
@@ -1,37 +1,36 @@
|
|||||||
# Claudia
|
# Claudia
|
||||||
|
|
||||||
Beautiful AI roleplay desktop companion built with Tauri and Rust.
|
Desktop AI chat application built with Tauri and Rust, focused on roleplay and character-based interactions.
|
||||||
|
|
||||||
## Vision
|
|
||||||
|
|
||||||
Claudia aims to be a lightweight, desktop-native alternative to SillyTavern, focusing on roleplay and character-based interactions while maintaining a clean, modern interface.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Core Chat Features
|
### Chat
|
||||||
- 🎨 **Beautiful glassmorphic UI** - Modern design with gradient backgrounds
|
- Streaming responses with real-time display
|
||||||
- 🔧 **Bring-your-own-API** - Supports any Anthropic-compatible API
|
- Full markdown rendering with syntax highlighting
|
||||||
- ✅ **API validation** - Automatic model detection via /v1/models
|
- Message swipes (multiple response alternatives)
|
||||||
- 💬 **Full conversation context** - AI remembers your entire conversation
|
- Edit and regenerate from any message
|
||||||
- 💾 **Persistent chat history** - Conversations saved per character
|
- Per-character conversation history
|
||||||
- 🎯 **Streaming responses** - Real-time token display (optional)
|
- Copy code blocks with one click
|
||||||
|
|
||||||
### Character System
|
### Characters
|
||||||
- 🎭 **Multiple characters** - Switch between different AI personas
|
- V2/V3 character card import/export (PNG format)
|
||||||
- 🖼️ **Character avatars** - Upload custom images with zoom preview
|
- Multiple characters with avatar support
|
||||||
- 📇 **V2/V3 character cards** - Import/export Tavern-compatible cards
|
- Full character editor (description, personality, scenario, examples, etc.)
|
||||||
- ✏️ **Full character editor** - All v2/v3 fields supported (description, scenario, examples, etc.)
|
- Character-specific chat history
|
||||||
|
|
||||||
### Advanced Chat Features
|
### Roleplay Tools
|
||||||
- 🔄 **Message swipes** - Generate multiple responses and swipe between them
|
- World Info/Lorebook system with keyword detection and priority
|
||||||
- ✏️ **Message editing** - Edit messages and regenerate from any point
|
- Author's Note with configurable positioning
|
||||||
- 🔀 **Chat branching** - Explore alternate conversation paths
|
- User Personas with chat/character locking
|
||||||
|
- Prompt Presets with instruction blocks
|
||||||
|
- Message Examples from character cards
|
||||||
|
- Regex Scripts for text transformations
|
||||||
|
- Token counter with per-section breakdown
|
||||||
|
|
||||||
### Message Display
|
### API
|
||||||
- 📝 **Full markdown rendering** - Headers, lists, tables, links, blockquotes
|
- Bring-your-own-API (Anthropic-compatible)
|
||||||
- 🎨 **Syntax highlighting** - Beautiful code blocks with highlight.js
|
- Automatic model detection via /v1/models
|
||||||
- 📋 **Copy code blocks** - One-click copy button on hover
|
- API validation and error handling
|
||||||
- ✨ **Smooth animations** - Elegant message transitions
|
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
@@ -40,59 +39,49 @@ npm install
|
|||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Build:
|
Build for production:
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: The dev script includes `WEBKIT_DISABLE_DMABUF_RENDERER=1` to fix Wayland compatibility issues on KDE Plasma.
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
On first launch, click settings and configure:
|
On first launch, configure in Settings:
|
||||||
- Base URL (e.g., https://api.anthropic.com)
|
- Base URL (e.g., https://api.anthropic.com)
|
||||||
- API Key
|
- API Key
|
||||||
- Model (validated from /v1/models endpoint)
|
- Model
|
||||||
|
|
||||||
- Config stored in `~/.config/claudia/config.json`
|
Config stored in `~/.config/claudia/config.json`
|
||||||
- Chat history stored in `~/.config/claudia/history.json`
|
|
||||||
|
|
||||||
## Usage
|
## Keyboard Shortcuts
|
||||||
|
|
||||||
### Keyboard Shortcuts
|
|
||||||
- **Enter** - Send message
|
- **Enter** - Send message
|
||||||
- **Shift+Enter** - New line in message
|
- **Shift+Enter** - New line
|
||||||
- **Up Arrow** - Edit last user message
|
- **Ctrl+Enter** - Send message (alternative)
|
||||||
- **Left/Right Arrow** - Swipe between alternative responses
|
- **Up Arrow** - Edit last user message (when input is at start)
|
||||||
|
- **Left/Right Arrow** - Navigate between response alternatives
|
||||||
### Character Management
|
- **Escape** - Close panels/modals, cancel editing
|
||||||
- **Character Dropdown** - Switch between characters
|
- **Ctrl+K** - Focus message input
|
||||||
- **Settings → Character Tab** - Edit current character
|
- **Ctrl+P** - Open command palette (quick access to all actions)
|
||||||
- **Import v2 Card** - Import Tavern character cards (PNG format)
|
- **Ctrl+/** - Toggle Roleplay Tools panel
|
||||||
- **Export v2 Card** - Export character as Tavern-compatible card
|
|
||||||
|
|
||||||
### Interface
|
|
||||||
- **Drag header** - Move window around your desktop
|
|
||||||
- **Trash icon** - Clear conversation history
|
|
||||||
- **Settings icon** - Configure API settings
|
|
||||||
- **Minimize/Maximize** - Window controls
|
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
Claudia is being developed to become a full-featured roleplay platform comparable to SillyTavern. See [ROADMAP.md](ROADMAP.md) for detailed plans including:
|
See [ROADMAP.md](ROADMAP.md) for detailed development plans.
|
||||||
|
|
||||||
**Coming Soon:**
|
**Current Focus:** Chat Branching/Checkpoints for non-linear conversation exploration
|
||||||
- 📚 World Info/Lorebooks for dynamic context
|
|
||||||
- 📝 Author's Note for better prompt control
|
|
||||||
- 👤 User Personas for identity management
|
|
||||||
- 😊 Character Expression Sprites
|
|
||||||
- 🔢 Token Counter and context visualization
|
|
||||||
- 👥 Group Chats with multiple characters
|
|
||||||
- ⚡ Quick Replies and macro system
|
|
||||||
|
|
||||||
**Current Version:** v0.1.0 - Basic character chat with swipes and card import/export
|
**Upcoming:**
|
||||||
**Next Version:** v0.2.0 - Roleplay Foundation (World Info, Author's Note, Token Counter)
|
- Chat branching with timeline visualization
|
||||||
|
- Character expression sprites
|
||||||
|
- Group chats with multiple characters
|
||||||
|
- Quick replies and macro system
|
||||||
|
- Context templates for different model formats
|
||||||
|
|
||||||
## Contributing
|
## Development
|
||||||
|
|
||||||
This is a personal project, but feedback and suggestions are welcome! If you encounter bugs or have feature requests, please open an issue on GitHub.
|
Built with:
|
||||||
|
- Tauri 2.0
|
||||||
|
- Rust backend
|
||||||
|
- Vanilla JavaScript frontend
|
||||||
|
- tiktoken-rs for token counting
|
||||||
|
|||||||
281
ROADMAP.md
281
ROADMAP.md
@@ -9,50 +9,61 @@
|
|||||||
- Character Management (multiple characters)
|
- Character Management (multiple characters)
|
||||||
- Character Avatars with upload and zoom
|
- Character Avatars with upload and zoom
|
||||||
- Expanded Character Editor (all v2/v3 fields)
|
- Expanded Character Editor (all v2/v3 fields)
|
||||||
|
- Prompt Presets System (built-in and custom presets with instruction blocks)
|
||||||
|
- Editable Built-in Presets with Restore to Default
|
||||||
|
- World Info/Lorebook System (keyword detection, priority, insertion)
|
||||||
|
- Author's Note (configurable depth and positioning)
|
||||||
|
- User Personas (identity management with chat/character locking)
|
||||||
|
- Regex Scripts (global and character-scoped text transformations)
|
||||||
|
- Chat History Import/Export (JSON format)
|
||||||
|
- Enhanced Message Controls (delete, pin, hide, continue, regenerate any message)
|
||||||
|
- Token Counter (real-time display with per-section breakdown)
|
||||||
|
- Message Examples (character card examples injected into context)
|
||||||
|
- Chat Branching/Checkpoints (create, switch, delete, rename branches from any message)
|
||||||
|
|
||||||
### 🎯 Current Focus: UI/UX Improvements
|
### 🎯 Current Focus: Quality of Life & Polish
|
||||||
**Decision:** Before adding complex roleplay features, we're focusing on polishing the existing UI/UX to establish a solid foundation. This includes better visual design, improved workflows, and enhanced user experience.
|
**Next Up:** Implementing high-impact QoL features to reduce friction and improve user experience - starting with Toast Notifications, Command Palette, Auto-save, Drag & Drop, and Chat Search.
|
||||||
|
|
||||||
See "Phase 7: Polish & UX" section for details on UI improvements being prioritized.
|
**Recent Completion:** Chat Branching/Checkpoints - Full conversation branching system allowing users to create and explore alternate conversation paths from any message point. Each branch maintains its own complete message history with a branch manager modal for easy navigation.
|
||||||
|
|
||||||
## Phase 1: Core Roleplay Infrastructure (High Priority)
|
## Phase 1: Core Roleplay Infrastructure (High Priority)
|
||||||
**Goal: Enable basic roleplay-focused prompt engineering**
|
**Goal: Enable basic roleplay-focused prompt engineering**
|
||||||
|
|
||||||
### 1. World Info/Lorebook System
|
### 1. World Info/Lorebook System ✅
|
||||||
- [ ] Create UI for managing lorebook entries (keyword, content, priority)
|
- [x] Create UI for managing lorebook entries (keyword, content, priority)
|
||||||
- [ ] Implement keyword detection in recent messages
|
- [x] Implement keyword detection in recent messages
|
||||||
- [ ] Add context injection before message generation
|
- [x] Add context injection before message generation
|
||||||
- [ ] Support recursive entry activation
|
- [x] Support recursive entry activation
|
||||||
- [ ] Per-character lorebook assignment
|
- [x] Per-character lorebook assignment
|
||||||
- [ ] Import/export lorebook files
|
- [x] Import/export lorebook files
|
||||||
|
|
||||||
**Why Important:** World Info is the foundation of consistent roleplay. It allows dynamic context injection based on what's currently relevant in the conversation, saving tokens while maintaining world consistency.
|
**Why Important:** World Info is the foundation of consistent roleplay. It allows dynamic context injection based on what's currently relevant in the conversation, saving tokens while maintaining world consistency.
|
||||||
|
|
||||||
### 2. Author's Note
|
### 2. Author's Note ✅
|
||||||
- [ ] Add configurable Author's Note field (inserted at depth 1-5)
|
- [x] Add configurable Author's Note field (inserted at depth 1-5)
|
||||||
- [ ] Position control (after system, before/after examples, etc.)
|
- [x] Position control (after system, before/after examples, etc.)
|
||||||
- [ ] Per-character Author's Note support
|
- [x] Per-character Author's Note support
|
||||||
- [ ] Template variables in Author's Note
|
- [x] Template variables in Author's Note
|
||||||
|
|
||||||
**Why Important:** Author's Note is considered better than system prompts for roleplay because it appears closer to the actual conversation, reducing AI tendency to ignore or forget instructions.
|
**Why Important:** Author's Note is considered better than system prompts for roleplay because it appears closer to the actual conversation, reducing AI tendency to ignore or forget instructions.
|
||||||
|
|
||||||
### 3. Jailbreak Templates
|
### 3. Jailbreak Templates ✅ (Implemented as Prompt Presets)
|
||||||
- [ ] Add jailbreak template field in settings
|
- [x] Add jailbreak template field in settings (Prompt Presets with system additions)
|
||||||
- [ ] Preset jailbreak templates for roleplay
|
- [x] Preset jailbreak templates for roleplay (Built-in presets: Default, Roleplay, Creative Writing, Assistant)
|
||||||
- [ ] Per-character jailbreak override option
|
- [x] Per-character jailbreak override option (Active preset per character)
|
||||||
- [ ] Template preview and testing
|
- [x] Template preview and testing (Editable instruction blocks with live preview)
|
||||||
|
|
||||||
**Why Important:** Many roleplay scenarios require specific prompting to work well with API safety filters and to maintain character consistency.
|
**Why Important:** Many roleplay scenarios require specific prompting to work well with API safety filters and to maintain character consistency.
|
||||||
|
|
||||||
## Phase 2: Enhanced Character Features (High Priority)
|
## Phase 2: Enhanced Character Features (High Priority)
|
||||||
**Goal: Better character representation and user identity**
|
**Goal: Better character representation and user identity**
|
||||||
|
|
||||||
### 1. User Personas
|
### 1. User Personas ✅
|
||||||
- [ ] Create persona management UI (name, description, avatar)
|
- [x] Create persona management UI (name, description, avatar)
|
||||||
- [ ] Chat-level persona locking
|
- [x] Chat-level persona locking
|
||||||
- [ ] Character-level persona locking
|
- [x] Character-level persona locking
|
||||||
- [ ] Default persona setting
|
- [x] Default persona setting
|
||||||
- [ ] Quick persona switching
|
- [x] Quick persona switching
|
||||||
|
|
||||||
**Why Important:** Allows users to have multiple identities for different roleplay scenarios without manually changing their name and description each time.
|
**Why Important:** Allows users to have multiple identities for different roleplay scenarios without manually changing their name and description each time.
|
||||||
|
|
||||||
@@ -66,34 +77,35 @@ See "Phase 7: Polish & UX" section for details on UI improvements being prioriti
|
|||||||
|
|
||||||
**Why Important:** Visual representation of character emotions dramatically enhances immersion and makes conversations feel more alive.
|
**Why Important:** Visual representation of character emotions dramatically enhances immersion and makes conversations feel more alive.
|
||||||
|
|
||||||
### 3. Message Examples in Context
|
### 3. Message Examples in Context ✅
|
||||||
- [ ] Actually use mes_example field from character cards
|
- [x] Actually use mes_example field from character cards
|
||||||
- [ ] Format and inject into prompt properly
|
- [x] Format and inject into prompt properly
|
||||||
- [ ] Position control in context
|
- [x] Position control in context
|
||||||
- [ ] Token budget allocation for examples
|
- [x] Token budget allocation for examples
|
||||||
|
|
||||||
**Why Important:** Message examples help the AI understand the character's voice and writing style, leading to more accurate portrayals.
|
**Why Important:** Message examples help the AI understand the character's voice and writing style, leading to more accurate portrayals.
|
||||||
|
|
||||||
## Phase 3: Advanced Chat Management (Medium Priority)
|
## Phase 3: Advanced Chat Management (Medium Priority)
|
||||||
**Goal: Non-linear conversation control**
|
**Goal: Non-linear conversation control**
|
||||||
|
|
||||||
### 1. Chat Branching/Checkpoints
|
### 1. Chat Branching/Checkpoints ✅
|
||||||
- [ ] Save conversation state at any message
|
- [x] Save conversation state at any message
|
||||||
- [ ] Create branches from any point
|
- [x] Create branches from any point
|
||||||
- [ ] Switch between branches
|
- [x] Switch between branches
|
||||||
- [ ] Visual branch indicator in UI
|
- [x] Visual branch indicator in UI
|
||||||
- [ ] Branch naming and organization
|
- [x] Branch naming and organization
|
||||||
- [ ] Delete/merge branches
|
- [x] Delete branches
|
||||||
|
- [ ] Merge branches (deferred - nice to have)
|
||||||
|
|
||||||
**Why Important:** Roleplay often involves exploring "what if" scenarios. Branching lets you explore different conversation paths without losing previous progress.
|
**Why Important:** Roleplay often involves exploring "what if" scenarios. Branching lets you explore different conversation paths without losing previous progress.
|
||||||
|
|
||||||
### 2. Enhanced Message Controls
|
### 2. Enhanced Message Controls ✅
|
||||||
- [ ] Delete individual messages (not just clearing all)
|
- [x] Delete individual messages (not just clearing all)
|
||||||
- [ ] Regenerate any message (not just last)
|
- [x] Regenerate any message (not just last)
|
||||||
- [ ] Continue incomplete messages
|
- [x] Continue incomplete messages
|
||||||
- [ ] Message pinning (keep certain messages in context)
|
- [x] Message pinning (keep certain messages in context)
|
||||||
- [ ] Message folding/hiding
|
- [x] Message folding/hiding
|
||||||
- [ ] Bulk message operations
|
- [ ] Bulk message operations (deferred - nice to have)
|
||||||
|
|
||||||
**Why Important:** Fine-grained control over conversation history allows users to craft the perfect roleplay session.
|
**Why Important:** Fine-grained control over conversation history allows users to craft the perfect roleplay session.
|
||||||
|
|
||||||
@@ -140,14 +152,14 @@ See "Phase 7: Polish & UX" section for details on UI improvements being prioriti
|
|||||||
## Phase 5: Context & Token Management (Medium Priority)
|
## Phase 5: Context & Token Management (Medium Priority)
|
||||||
**Goal: Visibility and control over context usage**
|
**Goal: Visibility and control over context usage**
|
||||||
|
|
||||||
### 1. Token Counter
|
### 1. Token Counter ✅
|
||||||
- [ ] Real-time token count display
|
- [x] Real-time token count display
|
||||||
- [ ] Per-section breakdown (system, history, WI, etc.)
|
- [x] Per-section breakdown (system, history, WI, etc.)
|
||||||
- [ ] Visual context budget indicator
|
- [ ] Visual context budget indicator (deferred)
|
||||||
- [ ] Dotted line showing context cutoff in chat
|
- [ ] Dotted line showing context cutoff in chat (deferred)
|
||||||
- [ ] Warning when approaching limit
|
- [ ] Warning when approaching limit (deferred)
|
||||||
|
|
||||||
**Why Important:** Understanding what's in context and what's being cut is crucial for debugging issues and optimizing prompts.
|
**Why Important:** Understanding what's in context and what's being cut is crucial for debugging issues and optimizing prompts. Core functionality complete - visual enhancements can be added later.
|
||||||
|
|
||||||
### 2. Context Templates
|
### 2. Context Templates
|
||||||
- [ ] Customizable prompt assembly order
|
- [ ] Customizable prompt assembly order
|
||||||
@@ -190,13 +202,13 @@ See "Phase 7: Polish & UX" section for details on UI improvements being prioriti
|
|||||||
|
|
||||||
**Why Important:** Makes prompts and messages dynamic and reusable across different scenarios.
|
**Why Important:** Makes prompts and messages dynamic and reusable across different scenarios.
|
||||||
|
|
||||||
### 3. Regex Scripts
|
### 3. Regex Scripts ✅
|
||||||
- [ ] Global and character-scoped scripts
|
- [x] Global and character-scoped scripts
|
||||||
- [ ] Text transformation on messages
|
- [x] Text transformation on messages
|
||||||
- [ ] Auto-markdown formatting
|
- [x] Auto-markdown formatting
|
||||||
- [ ] Import/export regex presets
|
- [x] Import/export regex presets
|
||||||
- [ ] Regex testing interface
|
- [x] Regex testing interface
|
||||||
- [ ] Script priority/ordering
|
- [x] Script priority/ordering
|
||||||
|
|
||||||
**Why Important:** Allows automatic text formatting, correction, and enhancement without manual intervention.
|
**Why Important:** Allows automatic text formatting, correction, and enhancement without manual intervention.
|
||||||
|
|
||||||
@@ -224,8 +236,8 @@ See "Phase 7: Polish & UX" section for details on UI improvements being prioriti
|
|||||||
### 2. Export/Import Improvements
|
### 2. Export/Import Improvements
|
||||||
- [ ] Export chats as markdown
|
- [ ] Export chats as markdown
|
||||||
- [ ] Export chats as formatted text
|
- [ ] Export chats as formatted text
|
||||||
- [ ] Export chats as JSON with metadata
|
- [x] Export chats as JSON with metadata
|
||||||
- [ ] Import chats from other formats
|
- [x] Import chats from other formats
|
||||||
- [ ] Bulk character import
|
- [ ] Bulk character import
|
||||||
- [ ] Character pack support (multiple characters + lorebooks)
|
- [ ] Character pack support (multiple characters + lorebooks)
|
||||||
|
|
||||||
@@ -242,6 +254,151 @@ See "Phase 7: Polish & UX" section for details on UI improvements being prioriti
|
|||||||
|
|
||||||
**Why Important:** Better UI means less friction and more immersion in roleplay.
|
**Why Important:** Better UI means less friction and more immersion in roleplay.
|
||||||
|
|
||||||
|
## Phase 8: Quality of Life & Polish (High Priority)
|
||||||
|
**Goal: Reduce friction, improve feedback, and enhance overall user experience**
|
||||||
|
|
||||||
|
### 1. Toast Notification System
|
||||||
|
- [ ] Create toast component (bottom-right positioning)
|
||||||
|
- [ ] Success/error/info/warning variants
|
||||||
|
- [ ] Auto-dismiss with configurable timeout
|
||||||
|
- [ ] Queue multiple toasts
|
||||||
|
- [ ] Hook into all major actions (save, delete, import, export, etc.)
|
||||||
|
|
||||||
|
**Why Important:** Users currently have no immediate feedback when actions succeed or fail. Toasts provide instant visual confirmation without blocking workflow.
|
||||||
|
|
||||||
|
### 2. Command Palette
|
||||||
|
- [ ] Ctrl+P to open command palette modal
|
||||||
|
- [ ] Fuzzy search for all actions
|
||||||
|
- [ ] Keyboard navigation (arrow keys, enter, escape)
|
||||||
|
- [ ] Recent/frequent actions at top
|
||||||
|
- [ ] Show keyboard shortcuts in results
|
||||||
|
- [ ] Categories (Chat, Character, Settings, etc.)
|
||||||
|
|
||||||
|
**Why Important:** Power users want keyboard-first workflow. Command palette dramatically speeds up common actions without memorizing shortcuts.
|
||||||
|
|
||||||
|
### 3. Auto-save & Recovery
|
||||||
|
- [ ] Auto-save unsent message in input field
|
||||||
|
- [ ] Restore unsent message after app restart
|
||||||
|
- [ ] Draft system for in-progress edits
|
||||||
|
- [ ] Session recovery (restore scroll position, open panels)
|
||||||
|
- [ ] Crash recovery with last known state
|
||||||
|
|
||||||
|
**Why Important:** Losing work due to crashes or accidental closes is extremely frustrating. Auto-save provides a safety net for all user work.
|
||||||
|
|
||||||
|
### 4. Drag & Drop Support
|
||||||
|
- [ ] Drag character card PNGs to import
|
||||||
|
- [ ] Drag lorebook JSON files to import
|
||||||
|
- [ ] Drag chat history JSON to import
|
||||||
|
- [ ] Drag images to set as character avatar
|
||||||
|
- [ ] Drop zone overlay with visual feedback
|
||||||
|
- [ ] Support for multiple file drops
|
||||||
|
|
||||||
|
**Why Important:** Drag & drop feels natural and is much faster than navigate-click-select workflow. Modern desktop apps are expected to support this.
|
||||||
|
|
||||||
|
### 5. Search in Chat History
|
||||||
|
- [ ] Ctrl+F to open search bar
|
||||||
|
- [ ] Highlight all matches in messages
|
||||||
|
- [ ] Navigate between results (prev/next buttons)
|
||||||
|
- [ ] Case-insensitive search
|
||||||
|
- [ ] Search counter (e.g., "3 of 42 matches")
|
||||||
|
- [ ] Clear search and restore view
|
||||||
|
|
||||||
|
**Why Important:** Long roleplay sessions can span hundreds of messages. Finding specific content without search is tedious and time-consuming.
|
||||||
|
|
||||||
|
### 6. Context Menus (Right-Click)
|
||||||
|
- [ ] Right-click messages for actions (edit, delete, regenerate, branch, copy)
|
||||||
|
- [ ] Right-click character dropdown for quick actions
|
||||||
|
- [ ] Right-click World Info entries for edit/delete
|
||||||
|
- [ ] Right-click in message input for paste/clear/templates
|
||||||
|
- [ ] Context-aware menu items
|
||||||
|
|
||||||
|
**Why Important:** Right-click is muscle memory for desktop users. Faster than hovering to reveal action buttons.
|
||||||
|
|
||||||
|
### 7. Better Feedback & Confirmations
|
||||||
|
- [ ] Confirmation dialogs for destructive actions (delete character, clear chat)
|
||||||
|
- [ ] Loading spinners for API calls
|
||||||
|
- [ ] Progress bars for file imports
|
||||||
|
- [ ] "Saving..." / "Saved" indicators
|
||||||
|
- [ ] Success messages for completed actions
|
||||||
|
|
||||||
|
**Why Important:** Users should never wonder if an action succeeded or is still processing. Clear feedback prevents confusion and repeated clicks.
|
||||||
|
|
||||||
|
### 8. Undo/Redo System
|
||||||
|
- [ ] Undo message edit (Ctrl+Z)
|
||||||
|
- [ ] Undo message delete
|
||||||
|
- [ ] Undo character field changes
|
||||||
|
- [ ] Undo World Info changes
|
||||||
|
- [ ] Action history panel (optional)
|
||||||
|
- [ ] Redo support (Ctrl+Shift+Z)
|
||||||
|
|
||||||
|
**Why Important:** Mistakes happen. An undo system provides a safety net and encourages experimentation without fear of losing work.
|
||||||
|
|
||||||
|
### 9. Settings Search
|
||||||
|
- [ ] Search bar at top of settings panel
|
||||||
|
- [ ] Fuzzy search across all setting names and descriptions
|
||||||
|
- [ ] Highlight matching settings
|
||||||
|
- [ ] Collapse/expand sections based on matches
|
||||||
|
- [ ] "Recently changed" section
|
||||||
|
|
||||||
|
**Why Important:** With 22+ features, finding specific settings is tedious. Search makes configuration much faster.
|
||||||
|
|
||||||
|
### 10. Character Management Enhancements
|
||||||
|
- [ ] Recent characters quick-switch dropdown
|
||||||
|
- [ ] Character search/filter by name or tags
|
||||||
|
- [ ] Character folders/categories
|
||||||
|
- [ ] Duplicate character (as template)
|
||||||
|
- [ ] Favorite/star characters
|
||||||
|
- [ ] Sort options (name, date created, last used)
|
||||||
|
|
||||||
|
**Why Important:** Managing 10+ characters becomes messy. Better organization tools scale with user's character collection.
|
||||||
|
|
||||||
|
### 11. Enhanced Keyboard Support
|
||||||
|
- [ ] Full keyboard navigation in all modals (Tab, Arrow keys, Enter)
|
||||||
|
- [ ] Escape to close any open panel/modal
|
||||||
|
- [ ] Vim-style navigation mode (optional, j/k for scroll)
|
||||||
|
- [ ] Keyboard shortcut hints on hover
|
||||||
|
- [ ] Focus indicators for keyboard navigation
|
||||||
|
|
||||||
|
**Why Important:** Keyboard navigation should work everywhere. Current implementation is inconsistent across different UI sections.
|
||||||
|
|
||||||
|
### 12. Export/Share Enhancements
|
||||||
|
- [ ] Export conversation as formatted HTML
|
||||||
|
- [ ] Export conversation as formatted PDF
|
||||||
|
- [ ] Export as markdown with proper formatting
|
||||||
|
- [ ] Copy conversation to clipboard (formatted)
|
||||||
|
- [ ] Export individual messages
|
||||||
|
|
||||||
|
**Why Important:** Users want to share and archive conversations in readable formats, not just JSON.
|
||||||
|
|
||||||
|
### 13. Accessibility Improvements
|
||||||
|
- [ ] ARIA labels for all interactive elements
|
||||||
|
- [ ] Screen reader support
|
||||||
|
- [ ] High contrast mode option
|
||||||
|
- [ ] Larger click targets option (accessibility mode)
|
||||||
|
- [ ] Reduced motion mode (respect prefers-reduced-motion)
|
||||||
|
- [ ] Focus indicators for keyboard navigation
|
||||||
|
|
||||||
|
**Why Important:** Accessibility makes the app usable for everyone, including users with disabilities. It's also often legally required.
|
||||||
|
|
||||||
|
### 14. Better Visual Feedback
|
||||||
|
- [ ] Smooth transitions for panel open/close
|
||||||
|
- [ ] Hover states for all interactive elements
|
||||||
|
- [ ] Active state indicators (focused panel)
|
||||||
|
- [ ] Better empty states with helpful text
|
||||||
|
- [ ] Skeleton loaders for content loading
|
||||||
|
- [ ] Micro-animations for actions (delete, save, etc.)
|
||||||
|
|
||||||
|
**Why Important:** Visual polish makes the app feel responsive and professional. Small animations provide context for state changes.
|
||||||
|
|
||||||
|
### 15. Smart Defaults & Templates
|
||||||
|
- [ ] Scenario templates (fantasy RPG, sci-fi, modern, etc.)
|
||||||
|
- [ ] Pre-filled World Info templates
|
||||||
|
- [ ] Character card templates
|
||||||
|
- [ ] Quick-start wizard for new users
|
||||||
|
- [ ] Import from popular character repositories
|
||||||
|
|
||||||
|
**Why Important:** Reduces friction for new users and speeds up common tasks. Templates provide starting points for customization.
|
||||||
|
|
||||||
## Implementation Priority Ranking
|
## Implementation Priority Ranking
|
||||||
|
|
||||||
### Must-Have for Basic Roleplay:
|
### Must-Have for Basic Roleplay:
|
||||||
@@ -344,4 +501,4 @@ This roadmap is based on research into SillyTavern's features and best practices
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Last updated: 2025-10-14
|
Last updated: 2025-10-16
|
||||||
|
|||||||
61
src-tauri/Cargo.lock
generated
61
src-tauri/Cargo.lock
generated
@@ -270,6 +270,21 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-set"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||||
|
dependencies = [
|
||||||
|
"bit-vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
@@ -352,6 +367,17 @@ dependencies = [
|
|||||||
"alloc-stdlib",
|
"alloc-stdlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
@@ -502,8 +528,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1020,6 +1048,16 @@ dependencies = [
|
|||||||
"zune-inflate",
|
"zune-inflate",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fancy-regex"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7493d4c459da9f84325ad297371a6b2b8a162800873a22e3b6b6512e61d18c05"
|
||||||
|
dependencies = [
|
||||||
|
"bit-set",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@@ -3459,6 +3497,12 @@ version = "0.1.26"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -4194,6 +4238,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"image",
|
"image",
|
||||||
"png",
|
"png",
|
||||||
@@ -4205,6 +4250,7 @@ dependencies = [
|
|||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-dialog",
|
"tauri-plugin-dialog",
|
||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
|
"tiktoken-rs",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
@@ -4526,6 +4572,21 @@ dependencies = [
|
|||||||
"weezl",
|
"weezl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiktoken-rs"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c314e7ce51440f9e8f5a497394682a57b7c323d0f4d0a6b1b13c429056e0e234"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"base64 0.21.7",
|
||||||
|
"bstr",
|
||||||
|
"fancy-regex",
|
||||||
|
"lazy_static",
|
||||||
|
"parking_lot",
|
||||||
|
"rustc-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.44"
|
version = "0.3.44"
|
||||||
|
|||||||
@@ -32,4 +32,6 @@ png = "0.17"
|
|||||||
base64 = "0.21"
|
base64 = "0.21"
|
||||||
image = "0.24"
|
image = "0.24"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
chrono = "0.4"
|
||||||
|
tiktoken-rs = "0.5"
|
||||||
|
|
||||||
|
|||||||
1539
src-tauri/src/lib.rs
1539
src-tauri/src/lib.rs
File diff suppressed because it is too large
Load Diff
267
src/index.html
267
src/index.html
@@ -26,6 +26,9 @@
|
|||||||
<div class="avatar-circle"></div>
|
<div class="avatar-circle"></div>
|
||||||
<span id="character-header-name"></span>
|
<span id="character-header-name"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="feature-badges" class="feature-badges">
|
||||||
|
<!-- Feature badges will be added here dynamically -->
|
||||||
|
</div>
|
||||||
<div class="character-controls">
|
<div class="character-controls">
|
||||||
<div class="select-wrapper">
|
<div class="select-wrapper">
|
||||||
<select id="character-select" class="character-select"></select>
|
<select id="character-select" class="character-select"></select>
|
||||||
@@ -93,6 +96,7 @@
|
|||||||
<button class="roleplay-tab-btn active" data-tab="worldinfo">World Info</button>
|
<button class="roleplay-tab-btn active" data-tab="worldinfo">World Info</button>
|
||||||
<button class="roleplay-tab-btn" data-tab="authorsnote">Author's Note</button>
|
<button class="roleplay-tab-btn" data-tab="authorsnote">Author's Note</button>
|
||||||
<button class="roleplay-tab-btn" data-tab="persona">Persona</button>
|
<button class="roleplay-tab-btn" data-tab="persona">Persona</button>
|
||||||
|
<button class="roleplay-tab-btn" data-tab="presets">Prompt Preset</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="worldinfo-tab" class="roleplay-tab-content active">
|
<div id="worldinfo-tab" class="roleplay-tab-content active">
|
||||||
@@ -102,6 +106,18 @@
|
|||||||
<p style="color: var(--text-secondary); font-size: 12px; margin-bottom: 12px;">
|
<p style="color: var(--text-secondary); font-size: 12px; margin-bottom: 12px;">
|
||||||
Create entries that inject context when keywords are mentioned.
|
Create entries that inject context when keywords are mentioned.
|
||||||
</p>
|
</p>
|
||||||
|
<label for="recursion-depth" style="font-size: 13px; margin-top: 8px;">Recursion Depth</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="recursion-depth"
|
||||||
|
min="0"
|
||||||
|
max="10"
|
||||||
|
value="3"
|
||||||
|
style="width: 80px; margin-bottom: 8px;"
|
||||||
|
/>
|
||||||
|
<p style="color: var(--text-secondary); font-size: 11px; margin-bottom: 12px;">
|
||||||
|
Maximum depth for cascading World Info activation. When a World Info entry is triggered, its content is scanned for additional keywords up to this depth. (Default: 3)
|
||||||
|
</p>
|
||||||
<button type="button" id="add-worldinfo-btn" class="btn-secondary" style="width: 100%;">
|
<button type="button" id="add-worldinfo-btn" class="btn-secondary" style="width: 100%;">
|
||||||
+ Add Entry
|
+ Add Entry
|
||||||
</button>
|
</button>
|
||||||
@@ -124,6 +140,15 @@
|
|||||||
placeholder="Write in present tense. Focus on sensory details..."
|
placeholder="Write in present tense. Focus on sensory details..."
|
||||||
rows="6"
|
rows="6"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
<div style="background: var(--bg-secondary); padding: 8px; border-radius: 4px; margin-top: 8px;">
|
||||||
|
<p style="color: var(--text-secondary); font-size: 11px; margin: 0 0 4px 0; font-weight: 500;">Template Variables:</p>
|
||||||
|
<p style="color: var(--text-secondary); font-size: 11px; margin: 0; font-family: monospace;">
|
||||||
|
{{char}} - Character name<br/>
|
||||||
|
{{user}} - User/Persona name<br/>
|
||||||
|
{{date}} - Current date (YYYY-MM-DD)<br/>
|
||||||
|
{{time}} - Current time (HH:MM)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>
|
<label>
|
||||||
@@ -131,9 +156,34 @@
|
|||||||
Enable Author's Note
|
Enable Author's Note
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="save-authors-note-btn" class="btn-primary" style="width: 100%;">
|
<button type="button" id="save-authors-note-btn" class="btn-primary" style="width: 100%; margin-bottom: 20px;">
|
||||||
Save Author's Note
|
Save Author's Note
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- Message Examples Section -->
|
||||||
|
<div class="form-group" style="border-top: 1px solid var(--border); padding-top: 16px;">
|
||||||
|
<label>Message Examples</label>
|
||||||
|
<p style="color: var(--text-secondary); font-size: 12px; margin-bottom: 8px;">
|
||||||
|
Use character card's message examples to teach the AI the character's voice and style.
|
||||||
|
</p>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="examples-enabled" />
|
||||||
|
Enable Message Examples
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="examples-position">Examples Position</label>
|
||||||
|
<select id="examples-position" style="width: 100%;">
|
||||||
|
<option value="after_system">After System Prompt (Recommended)</option>
|
||||||
|
<option value="before_history">Before Message History</option>
|
||||||
|
</select>
|
||||||
|
<p style="color: var(--text-secondary); font-size: 11px; margin-top: 4px;">
|
||||||
|
Where to inject examples in the context. After system prompt works best for most models.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="save-examples-btn" class="btn-primary" style="width: 100%;">
|
||||||
|
Save Examples Settings
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -169,6 +219,85 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="presets-tab" class="roleplay-tab-content">
|
||||||
|
<div class="roleplay-content">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="preset-select">Prompt Preset</label>
|
||||||
|
<p style="color: var(--text-secondary); font-size: 12px; margin-bottom: 8px;">
|
||||||
|
Choose a preset to apply specialized prompting strategies for different use cases.
|
||||||
|
</p>
|
||||||
|
<select id="preset-select" style="width: 100%; margin-bottom: 12px;">
|
||||||
|
<option value="">No Preset</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Preset Info/Editor -->
|
||||||
|
<div id="preset-info" style="display: none; background: var(--bg-secondary); padding: 12px; border-radius: 6px; margin-bottom: 16px;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||||
|
<div style="font-weight: 500; color: var(--text-primary);">
|
||||||
|
<span id="preset-name"></span>
|
||||||
|
<span id="preset-builtin-badge" style="display: none; font-size: 10px; color: var(--text-secondary); margin-left: 8px; padding: 2px 6px; background: var(--bg-primary); border-radius: 3px;">Built-in</span>
|
||||||
|
<span id="preset-modified-badge" style="display: none; font-size: 10px; color: var(--accent); margin-left: 8px; padding: 2px 6px; background: var(--bg-primary); border-radius: 3px;">Modified</span>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 4px;">
|
||||||
|
<button type="button" id="restore-preset-btn" class="worldinfo-btn" style="display: none; font-size: 11px; padding: 4px 8px;">Restore to Default</button>
|
||||||
|
<button type="button" id="duplicate-preset-btn" class="worldinfo-btn" style="display: none; font-size: 11px; padding: 4px 8px;">Duplicate</button>
|
||||||
|
<button type="button" id="delete-preset-btn" class="worldinfo-btn worldinfo-btn-danger" style="display: none; font-size: 11px; padding: 4px 8px;">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p id="preset-description" style="color: var(--text-secondary); font-size: 12px; margin-bottom: 12px;"></p>
|
||||||
|
|
||||||
|
<!-- System Additions (Read-only preview for built-in, editable for custom) -->
|
||||||
|
<div id="preset-system-section" style="margin-bottom: 12px;">
|
||||||
|
<div style="font-size: 11px; color: var(--text-secondary); margin-bottom: 4px;">
|
||||||
|
<strong>System Additions:</strong>
|
||||||
|
</div>
|
||||||
|
<div id="preset-system-readonly" style="display: none; background: var(--bg-primary); padding: 8px; border-radius: 4px; font-size: 11px; white-space: pre-wrap;"></div>
|
||||||
|
<textarea id="preset-system-editable" style="display: none; width: 100%; min-height: 60px; font-size: 11px;" placeholder="Additional text to prepend to system prompt..."></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Instruction Blocks Editor -->
|
||||||
|
<div id="preset-instructions-section">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||||
|
<div style="font-size: 11px; color: var(--text-secondary);">
|
||||||
|
<strong>Instruction Blocks:</strong>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="add-instruction-btn" class="worldinfo-btn" style="display: none; font-size: 11px; padding: 4px 8px;">+ Add Block</button>
|
||||||
|
</div>
|
||||||
|
<div id="preset-instructions-list" style="background: var(--bg-primary); padding: 8px; border-radius: 4px; margin-bottom: 12px;">
|
||||||
|
<!-- Instructions will be listed here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Author's Note Default -->
|
||||||
|
<div id="preset-authors-note-section" style="margin-bottom: 12px;">
|
||||||
|
<div style="font-size: 11px; color: var(--text-secondary); margin-bottom: 4px;">
|
||||||
|
<strong>Default Author's Note:</strong>
|
||||||
|
</div>
|
||||||
|
<div id="preset-authors-note-readonly" style="display: none; background: var(--bg-primary); padding: 8px; border-radius: 4px; font-size: 11px; white-space: pre-wrap;"></div>
|
||||||
|
<textarea id="preset-authors-note-editable" style="display: none; width: 100%; min-height: 60px; font-size: 11px;" placeholder="Default Author's Note if user hasn't set one..."></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Save Changes Button (only for custom presets) -->
|
||||||
|
<button type="button" id="save-preset-changes-btn" class="btn-secondary" style="display: none; width: 100%; margin-bottom: 8px;">
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" id="apply-preset-btn" class="btn-primary" style="width: 100%; margin-bottom: 8px;" disabled>
|
||||||
|
Apply Preset
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" id="create-preset-btn" class="btn-secondary" style="width: 100%;">
|
||||||
|
Create Custom Preset
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p style="color: var(--text-secondary); font-size: 11px; margin-top: 12px; padding: 8px; background: var(--bg-secondary); border-radius: 4px;">
|
||||||
|
<strong>Note:</strong> Custom presets will be stored in ~/.config/claudia/presets/ and will be available across all characters.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Settings overlay backdrop -->
|
<!-- Settings overlay backdrop -->
|
||||||
@@ -224,6 +353,19 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="context-limit">Context Limit (tokens)</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="context-limit"
|
||||||
|
placeholder="200000"
|
||||||
|
value="200000"
|
||||||
|
min="1000"
|
||||||
|
step="1000"
|
||||||
|
/>
|
||||||
|
<small style="color: var(--text-secondary); margin-top: 4px; display: block;">Maximum tokens for model context (e.g., 200000 for Claude)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="stream-toggle" />
|
<input type="checkbox" id="stream-toggle" />
|
||||||
@@ -536,10 +678,88 @@
|
|||||||
</form>
|
</form>
|
||||||
<div class="status-bar">
|
<div class="status-bar">
|
||||||
<span id="status-text" class="status-text">Ready</span>
|
<span id="status-text" class="status-text">Ready</span>
|
||||||
|
<div id="token-counter" class="token-counter">
|
||||||
|
<span id="token-count-total" class="token-count">0 tokens</span>
|
||||||
|
<button id="token-details-btn" class="token-details-btn" title="Show breakdown">
|
||||||
|
<svg width="12" height="12" viewBox="0 0 16 16" fill="none">
|
||||||
|
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/>
|
||||||
|
<path d="M8 7v4M8 5h.01" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Token Breakdown Tooltip -->
|
||||||
|
<div id="token-breakdown" class="token-breakdown" style="display: none;">
|
||||||
|
<div class="token-breakdown-header">Token Breakdown</div>
|
||||||
|
<div class="token-breakdown-list">
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>System Prompt:</span>
|
||||||
|
<span id="token-system">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>Preset Instructions:</span>
|
||||||
|
<span id="token-preset">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>Persona:</span>
|
||||||
|
<span id="token-persona">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>World Info:</span>
|
||||||
|
<span id="token-worldinfo">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>Author's Note:</span>
|
||||||
|
<span id="token-authorsnote">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>Message Examples:</span>
|
||||||
|
<span id="token-examples">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>Message History:</span>
|
||||||
|
<span id="token-history">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-item">
|
||||||
|
<span>Current Input:</span>
|
||||||
|
<span id="token-input">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="token-breakdown-total">
|
||||||
|
<span>Total:</span>
|
||||||
|
<span id="token-total-detail">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Toast Container -->
|
||||||
|
<div id="toast-container" class="toast-container"></div>
|
||||||
|
|
||||||
|
<!-- Command Palette -->
|
||||||
|
<div id="command-palette-modal" class="command-palette-modal" style="display: none;">
|
||||||
|
<div class="command-palette-overlay"></div>
|
||||||
|
<div class="command-palette-content">
|
||||||
|
<div class="command-palette-search">
|
||||||
|
<svg class="command-palette-search-icon" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||||
|
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2"/>
|
||||||
|
<path d="M12.5 12.5L17 17" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="command-palette-input"
|
||||||
|
class="command-palette-input"
|
||||||
|
placeholder="Type a command or search..."
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
<kbd class="command-palette-hint">Esc to close</kbd>
|
||||||
|
</div>
|
||||||
|
<div id="command-palette-results" class="command-palette-results">
|
||||||
|
<!-- Command results will be dynamically populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Avatar zoom modal -->
|
<!-- Avatar zoom modal -->
|
||||||
<div id="avatar-modal" class="avatar-modal" style="display: none;">
|
<div id="avatar-modal" class="avatar-modal" style="display: none;">
|
||||||
<div class="avatar-modal-overlay"></div>
|
<div class="avatar-modal-overlay"></div>
|
||||||
@@ -547,5 +767,50 @@
|
|||||||
<img id="avatar-modal-img" src="" alt="Avatar" />
|
<img id="avatar-modal-img" src="" alt="Avatar" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- New Character modal -->
|
||||||
|
<div id="new-character-modal" class="new-character-modal" style="display: none;">
|
||||||
|
<div class="new-character-overlay"></div>
|
||||||
|
<div class="new-character-content">
|
||||||
|
<div class="new-character-header">
|
||||||
|
<h3>Create New Character</h3>
|
||||||
|
<button id="close-new-character-btn" class="icon-btn">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
|
<line x1="4" y1="4" x2="12" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<line x1="12" y1="4" x2="4" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form id="new-character-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="new-character-name">Character Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="new-character-name"
|
||||||
|
placeholder="Enter a name for the new character"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="new-character-system-prompt">System Prompt</label>
|
||||||
|
<textarea
|
||||||
|
id="new-character-system-prompt"
|
||||||
|
placeholder="You are a helpful AI assistant..."
|
||||||
|
rows="6"
|
||||||
|
required
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="new-character-actions">
|
||||||
|
<button type="button" id="cancel-new-character-btn" class="btn-secondary">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn-primary">
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
2582
src/main.js
2582
src/main.js
File diff suppressed because it is too large
Load Diff
914
src/styles.css
914
src/styles.css
@@ -172,6 +172,190 @@ body {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Feature Badges */
|
||||||
|
.feature-badges {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
background: rgba(99, 102, 241, 0.1);
|
||||||
|
border: 1px solid rgba(99, 102, 241, 0.3);
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--accent);
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-badge:hover {
|
||||||
|
background: rgba(99, 102, 241, 0.15);
|
||||||
|
border-color: rgba(99, 102, 241, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-badge-icon {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-badge-count {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Branch Badge */
|
||||||
|
.branch-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
background: rgba(34, 197, 94, 0.1);
|
||||||
|
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #22c55e;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-badge:hover {
|
||||||
|
background: rgba(34, 197, 94, 0.15);
|
||||||
|
border-color: rgba(34, 197, 94, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-badge svg {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Branch Manager Modal */
|
||||||
|
.branch-manager-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 10002;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-manager-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
-webkit-backdrop-filter: blur(4px);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-manager-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
max-height: 80vh;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-manager-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-manager-header h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 60vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-item {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-item:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-item.active {
|
||||||
|
background: rgba(99, 102, 241, 0.1);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-meta {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-active-label {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-btn {
|
.icon-btn {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
@@ -276,10 +460,10 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message.user .message-content {
|
.message.user .message-content {
|
||||||
background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
|
background: linear-gradient(135deg, var(--accent) 0%, var(--user-msg) 100%);
|
||||||
color: white;
|
color: white;
|
||||||
border-bottom-right-radius: 4px;
|
border-bottom-right-radius: 4px;
|
||||||
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
|
box-shadow: 0 2px 8px color-mix(in srgb, var(--user-msg) 30%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.assistant .message-content {
|
.message.assistant .message-content {
|
||||||
@@ -332,19 +516,24 @@ body {
|
|||||||
/* Message action buttons */
|
/* Message action buttons */
|
||||||
.message-actions {
|
.message-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: -4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 8px;
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.user .message-actions {
|
.message.user .message-actions {
|
||||||
right: 8px;
|
right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.assistant .message-actions {
|
.message.assistant .message-actions {
|
||||||
right: 8px;
|
right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message:hover .message-actions {
|
.message:hover .message-actions {
|
||||||
@@ -352,26 +541,28 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message-action-btn {
|
.message-action-btn {
|
||||||
width: 24px;
|
width: 28px;
|
||||||
height: 24px;
|
height: 28px;
|
||||||
border: none;
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.85);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(8px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
color: var(--text-secondary);
|
color: rgba(255, 255, 255, 0.8);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-action-btn:hover {
|
.message-action-btn:hover {
|
||||||
background: rgba(0, 0, 0, 0.7);
|
background: rgba(0, 0, 0, 0.95);
|
||||||
color: var(--text-primary);
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
transform: scale(1.1);
|
color: white;
|
||||||
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-action-btn:active {
|
.message-action-btn:active {
|
||||||
@@ -379,8 +570,58 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message-action-btn svg {
|
.message-action-btn svg {
|
||||||
width: 14px;
|
width: 16px;
|
||||||
height: 14px;
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced message control buttons */
|
||||||
|
.message-delete-btn:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.8) !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-pin-btn.active,
|
||||||
|
.message-hide-btn.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-pin-btn.active:hover,
|
||||||
|
.message-hide-btn.active:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pinned message indicator */
|
||||||
|
.message.pinned::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -8px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 3px;
|
||||||
|
background: var(--accent);
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 0 8px var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.pinned .message-content {
|
||||||
|
border-left: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hidden message styling */
|
||||||
|
.message.hidden-message {
|
||||||
|
opacity: 0.4;
|
||||||
|
filter: blur(2px);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.hidden-message:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
filter: blur(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.hidden-message .message-actions {
|
||||||
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Swipe navigation */
|
/* Swipe navigation */
|
||||||
@@ -761,6 +1002,97 @@ body {
|
|||||||
color: #22c55e;
|
color: #22c55e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Token Counter */
|
||||||
|
.token-counter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-count {
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-details-btn {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-details-btn:hover {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-breakdown {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 52px;
|
||||||
|
right: 16px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 100;
|
||||||
|
min-width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-breakdown-header {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-breakdown-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-breakdown-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-breakdown-item span:first-child {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-breakdown-item span:last-child {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-breakdown-total {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent);
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -1120,6 +1452,24 @@ body {
|
|||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix theming for textareas with inline styles */
|
||||||
|
#preset-system-editable,
|
||||||
|
#preset-authors-note-editable {
|
||||||
|
background: var(--bg-tertiary) !important;
|
||||||
|
border: 1px solid var(--border) !important;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
font-family: inherit;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preset-system-editable:focus,
|
||||||
|
#preset-authors-note-editable:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-group select {
|
.form-group select {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
@@ -1252,6 +1602,69 @@ body {
|
|||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* New Character Modal */
|
||||||
|
.new-character-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 10001;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-character-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
-webkit-backdrop-filter: blur(4px);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-character-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 500px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-character-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-character-header h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-character-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-character-actions button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Theme Preview */
|
/* Theme Preview */
|
||||||
.theme-preview-container {
|
.theme-preview-container {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
@@ -1696,6 +2109,41 @@ body.view-comfortable .message-content pre {
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.worldinfo-edit-form,
|
||||||
|
.worldinfo-inline-edit {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 2px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worldinfo-edit-form input,
|
||||||
|
.worldinfo-edit-form textarea,
|
||||||
|
.worldinfo-inline-edit input,
|
||||||
|
.worldinfo-inline-edit textarea {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worldinfo-edit-form input:focus,
|
||||||
|
.worldinfo-edit-form textarea:focus,
|
||||||
|
.worldinfo-inline-edit input:focus,
|
||||||
|
.worldinfo-inline-edit textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.worldinfo-edit-form textarea,
|
||||||
|
.worldinfo-inline-edit textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
.header-left-controls {
|
.header-left-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -1733,3 +2181,435 @@ body.view-comfortable .message-content pre {
|
|||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Toast Notification System */
|
||||||
|
.toast-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 10003;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
pointer-events: none;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 400px;
|
||||||
|
pointer-events: auto;
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
animation: slideInRight 0.3s ease;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.removing {
|
||||||
|
animation: slideOutRight 0.3s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
transform: translateX(400px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideOutRight {
|
||||||
|
from {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(400px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-message {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-close {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
padding: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-close:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toast Variants */
|
||||||
|
.toast.success {
|
||||||
|
border-color: rgba(34, 197, 94, 0.5);
|
||||||
|
background: rgba(34, 197, 94, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.success .toast-icon {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.success .toast-title {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.error {
|
||||||
|
border-color: rgba(239, 68, 68, 0.5);
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.error .toast-icon {
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.error .toast-title {
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.warning {
|
||||||
|
border-color: rgba(249, 115, 22, 0.5);
|
||||||
|
background: rgba(249, 115, 22, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.warning .toast-icon {
|
||||||
|
color: #f97316;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.warning .toast-title {
|
||||||
|
color: #f97316;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.info {
|
||||||
|
border-color: rgba(99, 102, 241, 0.5);
|
||||||
|
background: rgba(99, 102, 241, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.info .toast-icon {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.info .toast-title {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress bar for auto-dismiss */
|
||||||
|
.toast-progress {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 0 0 12px 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: currentColor;
|
||||||
|
animation: progressShrink var(--duration) linear forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progressShrink {
|
||||||
|
from {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive toast positioning */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.toast-container {
|
||||||
|
left: 12px;
|
||||||
|
right: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Command Palette */
|
||||||
|
.command-palette-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 10004;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 15vh;
|
||||||
|
animation: fadeIn 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
max-height: 60vh;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: slideInDown 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInDown {
|
||||||
|
from {
|
||||||
|
transform: translateY(-20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-search {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-search-icon {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-input {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: inherit;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-input::placeholder {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-hint {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-results {
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 50vh;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-results::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-results::-webkit-scrollbar-track {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-results::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item:hover {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item.selected {
|
||||||
|
background: rgba(99, 102, 241, 0.1);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
color: var(--accent);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin: 0 0 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-description {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-shortcut {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-shortcut kbd {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-empty {
|
||||||
|
padding: 40px 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-empty-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin: 0 auto 12px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-empty-text {
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-section {
|
||||||
|
padding: 8px 12px 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-section:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive command palette */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.command-palette-modal {
|
||||||
|
padding-top: 10vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-palette-content {
|
||||||
|
width: 95%;
|
||||||
|
max-height: 70vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item-shortcut {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user