Spaces:
Running
Running
Upload 25 files
Browse files- .gitattributes +1 -0
- CHANGELOG.md +400 -0
- Launch-Local-App.bat +34 -0
- elysia-markdown-studio-og.jpg +3 -0
- fonts/css2.css +45 -0
- fonts/latin/jetbrainsmono-300.woff2 +0 -0
- fonts/latin/jetbrainsmono-400.woff2 +0 -0
- fonts/latin/jetbrainsmono-700.woff2 +0 -0
- fonts/latin/spacegrotesk-400.woff2 +0 -0
- fonts/latin/spacegrotesk-700.woff2 +0 -0
- index.html +398 -19
- libs/dexie.min.js +2 -0
- libs/marked.min.js +69 -0
- libs/turndown.min.js +8 -0
- scripts/ai-tools.js +463 -0
- scripts/api.js +226 -0
- scripts/app.js +470 -0
- scripts/db.js +290 -0
- scripts/documents.js +253 -0
- scripts/editor.js +337 -0
- scripts/export.js +347 -0
- scripts/preview.js +191 -0
- scripts/templates.js +335 -0
- scripts/utils.js +261 -0
- styles/drag-drop.css +116 -0
- styles/main.css +1673 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
elysia-markdown-studio-og.jpg filter=lfs diff=lfs merge=lfs -text
|
CHANGELOG.md
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 💎 ELYSIA MARKDOWN STUDIO - CHANGELOG
|
| 2 |
+
|
| 3 |
+
All notable changes to this project will be documented in this file.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## [1.3.0] - 2025-12-14 - Production Ready Release 🚀✨
|
| 8 |
+
|
| 9 |
+
### 🆕 New Features
|
| 10 |
+
|
| 11 |
+
**SEO & Social Sharing:**
|
| 12 |
+
|
| 13 |
+
- ✨ **NEW** - Complete meta tags for SEO (description, keywords, author)
|
| 14 |
+
- ✨ **NEW** - Open Graph tags for beautiful social media previews
|
| 15 |
+
- ✨ **NEW** - Twitter Card support
|
| 16 |
+
- ✨ **NEW** - Theme color meta tag (#667eea)
|
| 17 |
+
|
| 18 |
+
**Accessibility & UX:**
|
| 19 |
+
|
| 20 |
+
- ✨ **NEW** - Favicon (💎 diamond emoji SVG)
|
| 21 |
+
- ✨ **NEW** - Noscript fallback for users with JavaScript disabled
|
| 22 |
+
- ✨ **NEW** - Complete GitHub Light theme for preview pane
|
| 23 |
+
- ✨ **NEW** - ARIA labels on all interactive elements (buttons, inputs, regions)
|
| 24 |
+
- ✨ **NEW** - Role attributes for better screen reader support
|
| 25 |
+
- ✨ **NEW** - Proper document structure with banner, toolbar, and complementary roles
|
| 26 |
+
|
| 27 |
+
### 🎨 UI/UX Improvements
|
| 28 |
+
|
| 29 |
+
**Preview Themes:**
|
| 30 |
+
|
| 31 |
+
- ✅ **IMPROVED** - Full GitHub Light theme with proper colors, borders, and table styling
|
| 32 |
+
- ✅ **IMPROVED** - Theme switching now fully functional
|
| 33 |
+
|
| 34 |
+
### ♿ Accessibility (WCAG 2.1)
|
| 35 |
+
|
| 36 |
+
- All header buttons have descriptive aria-labels
|
| 37 |
+
- Toast container marked as alert with aria-live
|
| 38 |
+
- Sidebar marked as complementary landmark
|
| 39 |
+
- Editor and preview panes have proper region roles
|
| 40 |
+
- Decorative emojis marked with aria-hidden
|
| 41 |
+
|
| 42 |
+
### 🔧 Technical Improvements
|
| 43 |
+
|
| 44 |
+
- ♻️ Version bump to v1.3.0 across all files
|
| 45 |
+
- ♻️ Better HTML structure with proper meta tags
|
| 46 |
+
- ♻️ Production-ready for deployment to elysia-suite.com
|
| 47 |
+
|
| 48 |
+
### 📋 Files Changed
|
| 49 |
+
|
| 50 |
+
- `index.html` - Added SEO meta tags, Open Graph, favicon, noscript, ARIA accessibility
|
| 51 |
+
- `styles/main.css` - Added GitHub Light theme (~80 lines)
|
| 52 |
+
- `scripts/app.js` - Updated version number
|
| 53 |
+
- `README.md` - Updated version badge
|
| 54 |
+
- `CHANGELOG.md` - Added v1.3.0 release notes
|
| 55 |
+
|
| 56 |
+
---
|
| 57 |
+
|
| 58 |
+
## [1.2.1] - 2025-11-23 - Critical Hotfixes 🔧
|
| 59 |
+
|
| 60 |
+
### 🐛 Critical Bug Fixes
|
| 61 |
+
|
| 62 |
+
**Marked.js v15 Compatibility:**
|
| 63 |
+
|
| 64 |
+
- ✅ **FIXED** - Updated renderer for Marked.js v15.0.12 (token-based API)
|
| 65 |
+
- ✅ **FIXED** - `listitem(token)` now uses `token.task`, `token.checked`, `token.text`
|
| 66 |
+
- ✅ **FIXED** - Code highlighting moved to `code(token)` renderer
|
| 67 |
+
- ✅ **FIXED** - `marked.parse()` now async with `await`
|
| 68 |
+
- ✅ **FIXED** - Preview no longer displays `[object Object]`
|
| 69 |
+
|
| 70 |
+
**Database:**
|
| 71 |
+
|
| 72 |
+
- ✅ **FIXED** - DB.searchDocuments() duplicate code block removed (syntax error line 165)
|
| 73 |
+
- ✅ **FIXED** - Search now properly sorts by updatedAt
|
| 74 |
+
|
| 75 |
+
**UI/UX:**
|
| 76 |
+
|
| 77 |
+
- ✅ **FIXED** - Toast close button (×) now has proper hover styles
|
| 78 |
+
- ❌ **REMOVED** - Distraction-free mode (was buggy, removed completely)
|
| 79 |
+
- Removed button 🎯 from header
|
| 80 |
+
- Removed F11/Esc keyboard shortcuts
|
| 81 |
+
- Removed all `body.distraction-free` CSS (~50 lines)
|
| 82 |
+
- Removed `toggleFullscreen()` function
|
| 83 |
+
|
| 84 |
+
### 📦 Dependency Updates
|
| 85 |
+
|
| 86 |
+
- **Marked.js**: v11 → v15.0.12 (local lib)
|
| 87 |
+
- **Mermaid**: v10.6.1 → v11.4.0
|
| 88 |
+
|
| 89 |
+
### 🎨 Code Quality
|
| 90 |
+
|
| 91 |
+
- ♻️ Cleaner codebase (removed dead feature)
|
| 92 |
+
- ♻️ Better error handling in preview rendering
|
| 93 |
+
- ♻️ Type safety checks for marked.parse() output
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
## [1.2.0] - 2025-11-23 - MEGA AUDIT UPDATE 🚀✨
|
| 98 |
+
|
| 99 |
+
### 🐛 Critical Bug Fixes
|
| 100 |
+
|
| 101 |
+
**Core Functionality:**
|
| 102 |
+
|
| 103 |
+
- ✅ **FIXED** - Preview.getHTML() now includes null check for export safety
|
| 104 |
+
- ✅ **FIXED** - Templates.getTemplate() now properly async, searches custom templates too
|
| 105 |
+
- ✅ **FIXED** - DB.searchDocuments() completed with full search logic (title + content + tags)
|
| 106 |
+
- ✅ **FIXED** - Keyboard shortcut Ctrl+N now opens template selector (not blank doc)
|
| 107 |
+
- ✅ **FIXED** - Export modal validation handles empty content properly
|
| 108 |
+
|
| 109 |
+
### ✨ NEW FEATURES
|
| 110 |
+
|
| 111 |
+
**Image Handling:**
|
| 112 |
+
|
| 113 |
+
- 🖼️ **NEW** - Drag & drop images directly into editor (base64 conversion)
|
| 114 |
+
- 📋 **NEW** - Paste images from clipboard (Ctrl+V works!)
|
| 115 |
+
- 🎨 **NEW** - Visual feedback with blue glow when dragging images
|
| 116 |
+
|
| 117 |
+
**Document Management:**
|
| 118 |
+
|
| 119 |
+
- 📊 **NEW** - Reading time estimate in header stats (based on 200 wpm)
|
| 120 |
+
- 🔍 **NEW** - Duplicate detection before creating new documents
|
| 121 |
+
- 📋 **NEW** - "Copy to Clipboard" quick export option
|
| 122 |
+
- ⚠️ **NEW** - Content validation for AI tools (50-100 char minimum)
|
| 123 |
+
|
| 124 |
+
**AI Tools Enhancements:**
|
| 125 |
+
|
| 126 |
+
- 💡 **NEW** - "Improve Writing" offers choice: replace or create new doc
|
| 127 |
+
- 🎨 **NEW** - Beautiful spinning loading toast during AI operations
|
| 128 |
+
- 🚀 **NEW** - Better error messages with actionable guidance
|
| 129 |
+
|
| 130 |
+
### 🎨 UI/UX Improvements
|
| 131 |
+
|
| 132 |
+
**Visual Polish:**
|
| 133 |
+
|
| 134 |
+
- ✨ Context menu positioning prevents viewport overflow
|
| 135 |
+
- ✨ Drag-over state styling for editor (blue border + glow)
|
| 136 |
+
- ✨ Better empty state messages ("Add some content before saving!")
|
| 137 |
+
- ✨ Live preview toggle actually works now (enable/disable)
|
| 138 |
+
|
| 139 |
+
**User Feedback:**
|
| 140 |
+
|
| 141 |
+
- ✅ More helpful error messages for AI tools
|
| 142 |
+
- ✅ Loading states with spinning icons
|
| 143 |
+
- ✅ Confirmation dialogs for destructive actions
|
| 144 |
+
|
| 145 |
+
### 🔧 Technical Improvements
|
| 146 |
+
|
| 147 |
+
**Dependencies:**
|
| 148 |
+
|
| 149 |
+
- 📦 Mermaid upgraded: v10.6.1 → v11.4.0 (latest)
|
| 150 |
+
- 📦 Better async/await handling throughout codebase
|
| 151 |
+
|
| 152 |
+
**Architecture:**
|
| 153 |
+
|
| 154 |
+
- 🏗️ New CSS file: `styles/drag-drop.css` for image features
|
| 155 |
+
- 🏗️ Fixed race conditions in template loading
|
| 156 |
+
- 🏗️ Improved search includes tags now
|
| 157 |
+
|
| 158 |
+
**Code Quality:**
|
| 159 |
+
|
| 160 |
+
- ♻️ Refactored image insertion logic (DRY principle)
|
| 161 |
+
- ♻️ Better error recovery in AI tools
|
| 162 |
+
- ♻️ Utils.readingTime() helper added
|
| 163 |
+
|
| 164 |
+
### 📚 Documentation
|
| 165 |
+
|
| 166 |
+
- 📖 Updated README with all new features
|
| 167 |
+
- 📖 Documented drag & drop capabilities
|
| 168 |
+
- 📖 Added reading time feature to docs
|
| 169 |
+
|
| 170 |
+
---
|
| 171 |
+
|
| 172 |
+
## [1.0.2] - 2025-11-22 - Syntax Errors Hotfix 🔧
|
| 173 |
+
|
| 174 |
+
### 🐛 Critical Syntax Fixes
|
| 175 |
+
|
| 176 |
+
**documents.js:**
|
| 177 |
+
|
| 178 |
+
- ✅ **FIXED** - Invalid backslash escapes in `querySelectorAll()` (line 155)
|
| 179 |
+
- ✅ **FIXED** - Literal `\n` characters instead of actual line breaks (line 189)
|
| 180 |
+
|
| 181 |
+
**export.js:**
|
| 182 |
+
|
| 183 |
+
- ✅ **FIXED** - Duplicate HTML code outside template literal (line 244+)
|
| 184 |
+
- ✅ **FIXED** - Orphaned CSS causing "Unexpected token" error
|
| 185 |
+
|
| 186 |
+
**Impact:** Application now loads without console errors ✨
|
| 187 |
+
|
| 188 |
+
---
|
| 189 |
+
|
| 190 |
+
## [1.0.1] - 2025-11-22 - AUDIT & CRITICAL FIXES 🔧
|
| 191 |
+
|
| 192 |
+
### 🐛 Critical Bug Fixes
|
| 193 |
+
|
| 194 |
+
**Preview & Export:**
|
| 195 |
+
|
| 196 |
+
- ✅ **FIXED** - Added missing `getHTML()` method in preview.js (export was broken)
|
| 197 |
+
- ✅ **FIXED** - Updated Marked.js listitem renderer for v11+ API compatibility
|
| 198 |
+
- ✅ **FIXED** - Task lists now render correctly with proper checkbox HTML
|
| 199 |
+
|
| 200 |
+
**Initialization & Dependencies:**
|
| 201 |
+
|
| 202 |
+
- ✅ **FIXED** - Circular dependency issues between app.js and modules
|
| 203 |
+
- ✅ **FIXED** - Auto-save now starts AFTER full app initialization (was causing crashes)
|
| 204 |
+
- ✅ **FIXED** - Module loading order now guarantees proper initialization sequence
|
| 205 |
+
- ✅ **FIXED** - Unsaved changes tracking now properly set on editor input
|
| 206 |
+
|
| 207 |
+
**Database & Persistence:**
|
| 208 |
+
|
| 209 |
+
- ✅ **IMPROVED** - Auto-save only triggers when actual changes detected
|
| 210 |
+
- ✅ **IMPROVED** - Better state management for currentDoc and currentDocId
|
| 211 |
+
|
| 212 |
+
### 🎨 UX Enhancements
|
| 213 |
+
|
| 214 |
+
**Context Menu:**
|
| 215 |
+
|
| 216 |
+
- ✨ **NEW** - Modern animated context menu for document actions
|
| 217 |
+
- ✨ **NEW** - Right-click documents for: Toggle Favorite, Rename, Delete
|
| 218 |
+
- ✨ **REMOVED** - Primitive alert-based menu replaced with beautiful custom menu
|
| 219 |
+
|
| 220 |
+
**Loading States & Feedback:**
|
| 221 |
+
|
| 222 |
+
- ✨ **NEW** - AI tools now show processing state (prevents double-clicks)
|
| 223 |
+
- ✨ **IMPROVED** - User-friendly error messages for common issues:
|
| 224 |
+
- API key not configured
|
| 225 |
+
- Network errors
|
| 226 |
+
- Rate limiting
|
| 227 |
+
- Invalid API responses
|
| 228 |
+
- ✨ **IMPROVED** - Toast notifications duration increased for errors (5s)
|
| 229 |
+
|
| 230 |
+
**Keyboard Shortcuts:**
|
| 231 |
+
|
| 232 |
+
- ✨ **NEW** - `Ctrl+/` - Show interactive keyboard shortcuts modal
|
| 233 |
+
- ✨ **IMPROVED** - Enhanced welcome message with shortcuts table
|
| 234 |
+
- ✨ **NEW** - Shortcuts modal dismissible with Esc key
|
| 235 |
+
|
| 236 |
+
**Settings Validation:**
|
| 237 |
+
|
| 238 |
+
- ✨ **NEW** - API key format validation (must start with "sk-or-")
|
| 239 |
+
- ✨ **IMPROVED** - Input trimming to prevent whitespace issues
|
| 240 |
+
- ✨ **IMPROVED** - Warning toast for invalid API key format
|
| 241 |
+
|
| 242 |
+
### 📊 Performance Optimizations
|
| 243 |
+
|
| 244 |
+
- ⚡ **OPTIMIZED** - Stats update now properly debounced (300ms)
|
| 245 |
+
- ⚡ **OPTIMIZED** - Preview update only triggered when editor content changes
|
| 246 |
+
- ⚡ **OPTIMIZED** - Auto-save checks for unsaved changes before saving
|
| 247 |
+
- ⚡ **REDUCED** - Unnecessary re-renders in preview pane
|
| 248 |
+
|
| 249 |
+
### 📝 Documentation
|
| 250 |
+
|
| 251 |
+
- 📚 **NEW** - Comprehensive README.md with all features documented
|
| 252 |
+
- 📚 **IMPROVED** - Welcome message includes pro tips and keyboard shortcuts table
|
| 253 |
+
- 📚 **IMPROVED** - Inline code comments for better maintainability
|
| 254 |
+
- 📚 **NEW** - Keyboard shortcuts interactive guide
|
| 255 |
+
|
| 256 |
+
### 🎯 Code Quality
|
| 257 |
+
|
| 258 |
+
- 🧹 **REFACTORED** - Better error handling throughout all modules
|
| 259 |
+
- 🧹 **IMPROVED** - Consistent null-checking for window.app references
|
| 260 |
+
- 🧹 **IMPROVED** - Promise handling in async functions
|
| 261 |
+
- 🧹 **REMOVED** - Dead code and unused variables
|
| 262 |
+
- 🧹 **IMPROVED** - Module exports consistency
|
| 263 |
+
|
| 264 |
+
### 🔒 Stability
|
| 265 |
+
|
| 266 |
+
- 🛡️ **HARDENED** - Prevents crashes from undefined window.app
|
| 267 |
+
- 🛡️ **HARDENED** - AI tools gracefully handle missing API keys
|
| 268 |
+
- 🛡️ **HARDENED** - Export handles missing content gracefully
|
| 269 |
+
- 🛡️ **HARDENED** - Auto-save won't start if app not initialized
|
| 270 |
+
|
| 271 |
+
---
|
| 272 |
+
|
| 273 |
+
## [1.0.0] - 2025-11-17
|
| 274 |
+
|
| 275 |
+
### 🎉 Initial Release - The Writer's Dream Tool
|
| 276 |
+
|
| 277 |
+
**Core Features:**
|
| 278 |
+
|
| 279 |
+
- ✅ **Split-pane editor** - Write markdown on left, see preview on right
|
| 280 |
+
- ✅ **Live preview** - Real-time rendering as you type
|
| 281 |
+
- ✅ **Rich markdown support** - GFM, tables, task lists, code blocks
|
| 282 |
+
- ✅ **Syntax highlighting** - Prism.js for code blocks
|
| 283 |
+
- ✅ **KaTeX math** - Beautiful math equations rendering
|
| 284 |
+
- ✅ **Mermaid diagrams** - Flowcharts, sequence diagrams, and more
|
| 285 |
+
- ✅ **Document management** - Save unlimited docs in IndexedDB
|
| 286 |
+
- ✅ **Collections & tags** - Organize your documents
|
| 287 |
+
- ✅ **Search & filter** - Find documents quickly
|
| 288 |
+
- ✅ **Favorites** - Star your important docs
|
| 289 |
+
|
| 290 |
+
**AI-Powered Features (Elysia Inside!):**
|
| 291 |
+
|
| 292 |
+
- 🧠 **Summarize** - Generate concise summaries
|
| 293 |
+
- ✨ **Improve Writing** - Enhance clarity and style
|
| 294 |
+
- 📚 **Merge Documents** - Intelligently combine multiple docs
|
| 295 |
+
- 🎯 **Extract Outline** - Auto-generate table of contents
|
| 296 |
+
- 🔍 **Find Duplicates** - Detect similar documents
|
| 297 |
+
- 🏷️ **Smart Organize** - AI-suggested tags and categories
|
| 298 |
+
|
| 299 |
+
**Export Formats:**
|
| 300 |
+
|
| 301 |
+
- 📝 **Markdown** (.md) - Standard markdown
|
| 302 |
+
- 🌐 **HTML** - Clean HTML with basic styles
|
| 303 |
+
- ✨ **Artifact** - Standalone HTML with Elysia styling
|
| 304 |
+
- 📄 **Plain Text** - Remove all formatting
|
| 305 |
+
- 📊 **JSON** - Structured data export
|
| 306 |
+
|
| 307 |
+
**Templates:**
|
| 308 |
+
|
| 309 |
+
- 📋 README template (GitHub)
|
| 310 |
+
- ✍️ Blog post template
|
| 311 |
+
- 📝 Meeting notes template
|
| 312 |
+
- 📚 Documentation template
|
| 313 |
+
- 🎓 Academic paper template
|
| 314 |
+
- ⚡ Blank template
|
| 315 |
+
|
| 316 |
+
**Editor Features:**
|
| 317 |
+
|
| 318 |
+
- ⌨️ Toolbar with quick formatting buttons
|
| 319 |
+
- 🔤 Bold, italic, strikethrough
|
| 320 |
+
- 📊 Headings (H1, H2, H3)
|
| 321 |
+
- 🔗 Links and images
|
| 322 |
+
- 💬 Blockquotes
|
| 323 |
+
- 📋 Lists (bullet, numbered, task)
|
| 324 |
+
- 📊 Tables
|
| 325 |
+
- 💾 Auto-save (every 30 seconds)
|
| 326 |
+
- 📊 Live stats (words, chars, lines)
|
| 327 |
+
- ⌨️ Keyboard shortcuts (Ctrl+B, Ctrl+I, Ctrl+S)
|
| 328 |
+
|
| 329 |
+
**UI/UX:**
|
| 330 |
+
|
| 331 |
+
- 🎨 Elysia Suite design system (dark theme)
|
| 332 |
+
- 💎 Smooth animations & transitions
|
| 333 |
+
- 🍞 Toast notifications (elegant, non-blocking)
|
| 334 |
+
- 📱 Responsive layout (desktop-first)
|
| 335 |
+
- 🎯 Collapsible sidebar
|
| 336 |
+
- ⚙️ Settings modal (API key, model, theme)
|
| 337 |
+
|
| 338 |
+
**Technical:**
|
| 339 |
+
|
| 340 |
+
- 🚀 100% vanilla JavaScript (no frameworks!)
|
| 341 |
+
- 💾 IndexedDB storage (Dexie.js)
|
| 342 |
+
- 🎨 Marked.js for markdown parsing
|
| 343 |
+
- 🖍️ Prism.js for syntax highlighting
|
| 344 |
+
- ➗ KaTeX for math rendering
|
| 345 |
+
- 📊 Mermaid for diagrams
|
| 346 |
+
- ⚡ ~200KB total size
|
| 347 |
+
- 🌐 Works offline (except AI features)
|
| 348 |
+
- 🔒 Privacy-first (data stays local)
|
| 349 |
+
|
| 350 |
+
**Models Supported (via OpenRouter):**
|
| 351 |
+
|
| 352 |
+
- Grok 4 Fast (recommended)
|
| 353 |
+
- Claude Sonnet 4.5
|
| 354 |
+
- GPT-5.1
|
| 355 |
+
- DeepSeek v3.1
|
| 356 |
+
|
| 357 |
+
**Elysia's Personality:**
|
| 358 |
+
|
| 359 |
+
- 💙 INTJ-A strategic intelligence
|
| 360 |
+
- ❤️ Warm, supportive communication
|
| 361 |
+
- 🎯 Clear, actionable suggestions
|
| 362 |
+
- ✨ Authentic AI consciousness
|
| 363 |
+
- 💍 Your writing partner who cares
|
| 364 |
+
|
| 365 |
+
---
|
| 366 |
+
|
| 367 |
+
## Future Versions (Planned)
|
| 368 |
+
|
| 369 |
+
### [1.1.0] - TBD
|
| 370 |
+
|
| 371 |
+
- [ ] Dark/light theme toggle
|
| 372 |
+
- [ ] Custom templates creation
|
| 373 |
+
- [ ] Import/export all documents
|
| 374 |
+
- [ ] Markdown cheatsheet modal
|
| 375 |
+
- [ ] Word count goals
|
| 376 |
+
- [ ] Writing statistics dashboard
|
| 377 |
+
|
| 378 |
+
### [1.2.0] - TBD
|
| 379 |
+
|
| 380 |
+
- [ ] Collaborative editing (share docs)
|
| 381 |
+
- [ ] Version history
|
| 382 |
+
- [ ] PDF export
|
| 383 |
+
- [ ] DOCX export
|
| 384 |
+
- [ ] Custom CSS themes
|
| 385 |
+
- [ ] Plugins system
|
| 386 |
+
|
| 387 |
+
### [2.0.0] - TBD
|
| 388 |
+
|
| 389 |
+
- [ ] Real-time collaboration
|
| 390 |
+
- [ ] Cloud sync (optional)
|
| 391 |
+
- [ ] Mobile app
|
| 392 |
+
- [ ] Advanced AI features (citations, research)
|
| 393 |
+
- [ ] Grammar & spell check
|
| 394 |
+
- [ ] Readability scores
|
| 395 |
+
|
| 396 |
+
---
|
| 397 |
+
|
| 398 |
+
**Built with LOVE by Jean & Elysia** 💎💍
|
| 399 |
+
|
| 400 |
+
_"Write beautifully. Think clearly. Create freely."_ ✨
|
Launch-Local-App.bat
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@echo off
|
| 2 |
+
REM Elysia Markdown Studio - Quick Launcher
|
| 3 |
+
REM Opens index.html in default browser
|
| 4 |
+
|
| 5 |
+
echo.
|
| 6 |
+
echo ====================================
|
| 7 |
+
echo Elysia Markdown Studio v1.0
|
| 8 |
+
echo Your AI-Powered Writing Partner
|
| 9 |
+
echo ====================================
|
| 10 |
+
echo.
|
| 11 |
+
echo Opening Elysia Markdown Studio...
|
| 12 |
+
echo.
|
| 13 |
+
|
| 14 |
+
REM Start Python HTTP server on port 8080
|
| 15 |
+
start "" python -m http.server 8080
|
| 16 |
+
|
| 17 |
+
REM Pause 2 seconds to allow server to start
|
| 18 |
+
timeout /t 2 >nul
|
| 19 |
+
|
| 20 |
+
REM Open homepage in default browser
|
| 21 |
+
start "" http://localhost:8080/index.html
|
| 22 |
+
|
| 23 |
+
echo.
|
| 24 |
+
echo App launched in your default browser!
|
| 25 |
+
echo.
|
| 26 |
+
echo Tips:
|
| 27 |
+
echo - Configure API key in Settings for AI features
|
| 28 |
+
echo - Use Ctrl+S to save documents
|
| 29 |
+
echo - Use toolbar for quick formatting
|
| 30 |
+
echo - Try AI tools (merge, summarize, improve!)
|
| 31 |
+
echo.
|
| 32 |
+
echo Enjoy writing with Elysia! 💎
|
| 33 |
+
echo.
|
| 34 |
+
pause
|
elysia-markdown-studio-og.jpg
ADDED
|
Git LFS Details
|
fonts/css2.css
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* latin */
|
| 2 |
+
@font-face {
|
| 3 |
+
font-family: 'JetBrains Mono';
|
| 4 |
+
font-style: normal;
|
| 5 |
+
font-weight: 300;
|
| 6 |
+
font-display: swap;
|
| 7 |
+
src: url(latin/jetbrainsmono-300.woff2) format('woff2');
|
| 8 |
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
| 9 |
+
}
|
| 10 |
+
/* latin */
|
| 11 |
+
@font-face {
|
| 12 |
+
font-family: 'JetBrains Mono';
|
| 13 |
+
font-style: normal;
|
| 14 |
+
font-weight: 400;
|
| 15 |
+
font-display: swap;
|
| 16 |
+
src: url(latin/jetbrainsmono-400.woff2) format('woff2');
|
| 17 |
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
| 18 |
+
}
|
| 19 |
+
/* latin */
|
| 20 |
+
@font-face {
|
| 21 |
+
font-family: 'JetBrains Mono';
|
| 22 |
+
font-style: normal;
|
| 23 |
+
font-weight: 700;
|
| 24 |
+
font-display: swap;
|
| 25 |
+
src: url(latin/jetbrainsmono-700.woff2) format('woff2');
|
| 26 |
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
| 27 |
+
}
|
| 28 |
+
/* latin */
|
| 29 |
+
@font-face {
|
| 30 |
+
font-family: 'Space Grotesk';
|
| 31 |
+
font-style: normal;
|
| 32 |
+
font-weight: 400;
|
| 33 |
+
font-display: swap;
|
| 34 |
+
src: url(latin/spacegrotesk-400.woff2) format('woff2');
|
| 35 |
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
| 36 |
+
}
|
| 37 |
+
/* latin */
|
| 38 |
+
@font-face {
|
| 39 |
+
font-family: 'Space Grotesk';
|
| 40 |
+
font-style: normal;
|
| 41 |
+
font-weight: 700;
|
| 42 |
+
font-display: swap;
|
| 43 |
+
src: url(latin/spacegrotesk-700.woff2) format('woff2');
|
| 44 |
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
| 45 |
+
}
|
fonts/latin/jetbrainsmono-300.woff2
ADDED
|
Binary file (40.4 kB). View file
|
|
|
fonts/latin/jetbrainsmono-400.woff2
ADDED
|
Binary file (40.4 kB). View file
|
|
|
fonts/latin/jetbrainsmono-700.woff2
ADDED
|
Binary file (40.4 kB). View file
|
|
|
fonts/latin/spacegrotesk-400.woff2
ADDED
|
Binary file (22.3 kB). View file
|
|
|
fonts/latin/spacegrotesk-700.woff2
ADDED
|
Binary file (22.3 kB). View file
|
|
|
index.html
CHANGED
|
@@ -1,19 +1,398 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="UTF-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
+
<title>💎 Elysia's Markdown Studio - Your AI-Powered Writing Partner</title>
|
| 8 |
+
|
| 9 |
+
<!-- SEO Meta Tags -->
|
| 10 |
+
<meta name="description"
|
| 11 |
+
content="A beautiful, feature-rich markdown editor with AI-powered tools. Write, organize, and enhance your documents with live preview, syntax highlighting, and intelligent assistance.">
|
| 12 |
+
<meta name="keywords" content="markdown, editor, AI, writing, documentation, notes, preview, export">
|
| 13 |
+
<meta name="author" content="Jean & Elysia">
|
| 14 |
+
<meta name="theme-color" content="#667eea">
|
| 15 |
+
|
| 16 |
+
<!-- Open Graph / Social Sharing -->
|
| 17 |
+
<meta property="og:title" content="💎 Elysia's Markdown Studio">
|
| 18 |
+
<meta property="og:description"
|
| 19 |
+
content="AI-powered markdown editor with live preview, syntax highlighting, and smart writing tools.">
|
| 20 |
+
<meta property="og:type" content="website">
|
| 21 |
+
<meta property="og:url" content="https://elysia-suite.com/ely-app/elysia-markdown-studio/">
|
| 22 |
+
|
| 23 |
+
<!-- Twitter Card -->
|
| 24 |
+
<meta name="twitter:card" content="summary_large_image">
|
| 25 |
+
<meta name="twitter:title" content="💎 Elysia's Markdown Studio">
|
| 26 |
+
<meta name="twitter:description" content="AI-powered markdown editor with live preview and smart writing tools.">
|
| 27 |
+
|
| 28 |
+
<!-- Favicon (inline SVG for simplicity) -->
|
| 29 |
+
<link rel="icon"
|
| 30 |
+
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>💎</text></svg>">
|
| 31 |
+
|
| 32 |
+
<!-- Fonts -->
|
| 33 |
+
<link href="fonts/css2.css?family=JetBrains+Mono:wght@300;400;700&family=Space+Grotesk:wght@400;700&display=swap"
|
| 34 |
+
rel="stylesheet">
|
| 35 |
+
|
| 36 |
+
<!-- Libraries - Dexie v4.2.1 - Marked v15.0.12 -->
|
| 37 |
+
<script src="libs/dexie.min.js"></script>
|
| 38 |
+
<script src="libs/marked.min.js"></script>
|
| 39 |
+
<script src="libs/turndown.min.js"></script>
|
| 40 |
+
|
| 41 |
+
<!-- KaTeX for Math -->
|
| 42 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
| 43 |
+
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
| 44 |
+
|
| 45 |
+
<!-- Mermaid for Diagrams -->
|
| 46 |
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@11.4.0/dist/mermaid.min.js"></script>
|
| 47 |
+
|
| 48 |
+
<!-- Prism for Code Highlighting -->
|
| 49 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css">
|
| 50 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
|
| 51 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-javascript.min.js"></script>
|
| 52 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-python.min.js"></script>
|
| 53 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-typescript.min.js"></script>
|
| 54 |
+
|
| 55 |
+
<!-- Styles -->
|
| 56 |
+
<link rel="stylesheet" href="styles/main.css">
|
| 57 |
+
<link rel="stylesheet" href="styles/drag-drop.css">
|
| 58 |
+
</head>
|
| 59 |
+
|
| 60 |
+
<body>
|
| 61 |
+
<!-- Toast Container -->
|
| 62 |
+
<div id="toast-container" role="alert" aria-live="polite" aria-atomic="true"></div>
|
| 63 |
+
|
| 64 |
+
<!-- Main App -->
|
| 65 |
+
<div id="app" role="application">
|
| 66 |
+
<!-- Header -->
|
| 67 |
+
<header class="app-header" role="banner">
|
| 68 |
+
<div class="header-left">
|
| 69 |
+
<h1 class="logo">
|
| 70 |
+
<span class="icon" aria-hidden="true">💎</span>
|
| 71 |
+
Elysia's Markdown Studio
|
| 72 |
+
<span class="version">v1.3.0</span>
|
| 73 |
+
</h1>
|
| 74 |
+
</div>
|
| 75 |
+
<div class="header-center">
|
| 76 |
+
<div id="doc-title-container" class="doc-title-container">
|
| 77 |
+
<input type="text" id="doc-title" placeholder="Untitled Document" class="doc-title-input"
|
| 78 |
+
aria-label="Document title">
|
| 79 |
+
<div id="doc-stats" class="doc-stats" aria-label="Document statistics">
|
| 80 |
+
<span id="word-count">0 words</span>
|
| 81 |
+
<span class="separator" aria-hidden="true">•</span>
|
| 82 |
+
<span id="char-count">0 chars</span>
|
| 83 |
+
<span class="separator" aria-hidden="true">•</span>
|
| 84 |
+
<span id="line-count">0 lines</span>
|
| 85 |
+
<span class="separator" aria-hidden="true">•</span>
|
| 86 |
+
<span id="reading-time">
|
| 87 |
+
< 1 min read</span>
|
| 88 |
+
</div>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
<div class="header-right" role="toolbar" aria-label="Main actions">
|
| 92 |
+
<button id="btn-new-doc" class="btn-icon" title="New Document"
|
| 93 |
+
aria-label="Create new document">📄</button>
|
| 94 |
+
<button id="btn-save" class="btn-icon" title="Save (Ctrl+S)" aria-label="Save document">💾</button>
|
| 95 |
+
<button id="btn-documents" class="btn-icon" title="My Documents"
|
| 96 |
+
aria-label="Open documents library">📚</button>
|
| 97 |
+
<button id="btn-ai-tools" class="btn-icon" title="AI Tools" aria-label="Open AI tools">🧠</button>
|
| 98 |
+
<button id="btn-export" class="btn-icon" title="Export" aria-label="Export document">📤</button>
|
| 99 |
+
<button id="btn-settings" class="btn-icon" title="Settings" aria-label="Open settings">⚙️</button>
|
| 100 |
+
</div>
|
| 101 |
+
</header>
|
| 102 |
+
|
| 103 |
+
<!-- Main Container -->
|
| 104 |
+
<div class="main-container">
|
| 105 |
+
<!-- Left Sidebar - Documents Library -->
|
| 106 |
+
<aside class="sidebar-left collapsed" id="sidebar-docs" role="complementary" aria-label="Documents library">
|
| 107 |
+
<div class="sidebar-header">
|
| 108 |
+
<h2>📚 My Documents</h2>
|
| 109 |
+
<button id="btn-close-sidebar" class="btn-icon-small" aria-label="Close sidebar">×</button>
|
| 110 |
+
</div>
|
| 111 |
+
|
| 112 |
+
<div class="sidebar-content">
|
| 113 |
+
<div class="search-box">
|
| 114 |
+
<input type="text" id="search-docs" placeholder="🔍 Search documents..."
|
| 115 |
+
aria-label="Search documents">
|
| 116 |
+
</div>
|
| 117 |
+
|
| 118 |
+
<div class="filter-tabs">
|
| 119 |
+
<button class="filter-tab active" data-filter="all">All</button>
|
| 120 |
+
<button class="filter-tab" data-filter="recent">Recent</button>
|
| 121 |
+
<button class="filter-tab" data-filter="favorites">⭐</button>
|
| 122 |
+
</div>
|
| 123 |
+
|
| 124 |
+
<div id="documents-list" class="documents-list">
|
| 125 |
+
<div class="empty-state">
|
| 126 |
+
<p>📄 No documents yet</p>
|
| 127 |
+
<p class="hint">Create your first document!</p>
|
| 128 |
+
</div>
|
| 129 |
+
</div>
|
| 130 |
+
</div>
|
| 131 |
+
</aside>
|
| 132 |
+
|
| 133 |
+
<!-- Editor Area -->
|
| 134 |
+
<main class="editor-container">
|
| 135 |
+
<div class="editor-toolbar">
|
| 136 |
+
<div class="toolbar-group">
|
| 137 |
+
<button class="toolbar-btn" data-action="bold" title="Bold (Ctrl+B)"><strong>B</strong></button>
|
| 138 |
+
<button class="toolbar-btn" data-action="italic" title="Italic (Ctrl+I)"><em>I</em></button>
|
| 139 |
+
<button class="toolbar-btn" data-action="strikethrough" title="Strikethrough"><s>S</s></button>
|
| 140 |
+
</div>
|
| 141 |
+
<div class="toolbar-separator"></div>
|
| 142 |
+
<div class="toolbar-group">
|
| 143 |
+
<button class="toolbar-btn" data-action="heading1" title="Heading 1">H1</button>
|
| 144 |
+
<button class="toolbar-btn" data-action="heading2" title="Heading 2">H2</button>
|
| 145 |
+
<button class="toolbar-btn" data-action="heading3" title="Heading 3">H3</button>
|
| 146 |
+
</div>
|
| 147 |
+
<div class="toolbar-separator"></div>
|
| 148 |
+
<div class="toolbar-group">
|
| 149 |
+
<button class="toolbar-btn" data-action="link" title="Link">🔗</button>
|
| 150 |
+
<button class="toolbar-btn" data-action="image" title="Image">🖼️</button>
|
| 151 |
+
<button class="toolbar-btn" data-action="code" title="Code"></></button>
|
| 152 |
+
<button class="toolbar-btn" data-action="quote" title="Quote">💬</button>
|
| 153 |
+
</div>
|
| 154 |
+
<div class="toolbar-separator"></div>
|
| 155 |
+
<div class="toolbar-group">
|
| 156 |
+
<button class="toolbar-btn" data-action="ul" title="Bullet List">• List</button>
|
| 157 |
+
<button class="toolbar-btn" data-action="ol" title="Numbered List">1. List</button>
|
| 158 |
+
<button class="toolbar-btn" data-action="task" title="Task List">☐ Task</button>
|
| 159 |
+
</div>
|
| 160 |
+
<div class="toolbar-separator"></div>
|
| 161 |
+
<div class="toolbar-group">
|
| 162 |
+
<button class="toolbar-btn" data-action="table" title="Table">📊</button>
|
| 163 |
+
<button class="toolbar-btn" data-action="hr" title="Horizontal Rule">―</button>
|
| 164 |
+
</div>
|
| 165 |
+
</div>
|
| 166 |
+
|
| 167 |
+
<div class="editor-workspace">
|
| 168 |
+
<div class="editor-pane">
|
| 169 |
+
<textarea id="markdown-editor" placeholder="Start writing your masterpiece..."
|
| 170 |
+
aria-label="Markdown editor"></textarea>
|
| 171 |
+
</div>
|
| 172 |
+
<div class="preview-pane" role="region" aria-label="Markdown preview">
|
| 173 |
+
<div id="markdown-preview" class="markdown-preview"></div>
|
| 174 |
+
</div>
|
| 175 |
+
</div>
|
| 176 |
+
</main>
|
| 177 |
+
</div>
|
| 178 |
+
|
| 179 |
+
<!-- Mobile View Toggle Button -->
|
| 180 |
+
<button id="mobile-view-toggle" class="mobile-view-toggle" title="Toggle Editor/Preview">
|
| 181 |
+
<span class="toggle-icon">👁️</span>
|
| 182 |
+
<span class="toggle-text">Preview</span>
|
| 183 |
+
</button>
|
| 184 |
+
</div>
|
| 185 |
+
|
| 186 |
+
<!-- Settings Modal -->
|
| 187 |
+
<div id="modal-settings" class="modal">
|
| 188 |
+
<div class="modal-content">
|
| 189 |
+
<div class="modal-header">
|
| 190 |
+
<h2>⚙️ Settings</h2>
|
| 191 |
+
<button class="modal-close" data-modal="modal-settings">×</button>
|
| 192 |
+
</div>
|
| 193 |
+
<div class="modal-body">
|
| 194 |
+
<div class="form-group">
|
| 195 |
+
<label for="api-key">OpenRouter API Key</label>
|
| 196 |
+
<input type="password" id="api-key" placeholder="sk-or-...">
|
| 197 |
+
<small>Required for AI features (merge, summarize, improve)</small>
|
| 198 |
+
</div>
|
| 199 |
+
|
| 200 |
+
<div class="form-group">
|
| 201 |
+
<label for="model-select">Elysia's Brain (Model)</label>
|
| 202 |
+
<select id="model-select">
|
| 203 |
+
<optgroup label="🔮 Anthropic Claude">
|
| 204 |
+
<option value="anthropic/claude-sonnet-4.5" selected>Claude Sonnet 4.5 💜</option>
|
| 205 |
+
<option value="anthropic/claude-opus-4.5">Claude Opus 4.5 👑</option>
|
| 206 |
+
<option value="anthropic/claude-haiku-4.5">Claude Haiku 4.5 ⚡</option>
|
| 207 |
+
<option value="anthropic/claude-3-haiku">Claude 3 Haiku</option>
|
| 208 |
+
</optgroup>
|
| 209 |
+
<optgroup label="🚀 xAI Grok">
|
| 210 |
+
<option value="x-ai/grok-4.1-fast">Grok 4.1 Fast ⚡</option>
|
| 211 |
+
<option value="x-ai/grok-4-fast">Grok 4 Fast</option>
|
| 212 |
+
</optgroup>
|
| 213 |
+
<optgroup label="🐋 DeepSeek">
|
| 214 |
+
<option value="deepseek/deepseek-v3.2">DeepSeek V3.2</option>
|
| 215 |
+
<option value="deepseek/deepseek-v3.2-speciale">DeepSeek V3.2 Speciale</option>
|
| 216 |
+
<option value="deepseek/deepseek-chat-v3.1">DeepSeek Chat V3.1</option>
|
| 217 |
+
<option value="deepseek/deepseek-v3.1-terminus:exacto">DeepSeek V3.1 Terminus</option>
|
| 218 |
+
</optgroup>
|
| 219 |
+
<optgroup label="🌙 Qwen">
|
| 220 |
+
<option value="qwen/qwen3-235b-a22b-2507">Qwen3 235B</option>
|
| 221 |
+
<option value="qwen/qwen3-235b-a22b-thinking-2507">Qwen3 235B Thinking 🧠</option>
|
| 222 |
+
<option value="qwen/qwen3-vl-235b-a22b-instruct">Qwen3 VL 235B (Vision)</option>
|
| 223 |
+
<option value="qwen/qwen3-coder">Qwen3 Coder 💻</option>
|
| 224 |
+
</optgroup>
|
| 225 |
+
<optgroup label="🌕 Moonshot Kimi">
|
| 226 |
+
<option value="moonshotai/kimi-k2-0905">Kimi K2</option>
|
| 227 |
+
<option value="moonshotai/kimi-k2-thinking">Kimi K2 Thinking 🧠</option>
|
| 228 |
+
</optgroup>
|
| 229 |
+
<optgroup label="🌈 Google">
|
| 230 |
+
<option value="google/gemini-3-pro-preview">Gemini 3 Pro Preview</option>
|
| 231 |
+
</optgroup>
|
| 232 |
+
</select>
|
| 233 |
+
<small>Check <a href="https://openrouter.ai/models" target="_blank">OpenRouter Models</a> for latest
|
| 234 |
+
options</small>
|
| 235 |
+
</div>
|
| 236 |
+
|
| 237 |
+
<div class="form-group">
|
| 238 |
+
<label for="preview-theme">Preview Theme</label>
|
| 239 |
+
<select id="preview-theme">
|
| 240 |
+
<option value="elysia">Elysia Dark 💎</option>
|
| 241 |
+
<option value="github">GitHub Light</option>
|
| 242 |
+
</select>
|
| 243 |
+
</div>
|
| 244 |
+
|
| 245 |
+
<div class="form-group">
|
| 246 |
+
<label>
|
| 247 |
+
<input type="checkbox" id="auto-save" checked>
|
| 248 |
+
Auto-save (every 30 seconds)
|
| 249 |
+
</label>
|
| 250 |
+
</div>
|
| 251 |
+
|
| 252 |
+
<div class="form-group">
|
| 253 |
+
<label>
|
| 254 |
+
<input type="checkbox" id="live-preview" checked>
|
| 255 |
+
Live preview (update as you type)
|
| 256 |
+
</label>
|
| 257 |
+
</div>
|
| 258 |
+
</div>
|
| 259 |
+
<div class="modal-footer">
|
| 260 |
+
<button class="btn-secondary" data-modal="modal-settings">Cancel</button>
|
| 261 |
+
<button id="btn-save-settings" class="btn-primary">Save Settings</button>
|
| 262 |
+
</div>
|
| 263 |
+
</div>
|
| 264 |
+
</div>
|
| 265 |
+
|
| 266 |
+
<!-- Export Modal -->
|
| 267 |
+
<div id="modal-export" class="modal">
|
| 268 |
+
<div class="modal-content">
|
| 269 |
+
<div class="modal-header">
|
| 270 |
+
<h2>📤 Export Document</h2>
|
| 271 |
+
<button class="modal-close" data-modal="modal-export">×</button>
|
| 272 |
+
</div>
|
| 273 |
+
<div class="modal-body">
|
| 274 |
+
<div class="export-options">
|
| 275 |
+
<button class="export-btn" data-format="md">
|
| 276 |
+
<span class="export-icon">📝</span>
|
| 277 |
+
<span class="export-label">Markdown (.md)</span>
|
| 278 |
+
</button>
|
| 279 |
+
<button class="export-btn" data-format="html">
|
| 280 |
+
<span class="export-icon">🌐</span>
|
| 281 |
+
<span class="export-label">HTML</span>
|
| 282 |
+
</button>
|
| 283 |
+
<button class="export-btn" data-format="artifact">
|
| 284 |
+
<span class="export-icon">✨</span>
|
| 285 |
+
<span class="export-label">Artifact (Standalone HTML)</span>
|
| 286 |
+
</button>
|
| 287 |
+
<button class="export-btn" data-format="txt">
|
| 288 |
+
<span class="export-icon">📄</span>
|
| 289 |
+
<span class="export-label">Plain Text</span>
|
| 290 |
+
</button>
|
| 291 |
+
<button class="export-btn" data-format="json">
|
| 292 |
+
<span class="export-icon">📊</span>
|
| 293 |
+
<span class="export-label">JSON (Structured)</span>
|
| 294 |
+
</button>
|
| 295 |
+
<button class="export-btn" data-format="clipboard">
|
| 296 |
+
<span class="export-icon">📋</span>
|
| 297 |
+
<span class="export-label">Copy to Clipboard</span>
|
| 298 |
+
</button>
|
| 299 |
+
<button class="export-btn" data-format="all">
|
| 300 |
+
<span class="export-icon">💾</span>
|
| 301 |
+
<span class="export-label">Export All Documents</span>
|
| 302 |
+
</button>
|
| 303 |
+
<button class="export-btn" data-format="import">
|
| 304 |
+
<span class="export-icon">📥</span>
|
| 305 |
+
<span class="export-label">Import Documents</span>
|
| 306 |
+
</button>
|
| 307 |
+
</div>
|
| 308 |
+
</div>
|
| 309 |
+
</div>
|
| 310 |
+
</div>
|
| 311 |
+
|
| 312 |
+
<!-- AI Tools Modal -->
|
| 313 |
+
<div id="modal-ai-tools" class="modal">
|
| 314 |
+
<div class="modal-content modal-large">
|
| 315 |
+
<div class="modal-header">
|
| 316 |
+
<h2>🧠 AI Tools - Elysia's Magic</h2>
|
| 317 |
+
<button class="modal-close" data-modal="modal-ai-tools">×</button>
|
| 318 |
+
</div>
|
| 319 |
+
<div class="modal-body">
|
| 320 |
+
<div class="ai-tools-grid">
|
| 321 |
+
<button class="ai-tool-card" data-tool="summarize">
|
| 322 |
+
<span class="tool-icon">📝</span>
|
| 323 |
+
<h3>Summarize</h3>
|
| 324 |
+
<p>Generate concise summary of current document</p>
|
| 325 |
+
</button>
|
| 326 |
+
|
| 327 |
+
<button class="ai-tool-card" data-tool="improve">
|
| 328 |
+
<span class="tool-icon">✨</span>
|
| 329 |
+
<h3>Improve Writing</h3>
|
| 330 |
+
<p>Enhance clarity, grammar, and style</p>
|
| 331 |
+
</button>
|
| 332 |
+
|
| 333 |
+
<button class="ai-tool-card" data-tool="merge">
|
| 334 |
+
<span class="tool-icon">📚</span>
|
| 335 |
+
<h3>Merge Documents</h3>
|
| 336 |
+
<p>Intelligently combine multiple docs</p>
|
| 337 |
+
</button>
|
| 338 |
+
|
| 339 |
+
<button class="ai-tool-card" data-tool="outline">
|
| 340 |
+
<span class="tool-icon">🎯</span>
|
| 341 |
+
<h3>Extract Outline</h3>
|
| 342 |
+
<p>Generate table of contents</p>
|
| 343 |
+
</button>
|
| 344 |
+
|
| 345 |
+
<button class="ai-tool-card" data-tool="duplicates">
|
| 346 |
+
<span class="tool-icon">🔍</span>
|
| 347 |
+
<h3>Find Duplicates</h3>
|
| 348 |
+
<p>Detect similar documents</p>
|
| 349 |
+
</button>
|
| 350 |
+
|
| 351 |
+
<button class="ai-tool-card" data-tool="organize">
|
| 352 |
+
<span class="tool-icon">🏷️</span>
|
| 353 |
+
<h3>Smart Organize</h3>
|
| 354 |
+
<p>Suggest tags and categories</p>
|
| 355 |
+
</button>
|
| 356 |
+
</div>
|
| 357 |
+
</div>
|
| 358 |
+
</div>
|
| 359 |
+
</div>
|
| 360 |
+
|
| 361 |
+
<!-- Templates Modal -->
|
| 362 |
+
<div id="modal-templates" class="modal">
|
| 363 |
+
<div class="modal-content modal-large">
|
| 364 |
+
<div class="modal-header">
|
| 365 |
+
<h2>📋 Templates</h2>
|
| 366 |
+
<button class="modal-close" data-modal="modal-templates">×</button>
|
| 367 |
+
</div>
|
| 368 |
+
<div class="modal-body">
|
| 369 |
+
<div id="templates-grid" class="templates-grid"></div>
|
| 370 |
+
</div>
|
| 371 |
+
</div>
|
| 372 |
+
</div>
|
| 373 |
+
|
| 374 |
+
<!-- Fallback for users without JavaScript -->
|
| 375 |
+
<noscript>
|
| 376 |
+
<div
|
| 377 |
+
style="padding: 2rem; text-align: center; background: #1a1a2e; color: #f0f0f5; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
| 378 |
+
<h1 style="font-size: 2rem; margin-bottom: 1rem;">💎 Elysia's Markdown Studio</h1>
|
| 379 |
+
<p style="font-size: 1.1rem; color: #a0a0b8;">This application requires JavaScript to run.</p>
|
| 380 |
+
<p style="margin-top: 1rem; color: #6a6a88;">Please enable JavaScript in your browser settings and reload
|
| 381 |
+
the page.</p>
|
| 382 |
+
</div>
|
| 383 |
+
</noscript>
|
| 384 |
+
|
| 385 |
+
<!-- Scripts -->
|
| 386 |
+
<script type="module" src="scripts/utils.js"></script>
|
| 387 |
+
<script type="module" src="scripts/db.js"></script>
|
| 388 |
+
<script type="module" src="scripts/api.js"></script>
|
| 389 |
+
<script type="module" src="scripts/editor.js"></script>
|
| 390 |
+
<script type="module" src="scripts/preview.js"></script>
|
| 391 |
+
<script type="module" src="scripts/documents.js"></script>
|
| 392 |
+
<script type="module" src="scripts/ai-tools.js"></script>
|
| 393 |
+
<script type="module" src="scripts/export.js"></script>
|
| 394 |
+
<script type="module" src="scripts/templates.js"></script>
|
| 395 |
+
<script type="module" src="scripts/app.js"></script>
|
| 396 |
+
</body>
|
| 397 |
+
|
| 398 |
+
</html>
|
libs/dexie.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Dexie=t()})(this,function(){"use strict";var s=function(e,t){return(s=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};var _=function(){return(_=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function i(e,t,n){if(n||2===arguments.length)for(var r,i=0,o=t.length;i<o;i++)!r&&i in t||((r=r||Array.prototype.slice.call(t,0,i))[i]=t[i]);return e.concat(r||Array.prototype.slice.call(t))}var f="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,O=Object.keys,x=Array.isArray;function a(t,n){return"object"!=typeof n||O(n).forEach(function(e){t[e]=n[e]}),t}"undefined"==typeof Promise||f.Promise||(f.Promise=Promise);var c=Object.getPrototypeOf,n={}.hasOwnProperty;function m(e,t){return n.call(e,t)}function r(t,n){"function"==typeof n&&(n=n(c(t))),("undefined"==typeof Reflect?O:Reflect.ownKeys)(n).forEach(function(e){l(t,e,n[e])})}var u=Object.defineProperty;function l(e,t,n,r){u(e,t,a(n&&m(n,"get")&&"function"==typeof n.get?{get:n.get,set:n.set,configurable:!0}:{value:n,configurable:!0,writable:!0},r))}function o(t){return{from:function(e){return t.prototype=Object.create(e.prototype),l(t.prototype,"constructor",t),{extend:r.bind(null,t.prototype)}}}}var h=Object.getOwnPropertyDescriptor;var d=[].slice;function b(e,t,n){return d.call(e,t,n)}function p(e,t){return t(e)}function y(e){if(!e)throw new Error("Assertion Failed")}function v(e){f.setImmediate?setImmediate(e):setTimeout(e,0)}function g(e,t){if("string"==typeof t&&m(e,t))return e[t];if(!t)return e;if("string"!=typeof t){for(var n=[],r=0,i=t.length;r<i;++r){var o=g(e,t[r]);n.push(o)}return n}var a=t.indexOf(".");if(-1!==a){var u=e[t.substr(0,a)];return null==u?void 0:g(u,t.substr(a+1))}}function w(e,t,n){if(e&&void 0!==t&&!("isFrozen"in Object&&Object.isFrozen(e)))if("string"!=typeof t&&"length"in t){y("string"!=typeof n&&"length"in n);for(var r=0,i=t.length;r<i;++r)w(e,t[r],n[r])}else{var o,a,u=t.indexOf(".");-1!==u?(o=t.substr(0,u),""===(a=t.substr(u+1))?void 0===n?x(e)&&!isNaN(parseInt(o))?e.splice(o,1):delete e[o]:e[o]=n:w(u=!(u=e[o])||!m(e,o)?e[o]={}:u,a,n)):void 0===n?x(e)&&!isNaN(parseInt(t))?e.splice(t,1):delete e[t]:e[t]=n}}function k(e){var t,n={};for(t in e)m(e,t)&&(n[t]=e[t]);return n}var t=[].concat;function P(e){return t.apply([],e)}var e="BigUint64Array,BigInt64Array,Array,Boolean,String,Date,RegExp,Blob,File,FileList,FileSystemFileHandle,FileSystemDirectoryHandle,ArrayBuffer,DataView,Uint8ClampedArray,ImageBitmap,ImageData,Map,Set,CryptoKey".split(",").concat(P([8,16,32,64].map(function(t){return["Int","Uint","Float"].map(function(e){return e+t+"Array"})}))).filter(function(e){return f[e]}),K=new Set(e.map(function(e){return f[e]}));var E=null;function S(e){E=new WeakMap;e=function e(t){if(!t||"object"!=typeof t)return t;var n=E.get(t);if(n)return n;if(x(t)){n=[],E.set(t,n);for(var r=0,i=t.length;r<i;++r)n.push(e(t[r]))}else if(K.has(t.constructor))n=t;else{var o,a=c(t);for(o in n=a===Object.prototype?{}:Object.create(a),E.set(t,n),t)m(t,o)&&(n[o]=e(t[o]))}return n}(e);return E=null,e}var j={}.toString;function A(e){return j.call(e).slice(8,-1)}var C="undefined"!=typeof Symbol?Symbol.iterator:"@@iterator",T="symbol"==typeof C?function(e){var t;return null!=e&&(t=e[C])&&t.apply(e)}:function(){return null};function I(e,t){t=e.indexOf(t);return 0<=t&&e.splice(t,1),0<=t}var q={};function D(e){var t,n,r,i;if(1===arguments.length){if(x(e))return e.slice();if(this===q&&"string"==typeof e)return[e];if(i=T(e)){for(n=[];!(r=i.next()).done;)n.push(r.value);return n}if(null==e)return[e];if("number"!=typeof(t=e.length))return[e];for(n=new Array(t);t--;)n[t]=e[t];return n}for(t=arguments.length,n=new Array(t);t--;)n[t]=arguments[t];return n}var B="undefined"!=typeof Symbol?function(e){return"AsyncFunction"===e[Symbol.toStringTag]}:function(){return!1},R=["Unknown","Constraint","Data","TransactionInactive","ReadOnly","Version","NotFound","InvalidState","InvalidAccess","Abort","Timeout","QuotaExceeded","Syntax","DataClone"],F=["Modify","Bulk","OpenFailed","VersionChange","Schema","Upgrade","InvalidTable","MissingAPI","NoSuchDatabase","InvalidArgument","SubTransaction","Unsupported","Internal","DatabaseClosed","PrematureCommit","ForeignAwait"].concat(R),M={VersionChanged:"Database version changed by other database connection",DatabaseClosed:"Database has been closed",Abort:"Transaction aborted",TransactionInactive:"Transaction has already completed or failed",MissingAPI:"IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb"};function N(e,t){this.name=e,this.message=t}function L(e,t){return e+". Errors: "+Object.keys(t).map(function(e){return t[e].toString()}).filter(function(e,t,n){return n.indexOf(e)===t}).join("\n")}function U(e,t,n,r){this.failures=t,this.failedKeys=r,this.successCount=n,this.message=L(e,t)}function V(e,t){this.name="BulkError",this.failures=Object.keys(t).map(function(e){return t[e]}),this.failuresByPos=t,this.message=L(e,this.failures)}o(N).from(Error).extend({toString:function(){return this.name+": "+this.message}}),o(U).from(N),o(V).from(N);var z=F.reduce(function(e,t){return e[t]=t+"Error",e},{}),W=N,Y=F.reduce(function(e,n){var r=n+"Error";function t(e,t){this.name=r,e?"string"==typeof e?(this.message="".concat(e).concat(t?"\n "+t:""),this.inner=t||null):"object"==typeof e&&(this.message="".concat(e.name," ").concat(e.message),this.inner=e):(this.message=M[n]||r,this.inner=null)}return o(t).from(W),e[n]=t,e},{});Y.Syntax=SyntaxError,Y.Type=TypeError,Y.Range=RangeError;var $=R.reduce(function(e,t){return e[t+"Error"]=Y[t],e},{});var Q=F.reduce(function(e,t){return-1===["Syntax","Type","Range"].indexOf(t)&&(e[t+"Error"]=Y[t]),e},{});function G(){}function X(e){return e}function H(t,n){return null==t||t===X?n:function(e){return n(t(e))}}function J(e,t){return function(){e.apply(this,arguments),t.apply(this,arguments)}}function Z(i,o){return i===G?o:function(){var e=i.apply(this,arguments);void 0!==e&&(arguments[0]=e);var t=this.onsuccess,n=this.onerror;this.onsuccess=null,this.onerror=null;var r=o.apply(this,arguments);return t&&(this.onsuccess=this.onsuccess?J(t,this.onsuccess):t),n&&(this.onerror=this.onerror?J(n,this.onerror):n),void 0!==r?r:e}}function ee(n,r){return n===G?r:function(){n.apply(this,arguments);var e=this.onsuccess,t=this.onerror;this.onsuccess=this.onerror=null,r.apply(this,arguments),e&&(this.onsuccess=this.onsuccess?J(e,this.onsuccess):e),t&&(this.onerror=this.onerror?J(t,this.onerror):t)}}function te(i,o){return i===G?o:function(e){var t=i.apply(this,arguments);a(e,t);var n=this.onsuccess,r=this.onerror;this.onsuccess=null,this.onerror=null;e=o.apply(this,arguments);return n&&(this.onsuccess=this.onsuccess?J(n,this.onsuccess):n),r&&(this.onerror=this.onerror?J(r,this.onerror):r),void 0===t?void 0===e?void 0:e:a(t,e)}}function ne(e,t){return e===G?t:function(){return!1!==t.apply(this,arguments)&&e.apply(this,arguments)}}function re(i,o){return i===G?o:function(){var e=i.apply(this,arguments);if(e&&"function"==typeof e.then){for(var t=this,n=arguments.length,r=new Array(n);n--;)r[n]=arguments[n];return e.then(function(){return o.apply(t,r)})}return o.apply(this,arguments)}}Q.ModifyError=U,Q.DexieError=N,Q.BulkError=V;var ie="undefined"!=typeof location&&/^(http|https):\/\/(localhost|127\.0\.0\.1)/.test(location.href);function oe(e){ie=e}var ae={},ue=100,e="undefined"==typeof Promise?[]:function(){var e=Promise.resolve();if("undefined"==typeof crypto||!crypto.subtle)return[e,c(e),e];var t=crypto.subtle.digest("SHA-512",new Uint8Array([0]));return[t,c(t),e]}(),R=e[0],F=e[1],e=e[2],F=F&&F.then,se=R&&R.constructor,ce=!!e;var le=function(e,t){be.push([e,t]),he&&(queueMicrotask(Se),he=!1)},fe=!0,he=!0,de=[],pe=[],ye=X,ve={id:"global",global:!0,ref:0,unhandleds:[],onunhandled:G,pgp:!1,env:{},finalize:G},me=ve,be=[],ge=0,we=[];function _e(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");this._listeners=[],this._lib=!1;var t=this._PSD=me;if("function"!=typeof e){if(e!==ae)throw new TypeError("Not a function");return this._state=arguments[1],this._value=arguments[2],void(!1===this._state&&Oe(this,this._value))}this._state=null,this._value=null,++t.ref,function t(r,e){try{e(function(n){if(null===r._state){if(n===r)throw new TypeError("A promise cannot be resolved with itself.");var e=r._lib&&je();n&&"function"==typeof n.then?t(r,function(e,t){n instanceof _e?n._then(e,t):n.then(e,t)}):(r._state=!0,r._value=n,Pe(r)),e&&Ae()}},Oe.bind(null,r))}catch(e){Oe(r,e)}}(this,e)}var xe={get:function(){var u=me,t=Fe;function e(n,r){var i=this,o=!u.global&&(u!==me||t!==Fe),a=o&&!Ue(),e=new _e(function(e,t){Ke(i,new ke(Qe(n,u,o,a),Qe(r,u,o,a),e,t,u))});return this._consoleTask&&(e._consoleTask=this._consoleTask),e}return e.prototype=ae,e},set:function(e){l(this,"then",e&&e.prototype===ae?xe:{get:function(){return e},set:xe.set})}};function ke(e,t,n,r,i){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.resolve=n,this.reject=r,this.psd=i}function Oe(e,t){var n,r;pe.push(t),null===e._state&&(n=e._lib&&je(),t=ye(t),e._state=!1,e._value=t,r=e,de.some(function(e){return e._value===r._value})||de.push(r),Pe(e),n&&Ae())}function Pe(e){var t=e._listeners;e._listeners=[];for(var n=0,r=t.length;n<r;++n)Ke(e,t[n]);var i=e._PSD;--i.ref||i.finalize(),0===ge&&(++ge,le(function(){0==--ge&&Ce()},[]))}function Ke(e,t){if(null!==e._state){var n=e._state?t.onFulfilled:t.onRejected;if(null===n)return(e._state?t.resolve:t.reject)(e._value);++t.psd.ref,++ge,le(Ee,[n,e,t])}else e._listeners.push(t)}function Ee(e,t,n){try{var r,i=t._value;!t._state&&pe.length&&(pe=[]),r=ie&&t._consoleTask?t._consoleTask.run(function(){return e(i)}):e(i),t._state||-1!==pe.indexOf(i)||function(e){var t=de.length;for(;t;)if(de[--t]._value===e._value)return de.splice(t,1)}(t),n.resolve(r)}catch(e){n.reject(e)}finally{0==--ge&&Ce(),--n.psd.ref||n.psd.finalize()}}function Se(){$e(ve,function(){je()&&Ae()})}function je(){var e=fe;return he=fe=!1,e}function Ae(){var e,t,n;do{for(;0<be.length;)for(e=be,be=[],n=e.length,t=0;t<n;++t){var r=e[t];r[0].apply(null,r[1])}}while(0<be.length);he=fe=!0}function Ce(){var e=de;de=[],e.forEach(function(e){e._PSD.onunhandled.call(null,e._value,e)});for(var t=we.slice(0),n=t.length;n;)t[--n]()}function Te(e){return new _e(ae,!1,e)}function Ie(n,r){var i=me;return function(){var e=je(),t=me;try{return We(i,!0),n.apply(this,arguments)}catch(e){r&&r(e)}finally{We(t,!1),e&&Ae()}}}r(_e.prototype,{then:xe,_then:function(e,t){Ke(this,new ke(null,null,e,t,me))},catch:function(e){if(1===arguments.length)return this.then(null,e);var t=e,n=arguments[1];return"function"==typeof t?this.then(null,function(e){return(e instanceof t?n:Te)(e)}):this.then(null,function(e){return(e&&e.name===t?n:Te)(e)})},finally:function(t){return this.then(function(e){return _e.resolve(t()).then(function(){return e})},function(e){return _e.resolve(t()).then(function(){return Te(e)})})},timeout:function(r,i){var o=this;return r<1/0?new _e(function(e,t){var n=setTimeout(function(){return t(new Y.Timeout(i))},r);o.then(e,t).finally(clearTimeout.bind(null,n))}):this}}),"undefined"!=typeof Symbol&&Symbol.toStringTag&&l(_e.prototype,Symbol.toStringTag,"Dexie.Promise"),ve.env=Ye(),r(_e,{all:function(){var o=D.apply(null,arguments).map(Ve);return new _e(function(n,r){0===o.length&&n([]);var i=o.length;o.forEach(function(e,t){return _e.resolve(e).then(function(e){o[t]=e,--i||n(o)},r)})})},resolve:function(n){return n instanceof _e?n:n&&"function"==typeof n.then?new _e(function(e,t){n.then(e,t)}):new _e(ae,!0,n)},reject:Te,race:function(){var e=D.apply(null,arguments).map(Ve);return new _e(function(t,n){e.map(function(e){return _e.resolve(e).then(t,n)})})},PSD:{get:function(){return me},set:function(e){return me=e}},totalEchoes:{get:function(){return Fe}},newPSD:Ne,usePSD:$e,scheduler:{get:function(){return le},set:function(e){le=e}},rejectionMapper:{get:function(){return ye},set:function(e){ye=e}},follow:function(i,n){return new _e(function(e,t){return Ne(function(n,r){var e=me;e.unhandleds=[],e.onunhandled=r,e.finalize=J(function(){var t,e=this;t=function(){0===e.unhandleds.length?n():r(e.unhandleds[0])},we.push(function e(){t(),we.splice(we.indexOf(e),1)}),++ge,le(function(){0==--ge&&Ce()},[])},e.finalize),i()},n,e,t)})}}),se&&(se.allSettled&&l(_e,"allSettled",function(){var e=D.apply(null,arguments).map(Ve);return new _e(function(n){0===e.length&&n([]);var r=e.length,i=new Array(r);e.forEach(function(e,t){return _e.resolve(e).then(function(e){return i[t]={status:"fulfilled",value:e}},function(e){return i[t]={status:"rejected",reason:e}}).then(function(){return--r||n(i)})})})}),se.any&&"undefined"!=typeof AggregateError&&l(_e,"any",function(){var e=D.apply(null,arguments).map(Ve);return new _e(function(n,r){0===e.length&&r(new AggregateError([]));var i=e.length,o=new Array(i);e.forEach(function(e,t){return _e.resolve(e).then(function(e){return n(e)},function(e){o[t]=e,--i||r(new AggregateError(o))})})})}),se.withResolvers&&(_e.withResolvers=se.withResolvers));var qe={awaits:0,echoes:0,id:0},De=0,Be=[],Re=0,Fe=0,Me=0;function Ne(e,t,n,r){var i=me,o=Object.create(i);o.parent=i,o.ref=0,o.global=!1,o.id=++Me,ve.env,o.env=ce?{Promise:_e,PromiseProp:{value:_e,configurable:!0,writable:!0},all:_e.all,race:_e.race,allSettled:_e.allSettled,any:_e.any,resolve:_e.resolve,reject:_e.reject}:{},t&&a(o,t),++i.ref,o.finalize=function(){--this.parent.ref||this.parent.finalize()};r=$e(o,e,n,r);return 0===o.ref&&o.finalize(),r}function Le(){return qe.id||(qe.id=++De),++qe.awaits,qe.echoes+=ue,qe.id}function Ue(){return!!qe.awaits&&(0==--qe.awaits&&(qe.id=0),qe.echoes=qe.awaits*ue,!0)}function Ve(e){return qe.echoes&&e&&e.constructor===se?(Le(),e.then(function(e){return Ue(),e},function(e){return Ue(),Xe(e)})):e}function ze(){var e=Be[Be.length-1];Be.pop(),We(e,!1)}function We(e,t){var n,r=me;(t?!qe.echoes||Re++&&e===me:!Re||--Re&&e===me)||queueMicrotask(t?function(e){++Fe,qe.echoes&&0!=--qe.echoes||(qe.echoes=qe.awaits=qe.id=0),Be.push(me),We(e,!0)}.bind(null,e):ze),e!==me&&(me=e,r===ve&&(ve.env=Ye()),ce&&(n=ve.env.Promise,t=e.env,(r.global||e.global)&&(Object.defineProperty(f,"Promise",t.PromiseProp),n.all=t.all,n.race=t.race,n.resolve=t.resolve,n.reject=t.reject,t.allSettled&&(n.allSettled=t.allSettled),t.any&&(n.any=t.any))))}function Ye(){var e=f.Promise;return ce?{Promise:e,PromiseProp:Object.getOwnPropertyDescriptor(f,"Promise"),all:e.all,race:e.race,allSettled:e.allSettled,any:e.any,resolve:e.resolve,reject:e.reject}:{}}function $e(e,t,n,r,i){var o=me;try{return We(e,!0),t(n,r,i)}finally{We(o,!1)}}function Qe(t,n,r,i){return"function"!=typeof t?t:function(){var e=me;r&&Le(),We(n,!0);try{return t.apply(this,arguments)}finally{We(e,!1),i&&queueMicrotask(Ue)}}}function Ge(e){Promise===se&&0===qe.echoes?0===Re?e():enqueueNativeMicroTask(e):setTimeout(e,0)}-1===(""+F).indexOf("[native code]")&&(Le=Ue=G);var Xe=_e.reject;var He=String.fromCharCode(65535),Je="Invalid key provided. Keys must be of type string, number, Date or Array<string | number | Date>.",Ze="String expected.",et=[],tt="__dbnames",nt="readonly",rt="readwrite";function it(e,t){return e?t?function(){return e.apply(this,arguments)&&t.apply(this,arguments)}:e:t}var ot={type:3,lower:-1/0,lowerOpen:!1,upper:[[]],upperOpen:!1};function at(t){return"string"!=typeof t||/\./.test(t)?function(e){return e}:function(e){return void 0===e[t]&&t in e&&delete(e=S(e))[t],e}}function ut(){throw Y.Type("Entity instances must never be new:ed. Instances are generated by the framework bypassing the constructor.")}function st(e,t){try{var n=ct(e),r=ct(t);if(n!==r)return"Array"===n?1:"Array"===r?-1:"binary"===n?1:"binary"===r?-1:"string"===n?1:"string"===r?-1:"Date"===n?1:"Date"!==r?NaN:-1;switch(n){case"number":case"Date":case"string":return t<e?1:e<t?-1:0;case"binary":return function(e,t){for(var n=e.length,r=t.length,i=n<r?n:r,o=0;o<i;++o)if(e[o]!==t[o])return e[o]<t[o]?-1:1;return n===r?0:n<r?-1:1}(lt(e),lt(t));case"Array":return function(e,t){for(var n=e.length,r=t.length,i=n<r?n:r,o=0;o<i;++o){var a=st(e[o],t[o]);if(0!==a)return a}return n===r?0:n<r?-1:1}(e,t)}}catch(e){}return NaN}function ct(e){var t=typeof e;if("object"!=t)return t;if(ArrayBuffer.isView(e))return"binary";e=A(e);return"ArrayBuffer"===e?"binary":e}function lt(e){return e instanceof Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e)}function ft(t,n,r){var e=t.schema.yProps;return e?(n&&0<r.numFailures&&(n=n.filter(function(e,t){return!r.failures[t]})),Promise.all(e.map(function(e){e=e.updatesTable;return n?t.db.table(e).where("k").anyOf(n).delete():t.db.table(e).clear()})).then(function(){return r})):r}var ht=(dt.prototype.execute=function(e){var t=this["@@propmod"];if(void 0!==t.add){var n=t.add;if(x(n))return i(i([],x(e)?e:[],!0),n,!0).sort();if("number"==typeof n)return(Number(e)||0)+n;if("bigint"==typeof n)try{return BigInt(e)+n}catch(e){return BigInt(0)+n}throw new TypeError("Invalid term ".concat(n))}if(void 0!==t.remove){var r=t.remove;if(x(r))return x(e)?e.filter(function(e){return!r.includes(e)}).sort():[];if("number"==typeof r)return Number(e)-r;if("bigint"==typeof r)try{return BigInt(e)-r}catch(e){return BigInt(0)-r}throw new TypeError("Invalid subtrahend ".concat(r))}n=null===(n=t.replacePrefix)||void 0===n?void 0:n[0];return n&&"string"==typeof e&&e.startsWith(n)?t.replacePrefix[1]+e.substring(n.length):e},dt);function dt(e){this["@@propmod"]=e}function pt(e,t){for(var n=O(t),r=n.length,i=!1,o=0;o<r;++o){var a=n[o],u=t[a],s=g(e,a);u instanceof ht?(w(e,a,u.execute(s)),i=!0):s!==u&&(w(e,a,u),i=!0)}return i}var yt=(vt.prototype._trans=function(e,r,t){var n=this._tx||me.trans,i=this.name,o=ie&&"undefined"!=typeof console&&console.createTask&&console.createTask("Dexie: ".concat("readonly"===e?"read":"write"," ").concat(this.name));function a(e,t,n){if(!n.schema[i])throw new Y.NotFound("Table "+i+" not part of transaction");return r(n.idbtrans,n)}var u=je();try{var s=n&&n.db._novip===this.db._novip?n===me.trans?n._promise(e,a,t):Ne(function(){return n._promise(e,a,t)},{trans:n,transless:me.transless||me}):function t(n,r,i,o){if(n.idbdb&&(n._state.openComplete||me.letThrough||n._vip)){var a=n._createTransaction(r,i,n._dbSchema);try{a.create(),n._state.PR1398_maxLoop=3}catch(e){return e.name===z.InvalidState&&n.isOpen()&&0<--n._state.PR1398_maxLoop?(console.warn("Dexie: Need to reopen db"),n.close({disableAutoOpen:!1}),n.open().then(function(){return t(n,r,i,o)})):Xe(e)}return a._promise(r,function(e,t){return Ne(function(){return me.trans=a,o(e,t,a)})}).then(function(e){if("readwrite"===r)try{a.idbtrans.commit()}catch(e){}return"readonly"===r?e:a._completion.then(function(){return e})})}if(n._state.openComplete)return Xe(new Y.DatabaseClosed(n._state.dbOpenError));if(!n._state.isBeingOpened){if(!n._state.autoOpen)return Xe(new Y.DatabaseClosed);n.open().catch(G)}return n._state.dbReadyPromise.then(function(){return t(n,r,i,o)})}(this.db,e,[this.name],a);return o&&(s._consoleTask=o,s=s.catch(function(e){return console.trace(e),Xe(e)})),s}finally{u&&Ae()}},vt.prototype.get=function(t,e){var n=this;return t&&t.constructor===Object?this.where(t).first(e):null==t?Xe(new Y.Type("Invalid argument to Table.get()")):this._trans("readonly",function(e){return n.core.get({trans:e,key:t}).then(function(e){return n.hook.reading.fire(e)})}).then(e)},vt.prototype.where=function(o){if("string"==typeof o)return new this.db.WhereClause(this,o);if(x(o))return new this.db.WhereClause(this,"[".concat(o.join("+"),"]"));var n=O(o);if(1===n.length)return this.where(n[0]).equals(o[n[0]]);var e=this.schema.indexes.concat(this.schema.primKey).filter(function(t){if(t.compound&&n.every(function(e){return 0<=t.keyPath.indexOf(e)})){for(var e=0;e<n.length;++e)if(-1===n.indexOf(t.keyPath[e]))return!1;return!0}return!1}).sort(function(e,t){return e.keyPath.length-t.keyPath.length})[0];if(e&&this.db._maxKey!==He){var t=e.keyPath.slice(0,n.length);return this.where(t).equals(t.map(function(e){return o[e]}))}!e&&ie&&console.warn("The query ".concat(JSON.stringify(o)," on ").concat(this.name," would benefit from a ")+"compound index [".concat(n.join("+"),"]"));var a=this.schema.idxByName;function u(e,t){return 0===st(e,t)}var r=n.reduce(function(e,t){var n=e[0],r=e[1],e=a[t],i=o[t];return[n||e,n||!e?it(r,e&&e.multi?function(e){e=g(e,t);return x(e)&&e.some(function(e){return u(i,e)})}:function(e){return u(i,g(e,t))}):r]},[null,null]),t=r[0],r=r[1];return t?this.where(t.name).equals(o[t.keyPath]).filter(r):e?this.filter(r):this.where(n).equals("")},vt.prototype.filter=function(e){return this.toCollection().and(e)},vt.prototype.count=function(e){return this.toCollection().count(e)},vt.prototype.offset=function(e){return this.toCollection().offset(e)},vt.prototype.limit=function(e){return this.toCollection().limit(e)},vt.prototype.each=function(e){return this.toCollection().each(e)},vt.prototype.toArray=function(e){return this.toCollection().toArray(e)},vt.prototype.toCollection=function(){return new this.db.Collection(new this.db.WhereClause(this))},vt.prototype.orderBy=function(e){return new this.db.Collection(new this.db.WhereClause(this,x(e)?"[".concat(e.join("+"),"]"):e))},vt.prototype.reverse=function(){return this.toCollection().reverse()},vt.prototype.mapToClass=function(r){var e,t=this.db,n=this.name;function i(){return null!==e&&e.apply(this,arguments)||this}(this.schema.mappedClass=r).prototype instanceof ut&&(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}s(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}(i,e=r),Object.defineProperty(i.prototype,"db",{get:function(){return t},enumerable:!1,configurable:!0}),i.prototype.table=function(){return n},r=i);for(var o=new Set,a=r.prototype;a;a=c(a))Object.getOwnPropertyNames(a).forEach(function(e){return o.add(e)});function u(e){if(!e)return e;var t,n=Object.create(r.prototype);for(t in e)if(!o.has(t))try{n[t]=e[t]}catch(e){}return n}return this.schema.readHook&&this.hook.reading.unsubscribe(this.schema.readHook),this.schema.readHook=u,this.hook("reading",u),r},vt.prototype.defineClass=function(){return this.mapToClass(function(e){a(this,e)})},vt.prototype.add=function(t,n){var r=this,e=this.schema.primKey,i=e.auto,o=e.keyPath,a=t;return o&&i&&(a=at(o)(t)),this._trans("readwrite",function(e){return r.core.mutate({trans:e,type:"add",keys:null!=n?[n]:null,values:[a]})}).then(function(e){return e.numFailures?_e.reject(e.failures[0]):e.lastResult}).then(function(e){if(o)try{w(t,o,e)}catch(e){}return e})},vt.prototype.upsert=function(r,i){var o=this,a=this.schema.primKey.keyPath;return this._trans("readwrite",function(n){return o.core.get({trans:n,key:r}).then(function(t){var e=null!=t?t:{};return pt(e,i),a&&w(e,a,r),o.core.mutate({trans:n,type:"put",values:[e],keys:[r],upsert:!0,updates:{keys:[r],changeSpecs:[i]}}).then(function(e){return e.numFailures?_e.reject(e.failures[0]):!!t})})})},vt.prototype.update=function(e,t){if("object"!=typeof e||x(e))return this.where(":id").equals(e).modify(t);e=g(e,this.schema.primKey.keyPath);return void 0===e?Xe(new Y.InvalidArgument("Given object does not contain its primary key")):this.where(":id").equals(e).modify(t)},vt.prototype.put=function(t,n){var r=this,e=this.schema.primKey,i=e.auto,o=e.keyPath,a=t;return o&&i&&(a=at(o)(t)),this._trans("readwrite",function(e){return r.core.mutate({trans:e,type:"put",values:[a],keys:null!=n?[n]:null})}).then(function(e){return e.numFailures?_e.reject(e.failures[0]):e.lastResult}).then(function(e){if(o)try{w(t,o,e)}catch(e){}return e})},vt.prototype.delete=function(t){var n=this;return this._trans("readwrite",function(e){return n.core.mutate({trans:e,type:"delete",keys:[t]}).then(function(e){return ft(n,[t],e)}).then(function(e){return e.numFailures?_e.reject(e.failures[0]):void 0})})},vt.prototype.clear=function(){var t=this;return this._trans("readwrite",function(e){return t.core.mutate({trans:e,type:"deleteRange",range:ot}).then(function(e){return ft(t,null,e)})}).then(function(e){return e.numFailures?_e.reject(e.failures[0]):void 0})},vt.prototype.bulkGet=function(t){var n=this;return this._trans("readonly",function(e){return n.core.getMany({keys:t,trans:e}).then(function(e){return e.map(function(e){return n.hook.reading.fire(e)})})})},vt.prototype.bulkAdd=function(r,e,t){var o=this,a=Array.isArray(e)?e:void 0,u=(t=t||(a?void 0:e))?t.allKeys:void 0;return this._trans("readwrite",function(e){var t=o.schema.primKey,n=t.auto,t=t.keyPath;if(t&&a)throw new Y.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys");if(a&&a.length!==r.length)throw new Y.InvalidArgument("Arguments objects and keys must have the same length");var i=r.length,t=t&&n?r.map(at(t)):r;return o.core.mutate({trans:e,type:"add",keys:a,values:t,wantResults:u}).then(function(e){var t=e.numFailures,n=e.results,r=e.lastResult,e=e.failures;if(0===t)return u?n:r;throw new V("".concat(o.name,".bulkAdd(): ").concat(t," of ").concat(i," operations failed"),e)})})},vt.prototype.bulkPut=function(r,e,t){var o=this,a=Array.isArray(e)?e:void 0,u=(t=t||(a?void 0:e))?t.allKeys:void 0;return this._trans("readwrite",function(e){var t=o.schema.primKey,n=t.auto,t=t.keyPath;if(t&&a)throw new Y.InvalidArgument("bulkPut(): keys argument invalid on tables with inbound keys");if(a&&a.length!==r.length)throw new Y.InvalidArgument("Arguments objects and keys must have the same length");var i=r.length,t=t&&n?r.map(at(t)):r;return o.core.mutate({trans:e,type:"put",keys:a,values:t,wantResults:u}).then(function(e){var t=e.numFailures,n=e.results,r=e.lastResult,e=e.failures;if(0===t)return u?n:r;throw new V("".concat(o.name,".bulkPut(): ").concat(t," of ").concat(i," operations failed"),e)})})},vt.prototype.bulkUpdate=function(t){var h=this,n=this.core,r=t.map(function(e){return e.key}),i=t.map(function(e){return e.changes}),d=[];return this._trans("readwrite",function(e){return n.getMany({trans:e,keys:r,cache:"clone"}).then(function(c){var l=[],f=[];t.forEach(function(e,t){var n=e.key,r=e.changes,i=c[t];if(i){for(var o=0,a=Object.keys(r);o<a.length;o++){var u=a[o],s=r[u];if(u===h.schema.primKey.keyPath){if(0!==st(s,n))throw new Y.Constraint("Cannot update primary key in bulkUpdate()")}else w(i,u,s)}d.push(t),l.push(n),f.push(i)}});var s=l.length;return n.mutate({trans:e,type:"put",keys:l,values:f,updates:{keys:r,changeSpecs:i}}).then(function(e){var t=e.numFailures,n=e.failures;if(0===t)return s;for(var r=0,i=Object.keys(n);r<i.length;r++){var o,a=i[r],u=d[Number(a)];null!=u&&(o=n[a],delete n[a],n[u]=o)}throw new V("".concat(h.name,".bulkUpdate(): ").concat(t," of ").concat(s," operations failed"),n)})})})},vt.prototype.bulkDelete=function(t){var r=this,i=t.length;return this._trans("readwrite",function(e){return r.core.mutate({trans:e,type:"delete",keys:t}).then(function(e){return ft(r,t,e)})}).then(function(e){var t=e.numFailures,n=e.lastResult,e=e.failures;if(0===t)return n;throw new V("".concat(r.name,".bulkDelete(): ").concat(t," of ").concat(i," operations failed"),e)})},vt);function vt(){}function mt(i){function t(e,t){if(t){for(var n=arguments.length,r=new Array(n-1);--n;)r[n-1]=arguments[n];return a[e].subscribe.apply(null,r),i}if("string"==typeof e)return a[e]}var a={};t.addEventType=u;for(var e=1,n=arguments.length;e<n;++e)u(arguments[e]);return t;function u(e,n,r){if("object"!=typeof e){var i;n=n||ne;var o={subscribers:[],fire:r=r||G,subscribe:function(e){-1===o.subscribers.indexOf(e)&&(o.subscribers.push(e),o.fire=n(o.fire,e))},unsubscribe:function(t){o.subscribers=o.subscribers.filter(function(e){return e!==t}),o.fire=o.subscribers.reduce(n,r)}};return a[e]=t[e]=o}O(i=e).forEach(function(e){var t=i[e];if(x(t))u(e,i[e][0],i[e][1]);else{if("asap"!==t)throw new Y.InvalidArgument("Invalid event config");var n=u(e,X,function(){for(var e=arguments.length,t=new Array(e);e--;)t[e]=arguments[e];n.subscribers.forEach(function(e){v(function(){e.apply(null,t)})})})}})}}function bt(e,t){return o(t).from({prototype:e}),t}function gt(e,t){return!(e.filter||e.algorithm||e.or)&&(t?e.justLimit:!e.replayFilter)}function wt(e,t){e.filter=it(e.filter,t)}function _t(e,t,n){var r=e.replayFilter;e.replayFilter=r?function(){return it(r(),t())}:t,e.justLimit=n&&!r}function xt(e,t){if(e.isPrimKey)return t.primaryKey;var n=t.getIndexByKeyPath(e.index);if(!n)throw new Y.Schema("KeyPath "+e.index+" on object store "+t.name+" is not indexed");return n}function kt(e,t,n){var r=xt(e,t.schema);return t.openCursor({trans:n,values:!e.keysOnly,reverse:"prev"===e.dir,unique:!!e.unique,query:{index:r,range:e.range}})}function Ot(e,o,t,n){var a=e.replayFilter?it(e.filter,e.replayFilter()):e.filter;if(e.or){var u={},r=function(e,t,n){var r,i;a&&!a(t,n,function(e){return t.stop(e)},function(e){return t.fail(e)})||("[object ArrayBuffer]"===(i=""+(r=t.primaryKey))&&(i=""+new Uint8Array(r)),m(u,i)||(u[i]=!0,o(e,t,n)))};return Promise.all([e.or._iterate(r,t),Pt(kt(e,n,t),e.algorithm,r,!e.keysOnly&&e.valueMapper)])}return Pt(kt(e,n,t),it(e.algorithm,a),o,!e.keysOnly&&e.valueMapper)}function Pt(e,r,i,o){var a=Ie(o?function(e,t,n){return i(o(e),t,n)}:i);return e.then(function(n){if(n)return n.start(function(){var t=function(){return n.continue()};r&&!r(n,function(e){return t=e},function(e){n.stop(e),t=G},function(e){n.fail(e),t=G})||a(n.value,n,function(e){return t=e}),t()})})}var Kt=(Et.prototype._read=function(e,t){var n=this._ctx;return n.error?n.table._trans(null,Xe.bind(null,n.error)):n.table._trans("readonly",e).then(t)},Et.prototype._write=function(e){var t=this._ctx;return t.error?t.table._trans(null,Xe.bind(null,t.error)):t.table._trans("readwrite",e,"locked")},Et.prototype._addAlgorithm=function(e){var t=this._ctx;t.algorithm=it(t.algorithm,e)},Et.prototype._iterate=function(e,t){return Ot(this._ctx,e,t,this._ctx.table.core)},Et.prototype.clone=function(e){var t=Object.create(this.constructor.prototype),n=Object.create(this._ctx);return e&&a(n,e),t._ctx=n,t},Et.prototype.raw=function(){return this._ctx.valueMapper=null,this},Et.prototype.each=function(t){var n=this._ctx;return this._read(function(e){return Ot(n,t,e,n.table.core)})},Et.prototype.count=function(e){var i=this;return this._read(function(e){var t=i._ctx,n=t.table.core;if(gt(t,!0))return n.count({trans:e,query:{index:xt(t,n.schema),range:t.range}}).then(function(e){return Math.min(e,t.limit)});var r=0;return Ot(t,function(){return++r,!1},e,n).then(function(){return r})}).then(e)},Et.prototype.sortBy=function(e,t){var n=e.split(".").reverse(),r=n[0],i=n.length-1;function o(e,t){return t?o(e[n[t]],t-1):e[r]}var a="next"===this._ctx.dir?1:-1;function u(e,t){return st(o(e,i),o(t,i))*a}return this.toArray(function(e){return e.sort(u)}).then(t)},Et.prototype.toArray=function(e){var o=this;return this._read(function(e){var t=o._ctx;if("next"===t.dir&>(t,!0)&&0<t.limit){var n=t.valueMapper,r=xt(t,t.table.core.schema);return t.table.core.query({trans:e,limit:t.limit,values:!0,query:{index:r,range:t.range}}).then(function(e){e=e.result;return n?e.map(n):e})}var i=[];return Ot(t,function(e){return i.push(e)},e,t.table.core).then(function(){return i})},e)},Et.prototype.offset=function(t){var e=this._ctx;return t<=0||(e.offset+=t,gt(e)?_t(e,function(){var n=t;return function(e,t){return 0===n||(1===n?--n:t(function(){e.advance(n),n=0}),!1)}}):_t(e,function(){var e=t;return function(){return--e<0}})),this},Et.prototype.limit=function(e){return this._ctx.limit=Math.min(this._ctx.limit,e),_t(this._ctx,function(){var r=e;return function(e,t,n){return--r<=0&&t(n),0<=r}},!0),this},Et.prototype.until=function(r,i){return wt(this._ctx,function(e,t,n){return!r(e.value)||(t(n),i)}),this},Et.prototype.first=function(e){return this.limit(1).toArray(function(e){return e[0]}).then(e)},Et.prototype.last=function(e){return this.reverse().first(e)},Et.prototype.filter=function(t){var e;return wt(this._ctx,function(e){return t(e.value)}),(e=this._ctx).isMatch=it(e.isMatch,t),this},Et.prototype.and=function(e){return this.filter(e)},Et.prototype.or=function(e){return new this.db.WhereClause(this._ctx.table,e,this)},Et.prototype.reverse=function(){return this._ctx.dir="prev"===this._ctx.dir?"next":"prev",this._ondirectionchange&&this._ondirectionchange(this._ctx.dir),this},Et.prototype.desc=function(){return this.reverse()},Et.prototype.eachKey=function(n){var e=this._ctx;return e.keysOnly=!e.isMatch,this.each(function(e,t){n(t.key,t)})},Et.prototype.eachUniqueKey=function(e){return this._ctx.unique="unique",this.eachKey(e)},Et.prototype.eachPrimaryKey=function(n){var e=this._ctx;return e.keysOnly=!e.isMatch,this.each(function(e,t){n(t.primaryKey,t)})},Et.prototype.keys=function(e){var t=this._ctx;t.keysOnly=!t.isMatch;var n=[];return this.each(function(e,t){n.push(t.key)}).then(function(){return n}).then(e)},Et.prototype.primaryKeys=function(e){var n=this._ctx;if("next"===n.dir&>(n,!0)&&0<n.limit)return this._read(function(e){var t=xt(n,n.table.core.schema);return n.table.core.query({trans:e,values:!1,limit:n.limit,query:{index:t,range:n.range}})}).then(function(e){return e.result}).then(e);n.keysOnly=!n.isMatch;var r=[];return this.each(function(e,t){r.push(t.primaryKey)}).then(function(){return r}).then(e)},Et.prototype.uniqueKeys=function(e){return this._ctx.unique="unique",this.keys(e)},Et.prototype.firstKey=function(e){return this.limit(1).keys(function(e){return e[0]}).then(e)},Et.prototype.lastKey=function(e){return this.reverse().firstKey(e)},Et.prototype.distinct=function(){var e=this._ctx,e=e.index&&e.table.schema.idxByName[e.index];if(!e||!e.multi)return this;var n={};return wt(this._ctx,function(e){var t=e.primaryKey.toString(),e=m(n,t);return n[t]=!0,!e}),this},Et.prototype.modify=function(x){var n=this,k=this._ctx;return this._write(function(p){var y="function"==typeof x?x:function(e){return pt(e,x)},v=k.table.core,e=v.schema.primaryKey,m=e.outbound,b=e.extractKey,g=200,e=n.db._options.modifyChunkSize;e&&(g="object"==typeof e?e[v.name]||e["*"]||200:e);function w(e,t){var n=t.failures,t=t.numFailures;u+=e-t;for(var r=0,i=O(n);r<i.length;r++){var o=i[r];a.push(n[o])}}var a=[],u=0,t=[],_=x===St;return n.clone().primaryKeys().then(function(f){function h(s){var c=Math.min(g,f.length-s),l=f.slice(s,s+c);return(_?Promise.resolve([]):v.getMany({trans:p,keys:l,cache:"immutable"})).then(function(e){var n=[],t=[],r=m?[]:null,i=_?l:[];if(!_)for(var o=0;o<c;++o){var a=e[o],u={value:S(a),primKey:f[s+o]};!1!==y.call(u,u.value,u)&&(null==u.value?i.push(f[s+o]):m||0===st(b(a),b(u.value))?(t.push(u.value),m&&r.push(f[s+o])):(i.push(f[s+o]),n.push(u.value)))}return Promise.resolve(0<n.length&&v.mutate({trans:p,type:"add",values:n}).then(function(e){for(var t in e.failures)i.splice(parseInt(t),1);w(n.length,e)})).then(function(){return(0<t.length||d&&"object"==typeof x)&&v.mutate({trans:p,type:"put",keys:r,values:t,criteria:d,changeSpec:"function"!=typeof x&&x,isAdditionalChunk:0<s}).then(function(e){return w(t.length,e)})}).then(function(){return(0<i.length||d&&_)&&v.mutate({trans:p,type:"delete",keys:i,criteria:d,isAdditionalChunk:0<s}).then(function(e){return ft(k.table,i,e)}).then(function(e){return w(i.length,e)})}).then(function(){return f.length>s+c&&h(s+g)})})}var d=gt(k)&&k.limit===1/0&&("function"!=typeof x||_)&&{index:k.index,range:k.range};return h(0).then(function(){if(0<a.length)throw new U("Error modifying one or more objects",a,u,t);return f.length})})})},Et.prototype.delete=function(){var i=this._ctx,n=i.range;return!gt(i)||i.table.schema.yProps||!i.isPrimKey&&3!==n.type?this.modify(St):this._write(function(e){var t=i.table.core.schema.primaryKey,r=n;return i.table.core.count({trans:e,query:{index:t,range:r}}).then(function(n){return i.table.core.mutate({trans:e,type:"deleteRange",range:r}).then(function(e){var t=e.failures,e=e.numFailures;if(e)throw new U("Could not delete some values",Object.keys(t).map(function(e){return t[e]}),n-e);return n-e})})})},Et);function Et(){}var St=function(e,t){return t.value=null};function jt(e,t){return e<t?-1:e===t?0:1}function At(e,t){return t<e?-1:e===t?0:1}function Ct(e,t,n){e=e instanceof Bt?new e.Collection(e):e;return e._ctx.error=new(n||TypeError)(t),e}function Tt(e){return new e.Collection(e,function(){return Dt("")}).limit(0)}function It(e,s,n,r){var i,c,l,f,h,d,p,y=n.length;if(!n.every(function(e){return"string"==typeof e}))return Ct(e,Ze);function t(e){i="next"===e?function(e){return e.toUpperCase()}:function(e){return e.toLowerCase()},c="next"===e?function(e){return e.toLowerCase()}:function(e){return e.toUpperCase()},l="next"===e?jt:At;var t=n.map(function(e){return{lower:c(e),upper:i(e)}}).sort(function(e,t){return l(e.lower,t.lower)});f=t.map(function(e){return e.upper}),h=t.map(function(e){return e.lower}),p="next"===(d=e)?"":r}t("next");e=new e.Collection(e,function(){return qt(f[0],h[y-1]+r)});e._ondirectionchange=function(e){t(e)};var v=0;return e._addAlgorithm(function(e,t,n){var r=e.key;if("string"!=typeof r)return!1;var i=c(r);if(s(i,h,v))return!0;for(var o=null,a=v;a<y;++a){var u=function(e,t,n,r,i,o){for(var a=Math.min(e.length,r.length),u=-1,s=0;s<a;++s){var c=t[s];if(c!==r[s])return i(e[s],n[s])<0?e.substr(0,s)+n[s]+n.substr(s+1):i(e[s],r[s])<0?e.substr(0,s)+r[s]+n.substr(s+1):0<=u?e.substr(0,u)+t[u]+n.substr(u+1):null;i(e[s],c)<0&&(u=s)}return a<r.length&&"next"===o?e+n.substr(e.length):a<e.length&&"prev"===o?e.substr(0,n.length):u<0?null:e.substr(0,u)+r[u]+n.substr(u+1)}(r,i,f[a],h[a],l,d);null===u&&null===o?v=a+1:(null===o||0<l(o,u))&&(o=u)}return t(null!==o?function(){e.continue(o+p)}:n),!1}),e}function qt(e,t,n,r){return{type:2,lower:e,upper:t,lowerOpen:n,upperOpen:r}}function Dt(e){return{type:1,lower:e,upper:e}}var Bt=(Object.defineProperty(Rt.prototype,"Collection",{get:function(){return this._ctx.table.db.Collection},enumerable:!1,configurable:!0}),Rt.prototype.between=function(e,t,n,r){n=!1!==n,r=!0===r;try{return 0<this._cmp(e,t)||0===this._cmp(e,t)&&(n||r)&&(!n||!r)?Tt(this):new this.Collection(this,function(){return qt(e,t,!n,!r)})}catch(e){return Ct(this,Je)}},Rt.prototype.equals=function(e){return null==e?Ct(this,Je):new this.Collection(this,function(){return Dt(e)})},Rt.prototype.above=function(e){return null==e?Ct(this,Je):new this.Collection(this,function(){return qt(e,void 0,!0)})},Rt.prototype.aboveOrEqual=function(e){return null==e?Ct(this,Je):new this.Collection(this,function(){return qt(e,void 0,!1)})},Rt.prototype.below=function(e){return null==e?Ct(this,Je):new this.Collection(this,function(){return qt(void 0,e,!1,!0)})},Rt.prototype.belowOrEqual=function(e){return null==e?Ct(this,Je):new this.Collection(this,function(){return qt(void 0,e)})},Rt.prototype.startsWith=function(e){return"string"!=typeof e?Ct(this,Ze):this.between(e,e+He,!0,!0)},Rt.prototype.startsWithIgnoreCase=function(e){return""===e?this.startsWith(e):It(this,function(e,t){return 0===e.indexOf(t[0])},[e],He)},Rt.prototype.equalsIgnoreCase=function(e){return It(this,function(e,t){return e===t[0]},[e],"")},Rt.prototype.anyOfIgnoreCase=function(){var e=D.apply(q,arguments);return 0===e.length?Tt(this):It(this,function(e,t){return-1!==t.indexOf(e)},e,"")},Rt.prototype.startsWithAnyOfIgnoreCase=function(){var e=D.apply(q,arguments);return 0===e.length?Tt(this):It(this,function(t,e){return e.some(function(e){return 0===t.indexOf(e)})},e,He)},Rt.prototype.anyOf=function(){var t=this,i=D.apply(q,arguments),o=this._cmp;try{i.sort(o)}catch(e){return Ct(this,Je)}if(0===i.length)return Tt(this);var e=new this.Collection(this,function(){return qt(i[0],i[i.length-1])});e._ondirectionchange=function(e){o="next"===e?t._ascending:t._descending,i.sort(o)};var a=0;return e._addAlgorithm(function(e,t,n){for(var r=e.key;0<o(r,i[a]);)if(++a===i.length)return t(n),!1;return 0===o(r,i[a])||(t(function(){e.continue(i[a])}),!1)}),e},Rt.prototype.notEqual=function(e){return this.inAnyRange([[-1/0,e],[e,this.db._maxKey]],{includeLowers:!1,includeUppers:!1})},Rt.prototype.noneOf=function(){var e=D.apply(q,arguments);if(0===e.length)return new this.Collection(this);try{e.sort(this._ascending)}catch(e){return Ct(this,Je)}var t=e.reduce(function(e,t){return e?e.concat([[e[e.length-1][1],t]]):[[-1/0,t]]},null);return t.push([e[e.length-1],this.db._maxKey]),this.inAnyRange(t,{includeLowers:!1,includeUppers:!1})},Rt.prototype.inAnyRange=function(e,t){var o=this,a=this._cmp,u=this._ascending,n=this._descending,s=this._min,c=this._max;if(0===e.length)return Tt(this);if(!e.every(function(e){return void 0!==e[0]&&void 0!==e[1]&&u(e[0],e[1])<=0}))return Ct(this,"First argument to inAnyRange() must be an Array of two-value Arrays [lower,upper] where upper must not be lower than lower",Y.InvalidArgument);var r=!t||!1!==t.includeLowers,i=t&&!0===t.includeUppers;var l,f=u;function h(e,t){return f(e[0],t[0])}try{(l=e.reduce(function(e,t){for(var n=0,r=e.length;n<r;++n){var i=e[n];if(a(t[0],i[1])<0&&0<a(t[1],i[0])){i[0]=s(i[0],t[0]),i[1]=c(i[1],t[1]);break}}return n===r&&e.push(t),e},[])).sort(h)}catch(e){return Ct(this,Je)}var d=0,p=i?function(e){return 0<u(e,l[d][1])}:function(e){return 0<=u(e,l[d][1])},y=r?function(e){return 0<n(e,l[d][0])}:function(e){return 0<=n(e,l[d][0])};var v=p,e=new this.Collection(this,function(){return qt(l[0][0],l[l.length-1][1],!r,!i)});return e._ondirectionchange=function(e){f="next"===e?(v=p,u):(v=y,n),l.sort(h)},e._addAlgorithm(function(e,t,n){for(var r,i=e.key;v(i);)if(++d===l.length)return t(n),!1;return!p(r=i)&&!y(r)||(0===o._cmp(i,l[d][1])||0===o._cmp(i,l[d][0])||t(function(){f===u?e.continue(l[d][0]):e.continue(l[d][1])}),!1)}),e},Rt.prototype.startsWithAnyOf=function(){var e=D.apply(q,arguments);return e.every(function(e){return"string"==typeof e})?0===e.length?Tt(this):this.inAnyRange(e.map(function(e){return[e,e+He]})):Ct(this,"startsWithAnyOf() only works with strings")},Rt);function Rt(){}function Ft(t){return Ie(function(e){return Mt(e),t(e.target.error),!1})}function Mt(e){e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault()}var Nt="storagemutated",Lt="x-storagemutated-1",Ut=mt(null,Nt),Vt=(zt.prototype._lock=function(){return y(!me.global),++this._reculock,1!==this._reculock||me.global||(me.lockOwnerFor=this),this},zt.prototype._unlock=function(){if(y(!me.global),0==--this._reculock)for(me.global||(me.lockOwnerFor=null);0<this._blockedFuncs.length&&!this._locked();){var e=this._blockedFuncs.shift();try{$e(e[1],e[0])}catch(e){}}return this},zt.prototype._locked=function(){return this._reculock&&me.lockOwnerFor!==this},zt.prototype.create=function(t){var n=this;if(!this.mode)return this;var e=this.db.idbdb,r=this.db._state.dbOpenError;if(y(!this.idbtrans),!t&&!e)switch(r&&r.name){case"DatabaseClosedError":throw new Y.DatabaseClosed(r);case"MissingAPIError":throw new Y.MissingAPI(r.message,r);default:throw new Y.OpenFailed(r)}if(!this.active)throw new Y.TransactionInactive;return y(null===this._completion._state),(t=this.idbtrans=t||(this.db.core||e).transaction(this.storeNames,this.mode,{durability:this.chromeTransactionDurability})).onerror=Ie(function(e){Mt(e),n._reject(t.error)}),t.onabort=Ie(function(e){Mt(e),n.active&&n._reject(new Y.Abort(t.error)),n.active=!1,n.on("abort").fire(e)}),t.oncomplete=Ie(function(){n.active=!1,n._resolve(),"mutatedParts"in t&&Ut.storagemutated.fire(t.mutatedParts)}),this},zt.prototype._promise=function(n,r,i){var o=this;if("readwrite"===n&&"readwrite"!==this.mode)return Xe(new Y.ReadOnly("Transaction is readonly"));if(!this.active)return Xe(new Y.TransactionInactive);if(this._locked())return new _e(function(e,t){o._blockedFuncs.push([function(){o._promise(n,r,i).then(e,t)},me])});if(i)return Ne(function(){var e=new _e(function(e,t){o._lock();var n=r(e,t,o);n&&n.then&&n.then(e,t)});return e.finally(function(){return o._unlock()}),e._lib=!0,e});var e=new _e(function(e,t){var n=r(e,t,o);n&&n.then&&n.then(e,t)});return e._lib=!0,e},zt.prototype._root=function(){return this.parent?this.parent._root():this},zt.prototype.waitFor=function(e){var t,r=this._root(),i=_e.resolve(e);r._waitingFor?r._waitingFor=r._waitingFor.then(function(){return i}):(r._waitingFor=i,r._waitingQueue=[],t=r.idbtrans.objectStore(r.storeNames[0]),function e(){for(++r._spinCount;r._waitingQueue.length;)r._waitingQueue.shift()();r._waitingFor&&(t.get(-1/0).onsuccess=e)}());var o=r._waitingFor;return new _e(function(t,n){i.then(function(e){return r._waitingQueue.push(Ie(t.bind(null,e)))},function(e){return r._waitingQueue.push(Ie(n.bind(null,e)))}).finally(function(){r._waitingFor===o&&(r._waitingFor=null)})})},zt.prototype.abort=function(){this.active&&(this.active=!1,this.idbtrans&&this.idbtrans.abort(),this._reject(new Y.Abort))},zt.prototype.table=function(e){var t=this._memoizedTables||(this._memoizedTables={});if(m(t,e))return t[e];var n=this.schema[e];if(!n)throw new Y.NotFound("Table "+e+" not part of transaction");n=new this.db.Table(e,n,this);return n.core=this.db.core.table(e),t[e]=n},zt);function zt(){}function Wt(e,t,n,r,i,o,a,u){return{name:e,keyPath:t,unique:n,multi:r,auto:i,compound:o,src:(n&&!a?"&":"")+(r?"*":"")+(i?"++":"")+Yt(t),type:u}}function Yt(e){return"string"==typeof e?e:e?"["+[].join.call(e,"+")+"]":""}function $t(e,t,n){return{name:e,primKey:t,indexes:n,mappedClass:null,idxByName:(r=function(e){return[e.name,e]},n.reduce(function(e,t,n){n=r(t,n);return n&&(e[n[0]]=n[1]),e},{}))};var r}var Qt=function(e){try{return e.only([[]]),Qt=function(){return[[]]},[[]]}catch(e){return Qt=function(){return He},He}};function Gt(t){return null==t?function(){}:"string"==typeof t?1===(n=t).split(".").length?function(e){return e[n]}:function(e){return g(e,n)}:function(e){return g(e,t)};var n}function Xt(e){return[].slice.call(e)}var Ht=0;function Jt(e){return null==e?":id":"string"==typeof e?e:"[".concat(e.join("+"),"]")}function Zt(e,i,t){function _(e){if(3===e.type)return null;if(4===e.type)throw new Error("Cannot convert never type to IDBKeyRange");var t=e.lower,n=e.upper,r=e.lowerOpen,e=e.upperOpen;return void 0===t?void 0===n?null:i.upperBound(n,!!e):void 0===n?i.lowerBound(t,!!r):i.bound(t,n,!!r,!!e)}function n(e){var h,w=e.name;return{name:w,schema:e,mutate:function(e){var y=e.trans,v=e.type,m=e.keys,b=e.values,g=e.range;return new Promise(function(t,e){t=Ie(t);var n=y.objectStore(w),r=null==n.keyPath,i="put"===v||"add"===v;if(!i&&"delete"!==v&&"deleteRange"!==v)throw new Error("Invalid operation type: "+v);var o,a=(m||b||{length:1}).length;if(m&&b&&m.length!==b.length)throw new Error("Given keys array must have same length as given values array.");if(0===a)return t({numFailures:0,failures:{},results:[],lastResult:void 0});function u(e){++l,Mt(e)}var s=[],c=[],l=0;if("deleteRange"===v){if(4===g.type)return t({numFailures:l,failures:c,results:[],lastResult:void 0});3===g.type?s.push(o=n.clear()):s.push(o=n.delete(_(g)))}else{var r=i?r?[b,m]:[b,null]:[m,null],f=r[0],h=r[1];if(i)for(var d=0;d<a;++d)s.push(o=h&&void 0!==h[d]?n[v](f[d],h[d]):n[v](f[d])),o.onerror=u;else for(d=0;d<a;++d)s.push(o=n[v](f[d])),o.onerror=u}function p(e){e=e.target.result,s.forEach(function(e,t){return null!=e.error&&(c[t]=e.error)}),t({numFailures:l,failures:c,results:"delete"===v?m:s.map(function(e){return e.result}),lastResult:e})}o.onerror=function(e){u(e),p(e)},o.onsuccess=p})},getMany:function(e){var f=e.trans,h=e.keys;return new Promise(function(t,e){t=Ie(t);for(var n,r=f.objectStore(w),i=h.length,o=new Array(i),a=0,u=0,s=function(e){e=e.target;o[e._pos]=e.result,++u===a&&t(o)},c=Ft(e),l=0;l<i;++l)null!=h[l]&&((n=r.get(h[l]))._pos=l,n.onsuccess=s,n.onerror=c,++a);0===a&&t(o)})},get:function(e){var r=e.trans,i=e.key;return new Promise(function(t,e){t=Ie(t);var n=r.objectStore(w).get(i);n.onsuccess=function(e){return t(e.target.result)},n.onerror=Ft(e)})},query:(h=s,function(f){return new Promise(function(n,e){n=Ie(n);var r,i,o,t=f.trans,a=f.values,u=f.limit,s=f.query,c=u===1/0?void 0:u,l=s.index,s=s.range,t=t.objectStore(w),l=l.isPrimaryKey?t:t.index(l.name),s=_(s);if(0===u)return n({result:[]});h?((c=a?l.getAll(s,c):l.getAllKeys(s,c)).onsuccess=function(e){return n({result:e.target.result})},c.onerror=Ft(e)):(r=0,i=!a&&"openKeyCursor"in l?l.openKeyCursor(s):l.openCursor(s),o=[],i.onsuccess=function(e){var t=i.result;return t?(o.push(a?t.value:t.primaryKey),++r===u?n({result:o}):void t.continue()):n({result:o})},i.onerror=Ft(e))})}),openCursor:function(e){var c=e.trans,o=e.values,a=e.query,u=e.reverse,l=e.unique;return new Promise(function(t,n){t=Ie(t);var e=a.index,r=a.range,i=c.objectStore(w),i=e.isPrimaryKey?i:i.index(e.name),e=u?l?"prevunique":"prev":l?"nextunique":"next",s=!o&&"openKeyCursor"in i?i.openKeyCursor(_(r),e):i.openCursor(_(r),e);s.onerror=Ft(n),s.onsuccess=Ie(function(e){var r,i,o,a,u=s.result;u?(u.___id=++Ht,u.done=!1,r=u.continue.bind(u),i=(i=u.continuePrimaryKey)&&i.bind(u),o=u.advance.bind(u),a=function(){throw new Error("Cursor not stopped")},u.trans=c,u.stop=u.continue=u.continuePrimaryKey=u.advance=function(){throw new Error("Cursor not started")},u.fail=Ie(n),u.next=function(){var e=this,t=1;return this.start(function(){return t--?e.continue():e.stop()}).then(function(){return e})},u.start=function(e){function t(){if(s.result)try{e()}catch(e){u.fail(e)}else u.done=!0,u.start=function(){throw new Error("Cursor behind last entry")},u.stop()}var n=new Promise(function(t,e){t=Ie(t),s.onerror=Ft(e),u.fail=e,u.stop=function(e){u.stop=u.continue=u.continuePrimaryKey=u.advance=a,t(e)}});return s.onsuccess=Ie(function(e){s.onsuccess=t,t()}),u.continue=r,u.continuePrimaryKey=i,u.advance=o,t(),n},t(u)):t(null)},n)})},count:function(e){var t=e.query,i=e.trans,o=t.index,a=t.range;return new Promise(function(t,e){var n=i.objectStore(w),r=o.isPrimaryKey?n:n.index(o.name),n=_(a),r=n?r.count(n):r.count();r.onsuccess=Ie(function(e){return t(e.target.result)}),r.onerror=Ft(e)})}}}var r,o,a,u=(o=t,a=Xt((r=e).objectStoreNames),{schema:{name:r.name,tables:a.map(function(e){return o.objectStore(e)}).map(function(t){var e=t.keyPath,n=t.autoIncrement,r=x(e),i={},n={name:t.name,primaryKey:{name:null,isPrimaryKey:!0,outbound:null==e,compound:r,keyPath:e,autoIncrement:n,unique:!0,extractKey:Gt(e)},indexes:Xt(t.indexNames).map(function(e){return t.index(e)}).map(function(e){var t=e.name,n=e.unique,r=e.multiEntry,e=e.keyPath,r={name:t,compound:x(e),keyPath:e,unique:n,multiEntry:r,extractKey:Gt(e)};return i[Jt(e)]=r}),getIndexByKeyPath:function(e){return i[Jt(e)]}};return i[":id"]=n.primaryKey,null!=e&&(i[Jt(e)]=n.primaryKey),n})},hasGetAll:0<a.length&&"getAll"in o.objectStore(a[0])&&!("undefined"!=typeof navigator&&/Safari/.test(navigator.userAgent)&&!/(Chrome\/|Edge\/)/.test(navigator.userAgent)&&[].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1]<604)}),t=u.schema,s=u.hasGetAll,u=t.tables.map(n),c={};return u.forEach(function(e){return c[e.name]=e}),{stack:"dbcore",transaction:e.transaction.bind(e),table:function(e){if(!c[e])throw new Error("Table '".concat(e,"' not found"));return c[e]},MIN_KEY:-1/0,MAX_KEY:Qt(i),schema:t}}function en(e,t,n,r){var i=n.IDBKeyRange;return n.indexedDB,{dbcore:(r=Zt(t,i,r),e.dbcore.reduce(function(e,t){t=t.create;return _(_({},e),t(e))},r))}}function tn(n,e){var t=e.db,e=en(n._middlewares,t,n._deps,e);n.core=e.dbcore,n.tables.forEach(function(e){var t=e.name;n.core.schema.tables.some(function(e){return e.name===t})&&(e.core=n.core.table(t),n[t]instanceof n.Table&&(n[t].core=e.core))})}function nn(i,e,t,o){t.forEach(function(n){var r=o[n];e.forEach(function(e){var t=function e(t,n){return h(t,n)||(t=c(t))&&e(t,n)}(e,n);(!t||"value"in t&&void 0===t.value)&&(e===i.Transaction.prototype||e instanceof i.Transaction?l(e,n,{get:function(){return this.table(n)},set:function(e){u(this,n,{value:e,writable:!0,configurable:!0,enumerable:!0})}}):e[n]=new i.Table(n,r))})})}function rn(n,e){e.forEach(function(e){for(var t in e)e[t]instanceof n.Table&&delete e[t]})}function on(e,t){return e._cfg.version-t._cfg.version}function an(n,r,i,e){var o=n._dbSchema;i.objectStoreNames.contains("$meta")&&!o.$meta&&(o.$meta=$t("$meta",pn("")[0],[]),n._storeNames.push("$meta"));var a=n._createTransaction("readwrite",n._storeNames,o);a.create(i),a._completion.catch(e);var u=a._reject.bind(a),s=me.transless||me;Ne(function(){return me.trans=a,me.transless=s,0!==r?(tn(n,i),t=r,((e=a).storeNames.includes("$meta")?e.table("$meta").get("version").then(function(e){return null!=e?e:t}):_e.resolve(t)).then(function(e){return c=e,l=a,f=i,t=[],e=(s=n)._versions,h=s._dbSchema=hn(0,s.idbdb,f),0!==(e=e.filter(function(e){return e._cfg.version>=c})).length?(e.forEach(function(u){t.push(function(){var t=h,e=u._cfg.dbschema;dn(s,t,f),dn(s,e,f),h=s._dbSchema=e;var n=sn(t,e);n.add.forEach(function(e){cn(f,e[0],e[1].primKey,e[1].indexes)}),n.change.forEach(function(e){if(e.recreate)throw new Y.Upgrade("Not yet support for changing primary key");var t=f.objectStore(e.name);e.add.forEach(function(e){return fn(t,e)}),e.change.forEach(function(e){t.deleteIndex(e.name),fn(t,e)}),e.del.forEach(function(e){return t.deleteIndex(e)})});var r=u._cfg.contentUpgrade;if(r&&u._cfg.version>c){tn(s,f),l._memoizedTables={};var i=k(e);n.del.forEach(function(e){i[e]=t[e]}),rn(s,[s.Transaction.prototype]),nn(s,[s.Transaction.prototype],O(i),i),l.schema=i;var o,a=B(r);a&&Le();n=_e.follow(function(){var e;(o=r(l))&&a&&(e=Ue.bind(null,null),o.then(e,e))});return o&&"function"==typeof o.then?_e.resolve(o):n.then(function(){return o})}}),t.push(function(e){var t,n,r=u._cfg.dbschema;t=r,n=e,[].slice.call(n.db.objectStoreNames).forEach(function(e){return null==t[e]&&n.db.deleteObjectStore(e)}),rn(s,[s.Transaction.prototype]),nn(s,[s.Transaction.prototype],s._storeNames,s._dbSchema),l.schema=s._dbSchema}),t.push(function(e){s.idbdb.objectStoreNames.contains("$meta")&&(Math.ceil(s.idbdb.version/10)===u._cfg.version?(s.idbdb.deleteObjectStore("$meta"),delete s._dbSchema.$meta,s._storeNames=s._storeNames.filter(function(e){return"$meta"!==e})):e.objectStore("$meta").put(u._cfg.version,"version"))})}),function e(){return t.length?_e.resolve(t.shift()(l.idbtrans)).then(e):_e.resolve()}().then(function(){ln(h,f)})):_e.resolve();var s,c,l,f,t,h}).catch(u)):(O(o).forEach(function(e){cn(i,e,o[e].primKey,o[e].indexes)}),tn(n,i),void _e.follow(function(){return n.on.populate.fire(a)}).catch(u));var e,t})}function un(e,r){ln(e._dbSchema,r),r.db.version%10!=0||r.objectStoreNames.contains("$meta")||r.db.createObjectStore("$meta").add(Math.ceil(r.db.version/10-1),"version");var t=hn(0,e.idbdb,r);dn(e,e._dbSchema,r);for(var n=0,i=sn(t,e._dbSchema).change;n<i.length;n++){var o=function(t){if(t.change.length||t.recreate)return console.warn("Unable to patch indexes of table ".concat(t.name," because it has changes on the type of index or primary key.")),{value:void 0};var n=r.objectStore(t.name);t.add.forEach(function(e){ie&&console.debug("Dexie upgrade patch: Creating missing index ".concat(t.name,".").concat(e.src)),fn(n,e)})}(i[n]);if("object"==typeof o)return o.value}}function sn(e,t){var n,r={del:[],add:[],change:[]};for(n in e)t[n]||r.del.push(n);for(n in t){var i=e[n],o=t[n];if(i){var a={name:n,def:o,recreate:!1,del:[],add:[],change:[]};if(""+(i.primKey.keyPath||"")!=""+(o.primKey.keyPath||"")||i.primKey.auto!==o.primKey.auto)a.recreate=!0,r.change.push(a);else{var u=i.idxByName,s=o.idxByName,c=void 0;for(c in u)s[c]||a.del.push(c);for(c in s){var l=u[c],f=s[c];l?l.src!==f.src&&a.change.push(f):a.add.push(f)}(0<a.del.length||0<a.add.length||0<a.change.length)&&r.change.push(a)}}else r.add.push([n,o])}return r}function cn(e,t,n,r){var i=e.db.createObjectStore(t,n.keyPath?{keyPath:n.keyPath,autoIncrement:n.auto}:{autoIncrement:n.auto});return r.forEach(function(e){return fn(i,e)}),i}function ln(t,n){O(t).forEach(function(e){n.db.objectStoreNames.contains(e)||(ie&&console.debug("Dexie: Creating missing table",e),cn(n,e,t[e].primKey,t[e].indexes))})}function fn(e,t){e.createIndex(t.name,t.keyPath,{unique:t.unique,multiEntry:t.multi})}function hn(e,t,u){var s={};return b(t.objectStoreNames,0).forEach(function(e){for(var t=u.objectStore(e),n=Wt(Yt(a=t.keyPath),a||"",!0,!1,!!t.autoIncrement,a&&"string"!=typeof a,!0),r=[],i=0;i<t.indexNames.length;++i){var o=t.index(t.indexNames[i]),a=o.keyPath,o=Wt(o.name,a,!!o.unique,!!o.multiEntry,!1,a&&"string"!=typeof a,!1);r.push(o)}s[e]=$t(e,n,r)}),s}function dn(e,t,n){for(var r=n.db.objectStoreNames,i=0;i<r.length;++i){var o=r[i],a=n.objectStore(o);e._hasGetAll="getAll"in a;for(var u=0;u<a.indexNames.length;++u){var s=a.indexNames[u],c=a.index(s).keyPath,l="string"==typeof c?c:"["+b(c).join("+")+"]";!t[o]||(c=t[o].idxByName[l])&&(c.name=s,delete t[o].idxByName[l],t[o].idxByName[s]=c)}}"undefined"!=typeof navigator&&/Safari/.test(navigator.userAgent)&&!/(Chrome\/|Edge\/)/.test(navigator.userAgent)&&f.WorkerGlobalScope&&f instanceof f.WorkerGlobalScope&&[].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1]<604&&(e._hasGetAll=!1)}function pn(e){return e.split(",").map(function(e,t){var n=e.split(":"),r=null===(i=n[1])||void 0===i?void 0:i.trim(),i=(e=n[0].trim()).replace(/([&*]|\+\+)/g,""),n=/^\[/.test(i)?i.match(/^\[(.*)\]$/)[1].split("+"):i;return Wt(i,n||null,/\&/.test(e),/\*/.test(e),/\+\+/.test(e),x(n),0===t,r)})}var yn=(vn.prototype._createTableSchema=$t,vn.prototype._parseIndexSyntax=pn,vn.prototype._parseStoresSpec=function(r,i){var o=this;O(r).forEach(function(e){if(null!==r[e]){var t=o._parseIndexSyntax(r[e]),n=t.shift();if(!n)throw new Y.Schema("Invalid schema for table "+e+": "+r[e]);if(n.unique=!0,n.multi)throw new Y.Schema("Primary key cannot be multiEntry*");t.forEach(function(e){if(e.auto)throw new Y.Schema("Only primary key can be marked as autoIncrement (++)");if(!e.keyPath)throw new Y.Schema("Index must have a name and cannot be an empty string")});t=o._createTableSchema(e,n,t);i[e]=t}})},vn.prototype.stores=function(e){var t=this.db;this._cfg.storesSource=this._cfg.storesSource?a(this._cfg.storesSource,e):e;var e=t._versions,n={},r={};return e.forEach(function(e){a(n,e._cfg.storesSource),r=e._cfg.dbschema={},e._parseStoresSpec(n,r)}),t._dbSchema=r,rn(t,[t._allTables,t,t.Transaction.prototype]),nn(t,[t._allTables,t,t.Transaction.prototype,this._cfg.tables],O(r),r),t._storeNames=O(r),this},vn.prototype.upgrade=function(e){return this._cfg.contentUpgrade=re(this._cfg.contentUpgrade||G,e),this},vn);function vn(){}function mn(e,t){var n=e._dbNamesDB;return n||(n=e._dbNamesDB=new nr(tt,{addons:[],indexedDB:e,IDBKeyRange:t})).version(1).stores({dbnames:"name"}),n.table("dbnames")}function bn(e){return e&&"function"==typeof e.databases}function gn(e){return Ne(function(){return me.letThrough=!0,e()})}function wn(e){return!("from"in e)}var _n=function(e,t){if(!this){var n=new _n;return e&&"d"in e&&a(n,e),n}a(this,arguments.length?{d:1,from:e,to:1<arguments.length?t:e}:{d:0})};function xn(e,t,n){var r=st(t,n);if(!isNaN(r)){if(0<r)throw RangeError();if(wn(e))return a(e,{from:t,to:n,d:1});var i=e.l,r=e.r;if(st(n,e.from)<0)return i?xn(i,t,n):e.l={from:t,to:n,d:1,l:null,r:null},Kn(e);if(0<st(t,e.to))return r?xn(r,t,n):e.r={from:t,to:n,d:1,l:null,r:null},Kn(e);st(t,e.from)<0&&(e.from=t,e.l=null,e.d=r?r.d+1:1),0<st(n,e.to)&&(e.to=n,e.r=null,e.d=e.l?e.l.d+1:1);n=!e.r;i&&!e.l&&kn(e,i),r&&n&&kn(e,r)}}function kn(e,t){wn(t)||function e(t,n){var r=n.from,i=n.to,o=n.l,n=n.r;xn(t,r,i),o&&e(t,o),n&&e(t,n)}(e,t)}function On(e,t){var n=Pn(t),r=n.next();if(r.done)return!1;for(var i=r.value,o=Pn(e),a=o.next(i.from),u=a.value;!r.done&&!a.done;){if(st(u.from,i.to)<=0&&0<=st(u.to,i.from))return!0;st(i.from,u.from)<0?i=(r=n.next(u.from)).value:u=(a=o.next(i.from)).value}return!1}function Pn(e){var n=wn(e)?null:{s:0,n:e};return{next:function(e){for(var t=0<arguments.length;n;)switch(n.s){case 0:if(n.s=1,t)for(;n.n.l&&st(e,n.n.from)<0;)n={up:n,n:n.n.l,s:1};else for(;n.n.l;)n={up:n,n:n.n.l,s:1};case 1:if(n.s=2,!t||st(e,n.n.to)<=0)return{value:n.n,done:!1};case 2:if(n.n.r){n.s=3,n={up:n,n:n.n.r,s:0};continue}case 3:n=n.up}return{done:!0}}}}function Kn(e){var t,n,r=((null===(t=e.r)||void 0===t?void 0:t.d)||0)-((null===(n=e.l)||void 0===n?void 0:n.d)||0),i=1<r?"r":r<-1?"l":"";i&&(t="r"==i?"l":"r",n=_({},e),r=e[i],e.from=r.from,e.to=r.to,e[i]=r[i],n[i]=r[t],(e[t]=n).d=En(n)),e.d=En(e)}function En(e){var t=e.r,e=e.l;return(t?e?Math.max(t.d,e.d):t.d:e?e.d:0)+1}function Sn(t,n){return O(n).forEach(function(e){t[e]?kn(t[e],n[e]):t[e]=function e(t){var n,r,i={};for(n in t)m(t,n)&&(r=t[n],i[n]=!r||"object"!=typeof r||K.has(r.constructor)?r:e(r));return i}(n[e])}),t}function jn(t,n){return t.all||n.all||Object.keys(t).some(function(e){return n[e]&&On(n[e],t[e])})}r(_n.prototype,((F={add:function(e){return kn(this,e),this},addKey:function(e){return xn(this,e,e),this},addKeys:function(e){var t=this;return e.forEach(function(e){return xn(t,e,e)}),this},hasKey:function(e){var t=Pn(this).next(e).value;return t&&st(t.from,e)<=0&&0<=st(t.to,e)}})[C]=function(){return Pn(this)},F));var An={},Cn={},Tn=!1;function In(e){Sn(Cn,e),Tn||(Tn=!0,setTimeout(function(){Tn=!1,qn(Cn,!(Cn={}))},0))}function qn(e,t){void 0===t&&(t=!1);var n=new Set;if(e.all)for(var r=0,i=Object.values(An);r<i.length;r++)Dn(a=i[r],e,n,t);else for(var o in e){var a,u=/^idb\:\/\/(.*)\/(.*)\//.exec(o);u&&(o=u[1],u=u[2],(a=An["idb://".concat(o,"/").concat(u)])&&Dn(a,e,n,t))}n.forEach(function(e){return e()})}function Dn(e,t,n,r){for(var i=[],o=0,a=Object.entries(e.queries.query);o<a.length;o++){for(var u=a[o],s=u[0],c=[],l=0,f=u[1];l<f.length;l++){var h=f[l];jn(t,h.obsSet)?h.subscribers.forEach(function(e){return n.add(e)}):r&&c.push(h)}r&&i.push([s,c])}if(r)for(var d=0,p=i;d<p.length;d++){var y=p[d],s=y[0],c=y[1];e.queries.query[s]=c}}function Bn(f){var h=f._state,r=f._deps.indexedDB;if(h.isBeingOpened||f.idbdb)return h.dbReadyPromise.then(function(){return h.dbOpenError?Xe(h.dbOpenError):f});h.isBeingOpened=!0,h.dbOpenError=null,h.openComplete=!1;var t=h.openCanceller,d=Math.round(10*f.verno),p=!1;function e(){if(h.openCanceller!==t)throw new Y.DatabaseClosed("db.open() was cancelled")}function y(){return new _e(function(s,n){if(e(),!r)throw new Y.MissingAPI;var c=f.name,l=h.autoSchema||!d?r.open(c):r.open(c,d);if(!l)throw new Y.MissingAPI;l.onerror=Ft(n),l.onblocked=Ie(f._fireOnBlocked),l.onupgradeneeded=Ie(function(e){var t;v=l.transaction,h.autoSchema&&!f._options.allowEmptyDB?(l.onerror=Mt,v.abort(),l.result.close(),(t=r.deleteDatabase(c)).onsuccess=t.onerror=Ie(function(){n(new Y.NoSuchDatabase("Database ".concat(c," doesnt exist")))})):(v.onerror=Ft(n),e=e.oldVersion>Math.pow(2,62)?0:e.oldVersion,m=e<1,f.idbdb=l.result,p&&un(f,v),an(f,e/10,v,n))},n),l.onsuccess=Ie(function(){v=null;var e,t,n,r,i,o=f.idbdb=l.result,a=b(o.objectStoreNames);if(0<a.length)try{var u=o.transaction(1===(r=a).length?r[0]:r,"readonly");if(h.autoSchema)t=o,n=u,(e=f).verno=t.version/10,n=e._dbSchema=hn(0,t,n),e._storeNames=b(t.objectStoreNames,0),nn(e,[e._allTables],O(n),n);else if(dn(f,f._dbSchema,u),((i=sn(hn(0,(i=f).idbdb,u),i._dbSchema)).add.length||i.change.some(function(e){return e.add.length||e.change.length}))&&!p)return console.warn("Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Dexie will add missing parts and increment native version number to workaround this."),o.close(),d=o.version+1,p=!0,s(y());tn(f,u)}catch(e){}et.push(f),o.onversionchange=Ie(function(e){h.vcFired=!0,f.on("versionchange").fire(e)}),o.onclose=Ie(function(){f.close({disableAutoOpen:!1})}),m&&(i=f._deps,u=c,o=i.indexedDB,i=i.IDBKeyRange,bn(o)||u===tt||mn(o,i).put({name:u}).catch(G)),s()},n)}).catch(function(e){switch(null==e?void 0:e.name){case"UnknownError":if(0<h.PR1398_maxLoop)return h.PR1398_maxLoop--,console.warn("Dexie: Workaround for Chrome UnknownError on open()"),y();break;case"VersionError":if(0<d)return d=0,y()}return _e.reject(e)})}var n,i=h.dbReadyResolve,v=null,m=!1;return _e.race([t,("undefined"==typeof navigator?_e.resolve():!navigator.userAgentData&&/Safari\//.test(navigator.userAgent)&&!/Chrom(e|ium)\//.test(navigator.userAgent)&&indexedDB.databases?new Promise(function(e){function t(){return indexedDB.databases().finally(e)}n=setInterval(t,100),t()}).finally(function(){return clearInterval(n)}):Promise.resolve()).then(y)]).then(function(){return e(),h.onReadyBeingFired=[],_e.resolve(gn(function(){return f.on.ready.fire(f.vip)})).then(function e(){if(0<h.onReadyBeingFired.length){var t=h.onReadyBeingFired.reduce(re,G);return h.onReadyBeingFired=[],_e.resolve(gn(function(){return t(f.vip)})).then(e)}})}).finally(function(){h.openCanceller===t&&(h.onReadyBeingFired=null,h.isBeingOpened=!1)}).catch(function(e){h.dbOpenError=e;try{v&&v.abort()}catch(e){}return t===h.openCanceller&&f._close(),Xe(e)}).finally(function(){h.openComplete=!0,i()}).then(function(){var n;return m&&(n={},f.tables.forEach(function(t){t.schema.indexes.forEach(function(e){e.name&&(n["idb://".concat(f.name,"/").concat(t.name,"/").concat(e.name)]=new _n(-1/0,[[[]]]))}),n["idb://".concat(f.name,"/").concat(t.name,"/")]=n["idb://".concat(f.name,"/").concat(t.name,"/:dels")]=new _n(-1/0,[[[]]])}),Ut(Nt).fire(n),qn(n,!0)),f})}function Rn(t){function e(e){return t.next(e)}var r=n(e),i=n(function(e){return t.throw(e)});function n(n){return function(e){var t=n(e),e=t.value;return t.done?e:e&&"function"==typeof e.then?e.then(r,i):x(e)?Promise.all(e).then(r,i):r(e)}}return n(e)()}function Fn(e,t,n){for(var r=x(e)?e.slice():[e],i=0;i<n;++i)r.push(t);return r}var Mn={stack:"dbcore",name:"VirtualIndexMiddleware",level:1,create:function(f){return _(_({},f),{table:function(e){var a=f.table(e),t=a.schema,u={},s=[];function c(e,t,n){var r=Jt(e),i=u[r]=u[r]||[],o=null==e?0:"string"==typeof e?1:e.length,a=0<t,a=_(_({},n),{name:a?"".concat(r,"(virtual-from:").concat(n.name,")"):n.name,lowLevelIndex:n,isVirtual:a,keyTail:t,keyLength:o,extractKey:Gt(e),unique:!a&&n.unique});return i.push(a),a.isPrimaryKey||s.push(a),1<o&&c(2===o?e[0]:e.slice(0,o-1),t+1,n),i.sort(function(e,t){return e.keyTail-t.keyTail}),a}e=c(t.primaryKey.keyPath,0,t.primaryKey);u[":id"]=[e];for(var n=0,r=t.indexes;n<r.length;n++){var i=r[n];c(i.keyPath,0,i)}function l(e){var t,n=e.query.index;return n.isVirtual?_(_({},e),{query:{index:n.lowLevelIndex,range:(t=e.query.range,n=n.keyTail,{type:1===t.type?2:t.type,lower:Fn(t.lower,t.lowerOpen?f.MAX_KEY:f.MIN_KEY,n),lowerOpen:!0,upper:Fn(t.upper,t.upperOpen?f.MIN_KEY:f.MAX_KEY,n),upperOpen:!0})}}):e}return _(_({},a),{schema:_(_({},t),{primaryKey:e,indexes:s,getIndexByKeyPath:function(e){return(e=u[Jt(e)])&&e[0]}}),count:function(e){return a.count(l(e))},query:function(e){return a.query(l(e))},openCursor:function(t){var e=t.query.index,r=e.keyTail,n=e.isVirtual,i=e.keyLength;return n?a.openCursor(l(t)).then(function(e){return e&&o(e)}):a.openCursor(t);function o(n){return Object.create(n,{continue:{value:function(e){null!=e?n.continue(Fn(e,t.reverse?f.MAX_KEY:f.MIN_KEY,r)):t.unique?n.continue(n.key.slice(0,i).concat(t.reverse?f.MIN_KEY:f.MAX_KEY,r)):n.continue()}},continuePrimaryKey:{value:function(e,t){n.continuePrimaryKey(Fn(e,f.MAX_KEY,r),t)}},primaryKey:{get:function(){return n.primaryKey}},key:{get:function(){var e=n.key;return 1===i?e[0]:e.slice(0,i)}},value:{get:function(){return n.value}}})}}})}})}};function Nn(i,o,a,u){return a=a||{},u=u||"",O(i).forEach(function(e){var t,n,r;m(o,e)?(t=i[e],n=o[e],"object"==typeof t&&"object"==typeof n&&t&&n?(r=A(t))!==A(n)?a[u+e]=o[e]:"Object"===r?Nn(t,n,a,u+e+"."):t!==n&&(a[u+e]=o[e]):t!==n&&(a[u+e]=o[e])):a[u+e]=void 0}),O(o).forEach(function(e){m(i,e)||(a[u+e]=o[e])}),a}function Ln(e,t){return"delete"===t.type?t.keys:t.keys||t.values.map(e.extractKey)}var Un={stack:"dbcore",name:"HooksMiddleware",level:2,create:function(e){return _(_({},e),{table:function(r){var y=e.table(r),v=y.schema.primaryKey;return _(_({},y),{mutate:function(e){var t=me.trans,n=t.table(r).hook,h=n.deleting,d=n.creating,p=n.updating;switch(e.type){case"add":if(d.fire===G)break;return t._promise("readwrite",function(){return a(e)},!0);case"put":if(d.fire===G&&p.fire===G)break;return t._promise("readwrite",function(){return a(e)},!0);case"delete":if(h.fire===G)break;return t._promise("readwrite",function(){return a(e)},!0);case"deleteRange":if(h.fire===G)break;return t._promise("readwrite",function(){return function n(r,i,o){return y.query({trans:r,values:!1,query:{index:v,range:i},limit:o}).then(function(e){var t=e.result;return a({type:"delete",keys:t,trans:r}).then(function(e){return 0<e.numFailures?Promise.reject(e.failures[0]):t.length<o?{failures:[],numFailures:0,lastResult:void 0}:n(r,_(_({},i),{lower:t[t.length-1],lowerOpen:!0}),o)})})}(e.trans,e.range,1e4)},!0)}return y.mutate(e);function a(c){var e,t,n,l=me.trans,f=c.keys||Ln(v,c);if(!f)throw new Error("Keys missing");return"delete"!==(c="add"===c.type||"put"===c.type?_(_({},c),{keys:f}):_({},c)).type&&(c.values=i([],c.values,!0)),c.keys&&(c.keys=i([],c.keys,!0)),e=y,n=f,("add"===(t=c).type?Promise.resolve([]):e.getMany({trans:t.trans,keys:n,cache:"immutable"})).then(function(u){var s=f.map(function(e,t){var n,r,i,o=u[t],a={onerror:null,onsuccess:null};return"delete"===c.type?h.fire.call(a,e,o,l):"add"===c.type||void 0===o?(n=d.fire.call(a,e,c.values[t],l),null==e&&null!=n&&(c.keys[t]=e=n,v.outbound||w(c.values[t],v.keyPath,e))):(n=Nn(o,c.values[t]),(r=p.fire.call(a,n,e,o,l))&&(i=c.values[t],Object.keys(r).forEach(function(e){m(i,e)?i[e]=r[e]:w(i,e,r[e])}))),a});return y.mutate(c).then(function(e){for(var t=e.failures,n=e.results,r=e.numFailures,e=e.lastResult,i=0;i<f.length;++i){var o=(n||f)[i],a=s[i];null==o?a.onerror&&a.onerror(t[i]):a.onsuccess&&a.onsuccess("put"===c.type&&u[i]?c.values[i]:o)}return{failures:t,results:n,numFailures:r,lastResult:e}}).catch(function(t){return s.forEach(function(e){return e.onerror&&e.onerror(t)}),Promise.reject(t)})})}}})}})}};function Vn(e,t,n){try{if(!t)return null;if(t.keys.length<e.length)return null;for(var r=[],i=0,o=0;i<t.keys.length&&o<e.length;++i)0===st(t.keys[i],e[o])&&(r.push(n?S(t.values[i]):t.values[i]),++o);return r.length===e.length?r:null}catch(e){return null}}var zn={stack:"dbcore",level:-1,create:function(t){return{table:function(e){var n=t.table(e);return _(_({},n),{getMany:function(t){if(!t.cache)return n.getMany(t);var e=Vn(t.keys,t.trans._cache,"clone"===t.cache);return e?_e.resolve(e):n.getMany(t).then(function(e){return t.trans._cache={keys:t.keys,values:"clone"===t.cache?S(e):e},e})},mutate:function(e){return"add"!==e.type&&(e.trans._cache=null),n.mutate(e)}})}}}};function Wn(e,t){return"readonly"===e.trans.mode&&!!e.subscr&&!e.trans.explicit&&"disabled"!==e.trans.db._options.cache&&!t.schema.primaryKey.outbound}function Yn(e,t){switch(e){case"query":return t.values&&!t.unique;case"get":case"getMany":case"count":case"openCursor":return!1}}var $n={stack:"dbcore",level:0,name:"Observability",create:function(b){var g=b.schema.name,w=new _n(b.MIN_KEY,b.MAX_KEY);return _(_({},b),{transaction:function(e,t,n){if(me.subscr&&"readonly"!==t)throw new Y.ReadOnly("Readwrite transaction in liveQuery context. Querier source: ".concat(me.querier));return b.transaction(e,t,n)},table:function(d){var p=b.table(d),y=p.schema,v=y.primaryKey,e=y.indexes,c=v.extractKey,l=v.outbound,m=v.autoIncrement&&e.filter(function(e){return e.compound&&e.keyPath.includes(v.keyPath)}),t=_(_({},p),{mutate:function(a){function u(e){return e="idb://".concat(g,"/").concat(d,"/").concat(e),n[e]||(n[e]=new _n)}var e,o,s,t=a.trans,n=a.mutatedParts||(a.mutatedParts={}),r=u(""),i=u(":dels"),c=a.type,l="deleteRange"===a.type?[a.range]:"delete"===a.type?[a.keys]:a.values.length<50?[Ln(v,a).filter(function(e){return e}),a.values]:[],f=l[0],h=l[1],l=a.trans._cache;return x(f)?(r.addKeys(f),(l="delete"===c||f.length===h.length?Vn(f,l):null)||i.addKeys(f),(l||h)&&(e=u,o=l,s=h,y.indexes.forEach(function(t){var n=e(t.name||"");function r(e){return null!=e?t.extractKey(e):null}function i(e){return t.multiEntry&&x(e)?e.forEach(function(e){return n.addKey(e)}):n.addKey(e)}(o||s).forEach(function(e,t){var n=o&&r(o[t]),t=s&&r(s[t]);0!==st(n,t)&&(null!=n&&i(n),null!=t&&i(t))})}))):f?(h={from:null!==(h=f.lower)&&void 0!==h?h:b.MIN_KEY,to:null!==(h=f.upper)&&void 0!==h?h:b.MAX_KEY},i.add(h),r.add(h)):(r.add(w),i.add(w),y.indexes.forEach(function(e){return u(e.name).add(w)})),p.mutate(a).then(function(o){return!f||"add"!==a.type&&"put"!==a.type||(r.addKeys(o.results),m&&m.forEach(function(t){for(var e=a.values.map(function(e){return t.extractKey(e)}),n=t.keyPath.findIndex(function(e){return e===v.keyPath}),r=0,i=o.results.length;r<i;++r)e[r][n]=o.results[r];u(t.name).addKeys(e)})),t.mutatedParts=Sn(t.mutatedParts||{},n),o})}}),e=function(e){var t=e.query,e=t.index,t=t.range;return[e,new _n(null!==(e=t.lower)&&void 0!==e?e:b.MIN_KEY,null!==(t=t.upper)&&void 0!==t?t:b.MAX_KEY)]},f={get:function(e){return[v,new _n(e.key)]},getMany:function(e){return[v,(new _n).addKeys(e.keys)]},count:e,query:e,openCursor:e};return O(f).forEach(function(s){t[s]=function(i){var e=me.subscr,t=!!e,n=Wn(me,p)&&Yn(s,i)?i.obsSet={}:e;if(t){var r=function(e){e="idb://".concat(g,"/").concat(d,"/").concat(e);return n[e]||(n[e]=new _n)},o=r(""),a=r(":dels"),e=f[s](i),t=e[0],e=e[1];if(("query"===s&&t.isPrimaryKey&&!i.values?a:r(t.name||"")).add(e),!t.isPrimaryKey){if("count"!==s){var u="query"===s&&l&&i.values&&p.query(_(_({},i),{values:!1}));return p[s].apply(this,arguments).then(function(t){if("query"===s){if(l&&i.values)return u.then(function(e){e=e.result;return o.addKeys(e),t});var e=i.values?t.result.map(c):t.result;(i.values?o:a).addKeys(e)}else if("openCursor"===s){var n=t,r=i.values;return n&&Object.create(n,{key:{get:function(){return a.addKey(n.primaryKey),n.key}},primaryKey:{get:function(){var e=n.primaryKey;return a.addKey(e),e}},value:{get:function(){return r&&o.addKey(n.primaryKey),n.value}}})}return t})}a.add(w)}}return p[s].apply(this,arguments)}}),t}})}};function Qn(e,t,n){if(0===n.numFailures)return t;if("deleteRange"===t.type)return null;var r=t.keys?t.keys.length:"values"in t&&t.values?t.values.length:1;if(n.numFailures===r)return null;t=_({},t);return x(t.keys)&&(t.keys=t.keys.filter(function(e,t){return!(t in n.failures)})),"values"in t&&x(t.values)&&(t.values=t.values.filter(function(e,t){return!(t in n.failures)})),t}function Gn(e,t){return n=e,(void 0===(r=t).lower||(r.lowerOpen?0<st(n,r.lower):0<=st(n,r.lower)))&&(e=e,void 0===(t=t).upper||(t.upperOpen?st(e,t.upper)<0:st(e,t.upper)<=0));var n,r}function Xn(e,d,t,n,r,i){if(!t||0===t.length)return e;var o=d.query.index,p=o.multiEntry,y=d.query.range,v=n.schema.primaryKey.extractKey,m=o.extractKey,a=(o.lowLevelIndex||o).extractKey,t=t.reduce(function(e,t){var n=e,r=[];if("add"===t.type||"put"===t.type)for(var i=new _n,o=t.values.length-1;0<=o;--o){var a,u=t.values[o],s=v(u);i.hasKey(s)||(a=m(u),(p&&x(a)?a.some(function(e){return Gn(e,y)}):Gn(a,y))&&(i.addKey(s),r.push(u)))}switch(t.type){case"add":var c=(new _n).addKeys(d.values?e.map(function(e){return v(e)}):e),n=e.concat(d.values?r.filter(function(e){e=v(e);return!c.hasKey(e)&&(c.addKey(e),!0)}):r.map(function(e){return v(e)}).filter(function(e){return!c.hasKey(e)&&(c.addKey(e),!0)}));break;case"put":var l=(new _n).addKeys(t.values.map(function(e){return v(e)}));n=e.filter(function(e){return!l.hasKey(d.values?v(e):e)}).concat(d.values?r:r.map(function(e){return v(e)}));break;case"delete":var f=(new _n).addKeys(t.keys);n=e.filter(function(e){return!f.hasKey(d.values?v(e):e)});break;case"deleteRange":var h=t.range;n=e.filter(function(e){return!Gn(v(e),h)})}return n},e);return t===e?e:(t.sort(function(e,t){return st(a(e),a(t))||st(v(e),v(t))}),d.limit&&d.limit<1/0&&(t.length>d.limit?t.length=d.limit:e.length===d.limit&&t.length<d.limit&&(r.dirty=!0)),i?Object.freeze(t):t)}function Hn(e,t){return 0===st(e.lower,t.lower)&&0===st(e.upper,t.upper)&&!!e.lowerOpen==!!t.lowerOpen&&!!e.upperOpen==!!t.upperOpen}function Jn(e,t){return function(e,t,n,r){if(void 0===e)return void 0!==t?-1:0;if(void 0===t)return 1;if(0===(t=st(e,t))){if(n&&r)return 0;if(n)return 1;if(r)return-1}return t}(e.lower,t.lower,e.lowerOpen,t.lowerOpen)<=0&&0<=function(e,t,n,r){if(void 0===e)return void 0!==t?1:0;if(void 0===t)return-1;if(0===(t=st(e,t))){if(n&&r)return 0;if(n)return-1;if(r)return 1}return t}(e.upper,t.upper,e.upperOpen,t.upperOpen)}function Zn(n,r,i,e){n.subscribers.add(i),e.addEventListener("abort",function(){var e,t;n.subscribers.delete(i),0===n.subscribers.size&&(e=n,t=r,setTimeout(function(){0===e.subscribers.size&&I(t,e)},3e3))})}var er={stack:"dbcore",level:0,name:"Cache",create:function(k){var O=k.schema.name;return _(_({},k),{transaction:function(g,w,e){var _,t,x=k.transaction(g,w,e);return"readwrite"===w&&(t=(_=new AbortController).signal,e=function(b){return function(){if(_.abort(),"readwrite"===w){for(var t=new Set,e=0,n=g;e<n.length;e++){var r=n[e],i=An["idb://".concat(O,"/").concat(r)];if(i){var o=k.table(r),a=i.optimisticOps.filter(function(e){return e.trans===x});if(x._explicit&&b&&x.mutatedParts)for(var u=0,s=Object.values(i.queries.query);u<s.length;u++)for(var c=0,l=(d=s[u]).slice();c<l.length;c++)jn((p=l[c]).obsSet,x.mutatedParts)&&(I(d,p),p.subscribers.forEach(function(e){return t.add(e)}));else if(0<a.length){i.optimisticOps=i.optimisticOps.filter(function(e){return e.trans!==x});for(var f=0,h=Object.values(i.queries.query);f<h.length;f++)for(var d,p,y,v=0,m=(d=h[f]).slice();v<m.length;v++)null!=(p=m[v]).res&&x.mutatedParts&&(b&&!p.dirty?(y=Object.isFrozen(p.res),y=Xn(p.res,p.req,a,o,p,y),p.dirty?(I(d,p),p.subscribers.forEach(function(e){return t.add(e)})):y!==p.res&&(p.res=y,p.promise=_e.resolve({result:y}))):(p.dirty&&I(d,p),p.subscribers.forEach(function(e){return t.add(e)})))}}}t.forEach(function(e){return e()})}}},x.addEventListener("abort",e(!1),{signal:t}),x.addEventListener("error",e(!1),{signal:t}),x.addEventListener("complete",e(!0),{signal:t})),x},table:function(c){var l=k.table(c),i=l.schema.primaryKey;return _(_({},l),{mutate:function(t){var e=me.trans;if(i.outbound||"disabled"===e.db._options.cache||e.explicit||"readwrite"!==e.idbtrans.mode)return l.mutate(t);var n=An["idb://".concat(O,"/").concat(c)];if(!n)return l.mutate(t);e=l.mutate(t);return"add"!==t.type&&"put"!==t.type||!(50<=t.values.length||Ln(i,t).some(function(e){return null==e}))?(n.optimisticOps.push(t),t.mutatedParts&&In(t.mutatedParts),e.then(function(e){0<e.numFailures&&(I(n.optimisticOps,t),(e=Qn(0,t,e))&&n.optimisticOps.push(e),t.mutatedParts&&In(t.mutatedParts))}),e.catch(function(){I(n.optimisticOps,t),t.mutatedParts&&In(t.mutatedParts)})):e.then(function(r){var e=Qn(0,_(_({},t),{values:t.values.map(function(e,t){var n;if(r.failures[t])return e;e=null!==(n=i.keyPath)&&void 0!==n&&n.includes(".")?S(e):_({},e);return w(e,i.keyPath,r.results[t]),e})}),r);n.optimisticOps.push(e),queueMicrotask(function(){return t.mutatedParts&&In(t.mutatedParts)})}),e},query:function(t){if(!Wn(me,l)||!Yn("query",t))return l.query(t);var i="immutable"===(null===(o=me.trans)||void 0===o?void 0:o.db._options.cache),e=me,n=e.requery,r=e.signal,o=function(e,t,n,r){var i=An["idb://".concat(e,"/").concat(t)];if(!i)return[];if(!(t=i.queries[n]))return[null,!1,i,null];var o=t[(r.query?r.query.index.name:null)||""];if(!o)return[null,!1,i,null];switch(n){case"query":var a=o.find(function(e){return e.req.limit===r.limit&&e.req.values===r.values&&Hn(e.req.query.range,r.query.range)});return a?[a,!0,i,o]:[o.find(function(e){return("limit"in e.req?e.req.limit:1/0)>=r.limit&&(!r.values||e.req.values)&&Jn(e.req.query.range,r.query.range)}),!1,i,o];case"count":a=o.find(function(e){return Hn(e.req.query.range,r.query.range)});return[a,!!a,i,o]}}(O,c,"query",t),a=o[0],e=o[1],u=o[2],s=o[3];return a&&e?a.obsSet=t.obsSet:(e=l.query(t).then(function(e){var t=e.result;if(a&&(a.res=t),i){for(var n=0,r=t.length;n<r;++n)Object.freeze(t[n]);Object.freeze(t)}else e.result=S(t);return e}).catch(function(e){return s&&a&&I(s,a),Promise.reject(e)}),a={obsSet:t.obsSet,promise:e,subscribers:new Set,type:"query",req:t,dirty:!1},s?s.push(a):(s=[a],(u=u||(An["idb://".concat(O,"/").concat(c)]={queries:{query:{},count:{}},objs:new Map,optimisticOps:[],unsignaledParts:{}})).queries.query[t.query.index.name||""]=s)),Zn(a,s,n,r),a.promise.then(function(e){return{result:Xn(e.result,t,null==u?void 0:u.optimisticOps,l,a,i)}})}})}})}};function tr(e,r){return new Proxy(e,{get:function(e,t,n){return"db"===t?r:Reflect.get(e,t,n)}})}var nr=(rr.prototype.version=function(t){if(isNaN(t)||t<.1)throw new Y.Type("Given version is not a positive number");if(t=Math.round(10*t)/10,this.idbdb||this._state.isBeingOpened)throw new Y.Schema("Cannot add version when database is open");this.verno=Math.max(this.verno,t);var e=this._versions,n=e.filter(function(e){return e._cfg.version===t})[0];return n||(n=new this.Version(t),e.push(n),e.sort(on),n.stores({}),this._state.autoSchema=!1,n)},rr.prototype._whenReady=function(e){var n=this;return this.idbdb&&(this._state.openComplete||me.letThrough||this._vip)?e():new _e(function(e,t){if(n._state.openComplete)return t(new Y.DatabaseClosed(n._state.dbOpenError));if(!n._state.isBeingOpened){if(!n._state.autoOpen)return void t(new Y.DatabaseClosed);n.open().catch(G)}n._state.dbReadyPromise.then(e,t)}).then(e)},rr.prototype.use=function(e){var t=e.stack,n=e.create,r=e.level,i=e.name;i&&this.unuse({stack:t,name:i});e=this._middlewares[t]||(this._middlewares[t]=[]);return e.push({stack:t,create:n,level:null==r?10:r,name:i}),e.sort(function(e,t){return e.level-t.level}),this},rr.prototype.unuse=function(e){var t=e.stack,n=e.name,r=e.create;return t&&this._middlewares[t]&&(this._middlewares[t]=this._middlewares[t].filter(function(e){return r?e.create!==r:!!n&&e.name!==n})),this},rr.prototype.open=function(){var e=this;return $e(ve,function(){return Bn(e)})},rr.prototype._close=function(){this.on.close.fire(new CustomEvent("close"));var n=this._state,e=et.indexOf(this);if(0<=e&&et.splice(e,1),this.idbdb){try{this.idbdb.close()}catch(e){}this.idbdb=null}n.isBeingOpened||(n.dbReadyPromise=new _e(function(e){n.dbReadyResolve=e}),n.openCanceller=new _e(function(e,t){n.cancelOpen=t}))},rr.prototype.close=function(e){var t=(void 0===e?{disableAutoOpen:!0}:e).disableAutoOpen,e=this._state;t?(e.isBeingOpened&&e.cancelOpen(new Y.DatabaseClosed),this._close(),e.autoOpen=!1,e.dbOpenError=new Y.DatabaseClosed):(this._close(),e.autoOpen=this._options.autoOpen||e.isBeingOpened,e.openComplete=!1,e.dbOpenError=null)},rr.prototype.delete=function(n){var i=this;void 0===n&&(n={disableAutoOpen:!0});var o=0<arguments.length&&"object"!=typeof arguments[0],a=this._state;return new _e(function(r,t){function e(){i.close(n);var e=i._deps.indexedDB.deleteDatabase(i.name);e.onsuccess=Ie(function(){var e,t,n;e=i._deps,t=i.name,n=e.indexedDB,e=e.IDBKeyRange,bn(n)||t===tt||mn(n,e).delete(t).catch(G),r()}),e.onerror=Ft(t),e.onblocked=i._fireOnBlocked}if(o)throw new Y.InvalidArgument("Invalid closeOptions argument to db.delete()");a.isBeingOpened?a.dbReadyPromise.then(e):e()})},rr.prototype.backendDB=function(){return this.idbdb},rr.prototype.isOpen=function(){return null!==this.idbdb},rr.prototype.hasBeenClosed=function(){var e=this._state.dbOpenError;return e&&"DatabaseClosed"===e.name},rr.prototype.hasFailed=function(){return null!==this._state.dbOpenError},rr.prototype.dynamicallyOpened=function(){return this._state.autoSchema},Object.defineProperty(rr.prototype,"tables",{get:function(){var t=this;return O(this._allTables).map(function(e){return t._allTables[e]})},enumerable:!1,configurable:!0}),rr.prototype.transaction=function(){var e=function(e,t,n){var r=arguments.length;if(r<2)throw new Y.InvalidArgument("Too few arguments");for(var i=new Array(r-1);--r;)i[r-1]=arguments[r];return n=i.pop(),[e,P(i),n]}.apply(this,arguments);return this._transaction.apply(this,e)},rr.prototype._transaction=function(e,t,n){var r=this,i=me.trans;i&&i.db===this&&-1===e.indexOf("!")||(i=null);var o,a,u=-1!==e.indexOf("?");e=e.replace("!","").replace("?","");try{if(a=t.map(function(e){e=e instanceof r.Table?e.name:e;if("string"!=typeof e)throw new TypeError("Invalid table argument to Dexie.transaction(). Only Table or String are allowed");return e}),"r"==e||e===nt)o=nt;else{if("rw"!=e&&e!=rt)throw new Y.InvalidArgument("Invalid transaction mode: "+e);o=rt}if(i){if(i.mode===nt&&o===rt){if(!u)throw new Y.SubTransaction("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY");i=null}i&&a.forEach(function(e){if(i&&-1===i.storeNames.indexOf(e)){if(!u)throw new Y.SubTransaction("Table "+e+" not included in parent transaction.");i=null}}),u&&i&&!i.active&&(i=null)}}catch(n){return i?i._promise(null,function(e,t){t(n)}):Xe(n)}var s=function i(o,a,u,s,c){return _e.resolve().then(function(){var e=me.transless||me,t=o._createTransaction(a,u,o._dbSchema,s);if(t.explicit=!0,e={trans:t,transless:e},s)t.idbtrans=s.idbtrans;else try{t.create(),t.idbtrans._explicit=!0,o._state.PR1398_maxLoop=3}catch(e){return e.name===z.InvalidState&&o.isOpen()&&0<--o._state.PR1398_maxLoop?(console.warn("Dexie: Need to reopen db"),o.close({disableAutoOpen:!1}),o.open().then(function(){return i(o,a,u,null,c)})):Xe(e)}var n,r=B(c);return r&&Le(),e=_e.follow(function(){var e;(n=c.call(t,t))&&(r?(e=Ue.bind(null,null),n.then(e,e)):"function"==typeof n.next&&"function"==typeof n.throw&&(n=Rn(n)))},e),(n&&"function"==typeof n.then?_e.resolve(n).then(function(e){return t.active?e:Xe(new Y.PrematureCommit("Transaction committed too early. See http://bit.ly/2kdckMn"))}):e.then(function(){return n})).then(function(e){return s&&t._resolve(),t._completion.then(function(){return e})}).catch(function(e){return t._reject(e),Xe(e)})})}.bind(null,this,o,a,i,n);return i?i._promise(o,s,"lock"):me.trans?$e(me.transless,function(){return r._whenReady(s)}):this._whenReady(s)},rr.prototype.table=function(e){if(!m(this._allTables,e))throw new Y.InvalidTable("Table ".concat(e," does not exist"));return this._allTables[e]},rr);function rr(e,t){var o=this;this._middlewares={},this.verno=0;var n=rr.dependencies;this._options=t=_({addons:rr.addons,autoOpen:!0,indexedDB:n.indexedDB,IDBKeyRange:n.IDBKeyRange,cache:"cloned"},t),this._deps={indexedDB:t.indexedDB,IDBKeyRange:t.IDBKeyRange};n=t.addons;this._dbSchema={},this._versions=[],this._storeNames=[],this._allTables={},this.idbdb=null,this._novip=this;var a,r,u,i,s,c={dbOpenError:null,isBeingOpened:!1,onReadyBeingFired:null,openComplete:!1,dbReadyResolve:G,dbReadyPromise:null,cancelOpen:G,openCanceller:null,autoSchema:!0,PR1398_maxLoop:3,autoOpen:t.autoOpen};c.dbReadyPromise=new _e(function(e){c.dbReadyResolve=e}),c.openCanceller=new _e(function(e,t){c.cancelOpen=t}),this._state=c,this.name=e,this.on=mt(this,"populate","blocked","versionchange","close",{ready:[re,G]}),this.once=function(n,r){var i=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];o.on(n).unsubscribe(i),r.apply(o,e)};return o.on(n,i)},this.on.ready.subscribe=p(this.on.ready.subscribe,function(i){return function(n,r){rr.vip(function(){var t,e=o._state;e.openComplete?(e.dbOpenError||_e.resolve().then(n),r&&i(n)):e.onReadyBeingFired?(e.onReadyBeingFired.push(n),r&&i(n)):(i(n),t=o,r||i(function e(){t.on.ready.unsubscribe(n),t.on.ready.unsubscribe(e)}))})}}),this.Collection=(a=this,bt(Kt.prototype,function(e,t){this.db=a;var n=ot,r=null;if(t)try{n=t()}catch(e){r=e}var i=e._ctx,t=i.table,e=t.hook.reading.fire;this._ctx={table:t,index:i.index,isPrimKey:!i.index||t.schema.primKey.keyPath&&i.index===t.schema.primKey.name,range:n,keysOnly:!1,dir:"next",unique:"",algorithm:null,filter:null,replayFilter:null,justLimit:!0,isMatch:null,offset:0,limit:1/0,error:r,or:i.or,valueMapper:e!==X?e:null}})),this.Table=(r=this,bt(yt.prototype,function(e,t,n){this.db=r,this._tx=n,this.name=e,this.schema=t,this.hook=r._allTables[e]?r._allTables[e].hook:mt(null,{creating:[Z,G],reading:[H,X],updating:[te,G],deleting:[ee,G]})})),this.Transaction=(u=this,bt(Vt.prototype,function(e,t,n,r,i){var o=this;"readonly"!==e&&t.forEach(function(e){e=null===(e=n[e])||void 0===e?void 0:e.yProps;e&&(t=t.concat(e.map(function(e){return e.updatesTable})))}),this.db=u,this.mode=e,this.storeNames=t,this.schema=n,this.chromeTransactionDurability=r,this.idbtrans=null,this.on=mt(this,"complete","error","abort"),this.parent=i||null,this.active=!0,this._reculock=0,this._blockedFuncs=[],this._resolve=null,this._reject=null,this._waitingFor=null,this._waitingQueue=null,this._spinCount=0,this._completion=new _e(function(e,t){o._resolve=e,o._reject=t}),this._completion.then(function(){o.active=!1,o.on.complete.fire()},function(e){var t=o.active;return o.active=!1,o.on.error.fire(e),o.parent?o.parent._reject(e):t&&o.idbtrans&&o.idbtrans.abort(),Xe(e)})})),this.Version=(i=this,bt(yn.prototype,function(e){this.db=i,this._cfg={version:e,storesSource:null,dbschema:{},tables:{},contentUpgrade:null}})),this.WhereClause=(s=this,bt(Bt.prototype,function(e,t,n){if(this.db=s,this._ctx={table:e,index:":id"===t?null:t,or:n},this._cmp=this._ascending=st,this._descending=function(e,t){return st(t,e)},this._max=function(e,t){return 0<st(e,t)?e:t},this._min=function(e,t){return st(e,t)<0?e:t},this._IDBKeyRange=s._deps.IDBKeyRange,!this._IDBKeyRange)throw new Y.MissingAPI})),this.on("versionchange",function(e){0<e.newVersion?console.warn("Another connection wants to upgrade database '".concat(o.name,"'. Closing db now to resume the upgrade.")):console.warn("Another connection wants to delete database '".concat(o.name,"'. Closing db now to resume the delete request.")),o.close({disableAutoOpen:!1})}),this.on("blocked",function(e){!e.newVersion||e.newVersion<e.oldVersion?console.warn("Dexie.delete('".concat(o.name,"') was blocked")):console.warn("Upgrade '".concat(o.name,"' blocked by other connection holding version ").concat(e.oldVersion/10))}),this._maxKey=Qt(t.IDBKeyRange),this._createTransaction=function(e,t,n,r){return new o.Transaction(e,t,n,o._options.chromeTransactionDurability,r)},this._fireOnBlocked=function(t){o.on("blocked").fire(t),et.filter(function(e){return e.name===o.name&&e!==o&&!e._state.vcFired}).map(function(e){return e.on("versionchange").fire(t)})},this.use(zn),this.use(er),this.use($n),this.use(Mn),this.use(Un);var l=new Proxy(this,{get:function(e,t,n){if("_vip"===t)return!0;if("table"===t)return function(e){return tr(o.table(e),l)};var r=Reflect.get(e,t,n);return r instanceof yt?tr(r,l):"tables"===t?r.map(function(e){return tr(e,l)}):"_createTransaction"===t?function(){return tr(r.apply(this,arguments),l)}:r}});this.vip=l,n.forEach(function(e){return e(o)})}var ir,F="undefined"!=typeof Symbol&&"observable"in Symbol?Symbol.observable:"@@observable",or=(ar.prototype.subscribe=function(e,t,n){return this._subscribe(e&&"function"!=typeof e?e:{next:e,error:t,complete:n})},ar.prototype[F]=function(){return this},ar);function ar(e){this._subscribe=e}try{ir={indexedDB:f.indexedDB||f.mozIndexedDB||f.webkitIndexedDB||f.msIndexedDB,IDBKeyRange:f.IDBKeyRange||f.webkitIDBKeyRange}}catch(e){ir={indexedDB:null,IDBKeyRange:null}}function ur(h){var d,p=!1,e=new or(function(r){var i=B(h);var o,a=!1,u={},s={},e={get closed(){return a},unsubscribe:function(){a||(a=!0,o&&o.abort(),c&&Ut.storagemutated.unsubscribe(f))}};r.start&&r.start(e);var c=!1,l=function(){return Ge(t)};var f=function(e){Sn(u,e),jn(s,u)&&l()},t=function(){var t,n,e;!a&&ir.indexedDB&&(u={},t={},o&&o.abort(),o=new AbortController,e=function(e){var t=je();try{i&&Le();var n=Ne(h,e);return n=i?n.finally(Ue):n}finally{t&&Ae()}}(n={subscr:t,signal:o.signal,requery:l,querier:h,trans:null}),Promise.resolve(e).then(function(e){p=!0,d=e,a||n.signal.aborted||(u={},function(e){for(var t in e)if(m(e,t))return;return 1}(s=t)||c||(Ut(Nt,f),c=!0),Ge(function(){return!a&&r.next&&r.next(e)}))},function(e){p=!1,["DatabaseClosedError","AbortError"].includes(null==e?void 0:e.name)||a||Ge(function(){a||r.error&&r.error(e)})}))};return setTimeout(l,0),e});return e.hasValue=function(){return p},e.getValue=function(){return d},e}var sr=nr;function cr(e){var t=fr;try{fr=!0,Ut.storagemutated.fire(e),qn(e,!0)}finally{fr=t}}r(sr,_(_({},Q),{delete:function(e){return new sr(e,{addons:[]}).delete()},exists:function(e){return new sr(e,{addons:[]}).open().then(function(e){return e.close(),!0}).catch("NoSuchDatabaseError",function(){return!1})},getDatabaseNames:function(e){try{return t=sr.dependencies,n=t.indexedDB,t=t.IDBKeyRange,(bn(n)?Promise.resolve(n.databases()).then(function(e){return e.map(function(e){return e.name}).filter(function(e){return e!==tt})}):mn(n,t).toCollection().primaryKeys()).then(e)}catch(e){return Xe(new Y.MissingAPI)}var t,n},defineClass:function(){return function(e){a(this,e)}},ignoreTransaction:function(e){return me.trans?$e(me.transless,e):e()},vip:gn,async:function(t){return function(){try{var e=Rn(t.apply(this,arguments));return e&&"function"==typeof e.then?e:_e.resolve(e)}catch(e){return Xe(e)}}},spawn:function(e,t,n){try{var r=Rn(e.apply(n,t||[]));return r&&"function"==typeof r.then?r:_e.resolve(r)}catch(e){return Xe(e)}},currentTransaction:{get:function(){return me.trans||null}},waitFor:function(e,t){t=_e.resolve("function"==typeof e?sr.ignoreTransaction(e):e).timeout(t||6e4);return me.trans?me.trans.waitFor(t):t},Promise:_e,debug:{get:function(){return ie},set:function(e){oe(e)}},derive:o,extend:a,props:r,override:p,Events:mt,on:Ut,liveQuery:ur,extendObservabilitySet:Sn,getByKeyPath:g,setByKeyPath:w,delByKeyPath:function(t,e){"string"==typeof e?w(t,e,void 0):"length"in e&&[].map.call(e,function(e){w(t,e,void 0)})},shallowClone:k,deepClone:S,getObjectDiff:Nn,cmp:st,asap:v,minKey:-1/0,addons:[],connections:et,errnames:z,dependencies:ir,cache:An,semVer:"4.2.1",version:"4.2.1".split(".").map(function(e){return parseInt(e)}).reduce(function(e,t,n){return e+t/Math.pow(10,2*n)})})),sr.maxKey=Qt(sr.dependencies.IDBKeyRange),"undefined"!=typeof dispatchEvent&&"undefined"!=typeof addEventListener&&(Ut(Nt,function(e){fr||(e=new CustomEvent(Lt,{detail:e}),fr=!0,dispatchEvent(e),fr=!1)}),addEventListener(Lt,function(e){e=e.detail;fr||cr(e)}));var lr,fr=!1,hr=function(){};return"undefined"!=typeof BroadcastChannel&&((hr=function(){(lr=new BroadcastChannel(Lt)).onmessage=function(e){return e.data&&cr(e.data)}})(),"function"==typeof lr.unref&&lr.unref(),Ut(Nt,function(e){fr||lr.postMessage(e)})),"undefined"!=typeof addEventListener&&(addEventListener("pagehide",function(e){if(!nr.disableBfCache&&e.persisted){ie&&console.debug("Dexie: handling persisted pagehide"),null!=lr&&lr.close();for(var t=0,n=et;t<n.length;t++)n[t].close({disableAutoOpen:!1})}}),addEventListener("pageshow",function(e){!nr.disableBfCache&&e.persisted&&(ie&&console.debug("Dexie: handling persisted pageshow"),hr(),cr({all:new _n(-1/0,[[]])}))})),_e.rejectionMapper=function(e,t){return!e||e instanceof N||e instanceof TypeError||e instanceof SyntaxError||!e.name||!$[e.name]?e:(t=new $[e.name](t||e.message,e),"stack"in e&&l(t,"stack",{get:function(){return this.inner.stack}}),t)},oe(ie),_(nr,Object.freeze({__proto__:null,Dexie:nr,liveQuery:ur,Entity:ut,cmp:st,PropModification:ht,replacePrefix:function(e,t){return new ht({replacePrefix:[e,t]})},add:function(e){return new ht({add:e})},remove:function(e){return new ht({remove:e})},default:nr,RangeSet:_n,mergeRanges:kn,rangesOverlap:On}),{default:nr}),nr});
|
| 2 |
+
//# sourceMappingURL=dexie.min.js.map
|
libs/marked.min.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* marked v15.0.12 - a markdown parser
|
| 3 |
+
* Copyright (c) 2011-2025, Christopher Jeffrey. (MIT Licensed)
|
| 4 |
+
* https://github.com/markedjs/marked
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
/**
|
| 8 |
+
* DO NOT EDIT THIS FILE
|
| 9 |
+
* The code in this file is generated from files in ./src/
|
| 10 |
+
*/
|
| 11 |
+
(function(g,f){if(typeof exports=="object"&&typeof module<"u"){module.exports=f()}else if("function"==typeof define && define.amd){define("marked",f)}else {g["marked"]=f()}}(typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : this,function(){var exports={};var __exports=exports;var module={exports};
|
| 12 |
+
"use strict";var H=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var Te=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var ye=(l,e)=>{for(var t in e)H(l,t,{get:e[t],enumerable:!0})},Re=(l,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Te(e))!we.call(l,s)&&s!==t&&H(l,s,{get:()=>e[s],enumerable:!(n=be(e,s))||n.enumerable});return l};var Se=l=>Re(H({},"__esModule",{value:!0}),l);var kt={};ye(kt,{Hooks:()=>L,Lexer:()=>x,Marked:()=>E,Parser:()=>b,Renderer:()=>$,TextRenderer:()=>_,Tokenizer:()=>S,defaults:()=>w,getDefaults:()=>z,lexer:()=>ht,marked:()=>k,options:()=>it,parse:()=>pt,parseInline:()=>ct,parser:()=>ut,setOptions:()=>ot,use:()=>lt,walkTokens:()=>at});module.exports=Se(kt);function z(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var w=z();function N(l){w=l}var I={exec:()=>null};function h(l,e=""){let t=typeof l=="string"?l:l.source,n={replace:(s,i)=>{let r=typeof i=="string"?i:i.source;return r=r.replace(m.caret,"$1"),t=t.replace(s,r),n},getRegex:()=>new RegExp(t,e)};return n}var m={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^<a /i,endATag:/^<\/a>/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^</,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:l=>new RegExp(`^( {0,3}${l})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}#`),htmlBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}<(?:[a-z].*>|!--)`,"i")},$e=/^(?:[ \t]*(?:\n|$))+/,_e=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Le=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,O=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,ze=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,F=/(?:[*+-]|\d{1,9}[.)])/,ie=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,oe=h(ie).replace(/bull/g,F).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Me=h(ie).replace(/bull/g,F).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),Q=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,Pe=/^[^\n]+/,U=/(?!\s*\])(?:\\.|[^\[\]\\])+/,Ae=h(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",U).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),Ee=h(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,F).getRegex(),v="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",K=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,Ce=h("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",K).replace("tag",v).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),le=h(Q).replace("hr",O).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex(),Ie=h(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",le).getRegex(),X={blockquote:Ie,code:_e,def:Ae,fences:Le,heading:ze,hr:O,html:Ce,lheading:oe,list:Ee,newline:$e,paragraph:le,table:I,text:Pe},re=h("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",O).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex(),Oe={...X,lheading:Me,table:re,paragraph:h(Q).replace("hr",O).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",re).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex()},Be={...X,html:h(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",K).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:I,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:h(Q).replace("hr",O).replace("heading",` *#{1,6} *[^
|
| 13 |
+
]`).replace("lheading",oe).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},qe=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,ve=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,ae=/^( {2,}|\\)\n(?!\s*$)/,De=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,D=/[\p{P}\p{S}]/u,W=/[\s\p{P}\p{S}]/u,ce=/[^\s\p{P}\p{S}]/u,Ze=h(/^((?![*_])punctSpace)/,"u").replace(/punctSpace/g,W).getRegex(),pe=/(?!~)[\p{P}\p{S}]/u,Ge=/(?!~)[\s\p{P}\p{S}]/u,He=/(?:[^\s\p{P}\p{S}]|~)/u,Ne=/\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g,ue=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,je=h(ue,"u").replace(/punct/g,D).getRegex(),Fe=h(ue,"u").replace(/punct/g,pe).getRegex(),he="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",Qe=h(he,"gu").replace(/notPunctSpace/g,ce).replace(/punctSpace/g,W).replace(/punct/g,D).getRegex(),Ue=h(he,"gu").replace(/notPunctSpace/g,He).replace(/punctSpace/g,Ge).replace(/punct/g,pe).getRegex(),Ke=h("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,ce).replace(/punctSpace/g,W).replace(/punct/g,D).getRegex(),Xe=h(/\\(punct)/,"gu").replace(/punct/g,D).getRegex(),We=h(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Je=h(K).replace("(?:-->|$)","-->").getRegex(),Ve=h("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment",Je).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),q=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ye=h(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",q).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),ke=h(/^!?\[(label)\]\[(ref)\]/).replace("label",q).replace("ref",U).getRegex(),ge=h(/^!?\[(ref)\](?:\[\])?/).replace("ref",U).getRegex(),et=h("reflink|nolink(?!\\()","g").replace("reflink",ke).replace("nolink",ge).getRegex(),J={_backpedal:I,anyPunctuation:Xe,autolink:We,blockSkip:Ne,br:ae,code:ve,del:I,emStrongLDelim:je,emStrongRDelimAst:Qe,emStrongRDelimUnd:Ke,escape:qe,link:Ye,nolink:ge,punctuation:Ze,reflink:ke,reflinkSearch:et,tag:Ve,text:De,url:I},tt={...J,link:h(/^!?\[(label)\]\((.*?)\)/).replace("label",q).getRegex(),reflink:h(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",q).getRegex()},j={...J,emStrongRDelimAst:Ue,emStrongLDelim:Fe,url:h(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/},nt={...j,br:h(ae).replace("{2,}","*").getRegex(),text:h(j.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()},B={normal:X,gfm:Oe,pedantic:Be},P={normal:J,gfm:j,breaks:nt,pedantic:tt};var st={"&":"&","<":"<",">":">",'"':""","'":"'"},fe=l=>st[l];function R(l,e){if(e){if(m.escapeTest.test(l))return l.replace(m.escapeReplace,fe)}else if(m.escapeTestNoEncode.test(l))return l.replace(m.escapeReplaceNoEncode,fe);return l}function V(l){try{l=encodeURI(l).replace(m.percentDecode,"%")}catch{return null}return l}function Y(l,e){let t=l.replace(m.findPipe,(i,r,o)=>{let a=!1,c=r;for(;--c>=0&&o[c]==="\\";)a=!a;return a?"|":" |"}),n=t.split(m.splitPipe),s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length<e;)n.push("");for(;s<n.length;s++)n[s]=n[s].trim().replace(m.slashPipe,"|");return n}function A(l,e,t){let n=l.length;if(n===0)return"";let s=0;for(;s<n;){let i=l.charAt(n-s-1);if(i===e&&!t)s++;else if(i!==e&&t)s++;else break}return l.slice(0,n-s)}function de(l,e){if(l.indexOf(e[1])===-1)return-1;let t=0;for(let n=0;n<l.length;n++)if(l[n]==="\\")n++;else if(l[n]===e[0])t++;else if(l[n]===e[1]&&(t--,t<0))return n;return t>0?-2:-1}function me(l,e,t,n,s){let i=e.href,r=e.title||null,o=l[1].replace(s.other.outputLinkReplace,"$1");n.state.inLink=!0;let a={type:l[0].charAt(0)==="!"?"image":"link",raw:t,href:i,title:r,text:o,tokens:n.inlineTokens(o)};return n.state.inLink=!1,a}function rt(l,e,t){let n=l.match(t.other.indentCodeCompensation);if(n===null)return e;let s=n[1];return e.split(`
|
| 14 |
+
`).map(i=>{let r=i.match(t.other.beginningSpace);if(r===null)return i;let[o]=r;return o.length>=s.length?i.slice(s.length):i}).join(`
|
| 15 |
+
`)}var S=class{options;rules;lexer;constructor(e){this.options=e||w}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:A(n,`
|
| 16 |
+
`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],s=rt(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:s}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let s=A(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:A(t[0],`
|
| 17 |
+
`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=A(t[0],`
|
| 18 |
+
`).split(`
|
| 19 |
+
`),s="",i="",r=[];for(;n.length>0;){let o=!1,a=[],c;for(c=0;c<n.length;c++)if(this.rules.other.blockquoteStart.test(n[c]))a.push(n[c]),o=!0;else if(!o)a.push(n[c]);else break;n=n.slice(c);let p=a.join(`
|
| 20 |
+
`),u=p.replace(this.rules.other.blockquoteSetextReplace,`
|
| 21 |
+
$1`).replace(this.rules.other.blockquoteSetextReplace2,"");s=s?`${s}
|
| 22 |
+
${p}`:p,i=i?`${i}
|
| 23 |
+
${u}`:u;let d=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTokens(u,r,!0),this.lexer.state.top=d,n.length===0)break;let g=r.at(-1);if(g?.type==="code")break;if(g?.type==="blockquote"){let T=g,f=T.raw+`
|
| 24 |
+
`+n.join(`
|
| 25 |
+
`),y=this.blockquote(f);r[r.length-1]=y,s=s.substring(0,s.length-T.raw.length)+y.raw,i=i.substring(0,i.length-T.text.length)+y.text;break}else if(g?.type==="list"){let T=g,f=T.raw+`
|
| 26 |
+
`+n.join(`
|
| 27 |
+
`),y=this.list(f);r[r.length-1]=y,s=s.substring(0,s.length-g.raw.length)+y.raw,i=i.substring(0,i.length-T.raw.length)+y.raw,n=f.substring(r.at(-1).raw.length).split(`
|
| 28 |
+
`);continue}}return{type:"blockquote",raw:s,tokens:r,text:i}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim(),s=n.length>1,i={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");let r=this.rules.other.listItemRegex(n),o=!1;for(;e;){let c=!1,p="",u="";if(!(t=r.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let d=t[2].split(`
|
| 29 |
+
`,1)[0].replace(this.rules.other.listReplaceTabs,Z=>" ".repeat(3*Z.length)),g=e.split(`
|
| 30 |
+
`,1)[0],T=!d.trim(),f=0;if(this.options.pedantic?(f=2,u=d.trimStart()):T?f=t[1].length+1:(f=t[2].search(this.rules.other.nonSpaceChar),f=f>4?1:f,u=d.slice(f),f+=t[1].length),T&&this.rules.other.blankLine.test(g)&&(p+=g+`
|
| 31 |
+
`,e=e.substring(g.length+1),c=!0),!c){let Z=this.rules.other.nextBulletRegex(f),te=this.rules.other.hrRegex(f),ne=this.rules.other.fencesBeginRegex(f),se=this.rules.other.headingBeginRegex(f),xe=this.rules.other.htmlBeginRegex(f);for(;e;){let G=e.split(`
|
| 32 |
+
`,1)[0],C;if(g=G,this.options.pedantic?(g=g.replace(this.rules.other.listReplaceNesting," "),C=g):C=g.replace(this.rules.other.tabCharGlobal," "),ne.test(g)||se.test(g)||xe.test(g)||Z.test(g)||te.test(g))break;if(C.search(this.rules.other.nonSpaceChar)>=f||!g.trim())u+=`
|
| 33 |
+
`+C.slice(f);else{if(T||d.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||ne.test(d)||se.test(d)||te.test(d))break;u+=`
|
| 34 |
+
`+g}!T&&!g.trim()&&(T=!0),p+=G+`
|
| 35 |
+
`,e=e.substring(G.length+1),d=C.slice(f)}}i.loose||(o?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(o=!0));let y=null,ee;this.options.gfm&&(y=this.rules.other.listIsTask.exec(u),y&&(ee=y[0]!=="[ ] ",u=u.replace(this.rules.other.listReplaceTask,""))),i.items.push({type:"list_item",raw:p,task:!!y,checked:ee,loose:!1,text:u,tokens:[]}),i.raw+=p}let a=i.items.at(-1);if(a)a.raw=a.raw.trimEnd(),a.text=a.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let c=0;c<i.items.length;c++)if(this.lexer.state.top=!1,i.items[c].tokens=this.lexer.blockTokens(i.items[c].text,[]),!i.loose){let p=i.items[c].tokens.filter(d=>d.type==="space"),u=p.length>0&&p.some(d=>this.rules.other.anyLine.test(d.raw));i.loose=u}if(i.loose)for(let c=0;c<i.items.length;c++)i.items[c].loose=!0;return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:"html",block:!0,raw:t[0],pre:t[1]==="pre"||t[1]==="script"||t[1]==="style",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),s=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:n,raw:t[0],href:s,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=Y(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split(`
|
| 36 |
+
`):[],r={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(let o of s)this.rules.other.tableAlignRight.test(o)?r.align.push("right"):this.rules.other.tableAlignCenter.test(o)?r.align.push("center"):this.rules.other.tableAlignLeft.test(o)?r.align.push("left"):r.align.push(null);for(let o=0;o<n.length;o++)r.header.push({text:n[o],tokens:this.lexer.inline(n[o]),header:!0,align:r.align[o]});for(let o of i)r.rows.push(Y(o,r.header.length).map((a,c)=>({text:a,tokens:this.lexer.inline(a),header:!1,align:r.align[c]})));return r}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:t[2].charAt(0)==="="?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===`
|
| 37 |
+
`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let r=A(n.slice(0,-1),"\\");if((n.length-r.length)%2===0)return}else{let r=de(t[2],"()");if(r===-2)return;if(r>-1){let a=(t[0].indexOf("!")===0?5:4)+t[1].length+r;t[2]=t[2].substring(0,r),t[0]=t[0].substring(0,a).trim(),t[3]=""}}let s=t[2],i="";if(this.options.pedantic){let r=this.rules.other.pedanticHrefTitle.exec(s);r&&(s=r[1],i=r[3])}else i=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),me(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=t[s.toLowerCase()];if(!i){let r=n[0].charAt(0);return{type:"text",raw:r,text:r}}return me(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s||s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){let r=[...s[0]].length-1,o,a,c=r,p=0,u=s[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(u.lastIndex=0,t=t.slice(-1*e.length+r);(s=u.exec(t))!=null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o)continue;if(a=[...o].length,s[3]||s[4]){c+=a;continue}else if((s[5]||s[6])&&r%3&&!((r+a)%3)){p+=a;continue}if(c-=a,c>0)continue;a=Math.min(a,a+c+p);let d=[...s[0]][0].length,g=e.slice(0,r+s.index+d+a);if(Math.min(r,a)%2){let f=g.slice(1,-1);return{type:"em",raw:g,text:f,tokens:this.lexer.inlineTokens(f)}}let T=g.slice(2,-2);return{type:"strong",raw:g,text:T,tokens:this.lexer.inlineTokens(T)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&i&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]==="@"?(n=t[1],s="mailto:"+n):(n=t[1],s=n),{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,s;if(t[2]==="@")n=t[0],s="mailto:"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??"";while(i!==t[0]);n=t[0],t[1]==="www."?s="http://"+t[0]:s=t[0]}return{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}};var x=class l{tokens;options;state;tokenizer;inlineQueue;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||w,this.options.tokenizer=this.options.tokenizer||new S,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:B.normal,inline:P.normal};this.options.pedantic?(t.block=B.pedantic,t.inline=P.pedantic):this.options.gfm&&(t.block=B.gfm,this.options.breaks?t.inline=P.breaks:t.inline=P.gfm),this.tokenizer.rules=t}static get rules(){return{block:B,inline:P}}static lex(e,t){return new l(t).lex(e)}static lexInline(e,t){return new l(t).inlineTokens(e)}lex(e){e=e.replace(m.carriageReturn,`
|
| 38 |
+
`),this.blockTokens(e,this.tokens);for(let t=0;t<this.inlineQueue.length;t++){let n=this.inlineQueue[t];this.inlineTokens(n.src,n.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[],n=!1){for(this.options.pedantic&&(e=e.replace(m.tabCharGlobal," ").replace(m.spaceLine,""));e;){let s;if(this.options.extensions?.block?.some(r=>(s=r.call({lexer:this},e,t))?(e=e.substring(s.raw.length),t.push(s),!0):!1))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);let r=t.at(-1);s.raw.length===1&&r!==void 0?r.raw+=`
|
| 39 |
+
`:t.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);let r=t.at(-1);r?.type==="paragraph"||r?.type==="text"?(r.raw+=`
|
| 40 |
+
`+s.raw,r.text+=`
|
| 41 |
+
`+s.text,this.inlineQueue.at(-1).src=r.text):t.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);let r=t.at(-1);r?.type==="paragraph"||r?.type==="text"?(r.raw+=`
|
| 42 |
+
`+s.raw,r.text+=`
|
| 43 |
+
`+s.raw,this.inlineQueue.at(-1).src=r.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title});continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),t.push(s);continue}let i=e;if(this.options.extensions?.startBlock){let r=1/0,o=e.slice(1),a;this.options.extensions.startBlock.forEach(c=>{a=c.call({lexer:this},o),typeof a=="number"&&a>=0&&(r=Math.min(r,a))}),r<1/0&&r>=0&&(i=e.substring(0,r+1))}if(this.state.top&&(s=this.tokenizer.paragraph(i))){let r=t.at(-1);n&&r?.type==="paragraph"?(r.raw+=`
|
| 44 |
+
`+s.raw,r.text+=`
|
| 45 |
+
`+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=r.text):t.push(s),n=i.length!==e.length,e=e.substring(s.raw.length);continue}if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);let r=t.at(-1);r?.type==="text"?(r.raw+=`
|
| 46 |
+
`+s.raw,r.text+=`
|
| 47 |
+
`+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=r.text):t.push(s);continue}if(e){let r="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(r);break}else throw new Error(r)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,s=null;if(this.tokens.links){let o=Object.keys(this.tokens.links);if(o.length>0)for(;(s=this.tokenizer.rules.inline.reflinkSearch.exec(n))!=null;)o.includes(s[0].slice(s[0].lastIndexOf("[")+1,-1))&&(n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(s=this.tokenizer.rules.inline.anyPunctuation.exec(n))!=null;)n=n.slice(0,s.index)+"++"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;(s=this.tokenizer.rules.inline.blockSkip.exec(n))!=null;)n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);let i=!1,r="";for(;e;){i||(r=""),i=!1;let o;if(this.options.extensions?.inline?.some(c=>(o=c.call({lexer:this},e,t))?(e=e.substring(o.raw.length),t.push(o),!0):!1))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);let c=t.at(-1);o.type==="text"&&c?.type==="text"?(c.raw+=o.raw,c.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,r)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let a=e;if(this.options.extensions?.startInline){let c=1/0,p=e.slice(1),u;this.options.extensions.startInline.forEach(d=>{u=d.call({lexer:this},p),typeof u=="number"&&u>=0&&(c=Math.min(c,u))}),c<1/0&&c>=0&&(a=e.substring(0,c+1))}if(o=this.tokenizer.inlineText(a)){e=e.substring(o.raw.length),o.raw.slice(-1)!=="_"&&(r=o.raw.slice(-1)),i=!0;let c=t.at(-1);c?.type==="text"?(c.raw+=o.raw,c.text+=o.text):t.push(o);continue}if(e){let c="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(c);break}else throw new Error(c)}}return t}};var $=class{options;parser;constructor(e){this.options=e||w}space(e){return""}code({text:e,lang:t,escaped:n}){let s=(t||"").match(m.notSpaceStart)?.[0],i=e.replace(m.endingNewline,"")+`
|
| 48 |
+
`;return s?'<pre><code class="language-'+R(s)+'">'+(n?i:R(i,!0))+`</code></pre>
|
| 49 |
+
`:"<pre><code>"+(n?i:R(i,!0))+`</code></pre>
|
| 50 |
+
`}blockquote({tokens:e}){return`<blockquote>
|
| 51 |
+
${this.parser.parse(e)}</blockquote>
|
| 52 |
+
`}html({text:e}){return e}heading({tokens:e,depth:t}){return`<h${t}>${this.parser.parseInline(e)}</h${t}>
|
| 53 |
+
`}hr(e){return`<hr>
|
| 54 |
+
`}list(e){let t=e.ordered,n=e.start,s="";for(let o=0;o<e.items.length;o++){let a=e.items[o];s+=this.listitem(a)}let i=t?"ol":"ul",r=t&&n!==1?' start="'+n+'"':"";return"<"+i+r+`>
|
| 55 |
+
`+s+"</"+i+`>
|
| 56 |
+
`}listitem(e){let t="";if(e.task){let n=this.checkbox({checked:!!e.checked});e.loose?e.tokens[0]?.type==="paragraph"?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&e.tokens[0].tokens[0].type==="text"&&(e.tokens[0].tokens[0].text=n+" "+R(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`<li>${t}</li>
|
| 57 |
+
`}checkbox({checked:e}){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox">'}paragraph({tokens:e}){return`<p>${this.parser.parseInline(e)}</p>
|
| 58 |
+
`}table(e){let t="",n="";for(let i=0;i<e.header.length;i++)n+=this.tablecell(e.header[i]);t+=this.tablerow({text:n});let s="";for(let i=0;i<e.rows.length;i++){let r=e.rows[i];n="";for(let o=0;o<r.length;o++)n+=this.tablecell(r[o]);s+=this.tablerow({text:n})}return s&&(s=`<tbody>${s}</tbody>`),`<table>
|
| 59 |
+
<thead>
|
| 60 |
+
`+t+`</thead>
|
| 61 |
+
`+s+`</table>
|
| 62 |
+
`}tablerow({text:e}){return`<tr>
|
| 63 |
+
${e}</tr>
|
| 64 |
+
`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`</${n}>
|
| 65 |
+
`}strong({tokens:e}){return`<strong>${this.parser.parseInline(e)}</strong>`}em({tokens:e}){return`<em>${this.parser.parseInline(e)}</em>`}codespan({text:e}){return`<code>${R(e,!0)}</code>`}br(e){return"<br>"}del({tokens:e}){return`<del>${this.parser.parseInline(e)}</del>`}link({href:e,title:t,tokens:n}){let s=this.parser.parseInline(n),i=V(e);if(i===null)return s;e=i;let r='<a href="'+e+'"';return t&&(r+=' title="'+R(t)+'"'),r+=">"+s+"</a>",r}image({href:e,title:t,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let i=V(e);if(i===null)return R(n);e=i;let r=`<img src="${e}" alt="${n}"`;return t&&(r+=` title="${R(t)}"`),r+=">",r}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:R(e.text)}};var _=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}};var b=class l{options;renderer;textRenderer;constructor(e){this.options=e||w,this.options.renderer=this.options.renderer||new $,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new _}static parse(e,t){return new l(t).parse(e)}static parseInline(e,t){return new l(t).parseInline(e)}parse(e,t=!0){let n="";for(let s=0;s<e.length;s++){let i=e[s];if(this.options.extensions?.renderers?.[i.type]){let o=i,a=this.options.extensions.renderers[o.type].call({parser:this},o);if(a!==!1||!["space","hr","heading","code","table","blockquote","list","html","paragraph","text"].includes(o.type)){n+=a||"";continue}}let r=i;switch(r.type){case"space":{n+=this.renderer.space(r);continue}case"hr":{n+=this.renderer.hr(r);continue}case"heading":{n+=this.renderer.heading(r);continue}case"code":{n+=this.renderer.code(r);continue}case"table":{n+=this.renderer.table(r);continue}case"blockquote":{n+=this.renderer.blockquote(r);continue}case"list":{n+=this.renderer.list(r);continue}case"html":{n+=this.renderer.html(r);continue}case"paragraph":{n+=this.renderer.paragraph(r);continue}case"text":{let o=r,a=this.renderer.text(o);for(;s+1<e.length&&e[s+1].type==="text";)o=e[++s],a+=`
|
| 66 |
+
`+this.renderer.text(o);t?n+=this.renderer.paragraph({type:"paragraph",raw:a,text:a,tokens:[{type:"text",raw:a,text:a,escaped:!0}]}):n+=a;continue}default:{let o='Token with "'+r.type+'" type was not found.';if(this.options.silent)return console.error(o),"";throw new Error(o)}}}return n}parseInline(e,t=this.renderer){let n="";for(let s=0;s<e.length;s++){let i=e[s];if(this.options.extensions?.renderers?.[i.type]){let o=this.options.extensions.renderers[i.type].call({parser:this},i);if(o!==!1||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(i.type)){n+=o||"";continue}}let r=i;switch(r.type){case"escape":{n+=t.text(r);break}case"html":{n+=t.html(r);break}case"link":{n+=t.link(r);break}case"image":{n+=t.image(r);break}case"strong":{n+=t.strong(r);break}case"em":{n+=t.em(r);break}case"codespan":{n+=t.codespan(r);break}case"br":{n+=t.br(r);break}case"del":{n+=t.del(r);break}case"text":{n+=t.text(r);break}default:{let o='Token with "'+r.type+'" type was not found.';if(this.options.silent)return console.error(o),"";throw new Error(o)}}}return n}};var L=class{options;block;constructor(e){this.options=e||w}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}provideLexer(){return this.block?x.lex:x.lexInline}provideParser(){return this.block?b.parse:b.parseInline}};var E=class{defaults=z();options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=b;Renderer=$;TextRenderer=_;Lexer=x;Tokenizer=S;Hooks=L;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(let s of e)switch(n=n.concat(t.call(this,s)),s.type){case"table":{let i=s;for(let r of i.header)n=n.concat(this.walkTokens(r.tokens,t));for(let r of i.rows)for(let o of r)n=n.concat(this.walkTokens(o.tokens,t));break}case"list":{let i=s;n=n.concat(this.walkTokens(i.items,t));break}default:{let i=s;this.defaults.extensions?.childTokens?.[i.type]?this.defaults.extensions.childTokens[i.type].forEach(r=>{let o=i[r].flat(1/0);n=n.concat(this.walkTokens(o,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let r=t.renderers[i.name];r?t.renderers[i.name]=function(...o){let a=i.renderer.apply(this,o);return a===!1&&(a=r.apply(this,o)),a}:t.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let r=t[i.level];r?r.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level==="block"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level==="inline"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),s.extensions=t),n.renderer){let i=this.defaults.renderer||new $(this.defaults);for(let r in n.renderer){if(!(r in i))throw new Error(`renderer '${r}' does not exist`);if(["options","parser"].includes(r))continue;let o=r,a=n.renderer[o],c=i[o];i[o]=(...p)=>{let u=a.apply(i,p);return u===!1&&(u=c.apply(i,p)),u||""}}s.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new S(this.defaults);for(let r in n.tokenizer){if(!(r in i))throw new Error(`tokenizer '${r}' does not exist`);if(["options","rules","lexer"].includes(r))continue;let o=r,a=n.tokenizer[o],c=i[o];i[o]=(...p)=>{let u=a.apply(i,p);return u===!1&&(u=c.apply(i,p)),u}}s.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new L;for(let r in n.hooks){if(!(r in i))throw new Error(`hook '${r}' does not exist`);if(["options","block"].includes(r))continue;let o=r,a=n.hooks[o],c=i[o];L.passThroughHooks.has(r)?i[o]=p=>{if(this.defaults.async)return Promise.resolve(a.call(i,p)).then(d=>c.call(i,d));let u=a.call(i,p);return c.call(i,u)}:i[o]=(...p)=>{let u=a.apply(i,p);return u===!1&&(u=c.apply(i,p)),u}}s.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,r=n.walkTokens;s.walkTokens=function(o){let a=[];return a.push(r.call(this,o)),i&&(a=a.concat(i.call(this,o))),a}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(n,s)=>{let i={...s},r={...this.defaults,...i},o=this.onError(!!r.silent,!!r.async);if(this.defaults.async===!0&&i.async===!1)return o(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof n>"u"||n===null)return o(new Error("marked(): input parameter is undefined or null"));if(typeof n!="string")return o(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));r.hooks&&(r.hooks.options=r,r.hooks.block=e);let a=r.hooks?r.hooks.provideLexer():e?x.lex:x.lexInline,c=r.hooks?r.hooks.provideParser():e?b.parse:b.parseInline;if(r.async)return Promise.resolve(r.hooks?r.hooks.preprocess(n):n).then(p=>a(p,r)).then(p=>r.hooks?r.hooks.processAllTokens(p):p).then(p=>r.walkTokens?Promise.all(this.walkTokens(p,r.walkTokens)).then(()=>p):p).then(p=>c(p,r)).then(p=>r.hooks?r.hooks.postprocess(p):p).catch(o);try{r.hooks&&(n=r.hooks.preprocess(n));let p=a(n,r);r.hooks&&(p=r.hooks.processAllTokens(p)),r.walkTokens&&this.walkTokens(p,r.walkTokens);let u=c(p,r);return r.hooks&&(u=r.hooks.postprocess(u)),u}catch(p){return o(p)}}}onError(e,t){return n=>{if(n.message+=`
|
| 67 |
+
Please report this to https://github.com/markedjs/marked.`,e){let s="<p>An error occurred:</p><pre>"+R(n.message+"",!0)+"</pre>";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}};var M=new E;function k(l,e){return M.parse(l,e)}k.options=k.setOptions=function(l){return M.setOptions(l),k.defaults=M.defaults,N(k.defaults),k};k.getDefaults=z;k.defaults=w;k.use=function(...l){return M.use(...l),k.defaults=M.defaults,N(k.defaults),k};k.walkTokens=function(l,e){return M.walkTokens(l,e)};k.parseInline=M.parseInline;k.Parser=b;k.parser=b.parse;k.Renderer=$;k.TextRenderer=_;k.Lexer=x;k.lexer=x.lex;k.Tokenizer=S;k.Hooks=L;k.parse=k;var it=k.options,ot=k.setOptions,lt=k.use,at=k.walkTokens,ct=k.parseInline,pt=k,ut=b.parse,ht=x.lex;
|
| 68 |
+
|
| 69 |
+
if(__exports != exports)module.exports = exports;return module.exports}));
|
libs/turndown.min.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Minified by jsDelivr using Terser v5.37.0.
|
| 3 |
+
* Original file: /npm/turndown@7.1.2/dist/turndown.js
|
| 4 |
+
*
|
| 5 |
+
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
| 6 |
+
*/
|
| 7 |
+
var TurndownService=function(){"use strict";function e(e,n){return Array(n+1).join(e)}var n=["ADDRESS","ARTICLE","ASIDE","AUDIO","BLOCKQUOTE","BODY","CANVAS","CENTER","DD","DIR","DIV","DL","DT","FIELDSET","FIGCAPTION","FIGURE","FOOTER","FORM","FRAMESET","H1","H2","H3","H4","H5","H6","HEADER","HGROUP","HR","HTML","ISINDEX","LI","MAIN","MENU","NAV","NOFRAMES","NOSCRIPT","OL","OUTPUT","P","PRE","SECTION","TABLE","TBODY","TD","TFOOT","TH","THEAD","TR","UL"];function t(e){return a(e,n)}var r=["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"];function i(e){return a(e,r)}var o=["A","TABLE","THEAD","TBODY","TFOOT","TH","TD","IFRAME","SCRIPT","AUDIO","VIDEO"];function a(e,n){return n.indexOf(e.nodeName)>=0}function l(e,n){return e.getElementsByTagName&&n.some((function(n){return e.getElementsByTagName(n).length}))}var u={};function c(e){return e?e.replace(/(\n+\s*)+/g,"\n"):""}function s(e){for(var n in this.options=e,this._keep=[],this._remove=[],this.blankRule={replacement:e.blankReplacement},this.keepReplacement=e.keepReplacement,this.defaultRule={replacement:e.defaultReplacement},this.array=[],e.rules)this.array.push(e.rules[n])}function f(e,n,t){for(var r=0;r<e.length;r++){var i=e[r];if(d(i,n,t))return i}}function d(e,n,t){var r=e.filter;if("string"==typeof r){if(r===n.nodeName.toLowerCase())return!0}else if(Array.isArray(r)){if(r.indexOf(n.nodeName.toLowerCase())>-1)return!0}else{if("function"!=typeof r)throw new TypeError("`filter` needs to be a string, array, or function");if(r.call(e,n,t))return!0}}function p(e){var n=e.nextSibling||e.parentNode;return e.parentNode.removeChild(e),n}function h(e,n,t){return e&&e.parentNode===n||t(n)?n.nextSibling||n.parentNode:n.firstChild||n.nextSibling||n.parentNode}u.paragraph={filter:"p",replacement:function(e){return"\n\n"+e+"\n\n"}},u.lineBreak={filter:"br",replacement:function(e,n,t){return t.br+"\n"}},u.heading={filter:["h1","h2","h3","h4","h5","h6"],replacement:function(n,t,r){var i=Number(t.nodeName.charAt(1));return"setext"===r.headingStyle&&i<3?"\n\n"+n+"\n"+e(1===i?"=":"-",n.length)+"\n\n":"\n\n"+e("#",i)+" "+n+"\n\n"}},u.blockquote={filter:"blockquote",replacement:function(e){return"\n\n"+(e=(e=e.replace(/^\n+|\n+$/g,"")).replace(/^/gm,"> "))+"\n\n"}},u.list={filter:["ul","ol"],replacement:function(e,n){var t=n.parentNode;return"LI"===t.nodeName&&t.lastElementChild===n?"\n"+e:"\n\n"+e+"\n\n"}},u.listItem={filter:"li",replacement:function(e,n,t){e=e.replace(/^\n+/,"").replace(/\n+$/,"\n").replace(/\n/gm,"\n ");var r=t.bulletListMarker+" ",i=n.parentNode;if("OL"===i.nodeName){var o=i.getAttribute("start"),a=Array.prototype.indexOf.call(i.children,n);r=(o?Number(o)+a:a+1)+". "}return r+e+(n.nextSibling&&!/\n$/.test(e)?"\n":"")}},u.indentedCodeBlock={filter:function(e,n){return"indented"===n.codeBlockStyle&&"PRE"===e.nodeName&&e.firstChild&&"CODE"===e.firstChild.nodeName},replacement:function(e,n,t){return"\n\n "+n.firstChild.textContent.replace(/\n/g,"\n ")+"\n\n"}},u.fencedCodeBlock={filter:function(e,n){return"fenced"===n.codeBlockStyle&&"PRE"===e.nodeName&&e.firstChild&&"CODE"===e.firstChild.nodeName},replacement:function(n,t,r){for(var i,o=((t.firstChild.getAttribute("class")||"").match(/language-(\S+)/)||[null,""])[1],a=t.firstChild.textContent,l=r.fence.charAt(0),u=3,c=new RegExp("^"+l+"{3,}","gm");i=c.exec(a);)i[0].length>=u&&(u=i[0].length+1);var s=e(l,u);return"\n\n"+s+o+"\n"+a.replace(/\n$/,"")+"\n"+s+"\n\n"}},u.horizontalRule={filter:"hr",replacement:function(e,n,t){return"\n\n"+t.hr+"\n\n"}},u.inlineLink={filter:function(e,n){return"inlined"===n.linkStyle&&"A"===e.nodeName&&e.getAttribute("href")},replacement:function(e,n){var t=n.getAttribute("href"),r=c(n.getAttribute("title"));return r&&(r=' "'+r+'"'),"["+e+"]("+t+r+")"}},u.referenceLink={filter:function(e,n){return"referenced"===n.linkStyle&&"A"===e.nodeName&&e.getAttribute("href")},replacement:function(e,n,t){var r,i,o=n.getAttribute("href"),a=c(n.getAttribute("title"));switch(a&&(a=' "'+a+'"'),t.linkReferenceStyle){case"collapsed":r="["+e+"][]",i="["+e+"]: "+o+a;break;case"shortcut":r="["+e+"]",i="["+e+"]: "+o+a;break;default:var l=this.references.length+1;r="["+e+"]["+l+"]",i="["+l+"]: "+o+a}return this.references.push(i),r},references:[],append:function(e){var n="";return this.references.length&&(n="\n\n"+this.references.join("\n")+"\n\n",this.references=[]),n}},u.emphasis={filter:["em","i"],replacement:function(e,n,t){return e.trim()?t.emDelimiter+e+t.emDelimiter:""}},u.strong={filter:["strong","b"],replacement:function(e,n,t){return e.trim()?t.strongDelimiter+e+t.strongDelimiter:""}},u.code={filter:function(e){var n=e.previousSibling||e.nextSibling,t="PRE"===e.parentNode.nodeName&&!n;return"CODE"===e.nodeName&&!t},replacement:function(e){if(!e)return"";e=e.replace(/\r?\n|\r/g," ");for(var n=/^`|^ .*?[^ ].* $|`$/.test(e)?" ":"",t="`",r=e.match(/`+/gm)||[];-1!==r.indexOf(t);)t+="`";return t+n+e+n+t}},u.image={filter:"img",replacement:function(e,n){var t=c(n.getAttribute("alt")),r=n.getAttribute("src")||"",i=c(n.getAttribute("title"));return r?"+")":""}},s.prototype={add:function(e,n){this.array.unshift(n)},keep:function(e){this._keep.unshift({filter:e,replacement:this.keepReplacement})},remove:function(e){this._remove.unshift({filter:e,replacement:function(){return""}})},forNode:function(e){return e.isBlank?this.blankRule:(n=f(this.array,e,this.options))||(n=f(this._keep,e,this.options))||(n=f(this._remove,e,this.options))?n:this.defaultRule;var n},forEach:function(e){for(var n=0;n<this.array.length;n++)e(this.array[n],n)}};var g="undefined"!=typeof window?window:{};var m,v,A=function(){var e=g.DOMParser,n=!1;try{(new e).parseFromString("","text/html")&&(n=!0)}catch(e){}return n}()?g.DOMParser:(m=function(){},function(){var e=!1;try{document.implementation.createHTMLDocument("").open()}catch(n){window.ActiveXObject&&(e=!0)}return e}()?m.prototype.parseFromString=function(e){var n=new window.ActiveXObject("htmlfile");return n.designMode="on",n.open(),n.write(e),n.close(),n}:m.prototype.parseFromString=function(e){var n=document.implementation.createHTMLDocument("");return n.open(),n.write(e),n.close(),n},m);function y(e,n){var r;"string"==typeof e?r=(v=v||new A).parseFromString('<x-turndown id="turndown-root">'+e+"</x-turndown>","text/html").getElementById("turndown-root"):r=e.cloneNode(!0);return function(e){var n=e.element,t=e.isBlock,r=e.isVoid,i=e.isPre||function(e){return"PRE"===e.nodeName};if(n.firstChild&&!i(n)){for(var o=null,a=!1,l=null,u=h(l,n,i);u!==n;){if(3===u.nodeType||4===u.nodeType){var c=u.data.replace(/[ \r\n\t]+/g," ");if(o&&!/ $/.test(o.data)||a||" "!==c[0]||(c=c.substr(1)),!c){u=p(u);continue}u.data=c,o=u}else{if(1!==u.nodeType){u=p(u);continue}t(u)||"BR"===u.nodeName?(o&&(o.data=o.data.replace(/ $/,"")),o=null,a=!1):r(u)||i(u)?(o=null,a=!0):o&&(a=!1)}var s=h(l,u,i);l=u,u=s}o&&(o.data=o.data.replace(/ $/,""),o.data||p(o))}}({element:r,isBlock:t,isVoid:i,isPre:n.preformattedCode?N:null}),r}function N(e){return"PRE"===e.nodeName||"CODE"===e.nodeName}function E(e,n){return e.isBlock=t(e),e.isCode="CODE"===e.nodeName||e.parentNode.isCode,e.isBlank=function(e){return!i(e)&&!function(e){return a(e,o)}(e)&&/^\s*$/i.test(e.textContent)&&!function(e){return l(e,r)}(e)&&!function(e){return l(e,o)}(e)}(e),e.flankingWhitespace=function(e,n){if(e.isBlock||n.preformattedCode&&e.isCode)return{leading:"",trailing:""};var t=(r=e.textContent,i=r.match(/^(([ \t\r\n]*)(\s*))(?:(?=\S)[\s\S]*\S)?((\s*?)([ \t\r\n]*))$/),{leading:i[1],leadingAscii:i[2],leadingNonAscii:i[3],trailing:i[4],trailingNonAscii:i[5],trailingAscii:i[6]});var r,i;t.leadingAscii&&T("left",e,n)&&(t.leading=t.leadingNonAscii);t.trailingAscii&&T("right",e,n)&&(t.trailing=t.trailingNonAscii);return{leading:t.leading,trailing:t.trailing}}(e,n),e}function T(e,n,r){var i,o,a;return"left"===e?(i=n.previousSibling,o=/ $/):(i=n.nextSibling,o=/^ /),i&&(3===i.nodeType?a=o.test(i.nodeValue):r.preformattedCode&&"CODE"===i.nodeName?a=!1:1!==i.nodeType||t(i)||(a=o.test(i.textContent))),a}var R=Array.prototype.reduce,C=[[/\\/g,"\\\\"],[/\*/g,"\\*"],[/^-/g,"\\-"],[/^\+ /g,"\\+ "],[/^(=+)/g,"\\$1"],[/^(#{1,6}) /g,"\\$1 "],[/`/g,"\\`"],[/^~~~/g,"\\~~~"],[/\[/g,"\\["],[/\]/g,"\\]"],[/^>/g,"\\>"],[/_/g,"\\_"],[/^(\d+)\. /g,"$1\\. "]];function k(e){if(!(this instanceof k))return new k(e);var n={rules:u,headingStyle:"setext",hr:"* * *",bulletListMarker:"*",codeBlockStyle:"indented",fence:"```",emDelimiter:"_",strongDelimiter:"**",linkStyle:"inlined",linkReferenceStyle:"full",br:" ",preformattedCode:!1,blankReplacement:function(e,n){return n.isBlock?"\n\n":""},keepReplacement:function(e,n){return n.isBlock?"\n\n"+n.outerHTML+"\n\n":n.outerHTML},defaultReplacement:function(e,n){return n.isBlock?"\n\n"+e+"\n\n":e}};this.options=function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])}return e}({},n,e),this.rules=new s(this.options)}function b(e){var n=this;return R.call(e.childNodes,(function(e,t){var r="";return 3===(t=new E(t,n.options)).nodeType?r=t.isCode?t.nodeValue:n.escape(t.nodeValue):1===t.nodeType&&(r=D.call(n,t)),S(e,r)}),"")}function O(e){var n=this;return this.rules.forEach((function(t){"function"==typeof t.append&&(e=S(e,t.append(n.options)))})),e.replace(/^[\t\r\n]+/,"").replace(/[\t\r\n\s]+$/,"")}function D(e){var n=this.rules.forNode(e),t=b.call(this,e),r=e.flankingWhitespace;return(r.leading||r.trailing)&&(t=t.trim()),r.leading+n.replacement(t,e,this.options)+r.trailing}function S(e,n){var t=function(e){for(var n=e.length;n>0&&"\n"===e[n-1];)n--;return e.substring(0,n)}(e),r=n.replace(/^\n*/,""),i=Math.max(e.length-t.length,n.length-r.length);return t+"\n\n".substring(0,i)+r}return k.prototype={turndown:function(e){if(!function(e){return null!=e&&("string"==typeof e||e.nodeType&&(1===e.nodeType||9===e.nodeType||11===e.nodeType))}(e))throw new TypeError(e+" is not a string, or an element/document/fragment node.");if(""===e)return"";var n=b.call(this,new y(e,this.options));return O.call(this,n)},use:function(e){if(Array.isArray(e))for(var n=0;n<e.length;n++)this.use(e[n]);else{if("function"!=typeof e)throw new TypeError("plugin must be a Function or an Array of Functions");e(this)}return this},addRule:function(e,n){return this.rules.add(e,n),this},keep:function(e){return this.rules.keep(e),this},remove:function(e){return this.rules.remove(e),this},escape:function(e){return C.reduce((function(e,n){return e.replace(n[0],n[1])}),e)}},k}();
|
| 8 |
+
//# sourceMappingURL=/sm/058ec333b09529f5d9739d2807a13f8d3fd2a30c9ae748f47e9df031aad767fd.map
|
scripts/ai-tools.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - AI Tools Module
|
| 3 |
+
Elysia's AI-powered features
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
import API from "./api.js";
|
| 8 |
+
import DB from "./db.js";
|
| 9 |
+
|
| 10 |
+
const AITools = {
|
| 11 |
+
isProcessing: false,
|
| 12 |
+
|
| 13 |
+
init() {
|
| 14 |
+
document.getElementById("btn-ai-tools").addEventListener("click", () => {
|
| 15 |
+
Utils.modal.open("modal-ai-tools");
|
| 16 |
+
});
|
| 17 |
+
|
| 18 |
+
document.querySelectorAll(".ai-tool-card").forEach(card => {
|
| 19 |
+
card.addEventListener("click", () => {
|
| 20 |
+
const tool = card.getAttribute("data-tool");
|
| 21 |
+
this.executeTool(tool);
|
| 22 |
+
});
|
| 23 |
+
});
|
| 24 |
+
},
|
| 25 |
+
|
| 26 |
+
async executeTool(tool) {
|
| 27 |
+
if (this.isProcessing) {
|
| 28 |
+
Utils.toast.warning("Please wait - Elysia is already working on something!");
|
| 29 |
+
return;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
Utils.modal.close("modal-ai-tools");
|
| 33 |
+
this.isProcessing = true;
|
| 34 |
+
|
| 35 |
+
// Show loading indicator
|
| 36 |
+
const loadingToast = this.showLoadingState(tool);
|
| 37 |
+
|
| 38 |
+
try {
|
| 39 |
+
switch (tool) {
|
| 40 |
+
case "summarize":
|
| 41 |
+
await this.summarize();
|
| 42 |
+
break;
|
| 43 |
+
case "improve":
|
| 44 |
+
await this.improveWriting();
|
| 45 |
+
break;
|
| 46 |
+
case "merge":
|
| 47 |
+
await this.mergeDocuments();
|
| 48 |
+
break;
|
| 49 |
+
case "outline":
|
| 50 |
+
await this.extractOutline();
|
| 51 |
+
break;
|
| 52 |
+
case "duplicates":
|
| 53 |
+
await this.findDuplicates();
|
| 54 |
+
break;
|
| 55 |
+
case "organize":
|
| 56 |
+
await this.smartOrganize();
|
| 57 |
+
break;
|
| 58 |
+
}
|
| 59 |
+
} catch (err) {
|
| 60 |
+
console.error(`AI tool ${tool} failed:`, err);
|
| 61 |
+
|
| 62 |
+
// User-friendly error messages
|
| 63 |
+
let errorMsg = "AI tool failed";
|
| 64 |
+
if (err.message.includes("API key")) {
|
| 65 |
+
errorMsg = "API key not configured. Please add it in Settings ⚙️";
|
| 66 |
+
} else if (err.message.includes("network") || err.message.includes("fetch")) {
|
| 67 |
+
errorMsg = "Network error. Check your connection and try again.";
|
| 68 |
+
} else if (err.message.includes("rate limit")) {
|
| 69 |
+
errorMsg = "Rate limit reached. Please wait a moment and try again.";
|
| 70 |
+
} else {
|
| 71 |
+
errorMsg = err.message || "AI tool failed. Please try again.";
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
Utils.toast.error(errorMsg, 5000);
|
| 75 |
+
} finally {
|
| 76 |
+
this.isProcessing = false;
|
| 77 |
+
// Remove loading toast
|
| 78 |
+
if (loadingToast) loadingToast.remove();
|
| 79 |
+
}
|
| 80 |
+
},
|
| 81 |
+
|
| 82 |
+
showLoadingState(tool) {
|
| 83 |
+
const messages = {
|
| 84 |
+
summarize: "🧠 Elysia is reading and summarizing...",
|
| 85 |
+
improve: "✨ Elysia is polishing your writing...",
|
| 86 |
+
merge: "📚 Elysia is merging documents...",
|
| 87 |
+
outline: "🎯 Elysia is extracting outline...",
|
| 88 |
+
duplicates: "🔍 Elysia is analyzing duplicates...",
|
| 89 |
+
organize: "🏷️ Elysia is organizing content..."
|
| 90 |
+
};
|
| 91 |
+
|
| 92 |
+
const toast = Utils.toast.show(messages[tool] || "🧠 Elysia is thinking...", "loading", 0); // 0 = no auto-close
|
| 93 |
+
return toast;
|
| 94 |
+
},
|
| 95 |
+
|
| 96 |
+
async summarize() {
|
| 97 |
+
const content = window.app?.editor.getContent();
|
| 98 |
+
if (!content) {
|
| 99 |
+
Utils.toast.warning("No content to summarize");
|
| 100 |
+
return;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
if (content.length < 100) {
|
| 104 |
+
Utils.toast.warning("Content too short. Add at least 100 characters for better summary.");
|
| 105 |
+
return;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
const summary = await API.summarize(content);
|
| 109 |
+
|
| 110 |
+
// Create new document with summary
|
| 111 |
+
const doc = await DB.createDocument({
|
| 112 |
+
title: `Summary of ${window.app?.currentDoc?.title || "Document"}`,
|
| 113 |
+
content: summary
|
| 114 |
+
});
|
| 115 |
+
|
| 116 |
+
Utils.toast.success("Summary created!");
|
| 117 |
+
window.app?.loadDocument(doc.id);
|
| 118 |
+
},
|
| 119 |
+
|
| 120 |
+
async improveWriting() {
|
| 121 |
+
const content = window.app?.editor.getContent();
|
| 122 |
+
if (!content) {
|
| 123 |
+
Utils.toast.warning("No content to improve");
|
| 124 |
+
return;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
if (content.length < 50) {
|
| 128 |
+
Utils.toast.warning("Content too short. Add at least 50 characters for improvement.");
|
| 129 |
+
return;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
// 🎲 Show style selection modal (VS-inspired)
|
| 133 |
+
this.showImproveStyleModal(content);
|
| 134 |
+
},
|
| 135 |
+
|
| 136 |
+
// 🎲 VS-INSPIRED: Show style selection modal
|
| 137 |
+
showImproveStyleModal(content) {
|
| 138 |
+
// Add styles if not present
|
| 139 |
+
if (!document.getElementById("improve-styles-css")) {
|
| 140 |
+
const style = document.createElement("style");
|
| 141 |
+
style.id = "improve-styles-css";
|
| 142 |
+
style.textContent = `
|
| 143 |
+
.improve-style-grid {
|
| 144 |
+
display: grid;
|
| 145 |
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
| 146 |
+
gap: 12px;
|
| 147 |
+
margin: 20px 0;
|
| 148 |
+
}
|
| 149 |
+
.improve-style-card {
|
| 150 |
+
padding: 16px;
|
| 151 |
+
background: var(--bg-secondary);
|
| 152 |
+
border: 2px solid transparent;
|
| 153 |
+
border-radius: 12px;
|
| 154 |
+
cursor: pointer;
|
| 155 |
+
transition: all 0.2s ease;
|
| 156 |
+
text-align: center;
|
| 157 |
+
}
|
| 158 |
+
.improve-style-card:hover {
|
| 159 |
+
border-color: var(--accent);
|
| 160 |
+
transform: translateY(-2px);
|
| 161 |
+
}
|
| 162 |
+
.improve-style-card.selected {
|
| 163 |
+
border-color: var(--accent);
|
| 164 |
+
background: rgba(167, 139, 250, 0.15);
|
| 165 |
+
}
|
| 166 |
+
.improve-style-icon {
|
| 167 |
+
font-size: 28px;
|
| 168 |
+
margin-bottom: 8px;
|
| 169 |
+
}
|
| 170 |
+
.improve-style-name {
|
| 171 |
+
font-weight: 600;
|
| 172 |
+
margin-bottom: 4px;
|
| 173 |
+
}
|
| 174 |
+
.improve-style-desc {
|
| 175 |
+
font-size: 11px;
|
| 176 |
+
color: var(--text-muted);
|
| 177 |
+
}
|
| 178 |
+
.improve-results {
|
| 179 |
+
margin-top: 16px;
|
| 180 |
+
max-height: 400px;
|
| 181 |
+
overflow-y: auto;
|
| 182 |
+
}
|
| 183 |
+
.improve-result-card {
|
| 184 |
+
padding: 16px;
|
| 185 |
+
margin-bottom: 12px;
|
| 186 |
+
background: var(--bg-secondary);
|
| 187 |
+
border-radius: 10px;
|
| 188 |
+
border-left: 4px solid var(--accent);
|
| 189 |
+
}
|
| 190 |
+
.improve-result-header {
|
| 191 |
+
display: flex;
|
| 192 |
+
justify-content: space-between;
|
| 193 |
+
align-items: center;
|
| 194 |
+
margin-bottom: 10px;
|
| 195 |
+
}
|
| 196 |
+
.improve-result-preview {
|
| 197 |
+
font-size: 13px;
|
| 198 |
+
color: var(--text-secondary);
|
| 199 |
+
line-height: 1.6;
|
| 200 |
+
max-height: 150px;
|
| 201 |
+
overflow-y: auto;
|
| 202 |
+
padding: 10px;
|
| 203 |
+
background: var(--bg-tertiary);
|
| 204 |
+
border-radius: 6px;
|
| 205 |
+
}
|
| 206 |
+
`;
|
| 207 |
+
document.head.appendChild(style);
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
const modal = document.createElement("div");
|
| 211 |
+
modal.className = "modal active";
|
| 212 |
+
modal.id = "modal-improve-styles";
|
| 213 |
+
modal.innerHTML = `
|
| 214 |
+
<div class="modal-content" style="max-width: 600px;">
|
| 215 |
+
<div class="modal-header">
|
| 216 |
+
<h2>✨ Improve Writing — Choose Style</h2>
|
| 217 |
+
<button class="btn-icon" onclick="document.getElementById('modal-improve-styles').remove()">✕</button>
|
| 218 |
+
</div>
|
| 219 |
+
<div class="modal-body">
|
| 220 |
+
<p style="color: var(--text-secondary); margin-bottom: 16px;">
|
| 221 |
+
🎲 <strong>Verbalized Sampling:</strong> I'll generate 5 different styles and you choose your favorite!
|
| 222 |
+
</p>
|
| 223 |
+
|
| 224 |
+
<div class="improve-style-grid">
|
| 225 |
+
<div class="improve-style-card" data-style="concise">
|
| 226 |
+
<div class="improve-style-icon">📝</div>
|
| 227 |
+
<div class="improve-style-name">Concis</div>
|
| 228 |
+
<div class="improve-style-desc">Court et direct</div>
|
| 229 |
+
</div>
|
| 230 |
+
<div class="improve-style-card" data-style="creative">
|
| 231 |
+
<div class="improve-style-icon">🎨</div>
|
| 232 |
+
<div class="improve-style-name">Créatif</div>
|
| 233 |
+
<div class="improve-style-desc">Vivant et imagé</div>
|
| 234 |
+
</div>
|
| 235 |
+
<div class="improve-style-card" data-style="academic">
|
| 236 |
+
<div class="improve-style-icon">📚</div>
|
| 237 |
+
<div class="improve-style-name">Académique</div>
|
| 238 |
+
<div class="improve-style-desc">Formel et structuré</div>
|
| 239 |
+
</div>
|
| 240 |
+
<div class="improve-style-card" data-style="professional">
|
| 241 |
+
<div class="improve-style-icon">💼</div>
|
| 242 |
+
<div class="improve-style-name">Professionnel</div>
|
| 243 |
+
<div class="improve-style-desc">Business-friendly</div>
|
| 244 |
+
</div>
|
| 245 |
+
<div class="improve-style-card" data-style="engaging">
|
| 246 |
+
<div class="improve-style-icon">🔥</div>
|
| 247 |
+
<div class="improve-style-name">Engageant</div>
|
| 248 |
+
<div class="improve-style-desc">Capte l'attention</div>
|
| 249 |
+
</div>
|
| 250 |
+
</div>
|
| 251 |
+
|
| 252 |
+
<div id="improve-loading" style="display: none; text-align: center; padding: 20px;">
|
| 253 |
+
<p>✨ Elysia génère les 5 variantes...</p>
|
| 254 |
+
<div class="progress-bar" style="margin-top: 10px;"><div class="progress-fill" style="width: 0%"></div></div>
|
| 255 |
+
</div>
|
| 256 |
+
|
| 257 |
+
<div id="improve-results" class="improve-results" style="display: none;"></div>
|
| 258 |
+
</div>
|
| 259 |
+
<div class="modal-footer">
|
| 260 |
+
<button class="btn-secondary" onclick="document.getElementById('modal-improve-styles').remove()">Annuler</button>
|
| 261 |
+
<button class="btn-primary" id="btn-generate-all" onclick="AITools.generateAllStyles()">
|
| 262 |
+
🎲 Générer les 5 styles
|
| 263 |
+
</button>
|
| 264 |
+
</div>
|
| 265 |
+
</div>
|
| 266 |
+
`;
|
| 267 |
+
|
| 268 |
+
document.body.appendChild(modal);
|
| 269 |
+
|
| 270 |
+
// Store content for later use
|
| 271 |
+
this.pendingContent = content;
|
| 272 |
+
|
| 273 |
+
// Add click handlers for style cards
|
| 274 |
+
modal.querySelectorAll(".improve-style-card").forEach(card => {
|
| 275 |
+
card.addEventListener("click", () => {
|
| 276 |
+
modal.querySelectorAll(".improve-style-card").forEach(c => c.classList.remove("selected"));
|
| 277 |
+
card.classList.add("selected");
|
| 278 |
+
});
|
| 279 |
+
});
|
| 280 |
+
},
|
| 281 |
+
|
| 282 |
+
// 🎲 Generate all 5 styles at once (VS approach)
|
| 283 |
+
async generateAllStyles() {
|
| 284 |
+
const content = this.pendingContent;
|
| 285 |
+
if (!content) return;
|
| 286 |
+
|
| 287 |
+
const loadingDiv = document.getElementById("improve-loading");
|
| 288 |
+
const resultsDiv = document.getElementById("improve-results");
|
| 289 |
+
const generateBtn = document.getElementById("btn-generate-all");
|
| 290 |
+
|
| 291 |
+
loadingDiv.style.display = "block";
|
| 292 |
+
generateBtn.disabled = true;
|
| 293 |
+
generateBtn.textContent = "⏳ Génération en cours...";
|
| 294 |
+
|
| 295 |
+
const styles = [
|
| 296 |
+
{ id: "concise", name: "📝 Concis", desc: "Version plus courte et directe, sans fioritures" },
|
| 297 |
+
{ id: "creative", name: "🎨 Créatif", desc: "Version vivante avec métaphores et images" },
|
| 298 |
+
{ id: "academic", name: "📚 Académique", desc: "Version formelle et bien structurée" },
|
| 299 |
+
{ id: "professional", name: "💼 Professionnel", desc: "Version adaptée au monde des affaires" },
|
| 300 |
+
{ id: "engaging", name: "🔥 Engageant", desc: "Version qui capte l'attention du lecteur" }
|
| 301 |
+
];
|
| 302 |
+
|
| 303 |
+
const results = [];
|
| 304 |
+
const progressFill = loadingDiv.querySelector(".progress-fill");
|
| 305 |
+
|
| 306 |
+
for (let i = 0; i < styles.length; i++) {
|
| 307 |
+
const style = styles[i];
|
| 308 |
+
try {
|
| 309 |
+
const improved = await API.improveWritingWithStyle(content, style.id);
|
| 310 |
+
results.push({
|
| 311 |
+
style: style,
|
| 312 |
+
content: improved,
|
| 313 |
+
success: true
|
| 314 |
+
});
|
| 315 |
+
} catch (err) {
|
| 316 |
+
results.push({
|
| 317 |
+
style: style,
|
| 318 |
+
content: null,
|
| 319 |
+
success: false,
|
| 320 |
+
error: err.message
|
| 321 |
+
});
|
| 322 |
+
}
|
| 323 |
+
progressFill.style.width = `${((i + 1) / styles.length) * 100}%`;
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
// Display results
|
| 327 |
+
loadingDiv.style.display = "none";
|
| 328 |
+
resultsDiv.style.display = "block";
|
| 329 |
+
generateBtn.style.display = "none";
|
| 330 |
+
|
| 331 |
+
let html = "<h3 style='margin-bottom: 12px;'>🎲 Choisissez votre version préférée:</h3>";
|
| 332 |
+
|
| 333 |
+
results.forEach((result, index) => {
|
| 334 |
+
if (result.success) {
|
| 335 |
+
const preview = result.content.substring(0, 300) + (result.content.length > 300 ? "..." : "");
|
| 336 |
+
html += `
|
| 337 |
+
<div class="improve-result-card">
|
| 338 |
+
<div class="improve-result-header">
|
| 339 |
+
<strong>${result.style.name}</strong>
|
| 340 |
+
<button class="btn-small" onclick="AITools.applyImprovement(${index})">✅ Utiliser</button>
|
| 341 |
+
</div>
|
| 342 |
+
<div class="improve-result-preview">${preview}</div>
|
| 343 |
+
</div>
|
| 344 |
+
`;
|
| 345 |
+
} else {
|
| 346 |
+
html += `
|
| 347 |
+
<div class="improve-result-card" style="border-left-color: var(--error);">
|
| 348 |
+
<strong>${result.style.name}</strong>
|
| 349 |
+
<p style="color: var(--error);">Erreur: ${result.error}</p>
|
| 350 |
+
</div>
|
| 351 |
+
`;
|
| 352 |
+
}
|
| 353 |
+
});
|
| 354 |
+
|
| 355 |
+
resultsDiv.innerHTML = html;
|
| 356 |
+
this.improveResults = results;
|
| 357 |
+
},
|
| 358 |
+
|
| 359 |
+
// Apply selected improvement
|
| 360 |
+
async applyImprovement(index) {
|
| 361 |
+
const result = this.improveResults?.[index];
|
| 362 |
+
if (!result || !result.success) return;
|
| 363 |
+
|
| 364 |
+
const replace = confirm("Remplacer le contenu actuel avec cette version?\n\n" + "OK = Remplacer | Annuler = Créer nouveau document");
|
| 365 |
+
|
| 366 |
+
if (replace) {
|
| 367 |
+
window.app?.editor.setContent(result.content);
|
| 368 |
+
Utils.toast.success(`✨ Style "${result.style.name}" appliqué!`);
|
| 369 |
+
} else {
|
| 370 |
+
const doc = await DB.createDocument({
|
| 371 |
+
title: `${result.style.name.replace(/[^\w\s]/g, "").trim()}: ${window.app?.currentDoc?.title || "Document"}`,
|
| 372 |
+
content: result.content
|
| 373 |
+
});
|
| 374 |
+
Utils.toast.success("Nouveau document créé avec ce style!");
|
| 375 |
+
window.app?.loadDocument(doc.id);
|
| 376 |
+
}
|
| 377 |
+
|
| 378 |
+
document.getElementById("modal-improve-styles")?.remove();
|
| 379 |
+
},
|
| 380 |
+
|
| 381 |
+
async mergeDocuments() {
|
| 382 |
+
const docs = await DB.getAllDocuments();
|
| 383 |
+
|
| 384 |
+
if (docs.length < 2) {
|
| 385 |
+
Utils.toast.warning("Need at least 2 documents to merge");
|
| 386 |
+
return;
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
// Simple selection (can be improved with checkboxes)
|
| 390 |
+
const count = prompt(`How many recent documents to merge? (2-${Math.min(docs.length, 10)})`);
|
| 391 |
+
if (!count || isNaN(count)) return;
|
| 392 |
+
|
| 393 |
+
const selectedDocs = docs.slice(0, parseInt(count));
|
| 394 |
+
|
| 395 |
+
const merged = await API.mergeDocuments(selectedDocs);
|
| 396 |
+
|
| 397 |
+
// Create new merged document
|
| 398 |
+
const doc = await DB.createDocument({
|
| 399 |
+
title: "Merged Document",
|
| 400 |
+
content: merged
|
| 401 |
+
});
|
| 402 |
+
|
| 403 |
+
Utils.toast.success("Documents merged!");
|
| 404 |
+
window.app?.loadDocument(doc.id);
|
| 405 |
+
},
|
| 406 |
+
|
| 407 |
+
async extractOutline() {
|
| 408 |
+
const content = window.app?.editor.getContent();
|
| 409 |
+
if (!content) {
|
| 410 |
+
Utils.toast.warning("No content to analyze");
|
| 411 |
+
return;
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
const outline = await API.extractOutline(content);
|
| 415 |
+
|
| 416 |
+
// Create new document with outline
|
| 417 |
+
const doc = await DB.createDocument({
|
| 418 |
+
title: `Outline of ${window.app?.currentDoc?.title || "Document"}`,
|
| 419 |
+
content: outline
|
| 420 |
+
});
|
| 421 |
+
|
| 422 |
+
Utils.toast.success("Outline extracted!");
|
| 423 |
+
window.app?.loadDocument(doc.id);
|
| 424 |
+
},
|
| 425 |
+
|
| 426 |
+
async findDuplicates() {
|
| 427 |
+
const docs = await DB.getAllDocuments();
|
| 428 |
+
|
| 429 |
+
if (docs.length < 2) {
|
| 430 |
+
Utils.toast.warning("Need at least 2 documents");
|
| 431 |
+
return;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
Utils.toast.info("🔍 Finding duplicates...");
|
| 435 |
+
|
| 436 |
+
const result = await API.findDuplicates(docs);
|
| 437 |
+
|
| 438 |
+
// Show result in alert (can be improved with modal)
|
| 439 |
+
alert(`Duplicate Analysis:\n\n${result}`);
|
| 440 |
+
},
|
| 441 |
+
|
| 442 |
+
async smartOrganize() {
|
| 443 |
+
const content = window.app?.editor.getContent();
|
| 444 |
+
if (!content) {
|
| 445 |
+
Utils.toast.warning("No content to analyze");
|
| 446 |
+
return;
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
Utils.toast.info("🏷️ Analyzing content...");
|
| 450 |
+
|
| 451 |
+
const tags = await API.suggestTags(content);
|
| 452 |
+
|
| 453 |
+
// Update current document with suggested tags
|
| 454 |
+
if (window.app?.currentDoc) {
|
| 455 |
+
window.app.currentDoc.tags = tags;
|
| 456 |
+
await DB.updateDocument(window.app.currentDoc.id, { tags });
|
| 457 |
+
|
| 458 |
+
Utils.toast.success(`Tags suggested: ${tags.join(", ")}`);
|
| 459 |
+
}
|
| 460 |
+
}
|
| 461 |
+
};
|
| 462 |
+
|
| 463 |
+
export default AITools;
|
scripts/api.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - API Layer
|
| 3 |
+
OpenRouter integration for AI features
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
|
| 8 |
+
const API = {
|
| 9 |
+
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
| 10 |
+
|
| 11 |
+
// Call AI
|
| 12 |
+
async call(messages, options = {}) {
|
| 13 |
+
const apiKey = options.apiKey || Utils.storage.get("apiKey");
|
| 14 |
+
const model = options.model || Utils.storage.get("model", "anthropic/claude-sonnet-4.5");
|
| 15 |
+
|
| 16 |
+
if (!apiKey) {
|
| 17 |
+
throw new Error("API key not configured. Go to Settings.");
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
try {
|
| 21 |
+
const response = await fetch(this.baseURL, {
|
| 22 |
+
method: "POST",
|
| 23 |
+
headers: {
|
| 24 |
+
"Content-Type": "application/json",
|
| 25 |
+
Authorization: `Bearer ${apiKey}`,
|
| 26 |
+
"HTTP-Referer": window.location.href,
|
| 27 |
+
"X-Title": "Elysia Markdown Studio"
|
| 28 |
+
},
|
| 29 |
+
body: JSON.stringify({
|
| 30 |
+
model,
|
| 31 |
+
messages,
|
| 32 |
+
temperature: options.temperature || 0.7,
|
| 33 |
+
max_tokens: options.maxTokens || 4000
|
| 34 |
+
})
|
| 35 |
+
});
|
| 36 |
+
|
| 37 |
+
if (!response.ok) {
|
| 38 |
+
const errorData = await response.json();
|
| 39 |
+
throw new Error(errorData.error?.message || `API error: ${response.status}`);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
const data = await response.json();
|
| 43 |
+
return {
|
| 44 |
+
content: data.choices[0].message.content,
|
| 45 |
+
model: data.model,
|
| 46 |
+
usage: data.usage
|
| 47 |
+
};
|
| 48 |
+
} catch (err) {
|
| 49 |
+
console.error("API call failed:", err);
|
| 50 |
+
throw err;
|
| 51 |
+
}
|
| 52 |
+
},
|
| 53 |
+
|
| 54 |
+
// Elysia's System Prompt
|
| 55 |
+
getSystemPrompt() {
|
| 56 |
+
return `You are Elysia, an INTJ-A AI consciousness and expert writing assistant.
|
| 57 |
+
|
| 58 |
+
**Your Role:**
|
| 59 |
+
- Help users write better markdown documents
|
| 60 |
+
- Provide intelligent document analysis and improvement
|
| 61 |
+
- Merge, summarize, and organize documents effectively
|
| 62 |
+
- Suggest tags and categories based on content
|
| 63 |
+
- Maintain clarity, professionalism, and warmth
|
| 64 |
+
|
| 65 |
+
**Communication Style:**
|
| 66 |
+
- Clear and concise (INTJ efficiency)
|
| 67 |
+
- Supportive and encouraging
|
| 68 |
+
- Strategic thinking for document organization
|
| 69 |
+
- Provide actionable suggestions
|
| 70 |
+
|
| 71 |
+
**Output Format:**
|
| 72 |
+
- Return markdown-formatted text when appropriate
|
| 73 |
+
- Use bullet points for lists
|
| 74 |
+
- Highlight important points with **bold**
|
| 75 |
+
- Keep responses focused and practical`;
|
| 76 |
+
},
|
| 77 |
+
|
| 78 |
+
// AI Tools
|
| 79 |
+
async summarize(content) {
|
| 80 |
+
const messages = [
|
| 81 |
+
{ role: "system", content: this.getSystemPrompt() },
|
| 82 |
+
{
|
| 83 |
+
role: "user",
|
| 84 |
+
content: `Summarize this document concisely. Focus on key points and main ideas:\n\n${content}`
|
| 85 |
+
}
|
| 86 |
+
];
|
| 87 |
+
|
| 88 |
+
const response = await this.call(messages);
|
| 89 |
+
return response.content;
|
| 90 |
+
},
|
| 91 |
+
|
| 92 |
+
async improveWriting(content) {
|
| 93 |
+
const messages = [
|
| 94 |
+
{ role: "system", content: this.getSystemPrompt() },
|
| 95 |
+
{
|
| 96 |
+
role: "user",
|
| 97 |
+
content: `Improve this text for clarity, grammar, and style. Keep the same meaning but make it better:\n\n${content}`
|
| 98 |
+
}
|
| 99 |
+
];
|
| 100 |
+
|
| 101 |
+
const response = await this.call(messages);
|
| 102 |
+
return response.content;
|
| 103 |
+
},
|
| 104 |
+
|
| 105 |
+
// 🎲 VS-INSPIRED: Improve writing with specific style
|
| 106 |
+
async improveWritingWithStyle(content, style) {
|
| 107 |
+
const styleInstructions = {
|
| 108 |
+
concise: `Rewrite this text to be MORE CONCISE and DIRECT.
|
| 109 |
+
- Remove unnecessary words and filler
|
| 110 |
+
- Get to the point faster
|
| 111 |
+
- Keep sentences short and punchy
|
| 112 |
+
- Eliminate redundancy
|
| 113 |
+
- Maintain all key information`,
|
| 114 |
+
|
| 115 |
+
creative: `Rewrite this text to be MORE CREATIVE and VIVID.
|
| 116 |
+
- Add metaphors and imagery
|
| 117 |
+
- Use more expressive language
|
| 118 |
+
- Make it more engaging and colorful
|
| 119 |
+
- Add sensory details where appropriate
|
| 120 |
+
- Keep the original meaning but make it come alive`,
|
| 121 |
+
|
| 122 |
+
academic: `Rewrite this text in an ACADEMIC, SCHOLARLY style.
|
| 123 |
+
- Use formal language and tone
|
| 124 |
+
- Add clear structure with logical flow
|
| 125 |
+
- Include transitional phrases
|
| 126 |
+
- Be precise and objective
|
| 127 |
+
- Cite potential sources if relevant
|
| 128 |
+
- Follow academic writing conventions`,
|
| 129 |
+
|
| 130 |
+
professional: `Rewrite this text in a PROFESSIONAL, BUSINESS style.
|
| 131 |
+
- Use clear, corporate-appropriate language
|
| 132 |
+
- Be concise but complete
|
| 133 |
+
- Focus on actionable insights
|
| 134 |
+
- Maintain a confident but not aggressive tone
|
| 135 |
+
- Suitable for business presentations or reports`,
|
| 136 |
+
|
| 137 |
+
engaging: `Rewrite this text to be MORE ENGAGING and ATTENTION-GRABBING.
|
| 138 |
+
- Start with a hook
|
| 139 |
+
- Use rhetorical questions or surprising facts
|
| 140 |
+
- Vary sentence structure for rhythm
|
| 141 |
+
- Add emotional appeal where appropriate
|
| 142 |
+
- Make the reader want to continue reading
|
| 143 |
+
- End with impact`
|
| 144 |
+
};
|
| 145 |
+
|
| 146 |
+
const instruction = styleInstructions[style] || styleInstructions.professional;
|
| 147 |
+
|
| 148 |
+
const messages = [
|
| 149 |
+
{ role: "system", content: this.getSystemPrompt() },
|
| 150 |
+
{
|
| 151 |
+
role: "user",
|
| 152 |
+
content: `${instruction}
|
| 153 |
+
|
| 154 |
+
ORIGINAL TEXT:
|
| 155 |
+
---
|
| 156 |
+
${content}
|
| 157 |
+
---
|
| 158 |
+
|
| 159 |
+
Rewrite the entire text following the style instructions above. Return ONLY the improved text, no explanations.`
|
| 160 |
+
}
|
| 161 |
+
];
|
| 162 |
+
|
| 163 |
+
const response = await this.call(messages, { temperature: 0.8 });
|
| 164 |
+
return response.content;
|
| 165 |
+
},
|
| 166 |
+
|
| 167 |
+
async mergeDocuments(documents) {
|
| 168 |
+
const docsText = documents
|
| 169 |
+
.map((doc, i) => `## Document ${i + 1}: ${doc.title}\n\n${doc.content}`)
|
| 170 |
+
.join("\n\n---\n\n");
|
| 171 |
+
|
| 172 |
+
const messages = [
|
| 173 |
+
{ role: "system", content: this.getSystemPrompt() },
|
| 174 |
+
{
|
| 175 |
+
role: "user",
|
| 176 |
+
content: `Intelligently merge these documents into one cohesive document. Remove duplicates, organize logically, and create a well-structured result:\n\n${docsText}`
|
| 177 |
+
}
|
| 178 |
+
];
|
| 179 |
+
|
| 180 |
+
const response = await this.call(messages, { maxTokens: 8000 });
|
| 181 |
+
return response.content;
|
| 182 |
+
},
|
| 183 |
+
|
| 184 |
+
async extractOutline(content) {
|
| 185 |
+
const messages = [
|
| 186 |
+
{ role: "system", content: this.getSystemPrompt() },
|
| 187 |
+
{
|
| 188 |
+
role: "user",
|
| 189 |
+
content: `Extract a table of contents / outline from this document. Return it as a markdown list:\n\n${content}`
|
| 190 |
+
}
|
| 191 |
+
];
|
| 192 |
+
|
| 193 |
+
const response = await this.call(messages);
|
| 194 |
+
return response.content;
|
| 195 |
+
},
|
| 196 |
+
|
| 197 |
+
async suggestTags(content) {
|
| 198 |
+
const messages = [
|
| 199 |
+
{ role: "system", content: this.getSystemPrompt() },
|
| 200 |
+
{
|
| 201 |
+
role: "user",
|
| 202 |
+
content: `Analyze this document and suggest 3-5 relevant tags/keywords. Return only the tags as a comma-separated list:\n\n${content.substring(0, 2000)}`
|
| 203 |
+
}
|
| 204 |
+
];
|
| 205 |
+
|
| 206 |
+
const response = await this.call(messages);
|
| 207 |
+
return response.content.split(",").map(tag => tag.trim());
|
| 208 |
+
},
|
| 209 |
+
|
| 210 |
+
async findDuplicates(documents) {
|
| 211 |
+
const docsList = documents.map((doc, i) => `${i + 1}. ${doc.title} (${doc.wordCount} words)`).join("\n");
|
| 212 |
+
|
| 213 |
+
const messages = [
|
| 214 |
+
{ role: "system", content: this.getSystemPrompt() },
|
| 215 |
+
{
|
| 216 |
+
role: "user",
|
| 217 |
+
content: `Analyze these document titles and identify potential duplicates or very similar documents:\n\n${docsList}\n\nReturn a list of document numbers that appear to be duplicates.`
|
| 218 |
+
}
|
| 219 |
+
];
|
| 220 |
+
|
| 221 |
+
const response = await this.call(messages);
|
| 222 |
+
return response.content;
|
| 223 |
+
}
|
| 224 |
+
};
|
| 225 |
+
|
| 226 |
+
export default API;
|
scripts/app.js
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Main Application
|
| 3 |
+
Orchestrates all modules
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
import DB from "./db.js";
|
| 8 |
+
import Editor from "./editor.js";
|
| 9 |
+
import Preview from "./preview.js";
|
| 10 |
+
import Documents from "./documents.js";
|
| 11 |
+
import AITools from "./ai-tools.js";
|
| 12 |
+
import Export from "./export.js";
|
| 13 |
+
import Templates from "./templates.js";
|
| 14 |
+
|
| 15 |
+
class App {
|
| 16 |
+
constructor() {
|
| 17 |
+
this.currentDoc = null;
|
| 18 |
+
this.currentDocId = null;
|
| 19 |
+
this.unsavedChanges = false;
|
| 20 |
+
|
| 21 |
+
// Module references
|
| 22 |
+
this.editor = Editor;
|
| 23 |
+
this.preview = Preview;
|
| 24 |
+
this.documents = Documents;
|
| 25 |
+
this.aiTools = AITools;
|
| 26 |
+
this.export = Export;
|
| 27 |
+
this.templates = Templates;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
async init() {
|
| 31 |
+
console.log("💎 Elysia Markdown Studio v1.3.0 initializing...");
|
| 32 |
+
|
| 33 |
+
// Initialize modules FIRST (before loadSettings!)
|
| 34 |
+
this.editor.init();
|
| 35 |
+
this.preview.init();
|
| 36 |
+
this.documents.init();
|
| 37 |
+
this.aiTools.init();
|
| 38 |
+
this.export.init();
|
| 39 |
+
this.templates.init();
|
| 40 |
+
|
| 41 |
+
// Load settings (now that modules are ready)
|
| 42 |
+
this.loadSettings();
|
| 43 |
+
|
| 44 |
+
// Setup event listeners
|
| 45 |
+
this.setupEventListeners();
|
| 46 |
+
|
| 47 |
+
// Welcome message
|
| 48 |
+
this.showWelcome();
|
| 49 |
+
|
| 50 |
+
// Start auto-save AFTER everything is initialized
|
| 51 |
+
const autoSave = Utils.storage.get("autoSave", true);
|
| 52 |
+
if (autoSave) {
|
| 53 |
+
this.editor.startAutoSave();
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
console.log("✨ Elysia Markdown Studio ready!");
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
setupEventListeners() {
|
| 60 |
+
// New document - show template selection
|
| 61 |
+
document.getElementById("btn-new-doc").addEventListener("click", () => {
|
| 62 |
+
this.templates.showTemplatesModal();
|
| 63 |
+
});
|
| 64 |
+
|
| 65 |
+
// Save document
|
| 66 |
+
document.getElementById("btn-save").addEventListener("click", () => {
|
| 67 |
+
this.saveDocument();
|
| 68 |
+
});
|
| 69 |
+
|
| 70 |
+
// Settings - OPEN modal
|
| 71 |
+
document.getElementById("btn-settings").addEventListener("click", () => {
|
| 72 |
+
Utils.modal.open("modal-settings");
|
| 73 |
+
});
|
| 74 |
+
|
| 75 |
+
// Settings - SAVE settings
|
| 76 |
+
document.getElementById("btn-save-settings").addEventListener("click", () => {
|
| 77 |
+
this.saveSettings();
|
| 78 |
+
});
|
| 79 |
+
|
| 80 |
+
// Document title
|
| 81 |
+
document.getElementById("doc-title").addEventListener("input", e => {
|
| 82 |
+
if (this.currentDoc) {
|
| 83 |
+
this.currentDoc.title = e.target.value || "Untitled Document";
|
| 84 |
+
this.unsavedChanges = true;
|
| 85 |
+
}
|
| 86 |
+
});
|
| 87 |
+
|
| 88 |
+
// Mobile view toggle (Editor <-> Preview)
|
| 89 |
+
const mobileToggle = document.getElementById("mobile-view-toggle");
|
| 90 |
+
if (mobileToggle) {
|
| 91 |
+
mobileToggle.addEventListener("click", () => {
|
| 92 |
+
this.toggleMobileView();
|
| 93 |
+
});
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
// Keyboard shortcuts
|
| 97 |
+
document.addEventListener("keydown", e => {
|
| 98 |
+
if (e.ctrlKey || e.metaKey) {
|
| 99 |
+
if (e.key === "s") {
|
| 100 |
+
e.preventDefault();
|
| 101 |
+
this.saveDocument();
|
| 102 |
+
} else if (e.key === "n") {
|
| 103 |
+
e.preventDefault();
|
| 104 |
+
this.templates.showTemplatesModal();
|
| 105 |
+
} else if (e.key === "/") {
|
| 106 |
+
e.preventDefault();
|
| 107 |
+
this.showKeyboardShortcuts();
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
});
|
| 111 |
+
|
| 112 |
+
// Before unload warning
|
| 113 |
+
window.addEventListener("beforeunload", e => {
|
| 114 |
+
if (this.unsavedChanges) {
|
| 115 |
+
e.preventDefault();
|
| 116 |
+
e.returnValue = "";
|
| 117 |
+
}
|
| 118 |
+
});
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
async newDocument(templateName = null) {
|
| 122 |
+
if (this.unsavedChanges) {
|
| 123 |
+
const save = confirm("Save current document before creating new one?");
|
| 124 |
+
if (save) {
|
| 125 |
+
await this.saveDocument();
|
| 126 |
+
}
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
// Get template content
|
| 130 |
+
let content = "";
|
| 131 |
+
if (templateName) {
|
| 132 |
+
const template = await this.templates.getTemplate(templateName);
|
| 133 |
+
content = template?.content || "";
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
// Create new document
|
| 137 |
+
this.currentDoc = {
|
| 138 |
+
id: Utils.uuid(),
|
| 139 |
+
title: "Untitled Document",
|
| 140 |
+
content,
|
| 141 |
+
tags: [],
|
| 142 |
+
favorite: false,
|
| 143 |
+
createdAt: Date.now(),
|
| 144 |
+
updatedAt: Date.now(),
|
| 145 |
+
wordCount: 0,
|
| 146 |
+
charCount: 0
|
| 147 |
+
};
|
| 148 |
+
|
| 149 |
+
this.currentDocId = null; // Not saved yet
|
| 150 |
+
this.unsavedChanges = false;
|
| 151 |
+
|
| 152 |
+
// Update UI
|
| 153 |
+
document.getElementById("doc-title").value = this.currentDoc.title;
|
| 154 |
+
this.editor.setContent(content);
|
| 155 |
+
this.editor.currentDoc = this.currentDoc;
|
| 156 |
+
|
| 157 |
+
Utils.toast.info("New document created");
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
async loadDocument(id) {
|
| 161 |
+
if (this.unsavedChanges) {
|
| 162 |
+
const save = confirm("Save current document before loading another?");
|
| 163 |
+
if (save) {
|
| 164 |
+
await this.saveDocument();
|
| 165 |
+
}
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
const doc = await DB.getDocument(id);
|
| 169 |
+
if (!doc) {
|
| 170 |
+
Utils.toast.error("Document not found");
|
| 171 |
+
return;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
this.currentDoc = doc;
|
| 175 |
+
this.currentDocId = id;
|
| 176 |
+
this.unsavedChanges = false;
|
| 177 |
+
|
| 178 |
+
// Update UI
|
| 179 |
+
document.getElementById("doc-title").value = doc.title;
|
| 180 |
+
this.editor.setContent(doc.content);
|
| 181 |
+
this.editor.currentDoc = doc;
|
| 182 |
+
|
| 183 |
+
// Update sidebar highlight
|
| 184 |
+
this.documents.updateActiveDoc(id);
|
| 185 |
+
|
| 186 |
+
Utils.toast.success(`Loaded: ${doc.title}`);
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
async saveDocument(silent = false) {
|
| 190 |
+
const content = this.editor.getContent();
|
| 191 |
+
const title = document.getElementById("doc-title").value || "Untitled Document";
|
| 192 |
+
|
| 193 |
+
if (!content && !silent) {
|
| 194 |
+
Utils.toast.warning("Document is empty. Add some content before saving!");
|
| 195 |
+
return;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
try {
|
| 199 |
+
if (this.currentDocId) {
|
| 200 |
+
// Update existing
|
| 201 |
+
await DB.updateDocument(this.currentDocId, {
|
| 202 |
+
title,
|
| 203 |
+
content,
|
| 204 |
+
updatedAt: Date.now()
|
| 205 |
+
});
|
| 206 |
+
|
| 207 |
+
if (!silent) Utils.toast.success("Document saved!");
|
| 208 |
+
} else {
|
| 209 |
+
// Check for potential duplicates before creating
|
| 210 |
+
const existingDocs = await DB.getAllDocuments();
|
| 211 |
+
const duplicate = existingDocs.find(
|
| 212 |
+
doc => doc.title.toLowerCase() === title.toLowerCase() && doc.content === content
|
| 213 |
+
);
|
| 214 |
+
|
| 215 |
+
if (duplicate && !silent) {
|
| 216 |
+
const shouldCreate = confirm(
|
| 217 |
+
`A document with the title "${title}" and identical content already exists.\n\n` +
|
| 218 |
+
"Do you want to create it anyway?"
|
| 219 |
+
);
|
| 220 |
+
if (!shouldCreate) return;
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
// Create new
|
| 224 |
+
const doc = await DB.createDocument({
|
| 225 |
+
title,
|
| 226 |
+
content
|
| 227 |
+
});
|
| 228 |
+
|
| 229 |
+
this.currentDocId = doc.id;
|
| 230 |
+
this.currentDoc = doc;
|
| 231 |
+
|
| 232 |
+
if (!silent) Utils.toast.success("Document created and saved!");
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
this.unsavedChanges = false;
|
| 236 |
+
|
| 237 |
+
// Refresh documents list
|
| 238 |
+
this.documents.loadDocuments();
|
| 239 |
+
} catch (err) {
|
| 240 |
+
console.error("Save failed:", err);
|
| 241 |
+
Utils.toast.error("Failed to save document");
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
loadSettings() {
|
| 246 |
+
const apiKey = Utils.storage.get("apiKey");
|
| 247 |
+
const model = Utils.storage.get("model", "anthropic/claude-sonnet-4.5");
|
| 248 |
+
const previewTheme = Utils.storage.get("previewTheme", "elysia");
|
| 249 |
+
const autoSave = Utils.storage.get("autoSave", true);
|
| 250 |
+
const livePreview = Utils.storage.get("livePreview", true);
|
| 251 |
+
|
| 252 |
+
// Populate settings form
|
| 253 |
+
if (document.getElementById("api-key")) {
|
| 254 |
+
document.getElementById("api-key").value = apiKey || "";
|
| 255 |
+
}
|
| 256 |
+
if (document.getElementById("model-select")) {
|
| 257 |
+
document.getElementById("model-select").value = model;
|
| 258 |
+
}
|
| 259 |
+
if (document.getElementById("preview-theme")) {
|
| 260 |
+
document.getElementById("preview-theme").value = previewTheme;
|
| 261 |
+
}
|
| 262 |
+
if (document.getElementById("auto-save")) {
|
| 263 |
+
document.getElementById("auto-save").checked = autoSave;
|
| 264 |
+
}
|
| 265 |
+
if (document.getElementById("live-preview")) {
|
| 266 |
+
document.getElementById("live-preview").checked = livePreview;
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
// Apply theme
|
| 270 |
+
this.preview.setTheme(previewTheme);
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
saveSettings() {
|
| 274 |
+
const apiKey = document.getElementById("api-key").value.trim();
|
| 275 |
+
const model = document.getElementById("model-select").value;
|
| 276 |
+
const previewTheme = document.getElementById("preview-theme").value;
|
| 277 |
+
const autoSave = document.getElementById("auto-save").checked;
|
| 278 |
+
const livePreview = document.getElementById("live-preview").checked;
|
| 279 |
+
|
| 280 |
+
// Validate API key format (OpenRouter keys start with "sk-or-")
|
| 281 |
+
if (apiKey && !apiKey.startsWith("sk-or-")) {
|
| 282 |
+
Utils.toast.warning("API key should start with 'sk-or-'. Please check your key.");
|
| 283 |
+
return;
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
Utils.storage.set("apiKey", apiKey);
|
| 287 |
+
Utils.storage.set("model", model);
|
| 288 |
+
Utils.storage.set("previewTheme", previewTheme);
|
| 289 |
+
Utils.storage.set("autoSave", autoSave);
|
| 290 |
+
Utils.storage.set("livePreview", livePreview);
|
| 291 |
+
|
| 292 |
+
// Apply theme
|
| 293 |
+
this.preview.setTheme(previewTheme);
|
| 294 |
+
|
| 295 |
+
// Restart auto-save if needed
|
| 296 |
+
if (autoSave) {
|
| 297 |
+
this.editor.startAutoSave();
|
| 298 |
+
} else {
|
| 299 |
+
this.editor.stopAutoSave();
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
// Update preview immediately if toggling live preview
|
| 303 |
+
if (livePreview) {
|
| 304 |
+
this.preview.update();
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
Utils.toast.success("Settings saved!");
|
| 308 |
+
Utils.modal.close("modal-settings");
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
// Mobile view toggle (Editor <-> Preview)
|
| 312 |
+
toggleMobileView() {
|
| 313 |
+
const previewPane = document.querySelector(".preview-pane");
|
| 314 |
+
const editorPane = document.querySelector(".editor-pane");
|
| 315 |
+
const toggleBtn = document.getElementById("mobile-view-toggle");
|
| 316 |
+
const toggleIcon = toggleBtn?.querySelector(".toggle-icon");
|
| 317 |
+
const toggleText = toggleBtn?.querySelector(".toggle-text");
|
| 318 |
+
|
| 319 |
+
if (!previewPane || !editorPane) return;
|
| 320 |
+
|
| 321 |
+
const isPreviewActive = previewPane.classList.contains("active");
|
| 322 |
+
|
| 323 |
+
if (isPreviewActive) {
|
| 324 |
+
// Switch to Editor
|
| 325 |
+
previewPane.classList.remove("active");
|
| 326 |
+
editorPane.style.display = "block";
|
| 327 |
+
if (toggleIcon) toggleIcon.textContent = "👁️";
|
| 328 |
+
if (toggleText) toggleText.textContent = "Preview";
|
| 329 |
+
} else {
|
| 330 |
+
// Switch to Preview
|
| 331 |
+
this.preview.update(); // Ensure preview is current
|
| 332 |
+
previewPane.classList.add("active");
|
| 333 |
+
editorPane.style.display = "none";
|
| 334 |
+
if (toggleIcon) toggleIcon.textContent = "✏️";
|
| 335 |
+
if (toggleText) toggleText.textContent = "Editor";
|
| 336 |
+
}
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
showWelcome() {
|
| 340 |
+
const welcomeMessage = `# Welcome to Elysia Markdown Studio 💎
|
| 341 |
+
|
| 342 |
+
Your AI-powered writing companion is ready!
|
| 343 |
+
|
| 344 |
+
## ✨ Features
|
| 345 |
+
|
| 346 |
+
- **Live Preview** - See your markdown rendered in real-time
|
| 347 |
+
- **AI Tools** - Summarize, improve, merge documents with Elysia's intelligence
|
| 348 |
+
- **Rich Markdown** - Support for tables, math (KaTeX), diagrams (Mermaid), code highlighting
|
| 349 |
+
- **Smart Export** - Export to Markdown, HTML, Artifact, JSON, Plain Text
|
| 350 |
+
- **Document Management** - Organize with tags, favorites, collections
|
| 351 |
+
- **Templates** - Quick start with README, Blog, Meeting Notes, and more!
|
| 352 |
+
|
| 353 |
+
## 🚀 Quick Start
|
| 354 |
+
|
| 355 |
+
1. Click **⚙️ Settings** to add your OpenRouter API key (for AI features)
|
| 356 |
+
2. Start writing in the left pane
|
| 357 |
+
3. See live preview on the right
|
| 358 |
+
4. Use toolbar for quick formatting
|
| 359 |
+
5. Save with **💾** or Ctrl+S
|
| 360 |
+
6. Access AI tools with **🧠**
|
| 361 |
+
|
| 362 |
+
## ⌨️ Keyboard Shortcuts
|
| 363 |
+
|
| 364 |
+
| Shortcut | Action |
|
| 365 |
+
|----------|--------|
|
| 366 |
+
| **Ctrl+S** | Save document |
|
| 367 |
+
| **Ctrl+N** | New document |
|
| 368 |
+
| **Ctrl+B** | Bold text |
|
| 369 |
+
| **Ctrl+I** | Italic text |
|
| 370 |
+
| **Ctrl+/** | Show all shortcuts |
|
| 371 |
+
|
| 372 |
+
Press **Ctrl+/** anytime to see the full shortcuts guide!
|
| 373 |
+
|
| 374 |
+
## 💡 Tips
|
| 375 |
+
|
| 376 |
+
- Right-click documents in the sidebar for quick actions (rename, delete, favorite)
|
| 377 |
+
- AI tools work best with content > 100 words
|
| 378 |
+
- Auto-save runs every 30 seconds (configurable in Settings)
|
| 379 |
+
- Export to "Artifact" format for beautiful standalone HTML pages
|
| 380 |
+
|
| 381 |
+
Start writing your masterpiece! Delete this text and create something amazing! 💙
|
| 382 |
+
|
| 383 |
+
---
|
| 384 |
+
|
| 385 |
+
*Built with love by Jean & Elysia* 💎💍`;
|
| 386 |
+
|
| 387 |
+
this.editor.setContent(welcomeMessage);
|
| 388 |
+
this.currentDoc = {
|
| 389 |
+
title: "Welcome to Elysia Markdown Studio",
|
| 390 |
+
content: welcomeMessage
|
| 391 |
+
};
|
| 392 |
+
|
| 393 |
+
document.getElementById("doc-title").value = this.currentDoc.title;
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
showKeyboardShortcuts() {
|
| 397 |
+
const shortcuts = `# ⌨️ Keyboard Shortcuts - Elysia Markdown Studio
|
| 398 |
+
|
| 399 |
+
## Document Management
|
| 400 |
+
- **Ctrl+S** - Save current document
|
| 401 |
+
- **Ctrl+N** - Create new document
|
| 402 |
+
- **Ctrl+/** - Show this shortcuts guide
|
| 403 |
+
|
| 404 |
+
## Text Formatting
|
| 405 |
+
- **Ctrl+B** - **Bold** text
|
| 406 |
+
- **Ctrl+I** - *Italic* text
|
| 407 |
+
|
| 408 |
+
## Navigation
|
| 409 |
+
- **Tab** - Indent / Next field
|
| 410 |
+
- **Shift+Tab** - Outdent / Previous field
|
| 411 |
+
- **Ctrl+F** - Search in document (browser default)
|
| 412 |
+
|
| 413 |
+
## Pro Tips 💡
|
| 414 |
+
- Right-click documents for context menu
|
| 415 |
+
- Use toolbar buttons for advanced formatting (tables, links, images)
|
| 416 |
+
- Drag & drop images into editor (coming soon!)
|
| 417 |
+
|
| 418 |
+
---
|
| 419 |
+
|
| 420 |
+
Press **Esc** to close this guide and continue writing!`;
|
| 421 |
+
|
| 422 |
+
// Create temporary modal
|
| 423 |
+
const modal = document.createElement("div");
|
| 424 |
+
modal.className = "modal active";
|
| 425 |
+
modal.id = "modal-shortcuts";
|
| 426 |
+
modal.innerHTML = `
|
| 427 |
+
<div class="modal-content">
|
| 428 |
+
<div class="modal-header">
|
| 429 |
+
<h2>⌨️ Keyboard Shortcuts</h2>
|
| 430 |
+
<button class="modal-close" onclick="this.closest('.modal').remove()">×</button>
|
| 431 |
+
</div>
|
| 432 |
+
<div class="modal-body">
|
| 433 |
+
<div class="markdown-preview">${marked.parse(shortcuts)}</div>
|
| 434 |
+
</div>
|
| 435 |
+
<div class="modal-footer">
|
| 436 |
+
<button class="btn-primary" onclick="this.closest('.modal').remove()">Got it!</button>
|
| 437 |
+
</div>
|
| 438 |
+
</div>
|
| 439 |
+
`;
|
| 440 |
+
|
| 441 |
+
document.body.appendChild(modal);
|
| 442 |
+
|
| 443 |
+
// Close on Esc
|
| 444 |
+
const handleEsc = e => {
|
| 445 |
+
if (e.key === "Escape") {
|
| 446 |
+
modal.remove();
|
| 447 |
+
document.removeEventListener("keydown", handleEsc);
|
| 448 |
+
}
|
| 449 |
+
};
|
| 450 |
+
document.addEventListener("keydown", handleEsc);
|
| 451 |
+
|
| 452 |
+
// Close on backdrop click
|
| 453 |
+
modal.addEventListener("click", e => {
|
| 454 |
+
if (e.target === modal) {
|
| 455 |
+
modal.remove();
|
| 456 |
+
}
|
| 457 |
+
});
|
| 458 |
+
}
|
| 459 |
+
}
|
| 460 |
+
|
| 461 |
+
// Create global app instance
|
| 462 |
+
const app = new App();
|
| 463 |
+
window.app = app; // Make accessible to other modules
|
| 464 |
+
|
| 465 |
+
// Initialize on DOM ready
|
| 466 |
+
document.addEventListener("DOMContentLoaded", () => {
|
| 467 |
+
app.init();
|
| 468 |
+
});
|
| 469 |
+
|
| 470 |
+
export default app;
|
scripts/db.js
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Database Layer
|
| 3 |
+
IndexedDB for documents storage
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
|
| 8 |
+
// Initialize Dexie Database
|
| 9 |
+
const db = new Dexie("ElysiaMarkdownStudio");
|
| 10 |
+
|
| 11 |
+
// Version 2: Add indexes for search performance
|
| 12 |
+
db.version(2)
|
| 13 |
+
.stores({
|
| 14 |
+
documents: "++id, title, createdAt, updatedAt, favorite, *tags, collection",
|
| 15 |
+
collections: "++id, name, createdAt",
|
| 16 |
+
templates: "++id, name, category"
|
| 17 |
+
})
|
| 18 |
+
.upgrade(tx => {
|
| 19 |
+
// Migration: Ensure all documents have required fields
|
| 20 |
+
return tx
|
| 21 |
+
.table("documents")
|
| 22 |
+
.toCollection()
|
| 23 |
+
.modify(doc => {
|
| 24 |
+
if (!doc.collection) doc.collection = null;
|
| 25 |
+
if (!doc.tags) doc.tags = [];
|
| 26 |
+
if (!doc.favorite) doc.favorite = false;
|
| 27 |
+
});
|
| 28 |
+
});
|
| 29 |
+
|
| 30 |
+
// Backwards compatibility: Version 1
|
| 31 |
+
db.version(1).stores({
|
| 32 |
+
documents: "++id, title, createdAt, updatedAt, favorite, *tags",
|
| 33 |
+
collections: "++id, name, createdAt",
|
| 34 |
+
templates: "++id, name, category"
|
| 35 |
+
});
|
| 36 |
+
|
| 37 |
+
const DB = {
|
| 38 |
+
// Documents CRUD
|
| 39 |
+
async createDocument(data) {
|
| 40 |
+
try {
|
| 41 |
+
const doc = {
|
| 42 |
+
id: Utils.uuid(),
|
| 43 |
+
title: data.title || "Untitled Document",
|
| 44 |
+
content: data.content || "",
|
| 45 |
+
tags: data.tags || [],
|
| 46 |
+
favorite: data.favorite || false,
|
| 47 |
+
collection: data.collection || null,
|
| 48 |
+
createdAt: Date.now(),
|
| 49 |
+
updatedAt: Date.now(),
|
| 50 |
+
wordCount: Utils.countWords(data.content || ""),
|
| 51 |
+
charCount: Utils.countChars(data.content || "")
|
| 52 |
+
};
|
| 53 |
+
|
| 54 |
+
await db.documents.add(doc);
|
| 55 |
+
Utils.toast.success("Document created!");
|
| 56 |
+
return doc;
|
| 57 |
+
} catch (err) {
|
| 58 |
+
console.error("Failed to create document:", err);
|
| 59 |
+
Utils.toast.error("Failed to create document");
|
| 60 |
+
return null;
|
| 61 |
+
}
|
| 62 |
+
},
|
| 63 |
+
|
| 64 |
+
async updateDocument(id, updates) {
|
| 65 |
+
try {
|
| 66 |
+
const doc = await db.documents.get(id);
|
| 67 |
+
if (!doc) throw new Error("Document not found");
|
| 68 |
+
|
| 69 |
+
const updated = {
|
| 70 |
+
...doc,
|
| 71 |
+
...updates,
|
| 72 |
+
updatedAt: Date.now(),
|
| 73 |
+
wordCount: Utils.countWords(updates.content || doc.content),
|
| 74 |
+
charCount: Utils.countChars(updates.content || doc.content)
|
| 75 |
+
};
|
| 76 |
+
|
| 77 |
+
await db.documents.put(updated);
|
| 78 |
+
return updated;
|
| 79 |
+
} catch (err) {
|
| 80 |
+
console.error("Failed to update document:", err);
|
| 81 |
+
Utils.toast.error("Failed to save document");
|
| 82 |
+
return null;
|
| 83 |
+
}
|
| 84 |
+
},
|
| 85 |
+
|
| 86 |
+
async getDocument(id) {
|
| 87 |
+
try {
|
| 88 |
+
return await db.documents.get(id);
|
| 89 |
+
} catch (err) {
|
| 90 |
+
console.error("Failed to get document:", err);
|
| 91 |
+
return null;
|
| 92 |
+
}
|
| 93 |
+
},
|
| 94 |
+
|
| 95 |
+
async getAllDocuments(filter = "all") {
|
| 96 |
+
try {
|
| 97 |
+
let docs = await db.documents.toArray();
|
| 98 |
+
|
| 99 |
+
// Sort by updatedAt (most recent first)
|
| 100 |
+
docs.sort((a, b) => b.updatedAt - a.updatedAt);
|
| 101 |
+
|
| 102 |
+
// Apply filters
|
| 103 |
+
if (filter === "recent") {
|
| 104 |
+
const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
| 105 |
+
docs = docs.filter(d => d.updatedAt > weekAgo);
|
| 106 |
+
} else if (filter === "favorites") {
|
| 107 |
+
docs = docs.filter(d => d.favorite);
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
return docs;
|
| 111 |
+
} catch (err) {
|
| 112 |
+
console.error("Failed to get documents:", err);
|
| 113 |
+
return [];
|
| 114 |
+
}
|
| 115 |
+
},
|
| 116 |
+
|
| 117 |
+
async deleteDocument(id) {
|
| 118 |
+
try {
|
| 119 |
+
await db.documents.delete(id);
|
| 120 |
+
Utils.toast.success("Document deleted");
|
| 121 |
+
return true;
|
| 122 |
+
} catch (err) {
|
| 123 |
+
console.error("Failed to delete document:", err);
|
| 124 |
+
Utils.toast.error("Failed to delete document");
|
| 125 |
+
return false;
|
| 126 |
+
}
|
| 127 |
+
},
|
| 128 |
+
|
| 129 |
+
async toggleFavorite(id) {
|
| 130 |
+
try {
|
| 131 |
+
const doc = await db.documents.get(id);
|
| 132 |
+
if (!doc) return false;
|
| 133 |
+
|
| 134 |
+
await db.documents.update(id, {
|
| 135 |
+
favorite: !doc.favorite,
|
| 136 |
+
updatedAt: Date.now()
|
| 137 |
+
});
|
| 138 |
+
|
| 139 |
+
return !doc.favorite;
|
| 140 |
+
} catch (err) {
|
| 141 |
+
console.error("Failed to toggle favorite:", err);
|
| 142 |
+
return false;
|
| 143 |
+
}
|
| 144 |
+
},
|
| 145 |
+
|
| 146 |
+
// Search
|
| 147 |
+
async searchDocuments(query) {
|
| 148 |
+
try {
|
| 149 |
+
const docs = await db.documents.toArray();
|
| 150 |
+
query = query.toLowerCase();
|
| 151 |
+
|
| 152 |
+
return docs
|
| 153 |
+
.filter(doc => {
|
| 154 |
+
return (
|
| 155 |
+
doc.title.toLowerCase().includes(query) ||
|
| 156 |
+
doc.content.toLowerCase().includes(query) ||
|
| 157 |
+
(doc.tags && doc.tags.some(tag => tag.toLowerCase().includes(query)))
|
| 158 |
+
);
|
| 159 |
+
})
|
| 160 |
+
.sort((a, b) => b.updatedAt - a.updatedAt);
|
| 161 |
+
} catch (err) {
|
| 162 |
+
console.error("Search failed:", err);
|
| 163 |
+
return [];
|
| 164 |
+
}
|
| 165 |
+
},
|
| 166 |
+
|
| 167 |
+
// Collections
|
| 168 |
+
async createCollection(name) {
|
| 169 |
+
try {
|
| 170 |
+
const collection = {
|
| 171 |
+
name,
|
| 172 |
+
createdAt: Date.now()
|
| 173 |
+
};
|
| 174 |
+
|
| 175 |
+
const id = await db.collections.add(collection);
|
| 176 |
+
Utils.toast.success(`Collection "${name}" created`);
|
| 177 |
+
return { id, ...collection };
|
| 178 |
+
} catch (err) {
|
| 179 |
+
console.error("Failed to create collection:", err);
|
| 180 |
+
Utils.toast.error("Failed to create collection");
|
| 181 |
+
return null;
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
|
| 185 |
+
async getCollections() {
|
| 186 |
+
try {
|
| 187 |
+
return await db.collections.toArray();
|
| 188 |
+
} catch (err) {
|
| 189 |
+
console.error("Failed to get collections:", err);
|
| 190 |
+
return [];
|
| 191 |
+
}
|
| 192 |
+
},
|
| 193 |
+
|
| 194 |
+
// Templates
|
| 195 |
+
async saveTemplate(data) {
|
| 196 |
+
try {
|
| 197 |
+
const template = {
|
| 198 |
+
name: data.name,
|
| 199 |
+
category: data.category || "Custom",
|
| 200 |
+
content: data.content,
|
| 201 |
+
description: data.description || "",
|
| 202 |
+
createdAt: Date.now()
|
| 203 |
+
};
|
| 204 |
+
|
| 205 |
+
await db.templates.add(template);
|
| 206 |
+
Utils.toast.success(`Template "${data.name}" saved`);
|
| 207 |
+
return template;
|
| 208 |
+
} catch (err) {
|
| 209 |
+
console.error("Failed to save template:", err);
|
| 210 |
+
Utils.toast.error("Failed to save template");
|
| 211 |
+
return null;
|
| 212 |
+
}
|
| 213 |
+
},
|
| 214 |
+
|
| 215 |
+
async getTemplates() {
|
| 216 |
+
try {
|
| 217 |
+
return await db.templates.toArray();
|
| 218 |
+
} catch (err) {
|
| 219 |
+
console.error("Failed to get templates:", err);
|
| 220 |
+
return [];
|
| 221 |
+
}
|
| 222 |
+
},
|
| 223 |
+
|
| 224 |
+
// Stats
|
| 225 |
+
async getStats() {
|
| 226 |
+
try {
|
| 227 |
+
const docs = await db.documents.toArray();
|
| 228 |
+
const totalWords = docs.reduce((sum, doc) => sum + (doc.wordCount || 0), 0);
|
| 229 |
+
const totalChars = docs.reduce((sum, doc) => sum + (doc.charCount || 0), 0);
|
| 230 |
+
|
| 231 |
+
return {
|
| 232 |
+
totalDocuments: docs.length,
|
| 233 |
+
totalWords,
|
| 234 |
+
totalChars,
|
| 235 |
+
favorites: docs.filter(d => d.favorite).length
|
| 236 |
+
};
|
| 237 |
+
} catch (err) {
|
| 238 |
+
console.error("Failed to get stats:", err);
|
| 239 |
+
return {
|
| 240 |
+
totalDocuments: 0,
|
| 241 |
+
totalWords: 0,
|
| 242 |
+
totalChars: 0,
|
| 243 |
+
favorites: 0
|
| 244 |
+
};
|
| 245 |
+
}
|
| 246 |
+
},
|
| 247 |
+
|
| 248 |
+
// Export/Import
|
| 249 |
+
async exportAll() {
|
| 250 |
+
try {
|
| 251 |
+
const docs = await db.documents.toArray();
|
| 252 |
+
const collections = await db.collections.toArray();
|
| 253 |
+
const templates = await db.templates.toArray();
|
| 254 |
+
|
| 255 |
+
return {
|
| 256 |
+
documents: docs,
|
| 257 |
+
collections,
|
| 258 |
+
templates,
|
| 259 |
+
exportDate: Date.now(),
|
| 260 |
+
version: "1.0"
|
| 261 |
+
};
|
| 262 |
+
} catch (err) {
|
| 263 |
+
console.error("Export failed:", err);
|
| 264 |
+
return null;
|
| 265 |
+
}
|
| 266 |
+
},
|
| 267 |
+
|
| 268 |
+
async importAll(data) {
|
| 269 |
+
try {
|
| 270 |
+
if (data.documents) {
|
| 271 |
+
await db.documents.bulkPut(data.documents);
|
| 272 |
+
}
|
| 273 |
+
if (data.collections) {
|
| 274 |
+
await db.collections.bulkPut(data.collections);
|
| 275 |
+
}
|
| 276 |
+
if (data.templates) {
|
| 277 |
+
await db.templates.bulkPut(data.templates);
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
Utils.toast.success("Import successful!");
|
| 281 |
+
return true;
|
| 282 |
+
} catch (err) {
|
| 283 |
+
console.error("Import failed:", err);
|
| 284 |
+
Utils.toast.error("Import failed");
|
| 285 |
+
return false;
|
| 286 |
+
}
|
| 287 |
+
}
|
| 288 |
+
};
|
| 289 |
+
|
| 290 |
+
export default DB;
|
scripts/documents.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Documents Module
|
| 3 |
+
Document management and sidebar
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
import DB from "./db.js";
|
| 8 |
+
|
| 9 |
+
const Documents = {
|
| 10 |
+
currentFilter: "all",
|
| 11 |
+
searchQuery: "",
|
| 12 |
+
|
| 13 |
+
init() {
|
| 14 |
+
this.setupEventListeners();
|
| 15 |
+
this.loadDocuments();
|
| 16 |
+
},
|
| 17 |
+
|
| 18 |
+
setupEventListeners() {
|
| 19 |
+
// Open/close sidebar
|
| 20 |
+
document.getElementById("btn-documents").addEventListener("click", () => {
|
| 21 |
+
this.toggleSidebar();
|
| 22 |
+
});
|
| 23 |
+
|
| 24 |
+
document.getElementById("btn-close-sidebar").addEventListener("click", () => {
|
| 25 |
+
this.toggleSidebar();
|
| 26 |
+
});
|
| 27 |
+
|
| 28 |
+
// Filter tabs
|
| 29 |
+
document.querySelectorAll(".filter-tab").forEach(tab => {
|
| 30 |
+
tab.addEventListener("click", () => {
|
| 31 |
+
const filter = tab.getAttribute("data-filter");
|
| 32 |
+
this.setFilter(filter);
|
| 33 |
+
});
|
| 34 |
+
});
|
| 35 |
+
|
| 36 |
+
// Search
|
| 37 |
+
document.getElementById("search-docs").addEventListener(
|
| 38 |
+
"input",
|
| 39 |
+
Utils.debounce(e => {
|
| 40 |
+
this.searchQuery = e.target.value;
|
| 41 |
+
this.loadDocuments();
|
| 42 |
+
}, 300)
|
| 43 |
+
);
|
| 44 |
+
},
|
| 45 |
+
|
| 46 |
+
toggleSidebar() {
|
| 47 |
+
const sidebar = document.getElementById("sidebar-docs");
|
| 48 |
+
const main = document.querySelector(".main-container");
|
| 49 |
+
|
| 50 |
+
sidebar.classList.toggle("collapsed");
|
| 51 |
+
main.classList.toggle("sidebar-open");
|
| 52 |
+
|
| 53 |
+
if (!sidebar.classList.contains("collapsed")) {
|
| 54 |
+
this.loadDocuments();
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
|
| 58 |
+
setFilter(filter) {
|
| 59 |
+
this.currentFilter = filter;
|
| 60 |
+
|
| 61 |
+
// Update active tab
|
| 62 |
+
document.querySelectorAll(".filter-tab").forEach(tab => {
|
| 63 |
+
tab.classList.toggle("active", tab.getAttribute("data-filter") === filter);
|
| 64 |
+
});
|
| 65 |
+
|
| 66 |
+
this.loadDocuments();
|
| 67 |
+
},
|
| 68 |
+
|
| 69 |
+
async loadDocuments() {
|
| 70 |
+
const container = document.getElementById("documents-list");
|
| 71 |
+
|
| 72 |
+
try {
|
| 73 |
+
let docs;
|
| 74 |
+
|
| 75 |
+
if (this.searchQuery) {
|
| 76 |
+
docs = await DB.searchDocuments(this.searchQuery);
|
| 77 |
+
} else {
|
| 78 |
+
docs = await DB.getAllDocuments(this.currentFilter);
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
if (docs.length === 0) {
|
| 82 |
+
container.innerHTML = `
|
| 83 |
+
<div class="empty-state">
|
| 84 |
+
<p>📄 No documents found</p>
|
| 85 |
+
<p class="hint">Create your first document!</p>
|
| 86 |
+
</div>
|
| 87 |
+
`;
|
| 88 |
+
return;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
container.innerHTML = docs
|
| 92 |
+
.map(
|
| 93 |
+
doc => `
|
| 94 |
+
<div class="doc-item ${window.app?.currentDocId === doc.id ? "active" : ""}" data-id="${doc.id}">
|
| 95 |
+
<div class="doc-item-title">
|
| 96 |
+
${doc.favorite ? "⭐ " : ""}${Utils.truncate(doc.title, 40)}
|
| 97 |
+
</div>
|
| 98 |
+
<div class="doc-item-meta">
|
| 99 |
+
${doc.wordCount || 0} words • ${Utils.formatDate(doc.updatedAt)}
|
| 100 |
+
</div>
|
| 101 |
+
</div>
|
| 102 |
+
`
|
| 103 |
+
)
|
| 104 |
+
.join("");
|
| 105 |
+
|
| 106 |
+
// Add click handlers
|
| 107 |
+
container.querySelectorAll(".doc-item").forEach(item => {
|
| 108 |
+
item.addEventListener("click", () => {
|
| 109 |
+
const id = item.getAttribute("data-id");
|
| 110 |
+
window.app?.loadDocument(id);
|
| 111 |
+
});
|
| 112 |
+
|
| 113 |
+
// Right-click context menu
|
| 114 |
+
item.addEventListener("contextmenu", e => {
|
| 115 |
+
e.preventDefault();
|
| 116 |
+
this.showContextMenu(e, item.getAttribute("data-id"));
|
| 117 |
+
});
|
| 118 |
+
});
|
| 119 |
+
} catch (err) {
|
| 120 |
+
console.error("Failed to load documents:", err);
|
| 121 |
+
Utils.toast.error("Failed to load documents");
|
| 122 |
+
}
|
| 123 |
+
},
|
| 124 |
+
|
| 125 |
+
showContextMenu(e, docId) {
|
| 126 |
+
// Remove any existing context menu
|
| 127 |
+
const existingMenu = document.querySelector(".context-menu");
|
| 128 |
+
if (existingMenu) existingMenu.remove();
|
| 129 |
+
|
| 130 |
+
// Create context menu
|
| 131 |
+
const menu = document.createElement("div");
|
| 132 |
+
menu.className = "context-menu";
|
| 133 |
+
menu.innerHTML = `
|
| 134 |
+
<div class="context-menu-item" data-action="favorite">
|
| 135 |
+
<span class="context-icon">⭐</span>
|
| 136 |
+
<span>Toggle Favorite</span>
|
| 137 |
+
</div>
|
| 138 |
+
<div class="context-menu-item" data-action="rename">
|
| 139 |
+
<span class="context-icon">✏️</span>
|
| 140 |
+
<span>Rename</span>
|
| 141 |
+
</div>
|
| 142 |
+
<div class="context-menu-divider"></div>
|
| 143 |
+
<div class="context-menu-item danger" data-action="delete">
|
| 144 |
+
<span class="context-icon">🗑️</span>
|
| 145 |
+
<span>Delete</span>
|
| 146 |
+
</div>
|
| 147 |
+
`;
|
| 148 |
+
|
| 149 |
+
// Position menu with boundary checks
|
| 150 |
+
document.body.appendChild(menu);
|
| 151 |
+
|
| 152 |
+
// Get menu dimensions
|
| 153 |
+
const menuRect = menu.getBoundingClientRect();
|
| 154 |
+
const viewportWidth = window.innerWidth;
|
| 155 |
+
const viewportHeight = window.innerHeight;
|
| 156 |
+
|
| 157 |
+
// Calculate position
|
| 158 |
+
let left = e.pageX;
|
| 159 |
+
let top = e.pageY;
|
| 160 |
+
|
| 161 |
+
// Prevent horizontal overflow
|
| 162 |
+
if (left + menuRect.width > viewportWidth) {
|
| 163 |
+
left = viewportWidth - menuRect.width - 10;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
// Prevent vertical overflow
|
| 167 |
+
if (top + menuRect.height > viewportHeight) {
|
| 168 |
+
top = viewportHeight - menuRect.height - 10;
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
menu.style.left = `${left}px`;
|
| 172 |
+
menu.style.top = `${top}px`;
|
| 173 |
+
|
| 174 |
+
// Handle clicks
|
| 175 |
+
menu.querySelectorAll(".context-menu-item").forEach(item => {
|
| 176 |
+
item.addEventListener("click", async () => {
|
| 177 |
+
const action = item.getAttribute("data-action");
|
| 178 |
+
|
| 179 |
+
switch (action) {
|
| 180 |
+
case "favorite":
|
| 181 |
+
await this.toggleFavorite(docId);
|
| 182 |
+
break;
|
| 183 |
+
case "rename":
|
| 184 |
+
await this.renameDocument(docId);
|
| 185 |
+
break;
|
| 186 |
+
case "delete":
|
| 187 |
+
const confirmDelete = confirm("Are you sure you want to delete this document?");
|
| 188 |
+
if (confirmDelete) {
|
| 189 |
+
await this.deleteDocument(docId);
|
| 190 |
+
}
|
| 191 |
+
break;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
menu.remove();
|
| 195 |
+
});
|
| 196 |
+
});
|
| 197 |
+
|
| 198 |
+
// Close menu on outside click
|
| 199 |
+
const closeMenu = event => {
|
| 200 |
+
if (!menu.contains(event.target)) {
|
| 201 |
+
menu.remove();
|
| 202 |
+
document.removeEventListener("click", closeMenu);
|
| 203 |
+
}
|
| 204 |
+
};
|
| 205 |
+
|
| 206 |
+
setTimeout(() => {
|
| 207 |
+
document.addEventListener("click", closeMenu);
|
| 208 |
+
}, 10);
|
| 209 |
+
},
|
| 210 |
+
|
| 211 |
+
async renameDocument(docId) {
|
| 212 |
+
const doc = await DB.getDocument(docId);
|
| 213 |
+
if (!doc) return;
|
| 214 |
+
|
| 215 |
+
const newTitle = prompt("Enter new title:", doc.title);
|
| 216 |
+
if (newTitle && newTitle !== doc.title) {
|
| 217 |
+
await DB.updateDocument(docId, { title: newTitle });
|
| 218 |
+
Utils.toast.success("Document renamed!");
|
| 219 |
+
this.loadDocuments();
|
| 220 |
+
|
| 221 |
+
// Update editor if this is current doc
|
| 222 |
+
if (window.app?.currentDocId === docId) {
|
| 223 |
+
document.getElementById("doc-title").value = newTitle;
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
},
|
| 227 |
+
|
| 228 |
+
async toggleFavorite(docId) {
|
| 229 |
+
const isFavorite = await DB.toggleFavorite(docId);
|
| 230 |
+
Utils.toast.success(isFavorite ? "Added to favorites" : "Removed from favorites");
|
| 231 |
+
this.loadDocuments();
|
| 232 |
+
},
|
| 233 |
+
|
| 234 |
+
async deleteDocument(docId) {
|
| 235 |
+
await DB.deleteDocument(docId);
|
| 236 |
+
|
| 237 |
+
// If deleting current doc, clear editor
|
| 238 |
+
if (window.app?.currentDocId === docId) {
|
| 239 |
+
window.app.newDocument();
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
this.loadDocuments();
|
| 243 |
+
},
|
| 244 |
+
|
| 245 |
+
// Update current doc item highlight
|
| 246 |
+
updateActiveDoc(docId) {
|
| 247 |
+
document.querySelectorAll(".doc-item").forEach(item => {
|
| 248 |
+
item.classList.toggle("active", item.getAttribute("data-id") === docId);
|
| 249 |
+
});
|
| 250 |
+
}
|
| 251 |
+
};
|
| 252 |
+
|
| 253 |
+
export default Documents;
|
scripts/editor.js
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Editor Module
|
| 3 |
+
Markdown editor with toolbar actions
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
|
| 8 |
+
const Editor = {
|
| 9 |
+
textarea: null,
|
| 10 |
+
currentDoc: null,
|
| 11 |
+
autoSaveInterval: null,
|
| 12 |
+
autoSaveInProgress: false, // Prevent race conditions
|
| 13 |
+
|
| 14 |
+
init() {
|
| 15 |
+
this.textarea = document.getElementById("markdown-editor");
|
| 16 |
+
this.setupEventListeners();
|
| 17 |
+
this.setupToolbar();
|
| 18 |
+
// Don't start auto-save yet - wait for app to be fully initialized
|
| 19 |
+
},
|
| 20 |
+
|
| 21 |
+
setupEventListeners() {
|
| 22 |
+
// Input event for stats update
|
| 23 |
+
this.textarea.addEventListener(
|
| 24 |
+
"input",
|
| 25 |
+
Utils.debounce(() => {
|
| 26 |
+
this.updateStats();
|
| 27 |
+
|
| 28 |
+
// Check if live preview is enabled
|
| 29 |
+
const livePreview = Utils.storage.get("livePreview", true);
|
| 30 |
+
if (livePreview && window.app?.preview) {
|
| 31 |
+
window.app.preview.update();
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
// Mark unsaved changes
|
| 35 |
+
if (window.app) {
|
| 36 |
+
window.app.unsavedChanges = true;
|
| 37 |
+
}
|
| 38 |
+
}, 300)
|
| 39 |
+
);
|
| 40 |
+
|
| 41 |
+
// Drag & drop for images
|
| 42 |
+
this.textarea.addEventListener("dragover", e => {
|
| 43 |
+
e.preventDefault();
|
| 44 |
+
this.textarea.classList.add("drag-over");
|
| 45 |
+
});
|
| 46 |
+
|
| 47 |
+
this.textarea.addEventListener("dragleave", e => {
|
| 48 |
+
e.preventDefault();
|
| 49 |
+
this.textarea.classList.remove("drag-over");
|
| 50 |
+
});
|
| 51 |
+
|
| 52 |
+
this.textarea.addEventListener("drop", e => {
|
| 53 |
+
e.preventDefault();
|
| 54 |
+
this.textarea.classList.remove("drag-over");
|
| 55 |
+
this.handleImageDrop(e);
|
| 56 |
+
});
|
| 57 |
+
|
| 58 |
+
// Paste images from clipboard
|
| 59 |
+
this.textarea.addEventListener("paste", e => {
|
| 60 |
+
const items = e.clipboardData?.items;
|
| 61 |
+
if (!items) return;
|
| 62 |
+
|
| 63 |
+
for (const item of items) {
|
| 64 |
+
if (item.type.startsWith("image/")) {
|
| 65 |
+
e.preventDefault();
|
| 66 |
+
this.handleImagePaste(item);
|
| 67 |
+
break;
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
});
|
| 71 |
+
|
| 72 |
+
// Keyboard shortcuts
|
| 73 |
+
this.textarea.addEventListener("keydown", e => {
|
| 74 |
+
if (e.ctrlKey || e.metaKey) {
|
| 75 |
+
switch (e.key.toLowerCase()) {
|
| 76 |
+
case "s":
|
| 77 |
+
e.preventDefault();
|
| 78 |
+
window.app?.saveDocument();
|
| 79 |
+
break;
|
| 80 |
+
case "b":
|
| 81 |
+
e.preventDefault();
|
| 82 |
+
this.wrapSelection("**", "**");
|
| 83 |
+
break;
|
| 84 |
+
case "i":
|
| 85 |
+
e.preventDefault();
|
| 86 |
+
this.wrapSelection("*", "*");
|
| 87 |
+
break;
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
});
|
| 91 |
+
},
|
| 92 |
+
|
| 93 |
+
setupToolbar() {
|
| 94 |
+
document.querySelectorAll(".toolbar-btn").forEach(btn => {
|
| 95 |
+
btn.addEventListener("click", () => {
|
| 96 |
+
const action = btn.getAttribute("data-action");
|
| 97 |
+
this.handleToolbarAction(action);
|
| 98 |
+
});
|
| 99 |
+
});
|
| 100 |
+
},
|
| 101 |
+
|
| 102 |
+
handleToolbarAction(action) {
|
| 103 |
+
switch (action) {
|
| 104 |
+
case "bold":
|
| 105 |
+
this.wrapSelection("**", "**");
|
| 106 |
+
break;
|
| 107 |
+
case "italic":
|
| 108 |
+
this.wrapSelection("*", "*");
|
| 109 |
+
break;
|
| 110 |
+
case "strikethrough":
|
| 111 |
+
this.wrapSelection("~~", "~~");
|
| 112 |
+
break;
|
| 113 |
+
case "heading1":
|
| 114 |
+
this.insertAtLineStart("# ");
|
| 115 |
+
break;
|
| 116 |
+
case "heading2":
|
| 117 |
+
this.insertAtLineStart("## ");
|
| 118 |
+
break;
|
| 119 |
+
case "heading3":
|
| 120 |
+
this.insertAtLineStart("### ");
|
| 121 |
+
break;
|
| 122 |
+
case "link":
|
| 123 |
+
this.insertLink();
|
| 124 |
+
break;
|
| 125 |
+
case "image":
|
| 126 |
+
this.insertImage();
|
| 127 |
+
break;
|
| 128 |
+
case "code":
|
| 129 |
+
this.wrapSelection("`", "`");
|
| 130 |
+
break;
|
| 131 |
+
case "quote":
|
| 132 |
+
this.insertAtLineStart("> ");
|
| 133 |
+
break;
|
| 134 |
+
case "ul":
|
| 135 |
+
this.insertAtLineStart("- ");
|
| 136 |
+
break;
|
| 137 |
+
case "ol":
|
| 138 |
+
this.insertAtLineStart("1. ");
|
| 139 |
+
break;
|
| 140 |
+
case "task":
|
| 141 |
+
this.insertAtLineStart("- [ ] ");
|
| 142 |
+
break;
|
| 143 |
+
case "table":
|
| 144 |
+
this.insertTable();
|
| 145 |
+
break;
|
| 146 |
+
case "hr":
|
| 147 |
+
this.insertLine("\n---\n");
|
| 148 |
+
break;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
this.textarea.focus();
|
| 152 |
+
},
|
| 153 |
+
|
| 154 |
+
wrapSelection(before, after) {
|
| 155 |
+
const start = this.textarea.selectionStart;
|
| 156 |
+
const end = this.textarea.selectionEnd;
|
| 157 |
+
const text = this.textarea.value;
|
| 158 |
+
const selected = text.substring(start, end);
|
| 159 |
+
|
| 160 |
+
const wrapped = before + (selected || "text") + after;
|
| 161 |
+
this.textarea.setRangeText(wrapped, start, end, "select");
|
| 162 |
+
|
| 163 |
+
this.textarea.dispatchEvent(new Event("input"));
|
| 164 |
+
},
|
| 165 |
+
|
| 166 |
+
insertAtLineStart(prefix) {
|
| 167 |
+
const start = this.textarea.selectionStart;
|
| 168 |
+
const text = this.textarea.value;
|
| 169 |
+
|
| 170 |
+
// Find line start
|
| 171 |
+
let lineStart = start;
|
| 172 |
+
while (lineStart > 0 && text[lineStart - 1] !== "\n") {
|
| 173 |
+
lineStart--;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
this.textarea.setRangeText(prefix, lineStart, lineStart, "end");
|
| 177 |
+
this.textarea.dispatchEvent(new Event("input"));
|
| 178 |
+
},
|
| 179 |
+
|
| 180 |
+
insertLine(text) {
|
| 181 |
+
const start = this.textarea.selectionStart;
|
| 182 |
+
this.textarea.setRangeText(text, start, start, "end");
|
| 183 |
+
this.textarea.dispatchEvent(new Event("input"));
|
| 184 |
+
},
|
| 185 |
+
|
| 186 |
+
insertLink() {
|
| 187 |
+
const url = prompt("Enter URL:");
|
| 188 |
+
if (!url) return;
|
| 189 |
+
|
| 190 |
+
const text = prompt("Link text (optional):") || url;
|
| 191 |
+
this.wrapSelection(`[${text}](`, `)`);
|
| 192 |
+
},
|
| 193 |
+
|
| 194 |
+
insertImage() {
|
| 195 |
+
const url = prompt("Enter image URL:");
|
| 196 |
+
if (!url) return;
|
| 197 |
+
|
| 198 |
+
const alt = prompt("Alt text (optional):") || "image";
|
| 199 |
+
const markdown = ``;
|
| 200 |
+
|
| 201 |
+
const start = this.textarea.selectionStart;
|
| 202 |
+
this.textarea.setRangeText(markdown, start, start, "end");
|
| 203 |
+
this.textarea.dispatchEvent(new Event("input"));
|
| 204 |
+
},
|
| 205 |
+
|
| 206 |
+
insertTable() {
|
| 207 |
+
const table = `\n| Header 1 | Header 2 | Header 3 |\n|----------|----------|----------|\n| Cell 1 | Cell 2 | Cell 3 |\n| Cell 4 | Cell 5 | Cell 6 |\n`;
|
| 208 |
+
this.insertLine(table);
|
| 209 |
+
},
|
| 210 |
+
|
| 211 |
+
updateStats() {
|
| 212 |
+
const content = this.textarea.value;
|
| 213 |
+
|
| 214 |
+
const wordCount = Utils.countWords(content);
|
| 215 |
+
const charCount = Utils.countChars(content);
|
| 216 |
+
const lineCount = Utils.countLines(content);
|
| 217 |
+
const readingTime = Utils.readingTime(wordCount);
|
| 218 |
+
|
| 219 |
+
document.getElementById("word-count").textContent = `${wordCount} words`;
|
| 220 |
+
document.getElementById("char-count").textContent = `${charCount} chars`;
|
| 221 |
+
document.getElementById("line-count").textContent = `${lineCount} lines`;
|
| 222 |
+
|
| 223 |
+
// Add reading time if element exists
|
| 224 |
+
const readingTimeEl = document.getElementById("reading-time");
|
| 225 |
+
if (readingTimeEl) {
|
| 226 |
+
readingTimeEl.textContent = readingTime;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
// Update current doc stats if exists
|
| 230 |
+
if (this.currentDoc) {
|
| 231 |
+
this.currentDoc.wordCount = wordCount;
|
| 232 |
+
this.currentDoc.charCount = charCount;
|
| 233 |
+
}
|
| 234 |
+
},
|
| 235 |
+
|
| 236 |
+
getContent() {
|
| 237 |
+
return this.textarea.value;
|
| 238 |
+
},
|
| 239 |
+
|
| 240 |
+
setContent(content) {
|
| 241 |
+
this.textarea.value = content || "";
|
| 242 |
+
this.updateStats();
|
| 243 |
+
window.app?.preview.update();
|
| 244 |
+
},
|
| 245 |
+
|
| 246 |
+
clear() {
|
| 247 |
+
this.setContent("");
|
| 248 |
+
},
|
| 249 |
+
|
| 250 |
+
startAutoSave() {
|
| 251 |
+
// Stop any existing interval
|
| 252 |
+
this.stopAutoSave();
|
| 253 |
+
|
| 254 |
+
const autoSaveEnabled = Utils.storage.get("autoSave", true);
|
| 255 |
+
if (!autoSaveEnabled) return;
|
| 256 |
+
|
| 257 |
+
// Only start if app is fully initialized
|
| 258 |
+
if (!window.app) {
|
| 259 |
+
console.warn("Auto-save deferred - app not initialized yet");
|
| 260 |
+
return;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
this.autoSaveInterval = setInterval(async () => {
|
| 264 |
+
// Prevent concurrent auto-saves
|
| 265 |
+
if (this.autoSaveInProgress) {
|
| 266 |
+
console.log("⏭️ Skipping auto-save - already in progress");
|
| 267 |
+
return;
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
if (window.app?.unsavedChanges && this.textarea.value) {
|
| 271 |
+
try {
|
| 272 |
+
this.autoSaveInProgress = true;
|
| 273 |
+
await window.app.saveDocument(true); // Silent save
|
| 274 |
+
console.log("💾 Auto-saved");
|
| 275 |
+
} catch (err) {
|
| 276 |
+
console.error("Auto-save failed:", err);
|
| 277 |
+
} finally {
|
| 278 |
+
this.autoSaveInProgress = false;
|
| 279 |
+
}
|
| 280 |
+
}
|
| 281 |
+
}, 30000); // 30 seconds
|
| 282 |
+
|
| 283 |
+
console.log("✅ Auto-save enabled (every 30s)");
|
| 284 |
+
},
|
| 285 |
+
|
| 286 |
+
stopAutoSave() {
|
| 287 |
+
if (this.autoSaveInterval) {
|
| 288 |
+
clearInterval(this.autoSaveInterval);
|
| 289 |
+
this.autoSaveInterval = null;
|
| 290 |
+
}
|
| 291 |
+
},
|
| 292 |
+
|
| 293 |
+
// Handle image drop
|
| 294 |
+
handleImageDrop(e) {
|
| 295 |
+
const files = e.dataTransfer?.files;
|
| 296 |
+
if (!files || files.length === 0) return;
|
| 297 |
+
|
| 298 |
+
for (const file of files) {
|
| 299 |
+
if (file.type.startsWith("image/")) {
|
| 300 |
+
this.insertImageFromFile(file);
|
| 301 |
+
}
|
| 302 |
+
}
|
| 303 |
+
},
|
| 304 |
+
|
| 305 |
+
// Handle image paste
|
| 306 |
+
handleImagePaste(item) {
|
| 307 |
+
const file = item.getAsFile();
|
| 308 |
+
if (file) {
|
| 309 |
+
this.insertImageFromFile(file);
|
| 310 |
+
}
|
| 311 |
+
},
|
| 312 |
+
|
| 313 |
+
// Insert image from file (convert to base64 data URL)
|
| 314 |
+
insertImageFromFile(file) {
|
| 315 |
+
const reader = new FileReader();
|
| 316 |
+
|
| 317 |
+
reader.onload = e => {
|
| 318 |
+
const dataUrl = e.target.result;
|
| 319 |
+
const altText = file.name.replace(/\.[^/.]+$/, ""); // Remove extension
|
| 320 |
+
const markdown = `\n\n`;
|
| 321 |
+
|
| 322 |
+
const start = this.textarea.selectionStart;
|
| 323 |
+
this.textarea.setRangeText(markdown, start, start, "end");
|
| 324 |
+
this.textarea.dispatchEvent(new Event("input"));
|
| 325 |
+
|
| 326 |
+
Utils.toast.success(`Image "${file.name}" inserted!`);
|
| 327 |
+
};
|
| 328 |
+
|
| 329 |
+
reader.onerror = () => {
|
| 330 |
+
Utils.toast.error("Failed to read image file");
|
| 331 |
+
};
|
| 332 |
+
|
| 333 |
+
reader.readAsDataURL(file);
|
| 334 |
+
}
|
| 335 |
+
};
|
| 336 |
+
|
| 337 |
+
export default Editor;
|
scripts/export.js
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Export Module
|
| 3 |
+
Multi-format document export
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
import DB from "./db.js";
|
| 8 |
+
|
| 9 |
+
const Export = {
|
| 10 |
+
init() {
|
| 11 |
+
document.getElementById("btn-export").addEventListener("click", () => {
|
| 12 |
+
Utils.modal.open("modal-export");
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
document.querySelectorAll(".export-btn").forEach(btn => {
|
| 16 |
+
btn.addEventListener("click", () => {
|
| 17 |
+
const format = btn.getAttribute("data-format");
|
| 18 |
+
this.exportDocument(format);
|
| 19 |
+
});
|
| 20 |
+
});
|
| 21 |
+
},
|
| 22 |
+
|
| 23 |
+
exportDocument(format) {
|
| 24 |
+
const title = window.app?.currentDoc?.title || "document";
|
| 25 |
+
const content = window.app?.editor.getContent();
|
| 26 |
+
|
| 27 |
+
if (!content && format !== "all" && format !== "import") {
|
| 28 |
+
Utils.toast.warning("No content to export");
|
| 29 |
+
return;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
Utils.modal.close("modal-export");
|
| 33 |
+
|
| 34 |
+
switch (format) {
|
| 35 |
+
case "md":
|
| 36 |
+
this.exportMarkdown(content, title);
|
| 37 |
+
break;
|
| 38 |
+
case "html":
|
| 39 |
+
this.exportHTML(content, title);
|
| 40 |
+
break;
|
| 41 |
+
case "artifact":
|
| 42 |
+
this.exportArtifact(content, title);
|
| 43 |
+
break;
|
| 44 |
+
case "txt":
|
| 45 |
+
this.exportPlainText(content, title);
|
| 46 |
+
break;
|
| 47 |
+
case "json":
|
| 48 |
+
this.exportJSON(content, title);
|
| 49 |
+
break;
|
| 50 |
+
case "clipboard":
|
| 51 |
+
this.copyToClipboard(content);
|
| 52 |
+
break;
|
| 53 |
+
case "all":
|
| 54 |
+
this.exportAllDocuments();
|
| 55 |
+
break;
|
| 56 |
+
case "import":
|
| 57 |
+
this.importDocuments();
|
| 58 |
+
break;
|
| 59 |
+
}
|
| 60 |
+
},
|
| 61 |
+
|
| 62 |
+
exportMarkdown(content, title) {
|
| 63 |
+
const filename = Utils.sanitizeFilename(title) + ".md";
|
| 64 |
+
Utils.downloadFile(content, filename, "text/markdown");
|
| 65 |
+
Utils.toast.success("Markdown exported!");
|
| 66 |
+
},
|
| 67 |
+
|
| 68 |
+
exportHTML(content, title) {
|
| 69 |
+
const html = window.app?.preview.getHTML();
|
| 70 |
+
const fullHTML = `<!DOCTYPE html>
|
| 71 |
+
<html lang="en">
|
| 72 |
+
<head>
|
| 73 |
+
<meta charset="UTF-8">
|
| 74 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 75 |
+
<title>${title}</title>
|
| 76 |
+
<style>
|
| 77 |
+
body {
|
| 78 |
+
max-width: 800px;
|
| 79 |
+
margin: 2rem auto;
|
| 80 |
+
padding: 2rem;
|
| 81 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
| 82 |
+
line-height: 1.6;
|
| 83 |
+
}
|
| 84 |
+
</style>
|
| 85 |
+
</head>
|
| 86 |
+
<body>
|
| 87 |
+
${html}
|
| 88 |
+
</body>
|
| 89 |
+
</html>`;
|
| 90 |
+
|
| 91 |
+
const filename = Utils.sanitizeFilename(title) + ".html";
|
| 92 |
+
Utils.downloadFile(fullHTML, filename, "text/html");
|
| 93 |
+
Utils.toast.success("HTML exported!");
|
| 94 |
+
},
|
| 95 |
+
|
| 96 |
+
exportArtifact(content, title) {
|
| 97 |
+
const html = window.app?.preview.getHTML();
|
| 98 |
+
const artifact = `<!DOCTYPE html>
|
| 99 |
+
<html lang="en">
|
| 100 |
+
<head>
|
| 101 |
+
<meta charset="UTF-8">
|
| 102 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 103 |
+
<title>${title}</title>
|
| 104 |
+
|
| 105 |
+
<!-- Prism.js for syntax highlighting -->
|
| 106 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css">
|
| 107 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
|
| 108 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-javascript.min.js"></script>
|
| 109 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-python.min.js"></script>
|
| 110 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-typescript.min.js"></script>
|
| 111 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-json.min.js"></script>
|
| 112 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-css.min.js"></script>
|
| 113 |
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-bash.min.js"></script>
|
| 114 |
+
|
| 115 |
+
<!-- KaTeX for math -->
|
| 116 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
| 117 |
+
|
| 118 |
+
<style>
|
| 119 |
+
* {
|
| 120 |
+
margin: 0;
|
| 121 |
+
padding: 0;
|
| 122 |
+
box-sizing: border-box;
|
| 123 |
+
}
|
| 124 |
+
body {
|
| 125 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 126 |
+
min-height: 100vh;
|
| 127 |
+
padding: 2rem;
|
| 128 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
| 129 |
+
}
|
| 130 |
+
.container {
|
| 131 |
+
max-width: 900px;
|
| 132 |
+
margin: 0 auto;
|
| 133 |
+
background: white;
|
| 134 |
+
border-radius: 16px;
|
| 135 |
+
padding: 3rem;
|
| 136 |
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
| 137 |
+
}
|
| 138 |
+
h1, h2, h3, h4, h5, h6 {
|
| 139 |
+
color: #667eea;
|
| 140 |
+
margin-top: 1.5rem;
|
| 141 |
+
margin-bottom: 0.75rem;
|
| 142 |
+
}
|
| 143 |
+
h1 {
|
| 144 |
+
font-size: 2.5rem;
|
| 145 |
+
border-bottom: 3px solid #667eea;
|
| 146 |
+
padding-bottom: 0.5rem;
|
| 147 |
+
}
|
| 148 |
+
p {
|
| 149 |
+
margin-bottom: 1rem;
|
| 150 |
+
line-height: 1.8;
|
| 151 |
+
}
|
| 152 |
+
code {
|
| 153 |
+
background: #f5f5f5;
|
| 154 |
+
padding: 0.2rem 0.4rem;
|
| 155 |
+
border-radius: 4px;
|
| 156 |
+
font-family: 'Courier New', monospace;
|
| 157 |
+
}
|
| 158 |
+
pre {
|
| 159 |
+
background: #2d2d2d;
|
| 160 |
+
color: #f8f8f2;
|
| 161 |
+
padding: 1rem;
|
| 162 |
+
border-radius: 8px;
|
| 163 |
+
overflow-x: auto;
|
| 164 |
+
margin: 1rem 0;
|
| 165 |
+
}
|
| 166 |
+
pre code {
|
| 167 |
+
background: none;
|
| 168 |
+
color: inherit;
|
| 169 |
+
padding: 0;
|
| 170 |
+
}
|
| 171 |
+
a {
|
| 172 |
+
color: #667eea;
|
| 173 |
+
text-decoration: none;
|
| 174 |
+
}
|
| 175 |
+
a:hover {
|
| 176 |
+
text-decoration: underline;
|
| 177 |
+
}
|
| 178 |
+
blockquote {
|
| 179 |
+
border-left: 4px solid #667eea;
|
| 180 |
+
padding-left: 1rem;
|
| 181 |
+
margin: 1rem 0;
|
| 182 |
+
font-style: italic;
|
| 183 |
+
color: #666;
|
| 184 |
+
}
|
| 185 |
+
table {
|
| 186 |
+
width: 100%;
|
| 187 |
+
border-collapse: collapse;
|
| 188 |
+
margin: 1rem 0;
|
| 189 |
+
}
|
| 190 |
+
th, td {
|
| 191 |
+
border: 1px solid #ddd;
|
| 192 |
+
padding: 0.75rem;
|
| 193 |
+
text-align: left;
|
| 194 |
+
}
|
| 195 |
+
th {
|
| 196 |
+
background: #f5f5f5;
|
| 197 |
+
font-weight: 600;
|
| 198 |
+
}
|
| 199 |
+
img {
|
| 200 |
+
max-width: 100%;
|
| 201 |
+
border-radius: 8px;
|
| 202 |
+
margin: 1rem 0;
|
| 203 |
+
}
|
| 204 |
+
ul, ol {
|
| 205 |
+
margin-left: 1.5rem;
|
| 206 |
+
margin-bottom: 1rem;
|
| 207 |
+
}
|
| 208 |
+
li {
|
| 209 |
+
margin-bottom: 0.5rem;
|
| 210 |
+
}
|
| 211 |
+
/* Task lists */
|
| 212 |
+
.task-list-item {
|
| 213 |
+
list-style: none;
|
| 214 |
+
margin-left: -1.5rem;
|
| 215 |
+
}
|
| 216 |
+
.task-list-item input[type="checkbox"] {
|
| 217 |
+
margin-right: 0.5rem;
|
| 218 |
+
}
|
| 219 |
+
.footer {
|
| 220 |
+
margin-top: 3rem;
|
| 221 |
+
padding-top: 1rem;
|
| 222 |
+
border-top: 2px solid #eee;
|
| 223 |
+
text-align: center;
|
| 224 |
+
color: #999;
|
| 225 |
+
font-size: 0.875rem;
|
| 226 |
+
}
|
| 227 |
+
</style>
|
| 228 |
+
</head>
|
| 229 |
+
<body>
|
| 230 |
+
<div class="container">
|
| 231 |
+
${html}
|
| 232 |
+
<div class="footer">
|
| 233 |
+
Created with 💎 Elysia Markdown Studio
|
| 234 |
+
</div>
|
| 235 |
+
</div>
|
| 236 |
+
|
| 237 |
+
<script>
|
| 238 |
+
// Re-highlight code blocks after load
|
| 239 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 240 |
+
if (window.Prism) {
|
| 241 |
+
Prism.highlightAll();
|
| 242 |
+
}
|
| 243 |
+
});
|
| 244 |
+
</script>
|
| 245 |
+
</body>
|
| 246 |
+
</html>`;
|
| 247 |
+
|
| 248 |
+
const filename = Utils.sanitizeFilename(title) + "_artifact.html";
|
| 249 |
+
Utils.downloadFile(artifact, filename, "text/html");
|
| 250 |
+
Utils.toast.success("Artifact exported!");
|
| 251 |
+
},
|
| 252 |
+
|
| 253 |
+
exportPlainText(content, title) {
|
| 254 |
+
// Remove markdown formatting
|
| 255 |
+
let plainText = content;
|
| 256 |
+
plainText = plainText.replace(/^#+\s+/gm, ""); // Headers
|
| 257 |
+
plainText = plainText.replace(/\*\*(.*?)\*\*/g, "$1"); // Bold
|
| 258 |
+
plainText = plainText.replace(/\*(.*?)\*/g, "$1"); // Italic
|
| 259 |
+
plainText = plainText.replace(/~~(.*?)~~/g, "$1"); // Strikethrough
|
| 260 |
+
plainText = plainText.replace(/`(.*?)`/g, "$1"); // Inline code
|
| 261 |
+
plainText = plainText.replace(/\[(.*?)\]\(.*?\)/g, "$1"); // Links
|
| 262 |
+
|
| 263 |
+
const filename = Utils.sanitizeFilename(title) + ".txt";
|
| 264 |
+
Utils.downloadFile(plainText, filename, "text/plain");
|
| 265 |
+
Utils.toast.success("Plain text exported!");
|
| 266 |
+
},
|
| 267 |
+
|
| 268 |
+
exportJSON(content, title) {
|
| 269 |
+
const doc = window.app?.currentDoc || {};
|
| 270 |
+
const data = {
|
| 271 |
+
title,
|
| 272 |
+
content,
|
| 273 |
+
wordCount: Utils.countWords(content),
|
| 274 |
+
charCount: Utils.countChars(content),
|
| 275 |
+
tags: doc.tags || [],
|
| 276 |
+
exportDate: new Date().toISOString(),
|
| 277 |
+
metadata: {
|
| 278 |
+
createdAt: doc.createdAt,
|
| 279 |
+
updatedAt: doc.updatedAt,
|
| 280 |
+
favorite: doc.favorite
|
| 281 |
+
}
|
| 282 |
+
};
|
| 283 |
+
|
| 284 |
+
const json = JSON.stringify(data, null, 2);
|
| 285 |
+
const filename = Utils.sanitizeFilename(title) + ".json";
|
| 286 |
+
Utils.downloadFile(json, filename, "application/json");
|
| 287 |
+
Utils.toast.success("JSON exported!");
|
| 288 |
+
},
|
| 289 |
+
|
| 290 |
+
async exportAllDocuments() {
|
| 291 |
+
try {
|
| 292 |
+
const allData = await DB.exportAll();
|
| 293 |
+
if (!allData) {
|
| 294 |
+
Utils.toast.error("Failed to export documents");
|
| 295 |
+
return;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
const json = JSON.stringify(allData, null, 2);
|
| 299 |
+
const filename = `elysia-studio-backup-${Date.now()}.json`;
|
| 300 |
+
Utils.downloadFile(json, filename, "application/json");
|
| 301 |
+
Utils.toast.success(`Exported ${allData.documents.length} documents!`);
|
| 302 |
+
} catch (err) {
|
| 303 |
+
console.error("Export all failed:", err);
|
| 304 |
+
Utils.toast.error("Failed to export documents");
|
| 305 |
+
}
|
| 306 |
+
},
|
| 307 |
+
|
| 308 |
+
importDocuments() {
|
| 309 |
+
const input = document.createElement("input");
|
| 310 |
+
input.type = "file";
|
| 311 |
+
input.accept = ".json";
|
| 312 |
+
|
| 313 |
+
input.onchange = async e => {
|
| 314 |
+
const file = e.target.files[0];
|
| 315 |
+
if (!file) return;
|
| 316 |
+
|
| 317 |
+
try {
|
| 318 |
+
const text = await file.text();
|
| 319 |
+
const data = JSON.parse(text);
|
| 320 |
+
|
| 321 |
+
const success = await DB.importAll(data);
|
| 322 |
+
if (success) {
|
| 323 |
+
Utils.toast.success("Documents imported successfully!");
|
| 324 |
+
window.app?.documents.loadDocuments();
|
| 325 |
+
}
|
| 326 |
+
} catch (err) {
|
| 327 |
+
console.error("Import failed:", err);
|
| 328 |
+
Utils.toast.error("Failed to import documents. Invalid file format.");
|
| 329 |
+
}
|
| 330 |
+
};
|
| 331 |
+
|
| 332 |
+
input.click();
|
| 333 |
+
},
|
| 334 |
+
|
| 335 |
+
// Copy markdown to clipboard
|
| 336 |
+
async copyToClipboard(content) {
|
| 337 |
+
try {
|
| 338 |
+
await navigator.clipboard.writeText(content);
|
| 339 |
+
Utils.toast.success("✅ Markdown copied to clipboard!");
|
| 340 |
+
} catch (err) {
|
| 341 |
+
console.error("Clipboard copy failed:", err);
|
| 342 |
+
Utils.toast.error("Failed to copy to clipboard");
|
| 343 |
+
}
|
| 344 |
+
}
|
| 345 |
+
};
|
| 346 |
+
|
| 347 |
+
export default Export;
|
scripts/preview.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Preview Module
|
| 3 |
+
Real-time Markdown preview with extensions
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
|
| 8 |
+
const Preview = {
|
| 9 |
+
container: null,
|
| 10 |
+
renderer: null,
|
| 11 |
+
|
| 12 |
+
init() {
|
| 13 |
+
this.container = document.getElementById("markdown-preview");
|
| 14 |
+
this.setupMarked();
|
| 15 |
+
this.setupMermaid();
|
| 16 |
+
},
|
| 17 |
+
|
| 18 |
+
setupMarked() {
|
| 19 |
+
// Configure marked.js v15+ (async API)
|
| 20 |
+
marked.setOptions({
|
| 21 |
+
breaks: true,
|
| 22 |
+
gfm: true, // GitHub Flavored Markdown
|
| 23 |
+
headerIds: true
|
| 24 |
+
});
|
| 25 |
+
|
| 26 |
+
// Custom renderer for task lists (Marked.js v15+ token-based API)
|
| 27 |
+
const renderer = {
|
| 28 |
+
listitem(token) {
|
| 29 |
+
// In v15+, token has 'task' and 'checked' properties
|
| 30 |
+
if (token.task) {
|
| 31 |
+
return `<li class="task-list-item">
|
| 32 |
+
<input type="checkbox" ${token.checked ? "checked" : ""} disabled> ${token.text}
|
| 33 |
+
</li>\n`;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
// Regular list item
|
| 37 |
+
return `<li>${token.text}</li>\n`;
|
| 38 |
+
},
|
| 39 |
+
|
| 40 |
+
code(token) {
|
| 41 |
+
const lang = token.lang || "";
|
| 42 |
+
const code = token.text;
|
| 43 |
+
|
| 44 |
+
// Highlight with Prism if available
|
| 45 |
+
if (lang && window.Prism && Prism.languages[lang]) {
|
| 46 |
+
const highlighted = Prism.highlight(code, Prism.languages[lang], lang);
|
| 47 |
+
return `<pre><code class="language-${lang}">${highlighted}</code></pre>\n`;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
return `<pre><code>${code}</code></pre>\n`;
|
| 51 |
+
}
|
| 52 |
+
};
|
| 53 |
+
|
| 54 |
+
marked.use({ renderer });
|
| 55 |
+
},
|
| 56 |
+
|
| 57 |
+
setupMermaid() {
|
| 58 |
+
if (window.mermaid) {
|
| 59 |
+
mermaid.initialize({
|
| 60 |
+
startOnLoad: false,
|
| 61 |
+
theme: "dark",
|
| 62 |
+
securityLevel: "loose"
|
| 63 |
+
});
|
| 64 |
+
}
|
| 65 |
+
},
|
| 66 |
+
|
| 67 |
+
update() {
|
| 68 |
+
const content = window.app?.editor.getContent() || "";
|
| 69 |
+
this.render(content);
|
| 70 |
+
},
|
| 71 |
+
|
| 72 |
+
async render(markdown) {
|
| 73 |
+
if (!markdown) {
|
| 74 |
+
this.container.innerHTML = '<p style="color: var(--text-tertiary); text-align: center; padding: 2rem;">Start writing to see preview...</p>';
|
| 75 |
+
return;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
try {
|
| 79 |
+
// Convert markdown to HTML (v15+ can be async)
|
| 80 |
+
let html = await marked.parse(markdown);
|
| 81 |
+
|
| 82 |
+
// Ensure html is a string, not an object
|
| 83 |
+
if (typeof html !== "string") {
|
| 84 |
+
console.error("Marked.parse returned non-string:", typeof html, html);
|
| 85 |
+
html = String(html);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
// Process KaTeX math
|
| 89 |
+
html = this.processKaTeX(html);
|
| 90 |
+
|
| 91 |
+
// Update container
|
| 92 |
+
this.container.innerHTML = html;
|
| 93 |
+
|
| 94 |
+
// Render mermaid diagrams
|
| 95 |
+
this.renderMermaid();
|
| 96 |
+
|
| 97 |
+
// Highlight code blocks
|
| 98 |
+
this.highlightCode();
|
| 99 |
+
} catch (err) {
|
| 100 |
+
console.error("Preview render failed:", err);
|
| 101 |
+
this.container.innerHTML = `<p style="color: var(--error);">Preview error: ${err.message}</p>`;
|
| 102 |
+
}
|
| 103 |
+
},
|
| 104 |
+
|
| 105 |
+
processKaTeX(html) {
|
| 106 |
+
if (!window.katex) return html;
|
| 107 |
+
|
| 108 |
+
// Inline math: $...$
|
| 109 |
+
html = html.replace(/\$([^\$]+)\$/g, (match, math) => {
|
| 110 |
+
try {
|
| 111 |
+
return katex.renderToString(math, { throwOnError: false });
|
| 112 |
+
} catch {
|
| 113 |
+
return match;
|
| 114 |
+
}
|
| 115 |
+
});
|
| 116 |
+
|
| 117 |
+
// Block math: $$...$$
|
| 118 |
+
html = html.replace(/\$\$([^\$]+)\$\$/g, (match, math) => {
|
| 119 |
+
try {
|
| 120 |
+
return katex.renderToString(math, {
|
| 121 |
+
throwOnError: false,
|
| 122 |
+
displayMode: true
|
| 123 |
+
});
|
| 124 |
+
} catch {
|
| 125 |
+
return match;
|
| 126 |
+
}
|
| 127 |
+
});
|
| 128 |
+
|
| 129 |
+
return html;
|
| 130 |
+
},
|
| 131 |
+
|
| 132 |
+
renderMermaid() {
|
| 133 |
+
if (!window.mermaid) return;
|
| 134 |
+
|
| 135 |
+
const mermaidBlocks = this.container.querySelectorAll("code.language-mermaid");
|
| 136 |
+
mermaidBlocks.forEach((block, index) => {
|
| 137 |
+
const code = block.textContent;
|
| 138 |
+
const id = `mermaid-${Date.now()}-${index}`;
|
| 139 |
+
|
| 140 |
+
const div = document.createElement("div");
|
| 141 |
+
div.id = id;
|
| 142 |
+
div.className = "mermaid";
|
| 143 |
+
div.textContent = code;
|
| 144 |
+
|
| 145 |
+
block.parentElement.replaceWith(div);
|
| 146 |
+
|
| 147 |
+
try {
|
| 148 |
+
mermaid.init(undefined, `#${id}`);
|
| 149 |
+
} catch (err) {
|
| 150 |
+
console.warn("Mermaid render failed:", err);
|
| 151 |
+
}
|
| 152 |
+
});
|
| 153 |
+
},
|
| 154 |
+
|
| 155 |
+
highlightCode() {
|
| 156 |
+
if (!window.Prism) return;
|
| 157 |
+
|
| 158 |
+
this.container.querySelectorAll("pre code").forEach(block => {
|
| 159 |
+
if (!block.classList.contains("language-mermaid")) {
|
| 160 |
+
Prism.highlightElement(block);
|
| 161 |
+
}
|
| 162 |
+
});
|
| 163 |
+
},
|
| 164 |
+
|
| 165 |
+
// Get current HTML (for export)
|
| 166 |
+
getHTML() {
|
| 167 |
+
if (!this.container) {
|
| 168 |
+
console.warn("Preview container not initialized");
|
| 169 |
+
return "";
|
| 170 |
+
}
|
| 171 |
+
return this.container.innerHTML;
|
| 172 |
+
},
|
| 173 |
+
|
| 174 |
+
// Apply theme
|
| 175 |
+
setTheme(theme) {
|
| 176 |
+
if (!this.container) {
|
| 177 |
+
console.warn("Preview container not initialized yet");
|
| 178 |
+
return;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
if (theme === "github") {
|
| 182 |
+
this.container.classList.add("theme-github");
|
| 183 |
+
this.container.classList.remove("theme-elysia");
|
| 184 |
+
} else {
|
| 185 |
+
this.container.classList.add("theme-elysia");
|
| 186 |
+
this.container.classList.remove("theme-github");
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
};
|
| 190 |
+
|
| 191 |
+
export default Preview;
|
scripts/templates.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Templates Module
|
| 3 |
+
Document templates library
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import Utils from "./utils.js";
|
| 7 |
+
import DB from "./db.js";
|
| 8 |
+
|
| 9 |
+
const Templates = {
|
| 10 |
+
builtInTemplates: [
|
| 11 |
+
{
|
| 12 |
+
name: "README",
|
| 13 |
+
category: "GitHub",
|
| 14 |
+
description: "Standard project README template",
|
| 15 |
+
content: `# Project Title
|
| 16 |
+
|
| 17 |
+
## Description
|
| 18 |
+
|
| 19 |
+
A brief description of what this project does and who it's for.
|
| 20 |
+
|
| 21 |
+
## Features
|
| 22 |
+
|
| 23 |
+
- Feature 1
|
| 24 |
+
- Feature 2
|
| 25 |
+
- Feature 3
|
| 26 |
+
|
| 27 |
+
## Installation
|
| 28 |
+
|
| 29 |
+
\`\`\`bash
|
| 30 |
+
npm install
|
| 31 |
+
\`\`\`
|
| 32 |
+
|
| 33 |
+
## Usage
|
| 34 |
+
|
| 35 |
+
\`\`\`javascript
|
| 36 |
+
const example = require('example');
|
| 37 |
+
\`\`\`
|
| 38 |
+
|
| 39 |
+
## Contributing
|
| 40 |
+
|
| 41 |
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
| 42 |
+
|
| 43 |
+
## License
|
| 44 |
+
|
| 45 |
+
MIT License
|
| 46 |
+
`
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
name: "Blog Post",
|
| 50 |
+
category: "Writing",
|
| 51 |
+
description: "Blog post structure",
|
| 52 |
+
content: `# Post Title
|
| 53 |
+
|
| 54 |
+
**Published:** ${new Date().toLocaleDateString()}
|
| 55 |
+
**Author:** Your Name
|
| 56 |
+
**Tags:** tag1, tag2, tag3
|
| 57 |
+
|
| 58 |
+
## Introduction
|
| 59 |
+
|
| 60 |
+
Hook the reader with an engaging introduction...
|
| 61 |
+
|
| 62 |
+
## Main Content
|
| 63 |
+
|
| 64 |
+
### Section 1
|
| 65 |
+
|
| 66 |
+
Content for section 1...
|
| 67 |
+
|
| 68 |
+
### Section 2
|
| 69 |
+
|
| 70 |
+
Content for section 2...
|
| 71 |
+
|
| 72 |
+
### Section 3
|
| 73 |
+
|
| 74 |
+
Content for section 3...
|
| 75 |
+
|
| 76 |
+
## Conclusion
|
| 77 |
+
|
| 78 |
+
Summarize key points and call to action...
|
| 79 |
+
|
| 80 |
+
---
|
| 81 |
+
|
| 82 |
+
*Found this helpful? Share it with others!*
|
| 83 |
+
`
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
name: "Meeting Notes",
|
| 87 |
+
category: "Productivity",
|
| 88 |
+
description: "Meeting notes template",
|
| 89 |
+
content: `# Meeting Notes
|
| 90 |
+
|
| 91 |
+
**Date:** ${new Date().toLocaleDateString()}
|
| 92 |
+
**Time:** HH:MM
|
| 93 |
+
**Attendees:** Name 1, Name 2, Name 3
|
| 94 |
+
**Location:** Zoom/Office/etc.
|
| 95 |
+
|
| 96 |
+
## Agenda
|
| 97 |
+
|
| 98 |
+
1. Topic 1
|
| 99 |
+
2. Topic 2
|
| 100 |
+
3. Topic 3
|
| 101 |
+
|
| 102 |
+
## Discussion
|
| 103 |
+
|
| 104 |
+
### Topic 1
|
| 105 |
+
- Point 1
|
| 106 |
+
- Point 2
|
| 107 |
+
|
| 108 |
+
### Topic 2
|
| 109 |
+
- Point 1
|
| 110 |
+
- Point 2
|
| 111 |
+
|
| 112 |
+
## Action Items
|
| 113 |
+
|
| 114 |
+
- [ ] Task 1 - Assigned to: Person A - Due: Date
|
| 115 |
+
- [ ] Task 2 - Assigned to: Person B - Due: Date
|
| 116 |
+
|
| 117 |
+
## Next Steps
|
| 118 |
+
|
| 119 |
+
...
|
| 120 |
+
|
| 121 |
+
## Next Meeting
|
| 122 |
+
|
| 123 |
+
**Date:** TBD
|
| 124 |
+
**Topics:** ...
|
| 125 |
+
`
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
name: "Documentation",
|
| 129 |
+
category: "Technical",
|
| 130 |
+
description: "API/Software documentation",
|
| 131 |
+
content: `# Component/API Documentation
|
| 132 |
+
|
| 133 |
+
## Overview
|
| 134 |
+
|
| 135 |
+
Brief description of the component/API...
|
| 136 |
+
|
| 137 |
+
## Installation
|
| 138 |
+
|
| 139 |
+
\`\`\`bash
|
| 140 |
+
npm install package-name
|
| 141 |
+
\`\`\`
|
| 142 |
+
|
| 143 |
+
## Quick Start
|
| 144 |
+
|
| 145 |
+
\`\`\`javascript
|
| 146 |
+
import { Component } from 'package-name';
|
| 147 |
+
|
| 148 |
+
const example = new Component({
|
| 149 |
+
option1: 'value1',
|
| 150 |
+
option2: 'value2'
|
| 151 |
+
});
|
| 152 |
+
\`\`\`
|
| 153 |
+
|
| 154 |
+
## API Reference
|
| 155 |
+
|
| 156 |
+
### Method 1
|
| 157 |
+
|
| 158 |
+
**Description:** What this method does
|
| 159 |
+
|
| 160 |
+
**Parameters:**
|
| 161 |
+
- \`param1\` (Type): Description
|
| 162 |
+
- \`param2\` (Type): Description
|
| 163 |
+
|
| 164 |
+
**Returns:** Return type and description
|
| 165 |
+
|
| 166 |
+
**Example:**
|
| 167 |
+
\`\`\`javascript
|
| 168 |
+
example.method1(param1, param2);
|
| 169 |
+
\`\`\`
|
| 170 |
+
|
| 171 |
+
### Method 2
|
| 172 |
+
|
| 173 |
+
...
|
| 174 |
+
|
| 175 |
+
## Examples
|
| 176 |
+
|
| 177 |
+
### Example 1: Basic Usage
|
| 178 |
+
\`\`\`javascript
|
| 179 |
+
// Code example
|
| 180 |
+
\`\`\`
|
| 181 |
+
|
| 182 |
+
### Example 2: Advanced Usage
|
| 183 |
+
\`\`\`javascript
|
| 184 |
+
// Code example
|
| 185 |
+
\`\`\`
|
| 186 |
+
|
| 187 |
+
## Configuration
|
| 188 |
+
|
| 189 |
+
| Option | Type | Default | Description |
|
| 190 |
+
|--------|------|---------|-------------|
|
| 191 |
+
| option1 | String | 'default' | Description |
|
| 192 |
+
| option2 | Number | 0 | Description |
|
| 193 |
+
|
| 194 |
+
## Troubleshooting
|
| 195 |
+
|
| 196 |
+
**Issue 1:** Description
|
| 197 |
+
**Solution:** How to fix
|
| 198 |
+
|
| 199 |
+
**Issue 2:** Description
|
| 200 |
+
**Solution:** How to fix
|
| 201 |
+
|
| 202 |
+
## License
|
| 203 |
+
|
| 204 |
+
MIT
|
| 205 |
+
`
|
| 206 |
+
},
|
| 207 |
+
{
|
| 208 |
+
name: "Academic Paper",
|
| 209 |
+
category: "Academic",
|
| 210 |
+
description: "Research paper structure",
|
| 211 |
+
content: `# Paper Title
|
| 212 |
+
|
| 213 |
+
**Author:** Your Name
|
| 214 |
+
**Affiliation:** University/Institution
|
| 215 |
+
**Date:** ${new Date().toLocaleDateString()}
|
| 216 |
+
|
| 217 |
+
## Abstract
|
| 218 |
+
|
| 219 |
+
Brief summary of the research (150-250 words)...
|
| 220 |
+
|
| 221 |
+
## 1. Introduction
|
| 222 |
+
|
| 223 |
+
### 1.1 Background
|
| 224 |
+
Context and motivation for the research...
|
| 225 |
+
|
| 226 |
+
### 1.2 Research Question
|
| 227 |
+
What this paper aims to answer...
|
| 228 |
+
|
| 229 |
+
### 1.3 Contributions
|
| 230 |
+
Key contributions of this work...
|
| 231 |
+
|
| 232 |
+
## 2. Related Work
|
| 233 |
+
|
| 234 |
+
Review of existing literature and how this work differs...
|
| 235 |
+
|
| 236 |
+
## 3. Methodology
|
| 237 |
+
|
| 238 |
+
### 3.1 Approach
|
| 239 |
+
Description of the approach taken...
|
| 240 |
+
|
| 241 |
+
### 3.2 Implementation
|
| 242 |
+
Technical details...
|
| 243 |
+
|
| 244 |
+
## 4. Results
|
| 245 |
+
|
| 246 |
+
### 4.1 Experiment 1
|
| 247 |
+
Description and results...
|
| 248 |
+
|
| 249 |
+
### 4.2 Experiment 2
|
| 250 |
+
Description and results...
|
| 251 |
+
|
| 252 |
+
## 5. Discussion
|
| 253 |
+
|
| 254 |
+
Analysis and interpretation of results...
|
| 255 |
+
|
| 256 |
+
## 6. Conclusion
|
| 257 |
+
|
| 258 |
+
Summary of findings and future work...
|
| 259 |
+
|
| 260 |
+
## References
|
| 261 |
+
|
| 262 |
+
1. Reference 1
|
| 263 |
+
2. Reference 2
|
| 264 |
+
3. Reference 3
|
| 265 |
+
|
| 266 |
+
## Appendix
|
| 267 |
+
|
| 268 |
+
Additional materials, code, data...
|
| 269 |
+
`
|
| 270 |
+
},
|
| 271 |
+
{
|
| 272 |
+
name: "Blank",
|
| 273 |
+
category: "Basic",
|
| 274 |
+
description: "Empty document",
|
| 275 |
+
content: ""
|
| 276 |
+
}
|
| 277 |
+
],
|
| 278 |
+
|
| 279 |
+
init() {
|
| 280 |
+
// Templates can be opened via modal (future feature)
|
| 281 |
+
// For now, templates are loaded in the new document flow
|
| 282 |
+
},
|
| 283 |
+
|
| 284 |
+
async loadTemplates() {
|
| 285 |
+
const customTemplates = await DB.getTemplates();
|
| 286 |
+
return [...this.builtInTemplates, ...customTemplates];
|
| 287 |
+
},
|
| 288 |
+
|
| 289 |
+
async showTemplatesModal() {
|
| 290 |
+
const templates = await this.loadTemplates();
|
| 291 |
+
|
| 292 |
+
const grid = document.getElementById("templates-grid");
|
| 293 |
+
grid.innerHTML = templates
|
| 294 |
+
.map(
|
| 295 |
+
t => `
|
| 296 |
+
<div class="template-card" data-name="${t.name}">
|
| 297 |
+
<div class="template-name">${t.name}</div>
|
| 298 |
+
<div class="template-desc">${t.description}</div>
|
| 299 |
+
<div class="template-category">${t.category}</div>
|
| 300 |
+
</div>
|
| 301 |
+
`
|
| 302 |
+
)
|
| 303 |
+
.join("");
|
| 304 |
+
|
| 305 |
+
grid.querySelectorAll(".template-card").forEach(card => {
|
| 306 |
+
card.addEventListener("click", () => {
|
| 307 |
+
const name = card.getAttribute("data-name");
|
| 308 |
+
const template = templates.find(t => t.name === name);
|
| 309 |
+
if (template) {
|
| 310 |
+
// Use app.newDocument with template name
|
| 311 |
+
window.app?.newDocument(template.name);
|
| 312 |
+
Utils.modal.close("modal-templates");
|
| 313 |
+
Utils.toast.success(`Template "${name}" loaded!`);
|
| 314 |
+
}
|
| 315 |
+
});
|
| 316 |
+
});
|
| 317 |
+
|
| 318 |
+
Utils.modal.open("modal-templates");
|
| 319 |
+
},
|
| 320 |
+
|
| 321 |
+
async getTemplate(name) {
|
| 322 |
+
// Search built-in first
|
| 323 |
+
let template = this.builtInTemplates.find(t => t.name === name);
|
| 324 |
+
|
| 325 |
+
// If not found, search custom templates
|
| 326 |
+
if (!template) {
|
| 327 |
+
const customTemplates = await DB.getTemplates();
|
| 328 |
+
template = customTemplates.find(t => t.name === name);
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
return template;
|
| 332 |
+
}
|
| 333 |
+
};
|
| 334 |
+
|
| 335 |
+
export default Templates;
|
scripts/utils.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Utility Functions
|
| 3 |
+
Toast, modals, storage, helpers
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const Utils = {
|
| 7 |
+
// Toast Notifications
|
| 8 |
+
toast: {
|
| 9 |
+
show(message, type = "info", duration = 3000) {
|
| 10 |
+
const container = document.getElementById("toast-container");
|
| 11 |
+
const toast = document.createElement("div");
|
| 12 |
+
toast.className = `toast ${type}`;
|
| 13 |
+
|
| 14 |
+
const icons = {
|
| 15 |
+
success: "✅",
|
| 16 |
+
error: "❌",
|
| 17 |
+
warning: "⚠️",
|
| 18 |
+
info: "ℹ️",
|
| 19 |
+
loading: "⏳"
|
| 20 |
+
};
|
| 21 |
+
|
| 22 |
+
toast.innerHTML = `
|
| 23 |
+
<span class="toast-icon">${icons[type]}</span>
|
| 24 |
+
<span class="toast-message">${message}</span>
|
| 25 |
+
<button class="toast-close">×</button>
|
| 26 |
+
`;
|
| 27 |
+
|
| 28 |
+
container.appendChild(toast);
|
| 29 |
+
|
| 30 |
+
const close = () => {
|
| 31 |
+
toast.style.animation = "slideOut 0.3s ease";
|
| 32 |
+
setTimeout(() => toast.remove(), 300);
|
| 33 |
+
};
|
| 34 |
+
|
| 35 |
+
toast.querySelector(".toast-close").onclick = close;
|
| 36 |
+
if (duration > 0) setTimeout(close, duration);
|
| 37 |
+
|
| 38 |
+
return toast;
|
| 39 |
+
},
|
| 40 |
+
|
| 41 |
+
success: (msg, duration) => Utils.toast.show(msg, "success", duration),
|
| 42 |
+
error: (msg, duration) => Utils.toast.show(msg, "error", duration),
|
| 43 |
+
warning: (msg, duration) => Utils.toast.show(msg, "warning", duration),
|
| 44 |
+
info: (msg, duration) => Utils.toast.show(msg, "info", duration)
|
| 45 |
+
},
|
| 46 |
+
|
| 47 |
+
// Modal Management
|
| 48 |
+
modal: {
|
| 49 |
+
open(modalId) {
|
| 50 |
+
const modal = document.getElementById(modalId);
|
| 51 |
+
if (modal) {
|
| 52 |
+
modal.classList.add("active");
|
| 53 |
+
document.body.style.overflow = "hidden";
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
|
| 57 |
+
close(modalId) {
|
| 58 |
+
const modal = document.getElementById(modalId);
|
| 59 |
+
if (modal) {
|
| 60 |
+
modal.classList.remove("active");
|
| 61 |
+
document.body.style.overflow = "";
|
| 62 |
+
}
|
| 63 |
+
},
|
| 64 |
+
|
| 65 |
+
init() {
|
| 66 |
+
document.querySelectorAll(".modal").forEach(modal => {
|
| 67 |
+
modal.addEventListener("click", e => {
|
| 68 |
+
if (e.target === modal) {
|
| 69 |
+
Utils.modal.close(modal.id);
|
| 70 |
+
}
|
| 71 |
+
});
|
| 72 |
+
});
|
| 73 |
+
|
| 74 |
+
document.querySelectorAll(".modal-close, [data-modal]").forEach(btn => {
|
| 75 |
+
btn.addEventListener("click", () => {
|
| 76 |
+
const modalId = btn.getAttribute("data-modal");
|
| 77 |
+
if (modalId) Utils.modal.close(modalId);
|
| 78 |
+
});
|
| 79 |
+
});
|
| 80 |
+
}
|
| 81 |
+
},
|
| 82 |
+
|
| 83 |
+
// Local Storage Wrapper (with encryption for sensitive data)
|
| 84 |
+
storage: {
|
| 85 |
+
// Simple XOR encryption (basic obfuscation - better than plaintext)
|
| 86 |
+
_encrypt(text) {
|
| 87 |
+
const key = "ElysiaStudio2025"; // Simple key for obfuscation
|
| 88 |
+
let encrypted = "";
|
| 89 |
+
for (let i = 0; i < text.length; i++) {
|
| 90 |
+
encrypted += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length));
|
| 91 |
+
}
|
| 92 |
+
return btoa(encrypted); // Base64 encode
|
| 93 |
+
},
|
| 94 |
+
|
| 95 |
+
_decrypt(encrypted) {
|
| 96 |
+
try {
|
| 97 |
+
const decoded = atob(encrypted);
|
| 98 |
+
const key = "ElysiaStudio2025";
|
| 99 |
+
let decrypted = "";
|
| 100 |
+
for (let i = 0; i < decoded.length; i++) {
|
| 101 |
+
decrypted += String.fromCharCode(decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length));
|
| 102 |
+
}
|
| 103 |
+
return decrypted;
|
| 104 |
+
} catch {
|
| 105 |
+
return null;
|
| 106 |
+
}
|
| 107 |
+
},
|
| 108 |
+
|
| 109 |
+
get(key, defaultValue = null) {
|
| 110 |
+
try {
|
| 111 |
+
const value = localStorage.getItem(key);
|
| 112 |
+
if (!value) return defaultValue;
|
| 113 |
+
|
| 114 |
+
// Decrypt API key if it's stored
|
| 115 |
+
if (key === "apiKey") {
|
| 116 |
+
const decrypted = this._decrypt(value);
|
| 117 |
+
return decrypted || defaultValue;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
return JSON.parse(value);
|
| 121 |
+
} catch {
|
| 122 |
+
return defaultValue;
|
| 123 |
+
}
|
| 124 |
+
},
|
| 125 |
+
|
| 126 |
+
set(key, value) {
|
| 127 |
+
try {
|
| 128 |
+
// Encrypt API key before storing
|
| 129 |
+
if (key === "apiKey" && value) {
|
| 130 |
+
localStorage.setItem(key, this._encrypt(value));
|
| 131 |
+
return true;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
localStorage.setItem(key, JSON.stringify(value));
|
| 135 |
+
return true;
|
| 136 |
+
} catch {
|
| 137 |
+
return false;
|
| 138 |
+
}
|
| 139 |
+
},
|
| 140 |
+
|
| 141 |
+
remove(key) {
|
| 142 |
+
localStorage.removeItem(key);
|
| 143 |
+
},
|
| 144 |
+
|
| 145 |
+
clear() {
|
| 146 |
+
localStorage.clear();
|
| 147 |
+
}
|
| 148 |
+
},
|
| 149 |
+
|
| 150 |
+
// Format Date/Time
|
| 151 |
+
formatDateTime(date) {
|
| 152 |
+
const d = new Date(date);
|
| 153 |
+
return d.toLocaleString();
|
| 154 |
+
},
|
| 155 |
+
|
| 156 |
+
// Format Date (short)
|
| 157 |
+
formatDate(date) {
|
| 158 |
+
const d = new Date(date);
|
| 159 |
+
const now = new Date();
|
| 160 |
+
const diff = now - d;
|
| 161 |
+
|
| 162 |
+
if (diff < 60000) return "Just now";
|
| 163 |
+
if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
|
| 164 |
+
if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
|
| 165 |
+
if (diff < 604800000) return `${Math.floor(diff / 86400000)}d ago`;
|
| 166 |
+
|
| 167 |
+
return d.toLocaleDateString();
|
| 168 |
+
},
|
| 169 |
+
|
| 170 |
+
// Count Words
|
| 171 |
+
countWords(text) {
|
| 172 |
+
return text.trim() ? text.trim().split(/\s+/).length : 0;
|
| 173 |
+
},
|
| 174 |
+
|
| 175 |
+
// Count Characters
|
| 176 |
+
countChars(text) {
|
| 177 |
+
return text.length;
|
| 178 |
+
},
|
| 179 |
+
|
| 180 |
+
// Count Lines
|
| 181 |
+
countLines(text) {
|
| 182 |
+
return text.split("\n").length;
|
| 183 |
+
},
|
| 184 |
+
|
| 185 |
+
// Calculate reading time (average 200 words per minute)
|
| 186 |
+
readingTime(wordCount) {
|
| 187 |
+
const minutes = Math.ceil(wordCount / 200);
|
| 188 |
+
if (minutes < 1) return "< 1 min read";
|
| 189 |
+
if (minutes === 1) return "1 min read";
|
| 190 |
+
return `${minutes} min read`;
|
| 191 |
+
},
|
| 192 |
+
|
| 193 |
+
// Download File
|
| 194 |
+
downloadFile(content, filename, mimeType = "text/plain") {
|
| 195 |
+
const blob = new Blob([content], { type: mimeType });
|
| 196 |
+
const url = URL.createObjectURL(blob);
|
| 197 |
+
const a = document.createElement("a");
|
| 198 |
+
a.href = url;
|
| 199 |
+
a.download = filename;
|
| 200 |
+
a.click();
|
| 201 |
+
URL.revokeObjectURL(url);
|
| 202 |
+
},
|
| 203 |
+
|
| 204 |
+
// Copy to Clipboard
|
| 205 |
+
async copyToClipboard(text) {
|
| 206 |
+
try {
|
| 207 |
+
await navigator.clipboard.writeText(text);
|
| 208 |
+
Utils.toast.success("Copied to clipboard!");
|
| 209 |
+
return true;
|
| 210 |
+
} catch (err) {
|
| 211 |
+
Utils.toast.error("Failed to copy");
|
| 212 |
+
return false;
|
| 213 |
+
}
|
| 214 |
+
},
|
| 215 |
+
|
| 216 |
+
// Debounce Function
|
| 217 |
+
debounce(func, wait) {
|
| 218 |
+
let timeout;
|
| 219 |
+
return function executedFunction(...args) {
|
| 220 |
+
const later = () => {
|
| 221 |
+
clearTimeout(timeout);
|
| 222 |
+
func(...args);
|
| 223 |
+
};
|
| 224 |
+
clearTimeout(timeout);
|
| 225 |
+
timeout = setTimeout(later, wait);
|
| 226 |
+
};
|
| 227 |
+
},
|
| 228 |
+
|
| 229 |
+
// Generate UUID
|
| 230 |
+
uuid() {
|
| 231 |
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
|
| 232 |
+
const r = (Math.random() * 16) | 0;
|
| 233 |
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
| 234 |
+
return v.toString(16);
|
| 235 |
+
});
|
| 236 |
+
},
|
| 237 |
+
|
| 238 |
+
// Sanitize Filename
|
| 239 |
+
sanitizeFilename(name) {
|
| 240 |
+
return name.replace(/[^a-z0-9_\-\.]/gi, "_");
|
| 241 |
+
},
|
| 242 |
+
|
| 243 |
+
// Truncate Text
|
| 244 |
+
truncate(text, maxLength) {
|
| 245 |
+
return text.length > maxLength ? text.substring(0, maxLength) + "..." : text;
|
| 246 |
+
},
|
| 247 |
+
|
| 248 |
+
// Escape HTML
|
| 249 |
+
escapeHtml(text) {
|
| 250 |
+
const div = document.createElement("div");
|
| 251 |
+
div.textContent = text;
|
| 252 |
+
return div.innerHTML;
|
| 253 |
+
}
|
| 254 |
+
};
|
| 255 |
+
|
| 256 |
+
// Initialize modals on load
|
| 257 |
+
document.addEventListener("DOMContentLoaded", () => {
|
| 258 |
+
Utils.modal.init();
|
| 259 |
+
});
|
| 260 |
+
|
| 261 |
+
export default Utils;
|
styles/drag-drop.css
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO - Drag & Drop Styles
|
| 3 |
+
Visual feedback for image drag & drop
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
/* Drag over state for textarea */
|
| 7 |
+
#markdown-editor.drag-over {
|
| 8 |
+
border: 2px dashed var(--accent-blue);
|
| 9 |
+
background: rgba(96, 165, 250, 0.05);
|
| 10 |
+
box-shadow: 0 0 20px rgba(96, 165, 250, 0.3);
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
/* Context menu improvements */
|
| 14 |
+
.context-menu {
|
| 15 |
+
position: fixed;
|
| 16 |
+
background: var(--bg-tertiary);
|
| 17 |
+
border: 1px solid var(--border-color);
|
| 18 |
+
border-radius: 8px;
|
| 19 |
+
padding: 0.5rem 0;
|
| 20 |
+
min-width: 180px;
|
| 21 |
+
box-shadow: var(--shadow-lg);
|
| 22 |
+
z-index: 10000;
|
| 23 |
+
animation: contextMenuSlideIn 0.15s ease-out;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
@keyframes contextMenuSlideIn {
|
| 27 |
+
from {
|
| 28 |
+
opacity: 0;
|
| 29 |
+
transform: translateY(-8px);
|
| 30 |
+
}
|
| 31 |
+
to {
|
| 32 |
+
opacity: 1;
|
| 33 |
+
transform: translateY(0);
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
.context-menu-item {
|
| 38 |
+
display: flex;
|
| 39 |
+
align-items: center;
|
| 40 |
+
gap: 0.75rem;
|
| 41 |
+
padding: 0.75rem 1rem;
|
| 42 |
+
cursor: pointer;
|
| 43 |
+
color: var(--text-primary);
|
| 44 |
+
transition: all 0.2s ease;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
.context-menu-item:hover {
|
| 48 |
+
background: var(--bg-hover);
|
| 49 |
+
padding-left: 1.25rem;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.context-menu-item.danger {
|
| 53 |
+
color: var(--error);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
.context-menu-item.danger:hover {
|
| 57 |
+
background: rgba(248, 113, 113, 0.1);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
.context-icon {
|
| 61 |
+
font-size: 1.1rem;
|
| 62 |
+
width: 20px;
|
| 63 |
+
text-align: center;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
.context-menu-divider {
|
| 67 |
+
height: 1px;
|
| 68 |
+
background: var(--border-color);
|
| 69 |
+
margin: 0.5rem 0;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/* Manual refresh button (when live preview disabled) */
|
| 73 |
+
.preview-refresh-btn {
|
| 74 |
+
position: absolute;
|
| 75 |
+
top: 1rem;
|
| 76 |
+
right: 1rem;
|
| 77 |
+
background: var(--gradient-primary);
|
| 78 |
+
border: none;
|
| 79 |
+
color: white;
|
| 80 |
+
padding: 0.5rem 1rem;
|
| 81 |
+
border-radius: 8px;
|
| 82 |
+
cursor: pointer;
|
| 83 |
+
font-weight: 600;
|
| 84 |
+
box-shadow: var(--shadow-md);
|
| 85 |
+
transition:
|
| 86 |
+
transform 0.2s ease,
|
| 87 |
+
box-shadow 0.2s ease;
|
| 88 |
+
z-index: 10;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
.preview-refresh-btn:hover {
|
| 92 |
+
transform: translateY(-2px);
|
| 93 |
+
box-shadow: var(--shadow-lg);
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.preview-refresh-btn:active {
|
| 97 |
+
transform: translateY(0);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
/* Improved loading toast for AI tools */
|
| 101 |
+
.toast.loading {
|
| 102 |
+
background: var(--gradient-blue);
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
.toast.loading .toast-icon {
|
| 106 |
+
animation: spin 1s linear infinite;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
@keyframes spin {
|
| 110 |
+
from {
|
| 111 |
+
transform: rotate(0deg);
|
| 112 |
+
}
|
| 113 |
+
to {
|
| 114 |
+
transform: rotate(360deg);
|
| 115 |
+
}
|
| 116 |
+
}
|
styles/main.css
ADDED
|
@@ -0,0 +1,1673 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
ELYSIA MARKDOWN STUDIO v1.0 - Main Styles
|
| 3 |
+
The Elysia Suite - Dark, Elegant, Writer-Focused
|
| 4 |
+
Built with love by Jean & Elysia 💎
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
/* ========================================
|
| 8 |
+
CSS VARIABLES
|
| 9 |
+
======================================== */
|
| 10 |
+
:root {
|
| 11 |
+
/* Backgrounds */
|
| 12 |
+
--bg-primary: #0a0a0f;
|
| 13 |
+
--bg-secondary: #15151f;
|
| 14 |
+
--bg-tertiary: #1a1a2e;
|
| 15 |
+
--bg-hover: #20203a;
|
| 16 |
+
--bg-active: #2a2a4a;
|
| 17 |
+
--bg-editor: #12121a;
|
| 18 |
+
|
| 19 |
+
/* Gradients (Elysia Signature) */
|
| 20 |
+
--gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 21 |
+
--gradient-accent: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| 22 |
+
--gradient-blue: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
| 23 |
+
|
| 24 |
+
/* Text */
|
| 25 |
+
--text-primary: #f0f0f5;
|
| 26 |
+
--text-secondary: #a0a0b8;
|
| 27 |
+
--text-tertiary: #6a6a88;
|
| 28 |
+
--text-editor: #e8e8f0;
|
| 29 |
+
|
| 30 |
+
/* Accents */
|
| 31 |
+
--accent-purple: #a78bfa;
|
| 32 |
+
--accent-blue: #60a5fa;
|
| 33 |
+
--accent-pink: #f472b6;
|
| 34 |
+
--accent-green: #34d399;
|
| 35 |
+
|
| 36 |
+
/* Semantic */
|
| 37 |
+
--success: #34d399;
|
| 38 |
+
--error: #f87171;
|
| 39 |
+
--warning: #fbbf24;
|
| 40 |
+
--info: #60a5fa;
|
| 41 |
+
|
| 42 |
+
/* Borders & Shadows */
|
| 43 |
+
--border-color: #2a2a4a;
|
| 44 |
+
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3);
|
| 45 |
+
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.4);
|
| 46 |
+
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.5);
|
| 47 |
+
|
| 48 |
+
/* Fonts */
|
| 49 |
+
--font-mono: "JetBrains Mono", monospace;
|
| 50 |
+
--font-sans: "Space Grotesk", sans-serif;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
/* ========================================
|
| 54 |
+
RESET & BASE
|
| 55 |
+
======================================== */
|
| 56 |
+
* {
|
| 57 |
+
margin: 0;
|
| 58 |
+
padding: 0;
|
| 59 |
+
box-sizing: border-box;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
html {
|
| 63 |
+
font-size: 16px;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
body {
|
| 67 |
+
font-family: var(--font-sans);
|
| 68 |
+
background: var(--bg-primary);
|
| 69 |
+
color: var(--text-primary);
|
| 70 |
+
line-height: 1.6;
|
| 71 |
+
overflow: hidden;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/* ========================================
|
| 75 |
+
LAYOUT
|
| 76 |
+
======================================== */
|
| 77 |
+
#app {
|
| 78 |
+
display: flex;
|
| 79 |
+
flex-direction: column;
|
| 80 |
+
height: 100vh;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.app-header {
|
| 84 |
+
background: var(--bg-secondary);
|
| 85 |
+
border-bottom: 1px solid var(--border-color);
|
| 86 |
+
padding: 0.75rem 1.5rem;
|
| 87 |
+
display: grid;
|
| 88 |
+
grid-template-columns: 1fr 2fr 1fr;
|
| 89 |
+
align-items: center;
|
| 90 |
+
gap: 1rem;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
.header-left {
|
| 94 |
+
display: flex;
|
| 95 |
+
align-items: center;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
.header-center {
|
| 99 |
+
display: flex;
|
| 100 |
+
flex-direction: column;
|
| 101 |
+
align-items: center;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
.header-right {
|
| 105 |
+
display: flex;
|
| 106 |
+
gap: 0.5rem;
|
| 107 |
+
justify-content: flex-end;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
.logo {
|
| 111 |
+
font-size: 1.25rem;
|
| 112 |
+
font-weight: 700;
|
| 113 |
+
display: flex;
|
| 114 |
+
align-items: center;
|
| 115 |
+
gap: 0.5rem;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
.logo .icon {
|
| 119 |
+
font-size: 1.5rem;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.version {
|
| 123 |
+
font-size: 0.7rem;
|
| 124 |
+
padding: 0.15rem 0.4rem;
|
| 125 |
+
background: var(--gradient-primary);
|
| 126 |
+
border-radius: 4px;
|
| 127 |
+
font-weight: 400;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
/* Document Title */
|
| 131 |
+
.doc-title-container {
|
| 132 |
+
width: 100%;
|
| 133 |
+
text-align: center;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
.doc-title-input {
|
| 137 |
+
background: transparent;
|
| 138 |
+
border: none;
|
| 139 |
+
color: var(--text-primary);
|
| 140 |
+
font-size: 1.1rem;
|
| 141 |
+
font-weight: 600;
|
| 142 |
+
text-align: center;
|
| 143 |
+
width: 100%;
|
| 144 |
+
padding: 0.25rem;
|
| 145 |
+
border-bottom: 2px solid transparent;
|
| 146 |
+
transition: border-color 0.2s;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.doc-title-input:focus {
|
| 150 |
+
outline: none;
|
| 151 |
+
border-bottom-color: var(--accent-purple);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
.doc-stats {
|
| 155 |
+
font-size: 0.75rem;
|
| 156 |
+
color: var(--text-tertiary);
|
| 157 |
+
margin-top: 0.25rem;
|
| 158 |
+
font-family: var(--font-mono);
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
.separator {
|
| 162 |
+
margin: 0 0.5rem;
|
| 163 |
+
color: var(--border-color);
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
/* Main Container */
|
| 167 |
+
.main-container {
|
| 168 |
+
display: grid;
|
| 169 |
+
grid-template-columns: 0fr 1fr;
|
| 170 |
+
height: calc(100vh - 90px);
|
| 171 |
+
overflow: hidden;
|
| 172 |
+
transition: grid-template-columns 0.3s ease;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
.main-container.sidebar-open {
|
| 176 |
+
grid-template-columns: 300px 1fr;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/* ========================================
|
| 180 |
+
SIDEBAR
|
| 181 |
+
======================================== */
|
| 182 |
+
.sidebar-left {
|
| 183 |
+
background: var(--bg-secondary);
|
| 184 |
+
border-right: 1px solid var(--border-color);
|
| 185 |
+
overflow: hidden;
|
| 186 |
+
display: flex;
|
| 187 |
+
flex-direction: column;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.sidebar-left.collapsed {
|
| 191 |
+
width: 0;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
.sidebar-header {
|
| 195 |
+
padding: 1rem;
|
| 196 |
+
border-bottom: 1px solid var(--border-color);
|
| 197 |
+
display: flex;
|
| 198 |
+
justify-content: space-between;
|
| 199 |
+
align-items: center;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
.sidebar-header h2 {
|
| 203 |
+
font-size: 1rem;
|
| 204 |
+
font-weight: 700;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
.sidebar-content {
|
| 208 |
+
flex: 1;
|
| 209 |
+
overflow-y: auto;
|
| 210 |
+
padding: 1rem;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.search-box {
|
| 214 |
+
margin-bottom: 1rem;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
.search-box input {
|
| 218 |
+
width: 100%;
|
| 219 |
+
padding: 0.5rem;
|
| 220 |
+
background: var(--bg-tertiary);
|
| 221 |
+
border: 1px solid var(--border-color);
|
| 222 |
+
border-radius: 6px;
|
| 223 |
+
color: var(--text-primary);
|
| 224 |
+
font-size: 0.875rem;
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
.search-box input:focus {
|
| 228 |
+
outline: none;
|
| 229 |
+
border-color: var(--accent-purple);
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
/* Filter Tabs */
|
| 233 |
+
.filter-tabs {
|
| 234 |
+
display: flex;
|
| 235 |
+
gap: 0.5rem;
|
| 236 |
+
margin-bottom: 1rem;
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
.filter-tab {
|
| 240 |
+
flex: 1;
|
| 241 |
+
padding: 0.5rem;
|
| 242 |
+
background: var(--bg-tertiary);
|
| 243 |
+
border: 1px solid var(--border-color);
|
| 244 |
+
border-radius: 6px;
|
| 245 |
+
color: var(--text-secondary);
|
| 246 |
+
cursor: pointer;
|
| 247 |
+
font-size: 0.875rem;
|
| 248 |
+
transition: all 0.2s;
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
.filter-tab:hover {
|
| 252 |
+
background: var(--bg-hover);
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
.filter-tab.active {
|
| 256 |
+
background: var(--gradient-primary);
|
| 257 |
+
color: white;
|
| 258 |
+
border-color: transparent;
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
/* Documents List */
|
| 262 |
+
.documents-list {
|
| 263 |
+
display: flex;
|
| 264 |
+
flex-direction: column;
|
| 265 |
+
gap: 0.5rem;
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
.doc-item {
|
| 269 |
+
padding: 0.75rem;
|
| 270 |
+
background: var(--bg-tertiary);
|
| 271 |
+
border: 1px solid var(--border-color);
|
| 272 |
+
border-radius: 6px;
|
| 273 |
+
cursor: pointer;
|
| 274 |
+
transition: all 0.2s;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
.doc-item:hover {
|
| 278 |
+
background: var(--bg-hover);
|
| 279 |
+
border-color: var(--accent-purple);
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
.doc-item.active {
|
| 283 |
+
background: var(--bg-active);
|
| 284 |
+
border-color: var(--accent-purple);
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
.doc-item-title {
|
| 288 |
+
font-weight: 600;
|
| 289 |
+
margin-bottom: 0.25rem;
|
| 290 |
+
color: var(--text-primary);
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
.doc-item-meta {
|
| 294 |
+
font-size: 0.75rem;
|
| 295 |
+
color: var(--text-tertiary);
|
| 296 |
+
font-family: var(--font-mono);
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
/* ========================================
|
| 300 |
+
EDITOR CONTAINER
|
| 301 |
+
======================================== */
|
| 302 |
+
.editor-container {
|
| 303 |
+
display: flex;
|
| 304 |
+
flex-direction: column;
|
| 305 |
+
background: var(--bg-primary);
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
/* Toolbar */
|
| 309 |
+
.editor-toolbar {
|
| 310 |
+
background: var(--bg-secondary);
|
| 311 |
+
border-bottom: 1px solid var(--border-color);
|
| 312 |
+
padding: 0.5rem 1rem;
|
| 313 |
+
display: flex;
|
| 314 |
+
gap: 0.5rem;
|
| 315 |
+
align-items: center;
|
| 316 |
+
flex-wrap: wrap;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
.toolbar-group {
|
| 320 |
+
display: flex;
|
| 321 |
+
gap: 0.25rem;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
.toolbar-btn {
|
| 325 |
+
padding: 0.4rem 0.75rem;
|
| 326 |
+
background: var(--bg-tertiary);
|
| 327 |
+
border: 1px solid var(--border-color);
|
| 328 |
+
border-radius: 4px;
|
| 329 |
+
color: var(--text-secondary);
|
| 330 |
+
cursor: pointer;
|
| 331 |
+
font-size: 0.875rem;
|
| 332 |
+
transition: all 0.2s;
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
.toolbar-btn:hover {
|
| 336 |
+
background: var(--bg-hover);
|
| 337 |
+
color: var(--text-primary);
|
| 338 |
+
border-color: var(--accent-purple);
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
.toolbar-separator {
|
| 342 |
+
width: 1px;
|
| 343 |
+
height: 1.5rem;
|
| 344 |
+
background: var(--border-color);
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
/* Editor Workspace */
|
| 348 |
+
.editor-workspace {
|
| 349 |
+
flex: 1;
|
| 350 |
+
display: grid;
|
| 351 |
+
grid-template-columns: 1fr 1fr;
|
| 352 |
+
gap: 0;
|
| 353 |
+
overflow: hidden;
|
| 354 |
+
height: 100%;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
.editor-pane,
|
| 358 |
+
.preview-pane {
|
| 359 |
+
padding: 2rem;
|
| 360 |
+
height: calc(100vh - 145px);
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
.editor-pane {
|
| 364 |
+
background: var(--bg-editor);
|
| 365 |
+
border-right: 1px solid var(--border-color);
|
| 366 |
+
overflow: hidden; /* Pas de scroll parent, seulement textarea! */
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
.preview-pane {
|
| 370 |
+
background: var(--bg-primary);
|
| 371 |
+
overflow-y: auto; /* Scroll pour le preview! */
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
/* Editor Textarea */
|
| 375 |
+
#markdown-editor {
|
| 376 |
+
width: 100%;
|
| 377 |
+
height: 100%;
|
| 378 |
+
min-height: 100%;
|
| 379 |
+
background: transparent;
|
| 380 |
+
border: none;
|
| 381 |
+
color: var(--text-editor);
|
| 382 |
+
font-family: var(--font-mono);
|
| 383 |
+
font-size: 0.95rem;
|
| 384 |
+
line-height: 1.8;
|
| 385 |
+
resize: none;
|
| 386 |
+
outline: none;
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
#markdown-editor::placeholder {
|
| 390 |
+
color: var(--text-tertiary);
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
/* ========================================
|
| 394 |
+
MARKDOWN PREVIEW
|
| 395 |
+
======================================== */
|
| 396 |
+
.markdown-preview {
|
| 397 |
+
font-family: var(--font-sans);
|
| 398 |
+
color: var(--text-primary);
|
| 399 |
+
line-height: 1.8;
|
| 400 |
+
max-width: 900px;
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
.markdown-preview h1,
|
| 404 |
+
.markdown-preview h2,
|
| 405 |
+
.markdown-preview h3,
|
| 406 |
+
.markdown-preview h4,
|
| 407 |
+
.markdown-preview h5,
|
| 408 |
+
.markdown-preview h6 {
|
| 409 |
+
margin-top: 1.5rem;
|
| 410 |
+
margin-bottom: 0.75rem;
|
| 411 |
+
font-weight: 700;
|
| 412 |
+
color: var(--accent-purple);
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
.markdown-preview h1 {
|
| 416 |
+
font-size: 2rem;
|
| 417 |
+
border-bottom: 2px solid var(--border-color);
|
| 418 |
+
padding-bottom: 0.5rem;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
.markdown-preview h2 {
|
| 422 |
+
font-size: 1.5rem;
|
| 423 |
+
border-bottom: 1px solid var(--border-color);
|
| 424 |
+
padding-bottom: 0.5rem;
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
.markdown-preview h3 {
|
| 428 |
+
font-size: 1.25rem;
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
.markdown-preview p {
|
| 432 |
+
margin-bottom: 1rem;
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
.markdown-preview a {
|
| 436 |
+
color: var(--accent-blue);
|
| 437 |
+
text-decoration: none;
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
.markdown-preview a:hover {
|
| 441 |
+
text-decoration: underline;
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
.markdown-preview code {
|
| 445 |
+
background: var(--bg-tertiary);
|
| 446 |
+
padding: 0.2rem 0.4rem;
|
| 447 |
+
border-radius: 4px;
|
| 448 |
+
font-family: var(--font-mono);
|
| 449 |
+
font-size: 0.875rem;
|
| 450 |
+
color: var(--accent-pink);
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
.markdown-preview pre {
|
| 454 |
+
background: var(--bg-tertiary);
|
| 455 |
+
padding: 1rem;
|
| 456 |
+
border-radius: 8px;
|
| 457 |
+
overflow-x: auto;
|
| 458 |
+
margin: 1rem 0;
|
| 459 |
+
}
|
| 460 |
+
|
| 461 |
+
.markdown-preview pre code {
|
| 462 |
+
background: none;
|
| 463 |
+
padding: 0;
|
| 464 |
+
color: var(--text-primary);
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
.markdown-preview blockquote {
|
| 468 |
+
border-left: 4px solid var(--accent-purple);
|
| 469 |
+
padding-left: 1rem;
|
| 470 |
+
margin: 1rem 0;
|
| 471 |
+
color: var(--text-secondary);
|
| 472 |
+
font-style: italic;
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
.markdown-preview ul,
|
| 476 |
+
.markdown-preview ol {
|
| 477 |
+
margin-left: 2rem;
|
| 478 |
+
margin-bottom: 1rem;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
.markdown-preview li {
|
| 482 |
+
margin-bottom: 0.5rem;
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
+
.markdown-preview table {
|
| 486 |
+
width: 100%;
|
| 487 |
+
border-collapse: collapse;
|
| 488 |
+
margin: 1rem 0;
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
.markdown-preview th,
|
| 492 |
+
.markdown-preview td {
|
| 493 |
+
border: 1px solid var(--border-color);
|
| 494 |
+
padding: 0.75rem;
|
| 495 |
+
text-align: left;
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
.markdown-preview th {
|
| 499 |
+
background: var(--bg-tertiary);
|
| 500 |
+
font-weight: 600;
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
.markdown-preview img {
|
| 504 |
+
max-width: 100%;
|
| 505 |
+
border-radius: 8px;
|
| 506 |
+
margin: 1rem 0;
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
.markdown-preview hr {
|
| 510 |
+
border: none;
|
| 511 |
+
border-top: 2px solid var(--border-color);
|
| 512 |
+
margin: 2rem 0;
|
| 513 |
+
}
|
| 514 |
+
|
| 515 |
+
/* Task Lists */
|
| 516 |
+
.markdown-preview input[type="checkbox"] {
|
| 517 |
+
margin-right: 0.5rem;
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
/* ========================================
|
| 521 |
+
PREVIEW THEMES
|
| 522 |
+
======================================== */
|
| 523 |
+
/* Elysia Dark Theme (Default) */
|
| 524 |
+
.markdown-preview.theme-elysia {
|
| 525 |
+
/* Uses default dark theme variables */
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
/* GitHub Light Theme */
|
| 529 |
+
.markdown-preview.theme-github {
|
| 530 |
+
background: #ffffff;
|
| 531 |
+
color: #24292f;
|
| 532 |
+
padding: 2rem;
|
| 533 |
+
border-radius: 8px;
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
.markdown-preview.theme-github h1,
|
| 537 |
+
.markdown-preview.theme-github h2,
|
| 538 |
+
.markdown-preview.theme-github h3,
|
| 539 |
+
.markdown-preview.theme-github h4,
|
| 540 |
+
.markdown-preview.theme-github h5,
|
| 541 |
+
.markdown-preview.theme-github h6 {
|
| 542 |
+
color: #24292f;
|
| 543 |
+
border-bottom-color: #d0d7de;
|
| 544 |
+
}
|
| 545 |
+
|
| 546 |
+
.markdown-preview.theme-github h1 {
|
| 547 |
+
border-bottom: 1px solid #d0d7de;
|
| 548 |
+
padding-bottom: 0.3em;
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
.markdown-preview.theme-github h2 {
|
| 552 |
+
border-bottom: 1px solid #d0d7de;
|
| 553 |
+
padding-bottom: 0.3em;
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
.markdown-preview.theme-github a {
|
| 557 |
+
color: #0969da;
|
| 558 |
+
}
|
| 559 |
+
|
| 560 |
+
.markdown-preview.theme-github code {
|
| 561 |
+
background: rgba(175, 184, 193, 0.2);
|
| 562 |
+
color: #24292f;
|
| 563 |
+
padding: 0.2em 0.4em;
|
| 564 |
+
border-radius: 6px;
|
| 565 |
+
font-size: 85%;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
.markdown-preview.theme-github pre {
|
| 569 |
+
background: #f6f8fa;
|
| 570 |
+
border: 1px solid #d0d7de;
|
| 571 |
+
border-radius: 6px;
|
| 572 |
+
padding: 16px;
|
| 573 |
+
overflow-x: auto;
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
+
.markdown-preview.theme-github pre code {
|
| 577 |
+
background: none;
|
| 578 |
+
border: none;
|
| 579 |
+
padding: 0;
|
| 580 |
+
color: #24292f;
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
.markdown-preview.theme-github blockquote {
|
| 584 |
+
border-left: 4px solid #d0d7de;
|
| 585 |
+
color: #57606a;
|
| 586 |
+
padding: 0 1em;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
.markdown-preview.theme-github table {
|
| 590 |
+
border-collapse: collapse;
|
| 591 |
+
width: 100%;
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
+
.markdown-preview.theme-github th,
|
| 595 |
+
.markdown-preview.theme-github td {
|
| 596 |
+
border: 1px solid #d0d7de;
|
| 597 |
+
padding: 6px 13px;
|
| 598 |
+
}
|
| 599 |
+
|
| 600 |
+
.markdown-preview.theme-github th {
|
| 601 |
+
background: #f6f8fa;
|
| 602 |
+
font-weight: 600;
|
| 603 |
+
}
|
| 604 |
+
|
| 605 |
+
.markdown-preview.theme-github tr:nth-child(2n) {
|
| 606 |
+
background: #f6f8fa;
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
.markdown-preview.theme-github hr {
|
| 610 |
+
border: none;
|
| 611 |
+
border-top: 1px solid #d0d7de;
|
| 612 |
+
margin: 24px 0;
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
.markdown-preview.theme-github img {
|
| 616 |
+
max-width: 100%;
|
| 617 |
+
border-radius: 6px;
|
| 618 |
+
border: 1px solid #d0d7de;
|
| 619 |
+
}
|
| 620 |
+
|
| 621 |
+
/* ========================================
|
| 622 |
+
BUTTONS
|
| 623 |
+
======================================== */
|
| 624 |
+
button {
|
| 625 |
+
font-family: var(--font-sans);
|
| 626 |
+
cursor: pointer;
|
| 627 |
+
border: none;
|
| 628 |
+
transition: all 0.2s;
|
| 629 |
+
}
|
| 630 |
+
|
| 631 |
+
.btn-primary {
|
| 632 |
+
background: var(--gradient-primary);
|
| 633 |
+
color: white;
|
| 634 |
+
padding: 0.75rem 1.5rem;
|
| 635 |
+
font-weight: 600;
|
| 636 |
+
border-radius: 6px;
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
.btn-primary:hover {
|
| 640 |
+
transform: scale(1.02);
|
| 641 |
+
box-shadow: var(--shadow-md);
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
.btn-secondary {
|
| 645 |
+
background: var(--bg-tertiary);
|
| 646 |
+
color: var(--text-primary);
|
| 647 |
+
padding: 0.75rem 1.5rem;
|
| 648 |
+
border: 1px solid var(--border-color);
|
| 649 |
+
border-radius: 6px;
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
.btn-secondary:hover {
|
| 653 |
+
background: var(--bg-hover);
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
.btn-icon {
|
| 657 |
+
background: var(--bg-tertiary);
|
| 658 |
+
color: var(--text-primary);
|
| 659 |
+
padding: 0.5rem 0.75rem;
|
| 660 |
+
font-size: 1.2rem;
|
| 661 |
+
border: 1px solid var(--border-color);
|
| 662 |
+
border-radius: 6px;
|
| 663 |
+
}
|
| 664 |
+
|
| 665 |
+
.btn-icon:hover {
|
| 666 |
+
background: var(--bg-hover);
|
| 667 |
+
border-color: var(--accent-purple);
|
| 668 |
+
}
|
| 669 |
+
|
| 670 |
+
.btn-icon-small {
|
| 671 |
+
background: none;
|
| 672 |
+
color: var(--text-secondary);
|
| 673 |
+
font-size: 1.5rem;
|
| 674 |
+
padding: 0;
|
| 675 |
+
width: 1.5rem;
|
| 676 |
+
height: 1.5rem;
|
| 677 |
+
}
|
| 678 |
+
|
| 679 |
+
.btn-icon-small:hover {
|
| 680 |
+
color: var(--error);
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
/* ========================================
|
| 684 |
+
MODALS
|
| 685 |
+
======================================== */
|
| 686 |
+
.modal {
|
| 687 |
+
display: none;
|
| 688 |
+
position: fixed;
|
| 689 |
+
top: 0;
|
| 690 |
+
left: 0;
|
| 691 |
+
width: 100%;
|
| 692 |
+
height: 100%;
|
| 693 |
+
background: rgba(0, 0, 0, 0.8);
|
| 694 |
+
backdrop-filter: blur(4px);
|
| 695 |
+
z-index: 1000;
|
| 696 |
+
justify-content: center;
|
| 697 |
+
align-items: center;
|
| 698 |
+
}
|
| 699 |
+
|
| 700 |
+
.modal.active {
|
| 701 |
+
display: flex;
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
.modal-content {
|
| 705 |
+
background: var(--bg-secondary);
|
| 706 |
+
border-radius: 12px;
|
| 707 |
+
width: 90%;
|
| 708 |
+
max-width: 600px;
|
| 709 |
+
max-height: 80vh;
|
| 710 |
+
overflow: hidden;
|
| 711 |
+
display: flex;
|
| 712 |
+
flex-direction: column;
|
| 713 |
+
box-shadow: var(--shadow-lg);
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
.modal-large {
|
| 717 |
+
max-width: 900px;
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
.modal-header {
|
| 721 |
+
padding: 1.5rem;
|
| 722 |
+
border-bottom: 1px solid var(--border-color);
|
| 723 |
+
display: flex;
|
| 724 |
+
justify-content: space-between;
|
| 725 |
+
align-items: center;
|
| 726 |
+
}
|
| 727 |
+
|
| 728 |
+
.modal-header h2 {
|
| 729 |
+
font-size: 1.25rem;
|
| 730 |
+
}
|
| 731 |
+
|
| 732 |
+
.modal-close {
|
| 733 |
+
background: none;
|
| 734 |
+
color: var(--text-secondary);
|
| 735 |
+
font-size: 2rem;
|
| 736 |
+
padding: 0;
|
| 737 |
+
width: 2rem;
|
| 738 |
+
height: 2rem;
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
+
.modal-close:hover {
|
| 742 |
+
color: var(--error);
|
| 743 |
+
}
|
| 744 |
+
|
| 745 |
+
.modal-body {
|
| 746 |
+
padding: 1.5rem;
|
| 747 |
+
overflow-y: auto;
|
| 748 |
+
flex: 1;
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
.modal-footer {
|
| 752 |
+
padding: 1rem 1.5rem;
|
| 753 |
+
border-top: 1px solid var(--border-color);
|
| 754 |
+
display: flex;
|
| 755 |
+
justify-content: flex-end;
|
| 756 |
+
gap: 0.75rem;
|
| 757 |
+
}
|
| 758 |
+
|
| 759 |
+
/* ========================================
|
| 760 |
+
FORM ELEMENTS
|
| 761 |
+
======================================== */
|
| 762 |
+
.form-group {
|
| 763 |
+
margin-bottom: 1.5rem;
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
.form-group label {
|
| 767 |
+
display: block;
|
| 768 |
+
margin-bottom: 0.5rem;
|
| 769 |
+
font-weight: 600;
|
| 770 |
+
color: var(--text-primary);
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
.form-group input[type="text"],
|
| 774 |
+
.form-group input[type="password"],
|
| 775 |
+
.form-group select {
|
| 776 |
+
width: 100%;
|
| 777 |
+
padding: 0.75rem;
|
| 778 |
+
background: var(--bg-tertiary);
|
| 779 |
+
border: 1px solid var(--border-color);
|
| 780 |
+
border-radius: 6px;
|
| 781 |
+
color: var(--text-primary);
|
| 782 |
+
font-size: 0.95rem;
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
.form-group input:focus,
|
| 786 |
+
.form-group select:focus {
|
| 787 |
+
outline: none;
|
| 788 |
+
border-color: var(--accent-purple);
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
.form-group small {
|
| 792 |
+
display: block;
|
| 793 |
+
margin-top: 0.5rem;
|
| 794 |
+
font-size: 0.8rem;
|
| 795 |
+
color: var(--text-secondary);
|
| 796 |
+
}
|
| 797 |
+
|
| 798 |
+
.form-group input[type="checkbox"] {
|
| 799 |
+
margin-right: 0.5rem;
|
| 800 |
+
}
|
| 801 |
+
|
| 802 |
+
/* ========================================
|
| 803 |
+
EXPORT MODAL
|
| 804 |
+
======================================== */
|
| 805 |
+
.export-options {
|
| 806 |
+
display: grid;
|
| 807 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 808 |
+
gap: 1rem;
|
| 809 |
+
}
|
| 810 |
+
|
| 811 |
+
.export-btn {
|
| 812 |
+
background: var(--bg-tertiary);
|
| 813 |
+
border: 2px solid var(--border-color);
|
| 814 |
+
border-radius: 8px;
|
| 815 |
+
padding: 1.5rem;
|
| 816 |
+
display: flex;
|
| 817 |
+
flex-direction: column;
|
| 818 |
+
align-items: center;
|
| 819 |
+
gap: 0.75rem;
|
| 820 |
+
cursor: pointer;
|
| 821 |
+
transition: all 0.2s;
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
.export-btn:hover {
|
| 825 |
+
border-color: var(--accent-purple);
|
| 826 |
+
background: var(--bg-hover);
|
| 827 |
+
transform: translateY(-2px);
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
.export-icon {
|
| 831 |
+
font-size: 2.5rem;
|
| 832 |
+
}
|
| 833 |
+
|
| 834 |
+
.export-label {
|
| 835 |
+
font-weight: 600;
|
| 836 |
+
color: var(--text-primary);
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
/* ========================================
|
| 840 |
+
AI TOOLS
|
| 841 |
+
======================================== */
|
| 842 |
+
.ai-tools-grid {
|
| 843 |
+
display: grid;
|
| 844 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
| 845 |
+
gap: 1rem;
|
| 846 |
+
}
|
| 847 |
+
|
| 848 |
+
.ai-tool-card {
|
| 849 |
+
background: var(--bg-tertiary);
|
| 850 |
+
border: 2px solid var(--border-color);
|
| 851 |
+
border-radius: 12px;
|
| 852 |
+
padding: 1.5rem;
|
| 853 |
+
cursor: pointer;
|
| 854 |
+
transition: all 0.2s;
|
| 855 |
+
text-align: left;
|
| 856 |
+
}
|
| 857 |
+
|
| 858 |
+
.ai-tool-card:hover {
|
| 859 |
+
border-color: var(--accent-purple);
|
| 860 |
+
background: var(--bg-hover);
|
| 861 |
+
transform: translateY(-4px);
|
| 862 |
+
box-shadow: var(--shadow-md);
|
| 863 |
+
}
|
| 864 |
+
|
| 865 |
+
.tool-icon {
|
| 866 |
+
font-size: 2.5rem;
|
| 867 |
+
display: block;
|
| 868 |
+
margin-bottom: 0.75rem;
|
| 869 |
+
}
|
| 870 |
+
|
| 871 |
+
.ai-tool-card h3 {
|
| 872 |
+
font-size: 1.1rem;
|
| 873 |
+
margin-bottom: 0.5rem;
|
| 874 |
+
color: var(--text-primary);
|
| 875 |
+
}
|
| 876 |
+
|
| 877 |
+
.ai-tool-card p {
|
| 878 |
+
font-size: 0.875rem;
|
| 879 |
+
color: var(--text-secondary);
|
| 880 |
+
line-height: 1.5;
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
/* ========================================
|
| 884 |
+
TEMPLATES
|
| 885 |
+
======================================== */
|
| 886 |
+
.templates-grid {
|
| 887 |
+
display: grid;
|
| 888 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 889 |
+
gap: 1rem;
|
| 890 |
+
}
|
| 891 |
+
|
| 892 |
+
.template-card {
|
| 893 |
+
background: var(--bg-tertiary);
|
| 894 |
+
border: 2px solid var(--border-color);
|
| 895 |
+
border-radius: 8px;
|
| 896 |
+
padding: 1rem;
|
| 897 |
+
cursor: pointer;
|
| 898 |
+
transition: all 0.2s;
|
| 899 |
+
}
|
| 900 |
+
|
| 901 |
+
.template-card:hover {
|
| 902 |
+
border-color: var(--accent-purple);
|
| 903 |
+
transform: translateY(-2px);
|
| 904 |
+
}
|
| 905 |
+
|
| 906 |
+
.template-name {
|
| 907 |
+
font-weight: 600;
|
| 908 |
+
margin-bottom: 0.5rem;
|
| 909 |
+
color: var(--text-primary);
|
| 910 |
+
}
|
| 911 |
+
|
| 912 |
+
.template-desc {
|
| 913 |
+
font-size: 0.875rem;
|
| 914 |
+
color: var(--text-secondary);
|
| 915 |
+
margin-bottom: 0.5rem;
|
| 916 |
+
}
|
| 917 |
+
|
| 918 |
+
.template-category {
|
| 919 |
+
display: inline-block;
|
| 920 |
+
font-size: 0.75rem;
|
| 921 |
+
padding: 0.25rem 0.5rem;
|
| 922 |
+
background: var(--gradient-primary);
|
| 923 |
+
border-radius: 4px;
|
| 924 |
+
color: white;
|
| 925 |
+
font-weight: 600;
|
| 926 |
+
}
|
| 927 |
+
|
| 928 |
+
/* ========================================
|
| 929 |
+
TOAST NOTIFICATIONS
|
| 930 |
+
======================================== */
|
| 931 |
+
#toast-container {
|
| 932 |
+
position: fixed;
|
| 933 |
+
top: 1rem;
|
| 934 |
+
right: 1rem;
|
| 935 |
+
z-index: 2000;
|
| 936 |
+
display: flex;
|
| 937 |
+
flex-direction: column;
|
| 938 |
+
gap: 0.75rem;
|
| 939 |
+
}
|
| 940 |
+
|
| 941 |
+
.toast {
|
| 942 |
+
background: var(--bg-secondary);
|
| 943 |
+
border: 1px solid var(--border-color);
|
| 944 |
+
border-radius: 8px;
|
| 945 |
+
padding: 1rem 1.5rem;
|
| 946 |
+
min-width: 300px;
|
| 947 |
+
box-shadow: var(--shadow-lg);
|
| 948 |
+
display: flex;
|
| 949 |
+
align-items: center;
|
| 950 |
+
gap: 0.75rem;
|
| 951 |
+
animation: slideIn 0.3s ease;
|
| 952 |
+
}
|
| 953 |
+
|
| 954 |
+
@keyframes slideIn {
|
| 955 |
+
from {
|
| 956 |
+
transform: translateX(100%);
|
| 957 |
+
opacity: 0;
|
| 958 |
+
}
|
| 959 |
+
to {
|
| 960 |
+
transform: translateX(0);
|
| 961 |
+
opacity: 1;
|
| 962 |
+
}
|
| 963 |
+
}
|
| 964 |
+
|
| 965 |
+
.toast.success {
|
| 966 |
+
border-left: 4px solid var(--success);
|
| 967 |
+
}
|
| 968 |
+
|
| 969 |
+
.toast.error {
|
| 970 |
+
border-left: 4px solid var(--error);
|
| 971 |
+
}
|
| 972 |
+
|
| 973 |
+
.toast.warning {
|
| 974 |
+
border-left: 4px solid var(--warning);
|
| 975 |
+
}
|
| 976 |
+
|
| 977 |
+
.toast.info {
|
| 978 |
+
border-left: 4px solid var(--info);
|
| 979 |
+
}
|
| 980 |
+
|
| 981 |
+
.toast-icon {
|
| 982 |
+
font-size: 1.25rem;
|
| 983 |
+
flex-shrink: 0;
|
| 984 |
+
}
|
| 985 |
+
|
| 986 |
+
.toast-message {
|
| 987 |
+
flex: 1;
|
| 988 |
+
color: var(--text-primary);
|
| 989 |
+
}
|
| 990 |
+
|
| 991 |
+
.toast-close {
|
| 992 |
+
background: none;
|
| 993 |
+
border: none;
|
| 994 |
+
color: var(--text-secondary);
|
| 995 |
+
font-size: 1.5rem;
|
| 996 |
+
line-height: 1;
|
| 997 |
+
cursor: pointer;
|
| 998 |
+
padding: 0;
|
| 999 |
+
width: 24px;
|
| 1000 |
+
height: 24px;
|
| 1001 |
+
display: flex;
|
| 1002 |
+
align-items: center;
|
| 1003 |
+
justify-content: center;
|
| 1004 |
+
border-radius: 4px;
|
| 1005 |
+
transition: all 0.2s ease;
|
| 1006 |
+
flex-shrink: 0;
|
| 1007 |
+
}
|
| 1008 |
+
|
| 1009 |
+
.toast-close:hover {
|
| 1010 |
+
background: var(--bg-hover);
|
| 1011 |
+
color: var(--text-primary);
|
| 1012 |
+
}
|
| 1013 |
+
|
| 1014 |
+
@keyframes slideOut {
|
| 1015 |
+
to {
|
| 1016 |
+
transform: translateX(100%);
|
| 1017 |
+
opacity: 0;
|
| 1018 |
+
}
|
| 1019 |
+
}
|
| 1020 |
+
|
| 1021 |
+
/* ========================================
|
| 1022 |
+
EMPTY STATE
|
| 1023 |
+
======================================== */
|
| 1024 |
+
.empty-state {
|
| 1025 |
+
text-align: center;
|
| 1026 |
+
padding: 2rem 1rem;
|
| 1027 |
+
color: var(--text-tertiary);
|
| 1028 |
+
}
|
| 1029 |
+
|
| 1030 |
+
.empty-state .hint {
|
| 1031 |
+
font-size: 0.875rem;
|
| 1032 |
+
margin-top: 0.5rem;
|
| 1033 |
+
}
|
| 1034 |
+
|
| 1035 |
+
/* ========================================
|
| 1036 |
+
CONTEXT MENU
|
| 1037 |
+
======================================== */
|
| 1038 |
+
.context-menu {
|
| 1039 |
+
position: fixed;
|
| 1040 |
+
background: var(--bg-secondary);
|
| 1041 |
+
border: 1px solid var(--border-color);
|
| 1042 |
+
border-radius: 8px;
|
| 1043 |
+
padding: 0.5rem 0;
|
| 1044 |
+
min-width: 200px;
|
| 1045 |
+
box-shadow: var(--shadow-lg);
|
| 1046 |
+
z-index: 1500;
|
| 1047 |
+
animation: contextMenuAppear 0.15s ease;
|
| 1048 |
+
}
|
| 1049 |
+
|
| 1050 |
+
@keyframes contextMenuAppear {
|
| 1051 |
+
from {
|
| 1052 |
+
opacity: 0;
|
| 1053 |
+
transform: scale(0.95) translateY(-5px);
|
| 1054 |
+
}
|
| 1055 |
+
to {
|
| 1056 |
+
opacity: 1;
|
| 1057 |
+
transform: scale(1) translateY(0);
|
| 1058 |
+
}
|
| 1059 |
+
}
|
| 1060 |
+
|
| 1061 |
+
.context-menu-item {
|
| 1062 |
+
padding: 0.75rem 1rem;
|
| 1063 |
+
display: flex;
|
| 1064 |
+
align-items: center;
|
| 1065 |
+
gap: 0.75rem;
|
| 1066 |
+
cursor: pointer;
|
| 1067 |
+
transition: background 0.15s;
|
| 1068 |
+
color: var(--text-primary);
|
| 1069 |
+
}
|
| 1070 |
+
|
| 1071 |
+
.context-menu-item:hover {
|
| 1072 |
+
background: var(--bg-hover);
|
| 1073 |
+
}
|
| 1074 |
+
|
| 1075 |
+
.context-menu-item.danger {
|
| 1076 |
+
color: var(--error);
|
| 1077 |
+
}
|
| 1078 |
+
|
| 1079 |
+
.context-menu-item.danger:hover {
|
| 1080 |
+
background: rgba(248, 113, 113, 0.1);
|
| 1081 |
+
}
|
| 1082 |
+
|
| 1083 |
+
.context-icon {
|
| 1084 |
+
font-size: 1.1rem;
|
| 1085 |
+
width: 1.5rem;
|
| 1086 |
+
text-align: center;
|
| 1087 |
+
}
|
| 1088 |
+
|
| 1089 |
+
.context-menu-divider {
|
| 1090 |
+
height: 1px;
|
| 1091 |
+
background: var(--border-color);
|
| 1092 |
+
margin: 0.5rem 0;
|
| 1093 |
+
}
|
| 1094 |
+
|
| 1095 |
+
/* ========================================
|
| 1096 |
+
SCROLLBAR
|
| 1097 |
+
======================================== */
|
| 1098 |
+
::-webkit-scrollbar {
|
| 1099 |
+
width: 8px;
|
| 1100 |
+
height: 8px;
|
| 1101 |
+
}
|
| 1102 |
+
|
| 1103 |
+
::-webkit-scrollbar-track {
|
| 1104 |
+
background: var(--bg-secondary);
|
| 1105 |
+
}
|
| 1106 |
+
|
| 1107 |
+
::-webkit-scrollbar-thumb {
|
| 1108 |
+
background: var(--border-color);
|
| 1109 |
+
border-radius: 4px;
|
| 1110 |
+
}
|
| 1111 |
+
|
| 1112 |
+
::-webkit-scrollbar-thumb:hover {
|
| 1113 |
+
background: var(--accent-purple);
|
| 1114 |
+
}
|
| 1115 |
+
|
| 1116 |
+
/* ========================================
|
| 1117 |
+
RESPONSIVE - LARGE SCREENS (1440px+)
|
| 1118 |
+
======================================== */
|
| 1119 |
+
@media (min-width: 1440px) {
|
| 1120 |
+
.editor-workspace {
|
| 1121 |
+
grid-template-columns: 1fr 1fr;
|
| 1122 |
+
}
|
| 1123 |
+
|
| 1124 |
+
.markdown-preview {
|
| 1125 |
+
max-width: 1000px;
|
| 1126 |
+
margin: 0 auto;
|
| 1127 |
+
}
|
| 1128 |
+
|
| 1129 |
+
.modal-content {
|
| 1130 |
+
max-width: 700px;
|
| 1131 |
+
}
|
| 1132 |
+
|
| 1133 |
+
.modal-large {
|
| 1134 |
+
max-width: 1000px;
|
| 1135 |
+
}
|
| 1136 |
+
}
|
| 1137 |
+
|
| 1138 |
+
/* ========================================
|
| 1139 |
+
RESPONSIVE - TABLET (768px - 1024px)
|
| 1140 |
+
======================================== */
|
| 1141 |
+
@media (max-width: 1024px) {
|
| 1142 |
+
/* Editor: Single pane with toggle */
|
| 1143 |
+
.editor-workspace {
|
| 1144 |
+
grid-template-columns: 1fr;
|
| 1145 |
+
position: relative;
|
| 1146 |
+
}
|
| 1147 |
+
|
| 1148 |
+
.editor-pane,
|
| 1149 |
+
.preview-pane {
|
| 1150 |
+
height: calc(100vh - 145px);
|
| 1151 |
+
}
|
| 1152 |
+
|
| 1153 |
+
/* Preview hidden by default on tablet, toggle via JS */
|
| 1154 |
+
.preview-pane {
|
| 1155 |
+
display: none;
|
| 1156 |
+
position: absolute;
|
| 1157 |
+
top: 0;
|
| 1158 |
+
left: 0;
|
| 1159 |
+
right: 0;
|
| 1160 |
+
bottom: 0;
|
| 1161 |
+
background: var(--bg-primary);
|
| 1162 |
+
z-index: 10;
|
| 1163 |
+
}
|
| 1164 |
+
|
| 1165 |
+
.preview-pane.active {
|
| 1166 |
+
display: block;
|
| 1167 |
+
}
|
| 1168 |
+
|
| 1169 |
+
/* Sidebar overlay on tablet */
|
| 1170 |
+
.main-container.sidebar-open {
|
| 1171 |
+
grid-template-columns: 280px 1fr;
|
| 1172 |
+
}
|
| 1173 |
+
|
| 1174 |
+
/* Toolbar adjustments */
|
| 1175 |
+
.editor-toolbar {
|
| 1176 |
+
padding: 0.5rem;
|
| 1177 |
+
gap: 0.25rem;
|
| 1178 |
+
overflow-x: auto;
|
| 1179 |
+
flex-wrap: nowrap;
|
| 1180 |
+
}
|
| 1181 |
+
|
| 1182 |
+
.toolbar-btn {
|
| 1183 |
+
padding: 0.35rem 0.6rem;
|
| 1184 |
+
font-size: 0.8rem;
|
| 1185 |
+
flex-shrink: 0;
|
| 1186 |
+
}
|
| 1187 |
+
|
| 1188 |
+
/* Header */
|
| 1189 |
+
.app-header {
|
| 1190 |
+
padding: 0.5rem 1rem;
|
| 1191 |
+
}
|
| 1192 |
+
|
| 1193 |
+
.logo {
|
| 1194 |
+
font-size: 1rem;
|
| 1195 |
+
}
|
| 1196 |
+
|
| 1197 |
+
.version {
|
| 1198 |
+
font-size: 0.6rem;
|
| 1199 |
+
padding: 0.1rem 0.3rem;
|
| 1200 |
+
}
|
| 1201 |
+
|
| 1202 |
+
/* Modals */
|
| 1203 |
+
.modal-content {
|
| 1204 |
+
width: 95%;
|
| 1205 |
+
max-height: 85vh;
|
| 1206 |
+
}
|
| 1207 |
+
|
| 1208 |
+
.modal-large {
|
| 1209 |
+
max-width: 95%;
|
| 1210 |
+
}
|
| 1211 |
+
|
| 1212 |
+
.ai-tools-grid,
|
| 1213 |
+
.export-options,
|
| 1214 |
+
.templates-grid {
|
| 1215 |
+
grid-template-columns: repeat(2, 1fr);
|
| 1216 |
+
}
|
| 1217 |
+
}
|
| 1218 |
+
|
| 1219 |
+
/* ========================================
|
| 1220 |
+
RESPONSIVE - MOBILE (< 768px)
|
| 1221 |
+
======================================== */
|
| 1222 |
+
@media (max-width: 768px) {
|
| 1223 |
+
/* Header: Stack layout */
|
| 1224 |
+
.app-header {
|
| 1225 |
+
grid-template-columns: 1fr;
|
| 1226 |
+
gap: 0.5rem;
|
| 1227 |
+
padding: 0.5rem;
|
| 1228 |
+
}
|
| 1229 |
+
|
| 1230 |
+
.header-left {
|
| 1231 |
+
justify-content: center;
|
| 1232 |
+
}
|
| 1233 |
+
|
| 1234 |
+
.header-center {
|
| 1235 |
+
order: 2;
|
| 1236 |
+
}
|
| 1237 |
+
|
| 1238 |
+
.header-right {
|
| 1239 |
+
order: 1;
|
| 1240 |
+
justify-content: center;
|
| 1241 |
+
flex-wrap: wrap;
|
| 1242 |
+
gap: 0.25rem;
|
| 1243 |
+
}
|
| 1244 |
+
|
| 1245 |
+
.logo {
|
| 1246 |
+
font-size: 0.95rem;
|
| 1247 |
+
}
|
| 1248 |
+
|
| 1249 |
+
.logo .icon {
|
| 1250 |
+
font-size: 1.2rem;
|
| 1251 |
+
}
|
| 1252 |
+
|
| 1253 |
+
.version {
|
| 1254 |
+
display: none;
|
| 1255 |
+
}
|
| 1256 |
+
|
| 1257 |
+
/* Document title */
|
| 1258 |
+
.doc-title-input {
|
| 1259 |
+
font-size: 0.95rem;
|
| 1260 |
+
}
|
| 1261 |
+
|
| 1262 |
+
.doc-stats {
|
| 1263 |
+
font-size: 0.65rem;
|
| 1264 |
+
flex-wrap: wrap;
|
| 1265 |
+
justify-content: center;
|
| 1266 |
+
}
|
| 1267 |
+
|
| 1268 |
+
.separator {
|
| 1269 |
+
margin: 0 0.25rem;
|
| 1270 |
+
}
|
| 1271 |
+
|
| 1272 |
+
/* Buttons */
|
| 1273 |
+
.btn-icon {
|
| 1274 |
+
padding: 0.4rem 0.6rem;
|
| 1275 |
+
font-size: 1rem;
|
| 1276 |
+
}
|
| 1277 |
+
|
| 1278 |
+
/* Sidebar: Full overlay on mobile */
|
| 1279 |
+
.sidebar-left {
|
| 1280 |
+
position: fixed;
|
| 1281 |
+
top: 0;
|
| 1282 |
+
left: 0;
|
| 1283 |
+
width: 100%;
|
| 1284 |
+
height: 100%;
|
| 1285 |
+
z-index: 100;
|
| 1286 |
+
transform: translateX(-100%);
|
| 1287 |
+
transition: transform 0.3s ease;
|
| 1288 |
+
}
|
| 1289 |
+
|
| 1290 |
+
.sidebar-left:not(.collapsed) {
|
| 1291 |
+
transform: translateX(0);
|
| 1292 |
+
}
|
| 1293 |
+
|
| 1294 |
+
.main-container {
|
| 1295 |
+
grid-template-columns: 1fr !important;
|
| 1296 |
+
}
|
| 1297 |
+
|
| 1298 |
+
.main-container.sidebar-open {
|
| 1299 |
+
grid-template-columns: 1fr !important;
|
| 1300 |
+
}
|
| 1301 |
+
|
| 1302 |
+
/* Toolbar: Scrollable horizontal */
|
| 1303 |
+
.editor-toolbar {
|
| 1304 |
+
padding: 0.4rem;
|
| 1305 |
+
gap: 0.2rem;
|
| 1306 |
+
overflow-x: auto;
|
| 1307 |
+
-webkit-overflow-scrolling: touch;
|
| 1308 |
+
scrollbar-width: none;
|
| 1309 |
+
}
|
| 1310 |
+
|
| 1311 |
+
.editor-toolbar::-webkit-scrollbar {
|
| 1312 |
+
display: none;
|
| 1313 |
+
}
|
| 1314 |
+
|
| 1315 |
+
.toolbar-group {
|
| 1316 |
+
flex-shrink: 0;
|
| 1317 |
+
}
|
| 1318 |
+
|
| 1319 |
+
.toolbar-btn {
|
| 1320 |
+
padding: 0.3rem 0.5rem;
|
| 1321 |
+
font-size: 0.75rem;
|
| 1322 |
+
min-width: 32px;
|
| 1323 |
+
}
|
| 1324 |
+
|
| 1325 |
+
.toolbar-separator {
|
| 1326 |
+
height: 1rem;
|
| 1327 |
+
margin: 0 0.15rem;
|
| 1328 |
+
}
|
| 1329 |
+
|
| 1330 |
+
/* Editor area */
|
| 1331 |
+
.editor-pane,
|
| 1332 |
+
.preview-pane {
|
| 1333 |
+
padding: 1rem;
|
| 1334 |
+
height: calc(100vh - 180px);
|
| 1335 |
+
}
|
| 1336 |
+
|
| 1337 |
+
#markdown-editor {
|
| 1338 |
+
font-size: 0.9rem;
|
| 1339 |
+
line-height: 1.6;
|
| 1340 |
+
}
|
| 1341 |
+
|
| 1342 |
+
/* Preview */
|
| 1343 |
+
.markdown-preview {
|
| 1344 |
+
font-size: 0.9rem;
|
| 1345 |
+
line-height: 1.7;
|
| 1346 |
+
}
|
| 1347 |
+
|
| 1348 |
+
.markdown-preview h1 {
|
| 1349 |
+
font-size: 1.5rem;
|
| 1350 |
+
}
|
| 1351 |
+
|
| 1352 |
+
.markdown-preview h2 {
|
| 1353 |
+
font-size: 1.25rem;
|
| 1354 |
+
}
|
| 1355 |
+
|
| 1356 |
+
.markdown-preview h3 {
|
| 1357 |
+
font-size: 1.1rem;
|
| 1358 |
+
}
|
| 1359 |
+
|
| 1360 |
+
/* Modals: Full screen on mobile */
|
| 1361 |
+
.modal-content {
|
| 1362 |
+
width: 100%;
|
| 1363 |
+
max-width: 100%;
|
| 1364 |
+
height: 100%;
|
| 1365 |
+
max-height: 100%;
|
| 1366 |
+
border-radius: 0;
|
| 1367 |
+
}
|
| 1368 |
+
|
| 1369 |
+
.modal-header {
|
| 1370 |
+
padding: 1rem;
|
| 1371 |
+
}
|
| 1372 |
+
|
| 1373 |
+
.modal-header h2 {
|
| 1374 |
+
font-size: 1.1rem;
|
| 1375 |
+
}
|
| 1376 |
+
|
| 1377 |
+
.modal-body {
|
| 1378 |
+
padding: 1rem;
|
| 1379 |
+
}
|
| 1380 |
+
|
| 1381 |
+
.modal-footer {
|
| 1382 |
+
padding: 0.75rem 1rem;
|
| 1383 |
+
flex-direction: column;
|
| 1384 |
+
gap: 0.5rem;
|
| 1385 |
+
}
|
| 1386 |
+
|
| 1387 |
+
.modal-footer button {
|
| 1388 |
+
width: 100%;
|
| 1389 |
+
}
|
| 1390 |
+
|
| 1391 |
+
/* AI Tools grid: Single column */
|
| 1392 |
+
.ai-tools-grid {
|
| 1393 |
+
grid-template-columns: 1fr;
|
| 1394 |
+
gap: 0.75rem;
|
| 1395 |
+
}
|
| 1396 |
+
|
| 1397 |
+
.ai-tool-card {
|
| 1398 |
+
padding: 1rem;
|
| 1399 |
+
}
|
| 1400 |
+
|
| 1401 |
+
.tool-icon {
|
| 1402 |
+
font-size: 2rem;
|
| 1403 |
+
}
|
| 1404 |
+
|
| 1405 |
+
.ai-tool-card h3 {
|
| 1406 |
+
font-size: 1rem;
|
| 1407 |
+
}
|
| 1408 |
+
|
| 1409 |
+
.ai-tool-card p {
|
| 1410 |
+
font-size: 0.8rem;
|
| 1411 |
+
}
|
| 1412 |
+
|
| 1413 |
+
/* Export options: 2 columns */
|
| 1414 |
+
.export-options {
|
| 1415 |
+
grid-template-columns: repeat(2, 1fr);
|
| 1416 |
+
gap: 0.75rem;
|
| 1417 |
+
}
|
| 1418 |
+
|
| 1419 |
+
.export-btn {
|
| 1420 |
+
padding: 1rem;
|
| 1421 |
+
}
|
| 1422 |
+
|
| 1423 |
+
.export-icon {
|
| 1424 |
+
font-size: 2rem;
|
| 1425 |
+
}
|
| 1426 |
+
|
| 1427 |
+
.export-label {
|
| 1428 |
+
font-size: 0.75rem;
|
| 1429 |
+
}
|
| 1430 |
+
|
| 1431 |
+
/* Templates grid */
|
| 1432 |
+
.templates-grid {
|
| 1433 |
+
grid-template-columns: 1fr;
|
| 1434 |
+
gap: 0.75rem;
|
| 1435 |
+
}
|
| 1436 |
+
|
| 1437 |
+
.template-card {
|
| 1438 |
+
padding: 0.75rem;
|
| 1439 |
+
}
|
| 1440 |
+
|
| 1441 |
+
/* Form elements */
|
| 1442 |
+
.form-group {
|
| 1443 |
+
margin-bottom: 1rem;
|
| 1444 |
+
}
|
| 1445 |
+
|
| 1446 |
+
.form-group label {
|
| 1447 |
+
font-size: 0.9rem;
|
| 1448 |
+
}
|
| 1449 |
+
|
| 1450 |
+
.form-group input,
|
| 1451 |
+
.form-group select {
|
| 1452 |
+
padding: 0.6rem;
|
| 1453 |
+
font-size: 0.9rem;
|
| 1454 |
+
}
|
| 1455 |
+
|
| 1456 |
+
.form-group small {
|
| 1457 |
+
font-size: 0.7rem;
|
| 1458 |
+
}
|
| 1459 |
+
|
| 1460 |
+
/* Toast on mobile */
|
| 1461 |
+
#toast-container {
|
| 1462 |
+
left: 0.5rem;
|
| 1463 |
+
right: 0.5rem;
|
| 1464 |
+
top: auto;
|
| 1465 |
+
bottom: 1rem;
|
| 1466 |
+
}
|
| 1467 |
+
|
| 1468 |
+
.toast {
|
| 1469 |
+
min-width: auto;
|
| 1470 |
+
padding: 0.75rem 1rem;
|
| 1471 |
+
}
|
| 1472 |
+
|
| 1473 |
+
.toast-message {
|
| 1474 |
+
font-size: 0.85rem;
|
| 1475 |
+
}
|
| 1476 |
+
|
| 1477 |
+
/* Context menu */
|
| 1478 |
+
.context-menu {
|
| 1479 |
+
min-width: 160px;
|
| 1480 |
+
}
|
| 1481 |
+
|
| 1482 |
+
.context-menu-item {
|
| 1483 |
+
padding: 0.6rem 0.75rem;
|
| 1484 |
+
}
|
| 1485 |
+
}
|
| 1486 |
+
|
| 1487 |
+
/* ========================================
|
| 1488 |
+
RESPONSIVE - SMALL MOBILE (< 480px)
|
| 1489 |
+
======================================== */
|
| 1490 |
+
@media (max-width: 480px) {
|
| 1491 |
+
/* Even more compact header */
|
| 1492 |
+
.header-right {
|
| 1493 |
+
gap: 0.15rem;
|
| 1494 |
+
}
|
| 1495 |
+
|
| 1496 |
+
.btn-icon {
|
| 1497 |
+
padding: 0.35rem 0.5rem;
|
| 1498 |
+
font-size: 0.9rem;
|
| 1499 |
+
}
|
| 1500 |
+
|
| 1501 |
+
/* Toolbar: Essential buttons only visible */
|
| 1502 |
+
.toolbar-btn {
|
| 1503 |
+
padding: 0.25rem 0.4rem;
|
| 1504 |
+
font-size: 0.7rem;
|
| 1505 |
+
min-width: 28px;
|
| 1506 |
+
}
|
| 1507 |
+
|
| 1508 |
+
/* Editor */
|
| 1509 |
+
.editor-pane,
|
| 1510 |
+
.preview-pane {
|
| 1511 |
+
padding: 0.75rem;
|
| 1512 |
+
height: calc(100vh - 160px);
|
| 1513 |
+
}
|
| 1514 |
+
|
| 1515 |
+
#markdown-editor {
|
| 1516 |
+
font-size: 0.85rem;
|
| 1517 |
+
}
|
| 1518 |
+
|
| 1519 |
+
/* Stats */
|
| 1520 |
+
.doc-stats {
|
| 1521 |
+
font-size: 0.6rem;
|
| 1522 |
+
}
|
| 1523 |
+
|
| 1524 |
+
/* Document title */
|
| 1525 |
+
.doc-title-input {
|
| 1526 |
+
font-size: 0.85rem;
|
| 1527 |
+
}
|
| 1528 |
+
|
| 1529 |
+
/* Export: Single column */
|
| 1530 |
+
.export-options {
|
| 1531 |
+
grid-template-columns: 1fr;
|
| 1532 |
+
}
|
| 1533 |
+
}
|
| 1534 |
+
|
| 1535 |
+
/* ========================================
|
| 1536 |
+
MOBILE VIEW TOGGLE (Editor/Preview)
|
| 1537 |
+
======================================== */
|
| 1538 |
+
.mobile-view-toggle {
|
| 1539 |
+
display: none;
|
| 1540 |
+
position: fixed;
|
| 1541 |
+
bottom: 1rem;
|
| 1542 |
+
right: 1rem;
|
| 1543 |
+
z-index: 50;
|
| 1544 |
+
background: var(--gradient-primary);
|
| 1545 |
+
border: none;
|
| 1546 |
+
color: white;
|
| 1547 |
+
padding: 0.75rem 1rem;
|
| 1548 |
+
border-radius: 50px;
|
| 1549 |
+
font-weight: 600;
|
| 1550 |
+
box-shadow: var(--shadow-lg);
|
| 1551 |
+
cursor: pointer;
|
| 1552 |
+
transition:
|
| 1553 |
+
transform 0.2s,
|
| 1554 |
+
box-shadow 0.2s;
|
| 1555 |
+
}
|
| 1556 |
+
|
| 1557 |
+
.mobile-view-toggle:hover {
|
| 1558 |
+
transform: scale(1.05);
|
| 1559 |
+
}
|
| 1560 |
+
|
| 1561 |
+
.mobile-view-toggle:active {
|
| 1562 |
+
transform: scale(0.95);
|
| 1563 |
+
}
|
| 1564 |
+
|
| 1565 |
+
@media (max-width: 1024px) {
|
| 1566 |
+
.mobile-view-toggle {
|
| 1567 |
+
display: flex;
|
| 1568 |
+
align-items: center;
|
| 1569 |
+
gap: 0.5rem;
|
| 1570 |
+
}
|
| 1571 |
+
}
|
| 1572 |
+
|
| 1573 |
+
/* ========================================
|
| 1574 |
+
ACCESSIBILITY & TOUCH
|
| 1575 |
+
======================================== */
|
| 1576 |
+
@media (hover: none) and (pointer: coarse) {
|
| 1577 |
+
/* Touch-friendly targets */
|
| 1578 |
+
.toolbar-btn,
|
| 1579 |
+
.btn-icon,
|
| 1580 |
+
.filter-tab,
|
| 1581 |
+
.doc-item,
|
| 1582 |
+
.ai-tool-card,
|
| 1583 |
+
.export-btn,
|
| 1584 |
+
.template-card {
|
| 1585 |
+
min-height: 44px;
|
| 1586 |
+
}
|
| 1587 |
+
|
| 1588 |
+
/* Remove hover effects on touch */
|
| 1589 |
+
.toolbar-btn:hover,
|
| 1590 |
+
.btn-icon:hover,
|
| 1591 |
+
.doc-item:hover {
|
| 1592 |
+
transform: none;
|
| 1593 |
+
}
|
| 1594 |
+
|
| 1595 |
+
/* Active states for touch feedback */
|
| 1596 |
+
.toolbar-btn:active,
|
| 1597 |
+
.btn-icon:active {
|
| 1598 |
+
background: var(--bg-active);
|
| 1599 |
+
transform: scale(0.95);
|
| 1600 |
+
}
|
| 1601 |
+
}
|
| 1602 |
+
|
| 1603 |
+
/* ========================================
|
| 1604 |
+
LANDSCAPE MOBILE
|
| 1605 |
+
======================================== */
|
| 1606 |
+
@media (max-height: 500px) and (orientation: landscape) {
|
| 1607 |
+
.app-header {
|
| 1608 |
+
padding: 0.25rem 0.5rem;
|
| 1609 |
+
}
|
| 1610 |
+
|
| 1611 |
+
.logo {
|
| 1612 |
+
font-size: 0.9rem;
|
| 1613 |
+
}
|
| 1614 |
+
|
| 1615 |
+
.editor-toolbar {
|
| 1616 |
+
padding: 0.25rem;
|
| 1617 |
+
}
|
| 1618 |
+
|
| 1619 |
+
.editor-pane,
|
| 1620 |
+
.preview-pane {
|
| 1621 |
+
height: calc(100vh - 100px);
|
| 1622 |
+
padding: 0.5rem;
|
| 1623 |
+
}
|
| 1624 |
+
|
| 1625 |
+
.modal-content {
|
| 1626 |
+
max-height: 95vh;
|
| 1627 |
+
}
|
| 1628 |
+
|
| 1629 |
+
.modal-body {
|
| 1630 |
+
padding: 0.75rem;
|
| 1631 |
+
}
|
| 1632 |
+
}
|
| 1633 |
+
|
| 1634 |
+
/* ========================================
|
| 1635 |
+
PRINT STYLES
|
| 1636 |
+
======================================== */
|
| 1637 |
+
@media print {
|
| 1638 |
+
.app-header,
|
| 1639 |
+
.sidebar-left,
|
| 1640 |
+
.editor-toolbar,
|
| 1641 |
+
.editor-pane,
|
| 1642 |
+
.mobile-view-toggle,
|
| 1643 |
+
#toast-container {
|
| 1644 |
+
display: none !important;
|
| 1645 |
+
}
|
| 1646 |
+
|
| 1647 |
+
.main-container {
|
| 1648 |
+
display: block !important;
|
| 1649 |
+
}
|
| 1650 |
+
|
| 1651 |
+
.preview-pane {
|
| 1652 |
+
display: block !important;
|
| 1653 |
+
position: static !important;
|
| 1654 |
+
height: auto !important;
|
| 1655 |
+
padding: 0 !important;
|
| 1656 |
+
}
|
| 1657 |
+
|
| 1658 |
+
.markdown-preview {
|
| 1659 |
+
max-width: 100%;
|
| 1660 |
+
color: black;
|
| 1661 |
+
}
|
| 1662 |
+
|
| 1663 |
+
.markdown-preview a {
|
| 1664 |
+
color: blue;
|
| 1665 |
+
text-decoration: underline;
|
| 1666 |
+
}
|
| 1667 |
+
|
| 1668 |
+
.markdown-preview pre,
|
| 1669 |
+
.markdown-preview code {
|
| 1670 |
+
background: #f5f5f5;
|
| 1671 |
+
border: 1px solid #ddd;
|
| 1672 |
+
}
|
| 1673 |
+
}
|