Merge pull request #25 from gamosoft/features/mermaid-support
Features/mermaid support
This commit is contained in:
commit
7a51d2f01f
|
|
@ -7,6 +7,32 @@ from typing import List, Dict
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def parse_theme_metadata(theme_path: Path) -> Dict[str, str]:
|
||||||
|
"""Parse theme metadata from CSS file comments"""
|
||||||
|
metadata = {
|
||||||
|
"type": "dark" # Default to dark for backward compatibility
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(theme_path, 'r', encoding='utf-8') as f:
|
||||||
|
# Read first few lines to find metadata
|
||||||
|
for i, line in enumerate(f):
|
||||||
|
if i > 10: # Only check first 10 lines
|
||||||
|
break
|
||||||
|
|
||||||
|
# Look for @theme-type metadata
|
||||||
|
if '@theme-type:' in line:
|
||||||
|
# Extract the value (light or dark)
|
||||||
|
match = re.search(r'@theme-type:\s*(light|dark)', line)
|
||||||
|
if match:
|
||||||
|
metadata["type"] = match.group(1)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error parsing theme metadata from {theme_path}: {e}")
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
def get_available_themes(themes_dir: str) -> List[Dict[str, str]]:
|
def get_available_themes(themes_dir: str) -> List[Dict[str, str]]:
|
||||||
"""Get all available themes from the themes directory"""
|
"""Get all available themes from the themes directory"""
|
||||||
themes_path = Path(themes_dir)
|
themes_path = Path(themes_dir)
|
||||||
|
|
@ -30,9 +56,13 @@ def get_available_themes(themes_dir: str) -> List[Dict[str, str]]:
|
||||||
theme_name = theme_file.stem.replace("-", " ").replace("_", " ").title()
|
theme_name = theme_file.stem.replace("-", " ").replace("_", " ").title()
|
||||||
icon = theme_icons.get(theme_file.stem, "🎨")
|
icon = theme_icons.get(theme_file.stem, "🎨")
|
||||||
|
|
||||||
|
# Parse theme metadata
|
||||||
|
metadata = parse_theme_metadata(theme_file)
|
||||||
|
|
||||||
themes.append({
|
themes.append({
|
||||||
"id": theme_file.stem,
|
"id": theme_file.stem,
|
||||||
"name": f"{icon} {theme_name}",
|
"name": f"{icon} {theme_name}",
|
||||||
|
"type": metadata["type"], # Add theme type (light/dark)
|
||||||
"builtin": False
|
"builtin": False
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
- **Undo/Redo** - Ctrl+Z / Ctrl+Y support
|
- **Undo/Redo** - Ctrl+Z / Ctrl+Y support
|
||||||
- **Syntax highlighting** for code blocks (50+ languages)
|
- **Syntax highlighting** for code blocks (50+ languages)
|
||||||
- **Copy code blocks** - One-click copy button on hover
|
- **Copy code blocks** - One-click copy button on hover
|
||||||
- **LaTeX/Math rendering** - Beautiful mathematical equations with MathJax
|
- **LaTeX/Math rendering** - Beautiful mathematical equations with MathJax (see [MATHJAX.md](MATHJAX.md))
|
||||||
|
- **Mermaid diagrams** - Create flowcharts, sequence diagrams, and more (see [MERMAID.md](MERMAID.md))
|
||||||
- **HTML Export** - Export notes as standalone HTML files
|
- **HTML Export** - Export notes as standalone HTML files
|
||||||
|
|
||||||
### Organization
|
### Organization
|
||||||
|
|
@ -106,6 +107,31 @@ $$
|
||||||
|
|
||||||
📄 **See the [MATHJAX](MATHJAX.md) note for more examples and syntax reference.**
|
📄 **See the [MATHJAX](MATHJAX.md) note for more examples and syntax reference.**
|
||||||
|
|
||||||
|
## 📊 Mermaid Diagrams
|
||||||
|
|
||||||
|
### Visual Diagrams
|
||||||
|
- **Flowcharts** - Process flows and decision trees
|
||||||
|
- **Sequence diagrams** - System interactions over time
|
||||||
|
- **Class diagrams** - UML class relationships
|
||||||
|
- **State diagrams** - State machines and transitions
|
||||||
|
- **Gantt charts** - Project timelines
|
||||||
|
- **Pie charts** - Data visualization
|
||||||
|
- **Git graphs** - Branch and commit history
|
||||||
|
- **Theme support** - Adapts to your theme
|
||||||
|
|
||||||
|
### Example
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Start] --> B{Is it working?}
|
||||||
|
B -->|Yes| C[Great!]
|
||||||
|
B -->|No| D[Debug]
|
||||||
|
D --> B
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
📄 **See the [MERMAID](MERMAID.md) note for diagram examples and syntax reference.**
|
||||||
|
|
||||||
## ⚡ Keyboard Shortcuts
|
## ⚡ Keyboard Shortcuts
|
||||||
|
|
||||||
| Windows/Linux | Mac | Action |
|
| Windows/Linux | Mac | Action |
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,376 @@
|
||||||
|
# Mermaid Diagrams
|
||||||
|
|
||||||
|
NoteDiscovery supports **Mermaid** diagrams directly in your markdown notes! Mermaid lets you create diagrams and visualizations using text-based definitions, making it easy to version control and collaborate.
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
Simply create a code block with the language set to `mermaid`:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Start] --> B{Is it working?}
|
||||||
|
B -->|Yes| C[Great!]
|
||||||
|
B -->|No| D[Debug]
|
||||||
|
D --> B
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
## Basic Examples
|
||||||
|
|
||||||
|
### Flowchart
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A[Square Rect] --> B((Circle))
|
||||||
|
A --> C(Round Rect)
|
||||||
|
B --> D{Rhombus}
|
||||||
|
C --> D
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A[Square Rect] --> B((Circle))
|
||||||
|
A --> C(Round Rect)
|
||||||
|
B --> D{Rhombus}
|
||||||
|
C --> D
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sequence Diagram
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
Alice->>John: Hello John, how are you?
|
||||||
|
John-->>Alice: Great!
|
||||||
|
Alice-)John: See you later!
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
Alice->>John: Hello John, how are you?
|
||||||
|
John-->>Alice: Great!
|
||||||
|
Alice-)John: See you later!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Class Diagram
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
Animal <|-- Duck
|
||||||
|
Animal <|-- Fish
|
||||||
|
Animal : +int age
|
||||||
|
Animal : +String gender
|
||||||
|
Animal: +isMammal()
|
||||||
|
class Duck{
|
||||||
|
+String beakColor
|
||||||
|
+swim()
|
||||||
|
+quack()
|
||||||
|
}
|
||||||
|
class Fish{
|
||||||
|
-int sizeInFeet
|
||||||
|
-canEat()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
Animal <|-- Duck
|
||||||
|
Animal <|-- Fish
|
||||||
|
Animal : +int age
|
||||||
|
Animal : +String gender
|
||||||
|
Animal: +isMammal()
|
||||||
|
class Duck{
|
||||||
|
+String beakColor
|
||||||
|
+swim()
|
||||||
|
+quack()
|
||||||
|
}
|
||||||
|
class Fish{
|
||||||
|
-int sizeInFeet
|
||||||
|
-canEat()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### State Diagram
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Still
|
||||||
|
Still --> [*]
|
||||||
|
Still --> Moving
|
||||||
|
Moving --> Still
|
||||||
|
Moving --> Crash
|
||||||
|
Crash --> [*]
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Still
|
||||||
|
Still --> [*]
|
||||||
|
Still --> Moving
|
||||||
|
Moving --> Still
|
||||||
|
Moving --> Crash
|
||||||
|
Crash --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gantt Chart
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Project Timeline
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
section Planning
|
||||||
|
Research :a1, 2024-01-01, 30d
|
||||||
|
Design :after a1, 20d
|
||||||
|
section Development
|
||||||
|
Backend :2024-02-01, 40d
|
||||||
|
Frontend :2024-02-15, 35d
|
||||||
|
section Testing
|
||||||
|
Integration Tests :2024-03-20, 15d
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Project Timeline
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
section Planning
|
||||||
|
Research :a1, 2024-01-01, 30d
|
||||||
|
Design :after a1, 20d
|
||||||
|
section Development
|
||||||
|
Backend :2024-02-01, 40d
|
||||||
|
Frontend :2024-02-15, 35d
|
||||||
|
section Testing
|
||||||
|
Integration Tests :2024-03-20, 15d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Entity Relationship Diagram
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
CUSTOMER ||--o{ ORDER : places
|
||||||
|
ORDER ||--|{ LINE-ITEM : contains
|
||||||
|
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
|
||||||
|
|
||||||
|
CUSTOMER {
|
||||||
|
string name
|
||||||
|
string email
|
||||||
|
string phone
|
||||||
|
}
|
||||||
|
ORDER {
|
||||||
|
int orderNumber
|
||||||
|
date orderDate
|
||||||
|
string status
|
||||||
|
}
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
CUSTOMER ||--o{ ORDER : places
|
||||||
|
ORDER ||--|{ LINE-ITEM : contains
|
||||||
|
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
|
||||||
|
|
||||||
|
CUSTOMER {
|
||||||
|
string name
|
||||||
|
string email
|
||||||
|
string phone
|
||||||
|
}
|
||||||
|
ORDER {
|
||||||
|
int orderNumber
|
||||||
|
date orderDate
|
||||||
|
string status
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Pie Chart
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
pie title Pet Preferences
|
||||||
|
"Dogs" : 45
|
||||||
|
"Cats" : 30
|
||||||
|
"Birds" : 15
|
||||||
|
"Fish" : 10
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
pie title Pet Preferences
|
||||||
|
"Dogs" : 45
|
||||||
|
"Cats" : 30
|
||||||
|
"Birds" : 15
|
||||||
|
"Fish" : 10
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Git Graph
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
gitGraph
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
branch develop
|
||||||
|
checkout develop
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
checkout main
|
||||||
|
merge develop
|
||||||
|
commit
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gitGraph
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
branch develop
|
||||||
|
checkout develop
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
checkout main
|
||||||
|
merge develop
|
||||||
|
commit
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Journey
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
journey
|
||||||
|
title My Working Day
|
||||||
|
section Go to work
|
||||||
|
Make tea: 5: Me
|
||||||
|
Go upstairs: 3: Me
|
||||||
|
Do work: 1: Me, Cat
|
||||||
|
section Go home
|
||||||
|
Go downstairs: 5: Me
|
||||||
|
Sit down: 5: Me
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
journey
|
||||||
|
title My Working Day
|
||||||
|
section Go to work
|
||||||
|
Make tea: 5: Me
|
||||||
|
Go upstairs: 3: Me
|
||||||
|
Do work: 1: Me, Cat
|
||||||
|
section Go home
|
||||||
|
Go downstairs: 5: Me
|
||||||
|
Sit down: 5: Me
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Mindmap
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root((NoteDiscovery))
|
||||||
|
Features
|
||||||
|
Markdown
|
||||||
|
Themes
|
||||||
|
Search
|
||||||
|
Folders
|
||||||
|
Integrations
|
||||||
|
MathJax
|
||||||
|
Mermaid
|
||||||
|
Syntax Highlighting
|
||||||
|
Benefits
|
||||||
|
Fast
|
||||||
|
Simple
|
||||||
|
Offline-first
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Preview:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root((NoteDiscovery))
|
||||||
|
Features
|
||||||
|
Markdown
|
||||||
|
Themes
|
||||||
|
Search
|
||||||
|
Folders
|
||||||
|
Integrations
|
||||||
|
MathJax
|
||||||
|
Mermaid
|
||||||
|
Syntax Highlighting
|
||||||
|
Benefits
|
||||||
|
Fast
|
||||||
|
Simple
|
||||||
|
Offline-first
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Theme Support
|
||||||
|
|
||||||
|
Mermaid diagrams automatically adapt to your current NoteDiscovery theme:
|
||||||
|
- **Light themes** use the default Mermaid color scheme
|
||||||
|
- **Dark themes** use dark-optimized colors with proper contrast
|
||||||
|
- Theme changes automatically re-render all diagrams
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
1. **Keep it simple**: Start with basic diagrams and add complexity as needed
|
||||||
|
2. **Use comments**: Add `%%` for comments in your Mermaid code
|
||||||
|
3. **Test syntax**: If a diagram doesn't render, check the Mermaid [documentation](https://mermaid.js.org/)
|
||||||
|
4. **Export**: Diagrams are included when you export notes to HTML
|
||||||
|
|
||||||
|
## More Information
|
||||||
|
|
||||||
|
For the complete Mermaid syntax and more diagram types, visit the official documentation:
|
||||||
|
- [Mermaid Documentation](https://mermaid.js.org/)
|
||||||
|
- [Live Editor](https://mermaid.live/) - Test your diagrams online
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Pro Tip**: Combine Mermaid diagrams with LaTeX math expressions and code blocks for comprehensive technical documentation! 📊
|
||||||
|
|
||||||
|
|
@ -72,7 +72,32 @@ If your file is named `my-awesome-theme.css`, use `data-theme="my-awesome-theme"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. (Optional) Add a custom emoji icon
|
#### 3. Add theme type metadata (Recommended)
|
||||||
|
|
||||||
|
Add a comment at the **top of your CSS file** to indicate if your theme is light or dark:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* @theme-type: light */
|
||||||
|
/* OR */
|
||||||
|
/* @theme-type: dark */
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```css
|
||||||
|
/* @theme-type: dark */
|
||||||
|
/* My Awesome Theme - A beautiful custom theme */
|
||||||
|
|
||||||
|
:root[data-theme="my-awesome-theme"] {
|
||||||
|
/* ... your CSS variables ... */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why is this needed?**
|
||||||
|
Some features (like Mermaid diagrams, Chart.js) need to know if the background is light or dark to adjust their rendering colors accordingly. This metadata is automatically parsed by the application.
|
||||||
|
|
||||||
|
**Default behavior:** If you don't add this metadata, your theme will default to `dark` for backward compatibility.
|
||||||
|
|
||||||
|
#### 4. (Optional) Add a custom emoji icon
|
||||||
|
|
||||||
Edit `backend/themes.py` and add your theme to the `theme_icons` dictionary:
|
Edit `backend/themes.py` and add your theme to the `theme_icons` dictionary:
|
||||||
|
|
||||||
|
|
@ -85,7 +110,7 @@ theme_icons = {
|
||||||
|
|
||||||
If you skip this step, your theme will use 🎨 as the default icon.
|
If you skip this step, your theme will use 🎨 as the default icon.
|
||||||
|
|
||||||
#### 4. Restart the application
|
#### 5. Restart the application
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# If using Docker:
|
# If using Docker:
|
||||||
|
|
|
||||||
235
frontend/app.js
235
frontend/app.js
|
|
@ -59,6 +59,7 @@ function noteApp() {
|
||||||
expandedFolders: new Set(),
|
expandedFolders: new Set(),
|
||||||
draggedNote: null,
|
draggedNote: null,
|
||||||
draggedFolder: null,
|
draggedFolder: null,
|
||||||
|
dragOverFolder: null, // Track which folder is being hovered during drag
|
||||||
|
|
||||||
// Scroll sync state
|
// Scroll sync state
|
||||||
isScrolling: false,
|
isScrolling: false,
|
||||||
|
|
@ -91,6 +92,9 @@ function noteApp() {
|
||||||
// Dropdown state
|
// Dropdown state
|
||||||
showNewDropdown: false,
|
showNewDropdown: false,
|
||||||
|
|
||||||
|
// Mermaid state cache
|
||||||
|
lastMermaidTheme: null,
|
||||||
|
|
||||||
// DOM element cache (to avoid repeated querySelector calls)
|
// DOM element cache (to avoid repeated querySelector calls)
|
||||||
_domCache: {
|
_domCache: {
|
||||||
editor: null,
|
editor: null,
|
||||||
|
|
@ -294,6 +298,32 @@ function noteApp() {
|
||||||
highlightTheme.href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css';
|
highlightTheme.href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-render Mermaid diagrams with new theme if there's a current note
|
||||||
|
if (this.currentNote) {
|
||||||
|
// Small delay to allow theme CSS to load
|
||||||
|
setTimeout(() => {
|
||||||
|
// Clear existing Mermaid renders
|
||||||
|
const previewContent = document.querySelector('.markdown-preview');
|
||||||
|
if (previewContent) {
|
||||||
|
const mermaidContainers = previewContent.querySelectorAll('.mermaid-rendered');
|
||||||
|
mermaidContainers.forEach(container => {
|
||||||
|
// Replace with the original code block for re-rendering
|
||||||
|
const parent = container.parentElement;
|
||||||
|
if (parent && container.dataset.originalCode) {
|
||||||
|
const pre = document.createElement('pre');
|
||||||
|
const code = document.createElement('code');
|
||||||
|
code.className = 'language-mermaid';
|
||||||
|
code.textContent = container.dataset.originalCode;
|
||||||
|
pre.appendChild(code);
|
||||||
|
parent.replaceChild(pre, container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Re-render with new theme
|
||||||
|
this.renderMermaid();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load theme:', error);
|
console.error('Failed to load theme:', error);
|
||||||
}
|
}
|
||||||
|
|
@ -412,15 +442,25 @@ function noteApp() {
|
||||||
<div
|
<div
|
||||||
draggable="true"
|
draggable="true"
|
||||||
x-data="{}"
|
x-data="{}"
|
||||||
@dragstart="onFolderDragStart('${folder.path.replace(/'/g, "\\'")}' )"
|
@dragstart="onFolderDragStart('${folder.path.replace(/'/g, "\\'")}', $event)"
|
||||||
@dragend="onFolderDragEnd()"
|
@dragend="onFolderDragEnd()"
|
||||||
@dragover.prevent
|
@dragover.prevent="dragOverFolder = '${folder.path.replace(/'/g, "\\'")}'"
|
||||||
@drop.stop="onFolderDrop('${folder.path.replace(/'/g, "\\'")}')"
|
@dragenter.prevent="dragOverFolder = '${folder.path.replace(/'/g, "\\'")}'"
|
||||||
class="folder-item px-2 py-2 mb-1 text-sm rounded transition-all relative"
|
@dragleave="dragOverFolder = null"
|
||||||
|
@drop.stop="onFolderDrop('${folder.path.replace(/'/g, "\\'")}' )"
|
||||||
|
class="folder-item px-3 py-3 mb-1 text-sm rounded transition-all relative"
|
||||||
style="color: var(--text-primary); cursor: pointer;"
|
style="color: var(--text-primary); cursor: pointer;"
|
||||||
:class="draggedNote || draggedFolder ? 'border-2 border-dashed border-accent-primary bg-accent-light' : 'border-2 border-transparent'"
|
:class="{
|
||||||
|
'border-2 border-dashed bg-accent-light': (draggedNote || draggedFolder) && dragOverFolder === '${folder.path.replace(/'/g, "\\'")}',
|
||||||
|
'border-2 border-dashed': (draggedNote || draggedFolder) && dragOverFolder !== '${folder.path.replace(/'/g, "\\'")}',
|
||||||
|
'border-2 border-transparent': !draggedNote && !draggedFolder
|
||||||
|
}"
|
||||||
|
:style="{
|
||||||
|
'border-color': (draggedNote || draggedFolder) && dragOverFolder === '${folder.path.replace(/'/g, "\\'")}' ? 'var(--accent-primary)' : 'var(--border-secondary)',
|
||||||
|
'background-color': dragOverFolder === '${folder.path.replace(/'/g, "\\'")}' ? 'var(--accent-light)' : ''
|
||||||
|
}"
|
||||||
@mouseover="if(!draggedNote && !draggedFolder) $el.style.backgroundColor='var(--bg-hover)'"
|
@mouseover="if(!draggedNote && !draggedFolder) $el.style.backgroundColor='var(--bg-hover)'"
|
||||||
@mouseout="if(!draggedNote && !draggedFolder) $el.style.backgroundColor='transparent'"
|
@mouseout="if(!draggedNote && !draggedFolder && dragOverFolder !== '${folder.path.replace(/'/g, "\\'")}' ) $el.style.backgroundColor='transparent'"
|
||||||
@click="toggleFolder('${folder.path.replace(/'/g, "\\'")}')"
|
@click="toggleFolder('${folder.path.replace(/'/g, "\\'")}')"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
|
|
@ -496,7 +536,7 @@ function noteApp() {
|
||||||
@dragstart="onNoteDragStart('${note.path.replace(/'/g, "\\'")}', $event)"
|
@dragstart="onNoteDragStart('${note.path.replace(/'/g, "\\'")}', $event)"
|
||||||
@dragend="onNoteDragEnd()"
|
@dragend="onNoteDragEnd()"
|
||||||
@click="loadNote('${note.path.replace(/'/g, "\\'")}')"
|
@click="loadNote('${note.path.replace(/'/g, "\\'")}')"
|
||||||
class="note-item px-3 py-2 mb-1 text-sm rounded relative"
|
class="note-item px-3 py-2 mb-1 text-sm rounded relative border-2 border-transparent"
|
||||||
style="${isCurrentNote ? 'background-color: var(--accent-light); color: var(--accent-primary);' : 'color: var(--text-primary);'} cursor: pointer;"
|
style="${isCurrentNote ? 'background-color: var(--accent-light); color: var(--accent-primary);' : 'color: var(--text-primary);'} cursor: pointer;"
|
||||||
@mouseover="if('${note.path}' !== currentNote) $el.style.backgroundColor='var(--bg-hover)'"
|
@mouseover="if('${note.path}' !== currentNote) $el.style.backgroundColor='var(--bg-hover)'"
|
||||||
@mouseout="if('${note.path}' !== currentNote) $el.style.backgroundColor='transparent'"
|
@mouseout="if('${note.path}' !== currentNote) $el.style.backgroundColor='transparent'"
|
||||||
|
|
@ -636,12 +676,21 @@ function noteApp() {
|
||||||
// Move mode: drag to move note
|
// Move mode: drag to move note
|
||||||
this.draggedNote = notePath;
|
this.draggedNote = notePath;
|
||||||
event.dataTransfer.effectAllowed = 'move';
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
|
// Make drag image semi-transparent
|
||||||
|
if (event.target) {
|
||||||
|
event.target.style.opacity = '0.5';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onNoteDragEnd() {
|
onNoteDragEnd() {
|
||||||
this.draggedNote = null;
|
this.draggedNote = null;
|
||||||
this.draggedNoteForLink = null;
|
this.draggedNoteForLink = null;
|
||||||
|
this.dragOverFolder = null;
|
||||||
|
// Reset opacity of all note items
|
||||||
|
document.querySelectorAll('.note-item').forEach(el => {
|
||||||
|
el.style.opacity = '1';
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Handle dragover on editor to show cursor position
|
// Handle dragover on editor to show cursor position
|
||||||
|
|
@ -747,12 +796,21 @@ function noteApp() {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Folder drag handlers
|
// Folder drag handlers
|
||||||
onFolderDragStart(folderPath) {
|
onFolderDragStart(folderPath, event) {
|
||||||
this.draggedFolder = folderPath;
|
this.draggedFolder = folderPath;
|
||||||
|
// Make drag image semi-transparent
|
||||||
|
if (event && event.target) {
|
||||||
|
event.target.style.opacity = '0.5';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onFolderDragEnd() {
|
onFolderDragEnd() {
|
||||||
this.draggedFolder = null;
|
this.draggedFolder = null;
|
||||||
|
this.dragOverFolder = null;
|
||||||
|
// Reset opacity of all folder items
|
||||||
|
document.querySelectorAll('.folder-item').forEach(el => {
|
||||||
|
el.style.opacity = '1';
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async onFolderDrop(targetFolderPath) {
|
async onFolderDrop(targetFolderPath) {
|
||||||
|
|
@ -1635,6 +1693,102 @@ function noteApp() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Render Mermaid diagrams
|
||||||
|
async renderMermaid() {
|
||||||
|
if (typeof window.mermaid === 'undefined') {
|
||||||
|
console.warn('Mermaid not loaded yet');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use requestAnimationFrame for better performance than setTimeout
|
||||||
|
requestAnimationFrame(async () => {
|
||||||
|
const previewContent = document.querySelector('.markdown-preview');
|
||||||
|
if (!previewContent) return;
|
||||||
|
|
||||||
|
// Get the appropriate theme based on current app theme
|
||||||
|
const themeType = this.getThemeType();
|
||||||
|
const mermaidTheme = themeType === 'light' ? 'default' : 'dark';
|
||||||
|
|
||||||
|
// Only reinitialize if theme changed (performance optimization)
|
||||||
|
if (this.lastMermaidTheme !== mermaidTheme) {
|
||||||
|
window.mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
theme: mermaidTheme,
|
||||||
|
securityLevel: 'strict', // Use strict for better security
|
||||||
|
fontFamily: 'inherit'
|
||||||
|
});
|
||||||
|
this.lastMermaidTheme = mermaidTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all code blocks with language 'mermaid'
|
||||||
|
const mermaidBlocks = previewContent.querySelectorAll('pre code.language-mermaid');
|
||||||
|
|
||||||
|
// Early return if no diagrams to render
|
||||||
|
if (mermaidBlocks.length === 0) return;
|
||||||
|
|
||||||
|
for (let i = 0; i < mermaidBlocks.length; i++) {
|
||||||
|
const block = mermaidBlocks[i];
|
||||||
|
const pre = block.parentElement;
|
||||||
|
|
||||||
|
// Skip if already rendered (performance optimization)
|
||||||
|
if (pre.querySelector('.mermaid-rendered')) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const code = block.textContent;
|
||||||
|
const id = `mermaid-diagram-${Date.now()}-${i}`;
|
||||||
|
|
||||||
|
// Render the diagram
|
||||||
|
const { svg } = await window.mermaid.render(id, code);
|
||||||
|
|
||||||
|
// Create a container for the rendered diagram
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'mermaid-rendered';
|
||||||
|
container.style.cssText = 'background-color: transparent; padding: 20px; text-align: center; overflow-x: auto;';
|
||||||
|
container.innerHTML = svg;
|
||||||
|
// Store original code for theme re-rendering
|
||||||
|
container.dataset.originalCode = code;
|
||||||
|
|
||||||
|
// Replace the code block with the rendered diagram
|
||||||
|
pre.parentElement.replaceChild(container, pre);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Mermaid rendering error:', error);
|
||||||
|
// Add error indicator to the code block
|
||||||
|
const errorMsg = document.createElement('div');
|
||||||
|
errorMsg.style.cssText = 'color: var(--error); padding: 10px; border-left: 3px solid var(--error); margin-top: 10px;';
|
||||||
|
errorMsg.textContent = `⚠️ Mermaid diagram error: ${error.message}`;
|
||||||
|
pre.parentElement.insertBefore(errorMsg, pre.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get current theme type (light or dark)
|
||||||
|
// Returns: 'light' or 'dark'
|
||||||
|
// Used by features that need to adapt to theme brightness (e.g., Mermaid diagrams, Chart.js)
|
||||||
|
getThemeType() {
|
||||||
|
// Handle system theme
|
||||||
|
if (this.currentTheme === 'system') {
|
||||||
|
const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
return isDark ? 'dark' : 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get theme type from loaded theme metadata
|
||||||
|
const currentThemeData = this.availableThemes.find(t => t.id === this.currentTheme);
|
||||||
|
if (currentThemeData && currentThemeData.type) {
|
||||||
|
// Use metadata from theme file (light or dark)
|
||||||
|
return currentThemeData.type; // Already 'light' or 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility: fallback to hardcoded map if metadata not available
|
||||||
|
const fallbackMap = {
|
||||||
|
'light': 'light',
|
||||||
|
'vs-blue': 'light'
|
||||||
|
};
|
||||||
|
|
||||||
|
return fallbackMap[this.currentTheme] || 'dark';
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// Computed property for rendered markdown
|
// Computed property for rendered markdown
|
||||||
get renderedMarkdown() {
|
get renderedMarkdown() {
|
||||||
if (!this.noteContent) return '<p style="color: var(--text-tertiary);">Nothing to preview yet...</p>';
|
if (!this.noteContent) return '<p style="color: var(--text-tertiary);">Nothing to preview yet...</p>';
|
||||||
|
|
@ -1690,12 +1844,17 @@ function noteApp() {
|
||||||
// Trigger MathJax rendering after DOM updates
|
// Trigger MathJax rendering after DOM updates
|
||||||
this.typesetMath();
|
this.typesetMath();
|
||||||
|
|
||||||
|
// Render Mermaid diagrams
|
||||||
|
this.renderMermaid();
|
||||||
|
|
||||||
// Apply syntax highlighting and add copy buttons to code blocks
|
// Apply syntax highlighting and add copy buttons to code blocks
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Use cached reference if available, otherwise query
|
// Use cached reference if available, otherwise query
|
||||||
const previewEl = this._domCache.previewContent || document.querySelector('.markdown-preview');
|
const previewEl = this._domCache.previewContent || document.querySelector('.markdown-preview');
|
||||||
if (previewEl) {
|
if (previewEl) {
|
||||||
previewEl.querySelectorAll('pre code').forEach((block) => {
|
// Exclude code blocks that are rendered by other tools (e.g., Mermaid diagrams)
|
||||||
|
// Note: MathJax uses $$...$$ delimiters (not code blocks) so no exclusion needed
|
||||||
|
previewEl.querySelectorAll('pre code:not(.language-mermaid)').forEach((block) => {
|
||||||
// Apply syntax highlighting
|
// Apply syntax highlighting
|
||||||
if (!block.classList.contains('hljs')) {
|
if (!block.classList.contains('hljs')) {
|
||||||
hljs.highlightElement(block);
|
hljs.highlightElement(block);
|
||||||
|
|
@ -2228,19 +2387,32 @@ function noteApp() {
|
||||||
|
|
||||||
for (const sheet of styleSheets) {
|
for (const sheet of styleSheets) {
|
||||||
try {
|
try {
|
||||||
|
// Skip external stylesheets (CDN resources) to avoid CORS errors
|
||||||
|
// We link them directly in the exported HTML anyway
|
||||||
|
if (sheet.href && (sheet.href.startsWith('http://') || sheet.href.startsWith('https://'))) {
|
||||||
|
const currentOrigin = window.location.origin;
|
||||||
|
const sheetURL = new URL(sheet.href);
|
||||||
|
if (sheetURL.origin !== currentOrigin) {
|
||||||
|
// Skip cross-origin stylesheets (they're linked directly in export)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const rules = Array.from(sheet.cssRules || []);
|
const rules = Array.from(sheet.cssRules || []);
|
||||||
for (const rule of rules) {
|
for (const rule of rules) {
|
||||||
const cssText = rule.cssText;
|
const cssText = rule.cssText;
|
||||||
// Include rules that target markdown-preview or mjx-container
|
// Include rules that target markdown-preview, mjx-container, or mermaid-rendered
|
||||||
if (cssText.includes('.markdown-preview') ||
|
if (cssText.includes('.markdown-preview') ||
|
||||||
cssText.includes('mjx-container') ||
|
cssText.includes('mjx-container') ||
|
||||||
cssText.includes('.MathJax')) {
|
cssText.includes('.MathJax') ||
|
||||||
|
cssText.includes('.mermaid-rendered')) {
|
||||||
markdownStyles += cssText + '\n';
|
markdownStyles += cssText + '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Skip stylesheets that can't be accessed (CORS)
|
// Gracefully skip stylesheets that can't be accessed
|
||||||
console.warn('Could not access stylesheet:', sheet.href, e);
|
// (This should rarely happen now that we skip external stylesheets)
|
||||||
|
console.debug('Skipping stylesheet:', sheet.href);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2271,8 +2443,8 @@ function noteApp() {
|
||||||
startup: {
|
startup: {
|
||||||
pageReady: () => {
|
pageReady: () => {
|
||||||
return MathJax.startup.defaultPageReady().then(() => {
|
return MathJax.startup.defaultPageReady().then(() => {
|
||||||
// Highlight code blocks after MathJax is done
|
// Highlight code blocks after MathJax is done (exclude diagram renderers)
|
||||||
document.querySelectorAll('pre code').forEach((block) => {
|
document.querySelectorAll('pre code:not(.language-mermaid)').forEach((block) => {
|
||||||
hljs.highlightElement(block);
|
hljs.highlightElement(block);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -2282,6 +2454,39 @@ function noteApp() {
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
||||||
|
|
||||||
|
<!-- Mermaid.js for diagrams (if used in note) -->
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||||
|
const isDark = ${this.getThemeType() === 'dark'};
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
theme: isDark ? 'dark' : 'default',
|
||||||
|
securityLevel: 'strict',
|
||||||
|
fontFamily: 'inherit'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render any Mermaid code blocks
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
const mermaidBlocks = document.querySelectorAll('pre code.language-mermaid');
|
||||||
|
for (let i = 0; i < mermaidBlocks.length; i++) {
|
||||||
|
const block = mermaidBlocks[i];
|
||||||
|
const pre = block.parentElement;
|
||||||
|
try {
|
||||||
|
const code = block.textContent;
|
||||||
|
const id = 'mermaid-diagram-' + i;
|
||||||
|
const { svg } = await mermaid.render(id, code);
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'mermaid-rendered';
|
||||||
|
container.style.cssText = 'background-color: transparent; padding: 20px; text-align: center; overflow-x: auto;';
|
||||||
|
container.innerHTML = svg;
|
||||||
|
pre.parentElement.replaceChild(container, pre);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Mermaid rendering error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Theme CSS */
|
/* Theme CSS */
|
||||||
${themeCss}
|
${themeCss}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,13 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/http.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/http.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Mermaid.js for diagram rendering -->
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||||
|
// Note: Mermaid is initialized dynamically by app.js renderMermaid() based on current theme
|
||||||
|
window.mermaid = mermaid;
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Theme styles will be loaded dynamically -->
|
<!-- Theme styles will be loaded dynamically -->
|
||||||
<link rel="stylesheet" id="theme-stylesheet" href="">
|
<link rel="stylesheet" id="theme-stylesheet" href="">
|
||||||
|
|
||||||
|
|
@ -321,6 +328,12 @@
|
||||||
margin: 0 0.2rem;
|
margin: 0 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mermaid diagram styling - minimal, let Mermaid handle its own rendering */
|
||||||
|
.markdown-preview .mermaid-rendered svg {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/* Editor styling */
|
/* Editor styling */
|
||||||
.editor-textarea {
|
.editor-textarea {
|
||||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
|
@ -337,6 +350,7 @@
|
||||||
/* Folder tree styling */
|
/* Folder tree styling */
|
||||||
.folder-item {
|
.folder-item {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.folder-item:hover {
|
.folder-item:hover {
|
||||||
|
|
@ -349,6 +363,29 @@
|
||||||
|
|
||||||
.note-item {
|
.note-item {
|
||||||
padding-left: 1.5rem;
|
padding-left: 1.5rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drag and drop improvements */
|
||||||
|
.note-item[draggable="true"],
|
||||||
|
.folder-item[draggable="true"] {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item[draggable="true"]:active,
|
||||||
|
.folder-item[draggable="true"]:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth transitions for drag states */
|
||||||
|
.folder-item,
|
||||||
|
.note-item {
|
||||||
|
transition: opacity 0.2s ease, transform 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Better drop zone visibility */
|
||||||
|
.folder-item:has(+ .folder-contents) {
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hover-buttons {
|
.hover-buttons {
|
||||||
|
|
@ -872,21 +909,28 @@
|
||||||
>⊟</button>
|
>⊟</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs" style="color: var(--text-tertiary); opacity: 0.8;">
|
<!-- Dual-purpose: hint text OR root drop zone when dragging -->
|
||||||
💡 Drag=Move | <kbd style="padding: 1px 4px; border-radius: 3px; background: var(--bg-tertiary); font-size: 10px;">Ctrl</kbd>+Drag=Link
|
<div
|
||||||
</div>
|
class="text-xs px-2 py-1 mb-2 rounded transition-all font-medium text-center"
|
||||||
</div>
|
:class="{
|
||||||
|
'border-2 border-dashed bg-accent-light': (draggedNote || draggedFolder) && dragOverFolder === '',
|
||||||
<!-- Root drop zone - only visible when dragging -->
|
'border-2 border-dashed': (draggedNote || draggedFolder) && dragOverFolder !== '',
|
||||||
<div
|
'border-2 border-transparent': !draggedNote && !draggedFolder
|
||||||
x-show="draggedNote || draggedFolder"
|
}"
|
||||||
@dragover.prevent
|
:style="{
|
||||||
@drop="onFolderDrop('')"
|
'color': (draggedNote || draggedFolder) ? (dragOverFolder === '' ? 'var(--accent-primary)' : 'var(--text-secondary)') : 'var(--text-tertiary)',
|
||||||
class="mb-3 px-3 py-2 rounded-lg border-2 border-dashed transition-all"
|
'opacity': (draggedNote || draggedFolder) ? '1' : '0.8',
|
||||||
style="border-color: var(--accent-primary); background-color: var(--accent-light);"
|
'border-color': (draggedNote || draggedFolder) && dragOverFolder === '' ? 'var(--accent-primary)' : 'var(--border-secondary)',
|
||||||
>
|
'background-color': dragOverFolder === '' ? 'var(--accent-light)' : ''
|
||||||
<div class="text-xs text-center font-medium" style="color: var(--accent-primary);">
|
}"
|
||||||
📂 Drop here to move to root folder
|
@dragover.prevent="if(draggedNote || draggedFolder) dragOverFolder = '';"
|
||||||
|
@dragenter.prevent="if(draggedNote || draggedFolder) dragOverFolder = '';"
|
||||||
|
@dragleave="if(draggedNote || draggedFolder) dragOverFolder = null;"
|
||||||
|
@drop="if(draggedNote || draggedFolder) onFolderDrop('')"
|
||||||
|
style="height: 28px; line-height: 20px; display: flex; align-items: center; justify-content: center;"
|
||||||
|
>
|
||||||
|
<span x-show="!draggedNote && !draggedFolder">💡 Drag=Move | <kbd style="padding: 1px 4px; border-radius: 3px; background: var(--bg-tertiary); font-size: 10px;">Ctrl</kbd>+Drag=Link</span>
|
||||||
|
<span x-show="draggedNote || draggedFolder">📂 Root folder</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -902,7 +946,7 @@
|
||||||
@dragstart="onNoteDragStart(note.path, $event)"
|
@dragstart="onNoteDragStart(note.path, $event)"
|
||||||
@dragend="onNoteDragEnd()"
|
@dragend="onNoteDragEnd()"
|
||||||
@click="loadNote(note.path)"
|
@click="loadNote(note.path)"
|
||||||
class="note-item px-3 py-2 mb-1 text-sm rounded relative"
|
class="note-item px-3 py-2 mb-1 text-sm rounded relative border-2 border-transparent"
|
||||||
:style="(currentNote === note.path ? 'background-color: var(--accent-light); color: var(--accent-primary);' : 'color: var(--text-primary);') + (draggedNote || draggedFolder ? ' cursor: grabbing;' : ' cursor: pointer;')"
|
:style="(currentNote === note.path ? 'background-color: var(--accent-light); color: var(--accent-primary);' : 'color: var(--text-primary);') + (draggedNote || draggedFolder ? ' cursor: grabbing;' : ' cursor: pointer;')"
|
||||||
@mouseover="if(currentNote !== note.path) $el.style.backgroundColor='var(--bg-hover)'"
|
@mouseover="if(currentNote !== note.path) $el.style.backgroundColor='var(--bg-hover)'"
|
||||||
@mouseout="if(currentNote !== note.path) $el.style.backgroundColor='transparent'"
|
@mouseout="if(currentNote !== note.path) $el.style.backgroundColor='transparent'"
|
||||||
|
|
@ -948,16 +992,6 @@
|
||||||
@mouseout="if(!isResizing) $el.style.backgroundColor='transparent'"
|
@mouseout="if(!isResizing) $el.style.backgroundColor='transparent'"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- Drag Mode Indicator (floating) -->
|
|
||||||
<div
|
|
||||||
x-show="draggedNote || draggedNoteForLink"
|
|
||||||
class="fixed top-4 left-1/2 transform -translate-x-1/2 z-50 px-4 py-2 rounded-lg shadow-lg"
|
|
||||||
:style="'background-color: var(--accent-primary); color: white; font-weight: 600;'"
|
|
||||||
style="pointer-events: none;"
|
|
||||||
>
|
|
||||||
<span x-show="draggedNoteForLink">🔗 Link Mode - Drop in editor to create link</span>
|
|
||||||
<span x-show="draggedNote && !draggedNoteForLink">📦 Move Mode - Drop on folder to move</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Main Content Area -->
|
<!-- Main Content Area -->
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<div class="flex-1 flex flex-col overflow-hidden">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/* Cobalt2 Theme */
|
/* Cobalt2 Theme */
|
||||||
/* Inspired by Wes Bos's Cobalt2 theme for VS Code */
|
/* Inspired by Wes Bos's Cobalt2 theme for VS Code */
|
||||||
|
/* @theme-type: dark */
|
||||||
|
|
||||||
:root[data-theme="cobalt2"] {
|
:root[data-theme="cobalt2"] {
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
/* Dark Theme */
|
/* Dark Theme */
|
||||||
|
/* @theme-type: dark */
|
||||||
:root[data-theme="dark"] {
|
:root[data-theme="dark"] {
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
--bg-primary: #1f2937;
|
--bg-primary: #1f2937;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/* Dracula Theme - Inspired by Dracula Color Scheme */
|
/* Dracula Theme - Inspired by Dracula Color Scheme */
|
||||||
/* https://draculatheme.com/ */
|
/* https://draculatheme.com/ */
|
||||||
|
/* @theme-type: dark */
|
||||||
|
|
||||||
:root[data-theme="dracula"] {
|
:root[data-theme="dracula"] {
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
/* Light Theme - Default */
|
/* Light Theme - Default */
|
||||||
|
/* @theme-type: light */
|
||||||
:root[data-theme="light"] {
|
:root[data-theme="light"] {
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
--bg-primary: #ffffff;
|
--bg-primary: #ffffff;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/* Monokai Theme - Classic code editor theme */
|
/* Monokai Theme - Classic code editor theme */
|
||||||
/* Inspired by Sublime Text's Monokai */
|
/* Inspired by Sublime Text's Monokai */
|
||||||
|
/* @theme-type: dark */
|
||||||
|
|
||||||
:root[data-theme="monokai"] {
|
:root[data-theme="monokai"] {
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/* Nord Theme - Arctic, north-bluish color palette */
|
/* Nord Theme - Arctic, north-bluish color palette */
|
||||||
/* https://www.nordtheme.com/ */
|
/* https://www.nordtheme.com/ */
|
||||||
|
/* @theme-type: dark */
|
||||||
|
|
||||||
:root[data-theme="nord"] {
|
:root[data-theme="nord"] {
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/* Visual Studio Blue Theme */
|
/* Visual Studio Blue Theme */
|
||||||
/* Inspired by classic Visual Studio 2012-2015 Light Blue theme */
|
/* Inspired by classic Visual Studio 2012-2015 Light Blue theme */
|
||||||
|
/* @theme-type: light */
|
||||||
|
|
||||||
:root[data-theme="vs-blue"] {
|
:root[data-theme="vs-blue"] {
|
||||||
/* Background colors */
|
/* Background colors */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/* Vue High Contrast Theme */
|
/* Vue High Contrast Theme */
|
||||||
/* Inspired by Vue Theme High Contrast - Dark with greenish tint */
|
/* Inspired by Vue Theme High Contrast - Dark with greenish tint */
|
||||||
|
/* @theme-type: dark */
|
||||||
|
|
||||||
:root[data-theme="vue-high-contrast"] {
|
:root[data-theme="vue-high-contrast"] {
|
||||||
/* Background colors - dark with green tint */
|
/* Background colors - dark with green tint */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue