Import markdown documents and mermaid diagrams into Joplin using the Joplin CLI.
Imports markdown documents and mermaid diagrams into Joplin notebooks using the CLI.
/plugin marketplace add mindmorass/reflex/plugin install reflex@mindmorass-reflexThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Import markdown documents and mermaid diagrams into Joplin using the Joplin CLI.
Joplin uses a database-backed system. Content is imported via the Joplin CLI or API. This skill focuses on CLI-based imports.
# Using npm
npm install -g joplin
# Verify installation
joplin version
# Set sync target (if needed)
joplin config sync.target 2 # Filesystem
joplin config sync.2.path /path/to/sync/folder
# Or connect to Joplin Server/Cloud
joplin config sync.target 9
joplin config sync.9.path https://your-joplin-server.com
joplin config sync.9.username your-username
joplin ls / # List top-level notebooks
joplin ls /Notebook # List notes in notebook
joplin mkbook "Notebook Name"
joplin mkbook "Parent/Child" # Nested notebook
# Import single file to notebook
joplin import /path/to/file.md --notebook "Notebook Name"
# Import directory
joplin import /path/to/folder --notebook "Notebook Name"
# Import with format specification
joplin import /path/to/file.md --format md --notebook "Notebook Name"
# Create note from stdin
echo "# Title
Content" | joplin mknote "Note Title" --notebook "Notebook Name"
# Create from file content
cat file.md | joplin mknote "Note Title" --notebook "Notebook Name"
import subprocess
def notebook_exists(notebook: str) -> bool:
"""Check if a Joplin notebook exists."""
result = subprocess.run(
["joplin", "ls", "/"],
capture_output=True,
text=True
)
notebooks = result.stdout.strip().split('
')
return notebook in notebooks
def create_notebook_if_missing(notebook: str):
"""Create notebook if it doesn't exist."""
if not notebook_exists(notebook):
subprocess.run(
["joplin", "mkbook", notebook],
check=True
)
def prepare_markdown(
title: str,
content: str,
tags: list = None
) -> str:
"""
Prepare markdown content for Joplin import.
Joplin supports YAML frontmatter for metadata.
"""
lines = [f"# {title}", ""]
if tags:
lines.extend([
"---",
f"tags: {', '.join(tags)}",
"---",
""
])
lines.append(content)
return '
'.join(lines)
import tempfile
from pathlib import Path
def write_temp_markdown(content: str, filename: str) -> Path:
"""Write content to a temporary markdown file."""
temp_dir = Path(tempfile.mkdtemp())
file_path = temp_dir / f"{filename}.md"
file_path.write_text(content, encoding='utf-8')
return file_path
def import_to_joplin(
file_path: Path,
notebook: str
) -> bool:
"""Import markdown file to Joplin notebook."""
result = subprocess.run(
[
"joplin", "import",
str(file_path),
"--notebook", notebook
],
capture_output=True,
text=True
)
if result.returncode != 0:
raise JoplinImportError(f"Import failed: {result.stderr}")
return True
def publish_to_joplin(
notebook: str,
title: str,
content: str,
tags: list = None
) -> bool:
"""
Publish markdown content to a Joplin notebook.
Args:
notebook: Target notebook name
title: Note title
content: Markdown content (can include mermaid)
tags: Optional list of tags
Returns:
True if successful
"""
# Ensure notebook exists
create_notebook_if_missing(notebook)
# Prepare content
full_content = prepare_markdown(title, content, tags)
# Write to temp file
temp_file = write_temp_markdown(full_content, title)
try:
# Import to Joplin
import_to_joplin(temp_file, notebook)
return True
finally:
# Cleanup
temp_file.unlink()
temp_file.parent.rmdir()
# Note Title
Content goes here.
## Section
More content.
# Note Title
Content here.
Joplin supports mermaid diagrams natively in markdown:
# System Architecture
```mermaid
flowchart TD
A[Client] --> B[Server]
B --> C[(Database)]
The system consists of...
## Notebook Organization
### Flat Structure
Notebooks/ ├── Research ├── Projects ├── Meetings └── Archive
### Nested Structure
Notebooks/ ├── Work/ │ ├── Project Alpha │ └── Project Beta ├── Personal/ │ ├── Notes │ └── Ideas └── Archive/
Create nested notebooks:
```bash
joplin mkbook "Work"
joplin mkbook "Work/Project Alpha"
publish_to_joplin(
notebook="Research",
title="API Design Patterns",
content="""
## Overview
Key findings from API design research.
## REST Best Practices
1. Use nouns for resources
2. Use HTTP methods correctly
3. Version your API
## GraphQL Considerations
- Schema-first design
- Query optimization
""",
tags=["api", "research", "design"]
)
publish_to_joplin(
notebook="Architecture",
title="System Overview Diagram",
content="""
## Architecture Diagram
```mermaid
flowchart TB
subgraph Frontend
A[Web App]
B[Mobile App]
end
subgraph Backend
C[API Server]
D[Worker]
end
subgraph Data
E[(PostgreSQL)]
F[(Redis)]
end
A --> C
B --> C
C --> E
C --> F
D --> E
| Component | Technology | Purpose |
|---|---|---|
| Web App | React | User interface |
| API Server | FastAPI | REST API |
| Worker | Celery | Background jobs |
| """, |
tags=["architecture", "diagram"]
)
### Publish Meeting Notes
```python
publish_to_joplin(
notebook="Meetings/2024",
title="Project Sync - Jan 15",
content="""
## Attendees
- Alice
- Bob
- Charlie
## Agenda
1. Sprint review
2. Blockers
3. Next steps
## Notes
### Sprint Review
- Feature X completed
- Bug Y in progress
### Blockers
- Waiting on API access
### Action Items
- [ ] Alice: Follow up on API access
- [ ] Bob: Complete bug fix
- [ ] Charlie: Update documentation
""",
tags=["meeting", "project-alpha"]
)
For importing multiple documents:
def batch_import(
notebook: str,
documents: list[dict]
) -> dict:
"""
Import multiple documents to Joplin.
Args:
notebook: Target notebook
documents: List of {title, content, tags} dicts
Returns:
{success: int, failed: int, errors: list}
"""
results = {"success": 0, "failed": 0, "errors": []}
for doc in documents:
try:
publish_to_joplin(
notebook=notebook,
title=doc["title"],
content=doc["content"],
tags=doc.get("tags", [])
)
results["success"] += 1
except Exception as e:
results["failed"] += 1
results["errors"].append({
"title": doc["title"],
"error": str(e)
})
return results
class JoplinError(Exception):
"""Base exception for Joplin operations."""
pass
class JoplinNotInstalledError(JoplinError):
"""Joplin CLI not found."""
pass
class JoplinImportError(JoplinError):
"""Failed to import content."""
pass
class NotebookNotFoundError(JoplinError):
"""Notebook does not exist."""
pass
def check_joplin_installed():
"""Verify Joplin CLI is available."""
result = subprocess.run(
["joplin", "version"],
capture_output=True
)
if result.returncode != 0:
raise JoplinNotInstalledError(
"Joplin CLI not found. Install with: npm install -g joplin"
)
For more control, use the Joplin Data API:
import requests
class JoplinAPI:
def __init__(self, token: str, port: int = 41184):
self.base_url = f"http://localhost:{port}"
self.token = token
def create_note(
self,
title: str,
body: str,
parent_id: str = None
) -> dict:
"""Create a note via Joplin API."""
response = requests.post(
f"{self.base_url}/notes",
params={"token": self.token},
json={
"title": title,
"body": body,
"parent_id": parent_id
}
)
response.raise_for_status()
return response.json()
Enable the API in Joplin Desktop: Options → Web Clipper → Enable
Before publishing:
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.