added print preview option
This commit is contained in:
parent
96b5fd8cfc
commit
80797f9cfe
|
|
@ -300,7 +300,8 @@ def generate_export_html(
|
|||
title: str,
|
||||
content: str,
|
||||
theme_css: str,
|
||||
is_dark: bool = False
|
||||
is_dark: bool = False,
|
||||
show_print_button: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
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)
|
||||
theme_css: CSS content for theming
|
||||
is_dark: Whether using a dark theme (for Mermaid/Highlight.js)
|
||||
show_print_button: Whether to show a print button (for preview mode)
|
||||
|
||||
Returns:
|
||||
Complete HTML document as string
|
||||
|
|
@ -327,6 +329,24 @@ def generate_export_html(
|
|||
highlight_theme = 'github-dark' if is_dark else 'github'
|
||||
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 lang="en">
|
||||
<head>
|
||||
|
|
@ -667,10 +687,54 @@ def generate_export_html(
|
|||
padding: 0.5in;
|
||||
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>
|
||||
</head>
|
||||
<body>
|
||||
{print_toolbar_html}
|
||||
<div class="markdown-preview" id="content"></div>
|
||||
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -1230,7 +1230,7 @@ async def remove_note(request: Request, note_path: str):
|
|||
|
||||
@api_router.get("/export/{note_path:path}", tags=["Export"])
|
||||
@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.
|
||||
|
||||
|
|
@ -1239,9 +1239,10 @@ async def export_note_to_html(request: Request, note_path: str, theme: Optional[
|
|||
|
||||
Query Parameters:
|
||||
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:
|
||||
HTML file download
|
||||
HTML file (download or inline based on download parameter)
|
||||
"""
|
||||
try:
|
||||
notes_dir = Path(config['storage']['notes_dir'])
|
||||
|
|
@ -1288,23 +1289,28 @@ async def export_note_to_html(request: Request, note_path: str, theme: Optional[
|
|||
# Get note title
|
||||
title = Path(note_path).stem
|
||||
|
||||
# Generate HTML
|
||||
# Generate HTML (show print button only when not downloading)
|
||||
html_content = generate_export_html(
|
||||
title=title,
|
||||
content=content_with_links,
|
||||
theme_css=theme_css,
|
||||
is_dark=is_dark
|
||||
is_dark=is_dark,
|
||||
show_print_button=not download
|
||||
)
|
||||
|
||||
# Return as downloadable file
|
||||
filename = f"{title}.html"
|
||||
return Response(
|
||||
content=html_content,
|
||||
media_type="text/html",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{filename}"'
|
||||
}
|
||||
)
|
||||
# Return as downloadable file or inline (for print preview)
|
||||
if download:
|
||||
filename = f"{title}.html"
|
||||
return Response(
|
||||
content=html_content,
|
||||
media_type="text/html",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{filename}"'
|
||||
}
|
||||
)
|
||||
else:
|
||||
# Return inline for browser display (print preview)
|
||||
return HTMLResponse(content=html_content)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ Returns the relationship graph between notes with link detection.
|
|||
|
||||
### Export Note as HTML
|
||||
```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.
|
||||
|
|
@ -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`) |
|
||||
| `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:**
|
||||
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:**
|
||||
- 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
|
||||
- YAML frontmatter stripped
|
||||
- Responsive design with print support
|
||||
- Print toolbar with Print/Close buttons (preview mode only)
|
||||
|
||||
**Rate Limit:** 30 requests/minute
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
# Export with default theme
|
||||
# Export with default theme (downloads file)
|
||||
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"
|
||||
|
||||
# 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
|
||||
async copyNoteLink() {
|
||||
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>
|
||||
</svg>
|
||||
</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>
|
||||
<button
|
||||
@click="copyNoteLink()"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@
|
|||
"delete_note": "Notiz löschen",
|
||||
"delete_image": "Bild löschen",
|
||||
"export_html": "Als HTML exportieren",
|
||||
"print_preview": "Druckvorschau",
|
||||
"copy_link": "Link in Zwischenablage kopieren",
|
||||
"add_favorite": "Zu Favoriten hinzufügen",
|
||||
"remove_favorite": "Aus Favoriten entfernen"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
"delete_note": "Delete note",
|
||||
"delete_image": "Delete image",
|
||||
"export_html": "Export as HTML",
|
||||
"print_preview": "Print preview",
|
||||
"copy_link": "Copy link to clipboard",
|
||||
"add_favorite": "Add to favourites",
|
||||
"remove_favorite": "Remove from favourites"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@
|
|||
"delete_note": "Delete note",
|
||||
"delete_image": "Delete image",
|
||||
"export_html": "Export as HTML",
|
||||
"print_preview": "Print preview",
|
||||
"copy_link": "Copy link to clipboard",
|
||||
"add_favorite": "Add to favorites",
|
||||
"remove_favorite": "Remove from favorites"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@
|
|||
"delete_note": "Eliminar nota",
|
||||
"delete_image": "Eliminar imagen",
|
||||
"export_html": "Exportar como HTML",
|
||||
"print_preview": "Vista previa de impresión",
|
||||
"copy_link": "Copiar enlace al portapapeles",
|
||||
"add_favorite": "Añadir a favoritos",
|
||||
"remove_favorite": "Quitar de favoritos"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@
|
|||
"delete_note": "Supprimer la note",
|
||||
"delete_image": "Supprimer l'image",
|
||||
"export_html": "Exporter en HTML",
|
||||
"print_preview": "Aperçu avant impression",
|
||||
"copy_link": "Copier le lien dans le presse-papiers",
|
||||
"add_favorite": "Ajouter aux favoris",
|
||||
"remove_favorite": "Retirer des favoris"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@
|
|||
"delete_note": "Jegyzet törlése",
|
||||
"delete_image": "Kép törlése",
|
||||
"export_html": "Exportálás HTML-ként",
|
||||
"print_preview": "Nyomtatási előnézet",
|
||||
"copy_link": "Link másolása",
|
||||
"add_favorite": "Hozzáadás a kedvencekhez",
|
||||
"remove_favorite": "Eltávolítás a kedvencek közül"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
"delete_note": "Elimina nota",
|
||||
"delete_image": "Elimina immagine",
|
||||
"export_html": "Esporta come HTML",
|
||||
"print_preview": "Anteprima di stampa",
|
||||
"copy_link": "Copia link negli appunti",
|
||||
"add_favorite": "Aggiungi ai preferiti",
|
||||
"remove_favorite": "Rimuovi dai preferiti"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
"delete_note": "ノートを削除",
|
||||
"delete_image": "画像を削除",
|
||||
"export_html": "HTMLとしてエクスポート",
|
||||
"print_preview": "印刷プレビュー",
|
||||
"copy_link": "リンクをクリップボードにコピー",
|
||||
"add_favorite": "お気に入りに追加",
|
||||
"remove_favorite": "お気に入りから削除"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
"delete_note": "Удалить заметку",
|
||||
"delete_image": "Удалить изображение",
|
||||
"export_html": "Экспорт в HTML",
|
||||
"print_preview": "Предварительный просмотр печати",
|
||||
"copy_link": "Копировать ссылку",
|
||||
"add_favorite": "Добавить в избранное",
|
||||
"remove_favorite": "Удалить из избранного"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
"delete_note": "Izbriši zapis",
|
||||
"delete_image": "Izbriši sliko",
|
||||
"export_html": "Izvozi kot HTML",
|
||||
"print_preview": "Predogled tiskanja",
|
||||
"copy_link": "Kopiraj povezavo v odložišče",
|
||||
"add_favorite": "Dodaj med priljubljene",
|
||||
"remove_favorite": "Odstrani iz priljubljenih"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
"delete_note": "删除笔记",
|
||||
"delete_image": "删除图片",
|
||||
"export_html": "导出为 HTML",
|
||||
"print_preview": "打印预览",
|
||||
"copy_link": "复制链接到剪贴板",
|
||||
"add_favorite": "添加到收藏夹",
|
||||
"remove_favorite": "从收藏夹移除"
|
||||
|
|
|
|||
Loading…
Reference in New Issue