Elysia-Suite commited on
Commit
0b194e5
·
verified ·
1 Parent(s): bf25845

Upload 25 files

Browse files
.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

  • SHA256: 47e56752db7c2e36132a5028b83f109b76900f9a6fe60e754d127eff52c0891d
  • Pointer size: 131 Bytes
  • Size of remote file: 202 kB
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
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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">&lt;/&gt;</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&&gt(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&&gt(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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},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?"!["+t+"]("+r+(i?' "'+i+'"':"")+")":""}},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 = `![${alt}](${url})`;
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![${altText}](${dataUrl})\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
+ }