added print preview option
This commit is contained in:
parent
96b5fd8cfc
commit
80797f9cfe
|
|
@ -300,7 +300,8 @@ def generate_export_html(
|
||||||
title: str,
|
title: str,
|
||||||
content: str,
|
content: str,
|
||||||
theme_css: str,
|
theme_css: str,
|
||||||
is_dark: bool = False
|
is_dark: bool = False,
|
||||||
|
show_print_button: bool = False
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Generate a standalone HTML document for a note.
|
Generate a standalone HTML document for a note.
|
||||||
|
|
@ -311,6 +312,7 @@ def generate_export_html(
|
||||||
content: Raw markdown content (images should already be base64 embedded)
|
content: Raw markdown content (images should already be base64 embedded)
|
||||||
theme_css: CSS content for theming
|
theme_css: CSS content for theming
|
||||||
is_dark: Whether using a dark theme (for Mermaid/Highlight.js)
|
is_dark: Whether using a dark theme (for Mermaid/Highlight.js)
|
||||||
|
show_print_button: Whether to show a print button (for preview mode)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Complete HTML document as string
|
Complete HTML document as string
|
||||||
|
|
@ -327,6 +329,24 @@ def generate_export_html(
|
||||||
highlight_theme = 'github-dark' if is_dark else 'github'
|
highlight_theme = 'github-dark' if is_dark else 'github'
|
||||||
mermaid_theme = 'dark' if is_dark else 'default'
|
mermaid_theme = 'dark' if is_dark else 'default'
|
||||||
|
|
||||||
|
# Print toolbar HTML (only shown in preview mode)
|
||||||
|
print_toolbar_html = '''
|
||||||
|
<div class="print-toolbar">
|
||||||
|
<button onclick="window.print()" title="Print">
|
||||||
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"></path>
|
||||||
|
</svg>
|
||||||
|
Print
|
||||||
|
</button>
|
||||||
|
<button onclick="window.close()" title="Close">
|
||||||
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||||
|
</svg>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
''' if show_print_button else ''
|
||||||
|
|
||||||
html = f'''<!DOCTYPE html>
|
html = f'''<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -667,10 +687,54 @@ def generate_export_html(
|
||||||
padding: 0.5in;
|
padding: 0.5in;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}}
|
}}
|
||||||
|
.print-toolbar {{
|
||||||
|
display: none !important;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
/* Print toolbar (only shown in preview mode) */
|
||||||
|
.print-toolbar {{
|
||||||
|
position: fixed;
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
background: var(--bg-secondary, #f8f9fa);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
border: 1px solid var(--border-primary, #dee2e6);
|
||||||
|
}}
|
||||||
|
|
||||||
|
.print-toolbar button {{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid var(--border-primary, #dee2e6);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
background: var(--bg-primary, #ffffff);
|
||||||
|
color: var(--text-primary, #333333);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-family: inherit;
|
||||||
|
transition: background-color 0.15s, border-color 0.15s;
|
||||||
|
}}
|
||||||
|
|
||||||
|
.print-toolbar button:hover {{
|
||||||
|
background: var(--bg-tertiary, #e9ecef);
|
||||||
|
border-color: var(--accent-primary, #0366d6);
|
||||||
|
}}
|
||||||
|
|
||||||
|
.print-toolbar button svg {{
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
}}
|
}}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
{print_toolbar_html}
|
||||||
<div class="markdown-preview" id="content"></div>
|
<div class="markdown-preview" id="content"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
||||||
|
|
@ -1230,7 +1230,7 @@ async def remove_note(request: Request, note_path: str):
|
||||||
|
|
||||||
@api_router.get("/export/{note_path:path}", tags=["Export"])
|
@api_router.get("/export/{note_path:path}", tags=["Export"])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def export_note_to_html(request: Request, note_path: str, theme: Optional[str] = None):
|
async def export_note_to_html(request: Request, note_path: str, theme: Optional[str] = None, download: bool = True):
|
||||||
"""
|
"""
|
||||||
Export a note as a standalone HTML file.
|
Export a note as a standalone HTML file.
|
||||||
|
|
||||||
|
|
@ -1239,9 +1239,10 @@ async def export_note_to_html(request: Request, note_path: str, theme: Optional[
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
theme: Optional theme name (defaults to current theme or 'light')
|
theme: Optional theme name (defaults to current theme or 'light')
|
||||||
|
download: If true (default), returns as file download. If false, displays in browser with print button.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HTML file download
|
HTML file (download or inline based on download parameter)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
notes_dir = Path(config['storage']['notes_dir'])
|
notes_dir = Path(config['storage']['notes_dir'])
|
||||||
|
|
@ -1288,15 +1289,17 @@ async def export_note_to_html(request: Request, note_path: str, theme: Optional[
|
||||||
# Get note title
|
# Get note title
|
||||||
title = Path(note_path).stem
|
title = Path(note_path).stem
|
||||||
|
|
||||||
# Generate HTML
|
# Generate HTML (show print button only when not downloading)
|
||||||
html_content = generate_export_html(
|
html_content = generate_export_html(
|
||||||
title=title,
|
title=title,
|
||||||
content=content_with_links,
|
content=content_with_links,
|
||||||
theme_css=theme_css,
|
theme_css=theme_css,
|
||||||
is_dark=is_dark
|
is_dark=is_dark,
|
||||||
|
show_print_button=not download
|
||||||
)
|
)
|
||||||
|
|
||||||
# Return as downloadable file
|
# Return as downloadable file or inline (for print preview)
|
||||||
|
if download:
|
||||||
filename = f"{title}.html"
|
filename = f"{title}.html"
|
||||||
return Response(
|
return Response(
|
||||||
content=html_content,
|
content=html_content,
|
||||||
|
|
@ -1305,6 +1308,9 @@ async def export_note_to_html(request: Request, note_path: str, theme: Optional[
|
||||||
"Content-Disposition": f'attachment; filename="{filename}"'
|
"Content-Disposition": f'attachment; filename="{filename}"'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# Return inline for browser display (print preview)
|
||||||
|
return HTMLResponse(content=html_content)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -450,7 +450,7 @@ Returns the relationship graph between notes with link detection.
|
||||||
|
|
||||||
### Export Note as HTML
|
### Export Note as HTML
|
||||||
```http
|
```http
|
||||||
GET /api/export/{note_path}?theme={theme_name}
|
GET /api/export/{note_path}?theme={theme_name}&download={true|false}
|
||||||
```
|
```
|
||||||
|
|
||||||
Exports a note as a standalone HTML file with all dependencies embedded for offline viewing.
|
Exports a note as a standalone HTML file with all dependencies embedded for offline viewing.
|
||||||
|
|
@ -460,9 +460,11 @@ Exports a note as a standalone HTML file with all dependencies embedded for offl
|
||||||
|-----------|------|-------------|
|
|-----------|------|-------------|
|
||||||
| `note_path` | path | Path to the note (e.g., `folder/note.md`) |
|
| `note_path` | path | Path to the note (e.g., `folder/note.md`) |
|
||||||
| `theme` | query (optional) | Theme name for styling (defaults to `light`) |
|
| `theme` | query (optional) | Theme name for styling (defaults to `light`) |
|
||||||
|
| `download` | query (optional) | If `true` (default), returns as file download. If `false`, displays in browser with Print/Close buttons for print preview |
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
Returns an HTML file download with `Content-Disposition: attachment` header.
|
- `download=true`: Returns an HTML file with `Content-Disposition: attachment` header
|
||||||
|
- `download=false`: Returns inline HTML for browser display (print preview mode)
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
- Fully self-contained HTML with embedded CSS
|
- Fully self-contained HTML with embedded CSS
|
||||||
|
|
@ -473,16 +475,20 @@ Returns an HTML file download with `Content-Disposition: attachment` header.
|
||||||
- Wikilinks converted to decorative spans
|
- Wikilinks converted to decorative spans
|
||||||
- YAML frontmatter stripped
|
- YAML frontmatter stripped
|
||||||
- Responsive design with print support
|
- Responsive design with print support
|
||||||
|
- Print toolbar with Print/Close buttons (preview mode only)
|
||||||
|
|
||||||
**Rate Limit:** 30 requests/minute
|
**Rate Limit:** 30 requests/minute
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
```bash
|
```bash
|
||||||
# Export with default theme
|
# Export with default theme (downloads file)
|
||||||
curl -O http://localhost:8000/api/export/notes/Welcome.md
|
curl -O http://localhost:8000/api/export/notes/Welcome.md
|
||||||
|
|
||||||
# Export with dark theme
|
# Export with dark theme (downloads file)
|
||||||
curl -O "http://localhost:8000/api/export/docs/API.md?theme=dracula"
|
curl -O "http://localhost:8000/api/export/docs/API.md?theme=dracula"
|
||||||
|
|
||||||
|
# Print preview (open in browser)
|
||||||
|
# http://localhost:8000/api/export/docs/API.md?theme=light&download=false
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -5114,6 +5114,22 @@ function noteApp() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Open print preview in new window
|
||||||
|
printPreview() {
|
||||||
|
if (!this.currentNote || !this.noteContent) {
|
||||||
|
alert(this.t('notes.no_content'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build API URL with current theme and download=false for inline display
|
||||||
|
const currentTheme = this.currentTheme || 'light';
|
||||||
|
const encodedPath = this.currentNote.split('/').map(s => encodeURIComponent(s)).join('/');
|
||||||
|
const url = `/api/export/${encodedPath}?theme=${encodeURIComponent(currentTheme)}&download=false`;
|
||||||
|
|
||||||
|
// Open in new window/tab
|
||||||
|
window.open(url, '_blank');
|
||||||
|
},
|
||||||
|
|
||||||
// Copy current note link to clipboard
|
// Copy current note link to clipboard
|
||||||
async copyNoteLink() {
|
async copyNoteLink() {
|
||||||
if (!this.currentNote) return;
|
if (!this.currentNote) return;
|
||||||
|
|
|
||||||
|
|
@ -2542,6 +2542,18 @@
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
@click="printPreview()"
|
||||||
|
class="p-2 transition"
|
||||||
|
style="color: var(--text-secondary);"
|
||||||
|
:title="t('toolbar.print_preview')"
|
||||||
|
@mouseenter="$el.style.backgroundColor = 'var(--bg-secondary)'"
|
||||||
|
@mouseleave="$el.style.backgroundColor = 'transparent'"
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<div style="width: 1px; height: 20px; background-color: var(--border-primary);"></div>
|
<div style="width: 1px; height: 20px; background-color: var(--border-primary);"></div>
|
||||||
<button
|
<button
|
||||||
@click="copyNoteLink()"
|
@click="copyNoteLink()"
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@
|
||||||
"delete_note": "Notiz löschen",
|
"delete_note": "Notiz löschen",
|
||||||
"delete_image": "Bild löschen",
|
"delete_image": "Bild löschen",
|
||||||
"export_html": "Als HTML exportieren",
|
"export_html": "Als HTML exportieren",
|
||||||
|
"print_preview": "Druckvorschau",
|
||||||
"copy_link": "Link in Zwischenablage kopieren",
|
"copy_link": "Link in Zwischenablage kopieren",
|
||||||
"add_favorite": "Zu Favoriten hinzufügen",
|
"add_favorite": "Zu Favoriten hinzufügen",
|
||||||
"remove_favorite": "Aus Favoriten entfernen"
|
"remove_favorite": "Aus Favoriten entfernen"
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
"delete_note": "Delete note",
|
"delete_note": "Delete note",
|
||||||
"delete_image": "Delete image",
|
"delete_image": "Delete image",
|
||||||
"export_html": "Export as HTML",
|
"export_html": "Export as HTML",
|
||||||
|
"print_preview": "Print preview",
|
||||||
"copy_link": "Copy link to clipboard",
|
"copy_link": "Copy link to clipboard",
|
||||||
"add_favorite": "Add to favourites",
|
"add_favorite": "Add to favourites",
|
||||||
"remove_favorite": "Remove from favourites"
|
"remove_favorite": "Remove from favourites"
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@
|
||||||
"delete_note": "Delete note",
|
"delete_note": "Delete note",
|
||||||
"delete_image": "Delete image",
|
"delete_image": "Delete image",
|
||||||
"export_html": "Export as HTML",
|
"export_html": "Export as HTML",
|
||||||
|
"print_preview": "Print preview",
|
||||||
"copy_link": "Copy link to clipboard",
|
"copy_link": "Copy link to clipboard",
|
||||||
"add_favorite": "Add to favorites",
|
"add_favorite": "Add to favorites",
|
||||||
"remove_favorite": "Remove from favorites"
|
"remove_favorite": "Remove from favorites"
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@
|
||||||
"delete_note": "Eliminar nota",
|
"delete_note": "Eliminar nota",
|
||||||
"delete_image": "Eliminar imagen",
|
"delete_image": "Eliminar imagen",
|
||||||
"export_html": "Exportar como HTML",
|
"export_html": "Exportar como HTML",
|
||||||
|
"print_preview": "Vista previa de impresión",
|
||||||
"copy_link": "Copiar enlace al portapapeles",
|
"copy_link": "Copiar enlace al portapapeles",
|
||||||
"add_favorite": "Añadir a favoritos",
|
"add_favorite": "Añadir a favoritos",
|
||||||
"remove_favorite": "Quitar de favoritos"
|
"remove_favorite": "Quitar de favoritos"
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@
|
||||||
"delete_note": "Supprimer la note",
|
"delete_note": "Supprimer la note",
|
||||||
"delete_image": "Supprimer l'image",
|
"delete_image": "Supprimer l'image",
|
||||||
"export_html": "Exporter en HTML",
|
"export_html": "Exporter en HTML",
|
||||||
|
"print_preview": "Aperçu avant impression",
|
||||||
"copy_link": "Copier le lien dans le presse-papiers",
|
"copy_link": "Copier le lien dans le presse-papiers",
|
||||||
"add_favorite": "Ajouter aux favoris",
|
"add_favorite": "Ajouter aux favoris",
|
||||||
"remove_favorite": "Retirer des favoris"
|
"remove_favorite": "Retirer des favoris"
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@
|
||||||
"delete_note": "Jegyzet törlése",
|
"delete_note": "Jegyzet törlése",
|
||||||
"delete_image": "Kép törlése",
|
"delete_image": "Kép törlése",
|
||||||
"export_html": "Exportálás HTML-ként",
|
"export_html": "Exportálás HTML-ként",
|
||||||
|
"print_preview": "Nyomtatási előnézet",
|
||||||
"copy_link": "Link másolása",
|
"copy_link": "Link másolása",
|
||||||
"add_favorite": "Hozzáadás a kedvencekhez",
|
"add_favorite": "Hozzáadás a kedvencekhez",
|
||||||
"remove_favorite": "Eltávolítás a kedvencek közül"
|
"remove_favorite": "Eltávolítás a kedvencek közül"
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
"delete_note": "Elimina nota",
|
"delete_note": "Elimina nota",
|
||||||
"delete_image": "Elimina immagine",
|
"delete_image": "Elimina immagine",
|
||||||
"export_html": "Esporta come HTML",
|
"export_html": "Esporta come HTML",
|
||||||
|
"print_preview": "Anteprima di stampa",
|
||||||
"copy_link": "Copia link negli appunti",
|
"copy_link": "Copia link negli appunti",
|
||||||
"add_favorite": "Aggiungi ai preferiti",
|
"add_favorite": "Aggiungi ai preferiti",
|
||||||
"remove_favorite": "Rimuovi dai preferiti"
|
"remove_favorite": "Rimuovi dai preferiti"
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
"delete_note": "ノートを削除",
|
"delete_note": "ノートを削除",
|
||||||
"delete_image": "画像を削除",
|
"delete_image": "画像を削除",
|
||||||
"export_html": "HTMLとしてエクスポート",
|
"export_html": "HTMLとしてエクスポート",
|
||||||
|
"print_preview": "印刷プレビュー",
|
||||||
"copy_link": "リンクをクリップボードにコピー",
|
"copy_link": "リンクをクリップボードにコピー",
|
||||||
"add_favorite": "お気に入りに追加",
|
"add_favorite": "お気に入りに追加",
|
||||||
"remove_favorite": "お気に入りから削除"
|
"remove_favorite": "お気に入りから削除"
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
"delete_note": "Удалить заметку",
|
"delete_note": "Удалить заметку",
|
||||||
"delete_image": "Удалить изображение",
|
"delete_image": "Удалить изображение",
|
||||||
"export_html": "Экспорт в HTML",
|
"export_html": "Экспорт в HTML",
|
||||||
|
"print_preview": "Предварительный просмотр печати",
|
||||||
"copy_link": "Копировать ссылку",
|
"copy_link": "Копировать ссылку",
|
||||||
"add_favorite": "Добавить в избранное",
|
"add_favorite": "Добавить в избранное",
|
||||||
"remove_favorite": "Удалить из избранного"
|
"remove_favorite": "Удалить из избранного"
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
"delete_note": "Izbriši zapis",
|
"delete_note": "Izbriši zapis",
|
||||||
"delete_image": "Izbriši sliko",
|
"delete_image": "Izbriši sliko",
|
||||||
"export_html": "Izvozi kot HTML",
|
"export_html": "Izvozi kot HTML",
|
||||||
|
"print_preview": "Predogled tiskanja",
|
||||||
"copy_link": "Kopiraj povezavo v odložišče",
|
"copy_link": "Kopiraj povezavo v odložišče",
|
||||||
"add_favorite": "Dodaj med priljubljene",
|
"add_favorite": "Dodaj med priljubljene",
|
||||||
"remove_favorite": "Odstrani iz priljubljenih"
|
"remove_favorite": "Odstrani iz priljubljenih"
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
"delete_note": "删除笔记",
|
"delete_note": "删除笔记",
|
||||||
"delete_image": "删除图片",
|
"delete_image": "删除图片",
|
||||||
"export_html": "导出为 HTML",
|
"export_html": "导出为 HTML",
|
||||||
|
"print_preview": "打印预览",
|
||||||
"copy_link": "复制链接到剪贴板",
|
"copy_link": "复制链接到剪贴板",
|
||||||
"add_favorite": "添加到收藏夹",
|
"add_favorite": "添加到收藏夹",
|
||||||
"remove_favorite": "从收藏夹移除"
|
"remove_favorite": "从收藏夹移除"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue