From 3e50a2dba84bcc3193dbe11e379c6c0f8dbcdc79 Mon Sep 17 00:00:00 2001 From: Gamosoft Date: Wed, 12 Nov 2025 11:16:12 +0100 Subject: [PATCH] added html export function --- README.md | 1 + data/notes/FEATURES.md | 1 + frontend/app.js | 164 +++++++++++++++++++++++++++++++++++++++++ frontend/index.html | 11 +++ 4 files changed, 177 insertions(+) diff --git a/README.md b/README.md index 6938b7b..eba1048 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ NoteDiscovery is a **lightweight, self-hosted note-taking application** that put - 📱 **Responsive** - Works on desktop, tablet, and mobile - 📂 **Simple Storage** - Plain markdown files in folders - 🧮 **Math Support** - LaTeX/MathJax for beautiful equations +- 📄 **HTML Export** - Share notes as standalone HTML files ## 🚀 Quick Start diff --git a/data/notes/FEATURES.md b/data/notes/FEATURES.md index c1292e4..93993f7 100644 --- a/data/notes/FEATURES.md +++ b/data/notes/FEATURES.md @@ -10,6 +10,7 @@ - **Syntax highlighting** for code blocks (50+ languages) - **Copy code blocks** - One-click copy button on hover - **LaTeX/Math rendering** - Beautiful mathematical equations with MathJax +- **HTML Export** - Export notes as standalone HTML files ### Organization - **Folder hierarchy** - Organize notes in nested folders diff --git a/frontend/app.js b/frontend/app.js index dbc95d2..086282c 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -2125,6 +2125,170 @@ function noteApp() { setTimeout(() => { this.isScrolling = false; }, CONFIG.SCROLL_SYNC_DELAY); + }, + + // Export current note as HTML + async exportToHTML() { + if (!this.currentNote || !this.noteContent) { + alert('No note content to export'); + return; + } + + try { + // Get the note name without extension + const noteName = this.currentNoteName || 'note'; + + // Get current rendered HTML (this already has markdown converted and will have LaTeX delimiters) + const renderedHTML = this.renderedMarkdown; + + // Get current theme CSS + const currentTheme = this.currentTheme || 'light'; + const themeResponse = await fetch(`/api/themes/${currentTheme}`); + const themeText = await themeResponse.text(); + + // Check if response is JSON or plain CSS + let themeCss; + try { + const themeJson = JSON.parse(themeText); + // If it's JSON, extract the css field + themeCss = themeJson.css || themeText; + } catch (e) { + // If it's not JSON, use it as-is + themeCss = themeText; + } + + // Theme CSS uses :root[data-theme="..."] selector, but we need plain :root for export + // Strip the data-theme attribute selector so variables apply globally + themeCss = themeCss.replace(/:root\[data-theme="[^"]+"\]/g, ':root'); + + // Get highlight.js theme URL from current page + const highlightLinkElement = document.getElementById('highlight-theme'); + if (!highlightLinkElement || !highlightLinkElement.href) { + console.warn('Could not detect highlight.js theme, export may not match preview exactly'); + } + const highlightTheme = highlightLinkElement ? highlightLinkElement.href : ''; + + // Extract all markdown preview styles from current page + let markdownStyles = ''; + const styleSheets = Array.from(document.styleSheets); + + for (const sheet of styleSheets) { + try { + const rules = Array.from(sheet.cssRules || []); + for (const rule of rules) { + const cssText = rule.cssText; + // Include rules that target markdown-preview or mjx-container + if (cssText.includes('.markdown-preview') || + cssText.includes('mjx-container') || + cssText.includes('.MathJax')) { + markdownStyles += cssText + '\n'; + } + } + } catch (e) { + // Skip stylesheets that can't be accessed (CORS) + console.warn('Could not access stylesheet:', sheet.href, e); + } + } + + // Create standalone HTML document with MathJax + const htmlDocument = ` + + + + + ${noteName} + + + ${highlightTheme ? `` : ''} + + + + + + + + + +
+ ${renderedHTML} +
+ +`; + + // Create blob and download + const blob = new Blob([htmlDocument], { type: 'text/html;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${noteName}.html`; + document.body.appendChild(a); + a.click(); + + // Cleanup + URL.revokeObjectURL(url); + document.body.removeChild(a); + + } catch (error) { + console.error('HTML export failed:', error); + alert(`Failed to export HTML: ${error.message}`); + } } } } diff --git a/frontend/index.html b/frontend/index.html index 8054b4c..63add0f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -920,6 +920,17 @@ Preview + + +