Spaces:
Sleeping
Sleeping
Readme Editor
Browse files- app.py +63 -0
- requirements.txt +2 -0
- static/favicon/android-chrome-192x192.png +0 -0
- static/favicon/android-chrome-512x512.png +0 -0
- static/favicon/apple-touch-icon.png +0 -0
- static/favicon/favicon-16x16.png +0 -0
- static/favicon/favicon-32x32.png +0 -0
- static/favicon/favicon.ico +0 -0
- static/favicon/site.webmanifest +1 -0
- static/main.js +511 -0
- static/style.css +414 -0
- templates/index.html +324 -0
app.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import io
|
| 4 |
+
import os
|
| 5 |
+
from flask import Flask, render_template, request, send_file, jsonify
|
| 6 |
+
|
| 7 |
+
app = Flask(__name__)
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@app.get("/")
|
| 11 |
+
def index():
|
| 12 |
+
return render_template("index.html")
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@app.post("/export/md")
|
| 16 |
+
def export_md():
|
| 17 |
+
content = request.json.get("markdown", "")
|
| 18 |
+
buf = io.BytesIO(content.encode("utf-8"))
|
| 19 |
+
return send_file(buf, as_attachment=True, download_name="document.md", mimetype="text/markdown")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@app.post("/export/html")
|
| 23 |
+
def export_html():
|
| 24 |
+
html = request.json.get("html", "")
|
| 25 |
+
with_styles = bool(request.args.get("withStyles", "1") not in ("0", "false", "False"))
|
| 26 |
+
if with_styles:
|
| 27 |
+
wrapped = f"""<!DOCTYPE html><html><head>
|
| 28 |
+
<meta charset='utf-8'>
|
| 29 |
+
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
| 30 |
+
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css' rel='stylesheet'>
|
| 31 |
+
<link href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css' rel='stylesheet'>
|
| 32 |
+
<link href='https://unpkg.com/@primer/css@21.0.7/dist/primer.css' rel='stylesheet'>
|
| 33 |
+
<style> body{{padding:2rem}} </style>
|
| 34 |
+
</head><body class='markdown-body'>{html}</body></html>"""
|
| 35 |
+
else:
|
| 36 |
+
wrapped = html
|
| 37 |
+
|
| 38 |
+
buf = io.BytesIO(wrapped.encode("utf-8"))
|
| 39 |
+
return send_file(buf, as_attachment=True, download_name="document.html", mimetype="text/html")
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@app.post("/import/html")
|
| 43 |
+
def import_html():
|
| 44 |
+
file = request.files.get("file")
|
| 45 |
+
if not file:
|
| 46 |
+
return jsonify({"error": "No file"}), 400
|
| 47 |
+
text = file.read().decode("utf-8", errors="ignore")
|
| 48 |
+
return jsonify({"html": text})
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@app.post("/import/md")
|
| 52 |
+
def import_md():
|
| 53 |
+
file = request.files.get("file")
|
| 54 |
+
if not file:
|
| 55 |
+
return jsonify({"error": "No file"}), 400
|
| 56 |
+
text = file.read().decode("utf-8", errors="ignore")
|
| 57 |
+
return jsonify({"markdown": text})
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
if __name__ == "__main__":
|
| 61 |
+
port = int(os.environ.get("PORT", "7860"))
|
| 62 |
+
host = os.environ.get("HOST", "0.0.0.0")
|
| 63 |
+
app.run(host=host, port=port)
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask==3.0.3
|
| 2 |
+
gunicorn==23.0.0
|
static/favicon/android-chrome-192x192.png
ADDED
|
|
static/favicon/android-chrome-512x512.png
ADDED
|
|
static/favicon/apple-touch-icon.png
ADDED
|
|
static/favicon/favicon-16x16.png
ADDED
|
|
static/favicon/favicon-32x32.png
ADDED
|
|
static/favicon/favicon.ico
ADDED
|
|
static/favicon/site.webmanifest
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
static/main.js
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
let easyMDE;
|
| 3 |
+
const previewEl = document.getElementById('preview');
|
| 4 |
+
const hljsTheme = document.getElementById('hljs-theme');
|
| 5 |
+
const docTitlePill = document.getElementById('doc-title-pill');
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
marked.setOptions({
|
| 9 |
+
breaks: true,
|
| 10 |
+
highlight: (code, lang) => {
|
| 11 |
+
try { return hljs.highlight(code, {language: lang}).value; }
|
| 12 |
+
catch { return hljs.highlightAuto(code).value; }
|
| 13 |
+
}
|
| 14 |
+
});
|
| 15 |
+
|
| 16 |
+
function renderPreview(md) {
|
| 17 |
+
const html = marked.parse(md || '');
|
| 18 |
+
previewEl.innerHTML = `<div class="markdown-body container">${html}</div>`;
|
| 19 |
+
|
| 20 |
+
try { if (window.renderMathInElement) renderMathInElement(previewEl, { delimiters: [
|
| 21 |
+
{left: '$$', right: '$$', display: true},
|
| 22 |
+
{left: '$', right: '$', display: false},
|
| 23 |
+
{left: '\\(', right: '\\)', display: false},
|
| 24 |
+
{left: '\\[', right: '\\]', display: true}
|
| 25 |
+
]}); } catch {}
|
| 26 |
+
|
| 27 |
+
try {
|
| 28 |
+
if (window.mermaid && !window.__MERMAID_INIT__) {
|
| 29 |
+
window.__MERMAID_INIT__ = true;
|
| 30 |
+
mermaid.initialize({ startOnLoad: false, theme: document.documentElement.classList.contains('dark') ? 'dark' : 'default' });
|
| 31 |
+
}
|
| 32 |
+
const blocks = previewEl.querySelectorAll('pre code.language-mermaid, code.language-mermaid');
|
| 33 |
+
blocks.forEach((node, i) => {
|
| 34 |
+
const src = node.textContent.trim();
|
| 35 |
+
const container = document.createElement('div');
|
| 36 |
+
container.className = 'mermaid';
|
| 37 |
+
container.textContent = src;
|
| 38 |
+
const pre = node.closest('pre');
|
| 39 |
+
if (pre) pre.replaceWith(container); else node.replaceWith(container);
|
| 40 |
+
});
|
| 41 |
+
if (window.mermaid && previewEl.querySelector('.mermaid')) mermaid.run({ querySelector: '.mermaid' });
|
| 42 |
+
} catch {}
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
function getMarkdown() { return easyMDE ? easyMDE.value() : ''; }
|
| 46 |
+
|
| 47 |
+
function setupEditor() {
|
| 48 |
+
easyMDE = new EasyMDE({
|
| 49 |
+
element: document.getElementById('editor'),
|
| 50 |
+
spellChecker: false,
|
| 51 |
+
autosave: { enabled: true, uniqueId: 'md-editor-autosave', delay: 1000 },
|
| 52 |
+
previewRender: (plainText) => marked.parse(plainText),
|
| 53 |
+
toolbar: [
|
| 54 |
+
{ name: 'open', action: () => document.getElementById('btn-import-md').click(), className: 'fa fa-folder-open', title: 'Open Markdown file' },
|
| 55 |
+
'undo', 'redo', '|',
|
| 56 |
+
'bold', 'italic', 'heading-1', 'heading-2', 'heading-3', 'table', '|',
|
| 57 |
+
{ name: 'indent', action: (ed) => ed.codemirror.execCommand('indentMore'), className: 'fa fa-indent', title: 'Indent' },
|
| 58 |
+
{ name: 'outdent', action: (ed) => ed.codemirror.execCommand('indentLess'), className: 'fa fa-outdent', title: 'Outdent' },
|
| 59 |
+
'|', 'unordered-list', 'ordered-list', '|', 'quote', 'code', 'horizontal-rule', '|', 'link', 'image', '|',
|
| 60 |
+
'preview', 'side-by-side', 'fullscreen', '|',
|
| 61 |
+
{ name: 'clear', action: () => { easyMDE.value(''); renderPreview(''); }, className: 'fa fa-eraser', title: 'Clear' }
|
| 62 |
+
],
|
| 63 |
+
});
|
| 64 |
+
easyMDE.codemirror.on('change', () => renderPreview(getMarkdown()));
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
const initial = getMarkdown();
|
| 68 |
+
if (!initial || initial.trim() === '') {
|
| 69 |
+
const sample = `# Welcome to Markdown Editor!\n\nThis page works like **StackEdit**. Edit on the left, preview on the right.\nIf you want to learn Markdown quickly, read and modify this document.\n\n# Files\n\nYour content is autosaved in your browser and works **offline**.\n\n## Create files and folders\nUse the Samples menu for starters or begin typing.\n\n## Switch to another file\nYou can paste any Markdown content here and it will render instantly.\n\n## Export a file\nUse the Export menu to save as Markdown, raw HTML, styled HTML, or PDF.\n\n# Synchronization\n\nThis demo does not connect to cloud storage, but you can copy/paste to any service.\n\n# Markdown extensions\n\n## Code blocks\n\n\`\`\`python\nprint('Hello, Markdown!')\n\`\`\`\n\n## Tables\n\n| Feature | Status |\n|---|---|\n| Autosave | ✅ |\n| Dark Mode | ✅ |\n| Export | ✅ |\n\n## Blockquotes\n\n> Tips: Use the toolbar to format text, add lists, tables, and more.\n\n## Mermaid diagrams\n\n\`\`\`mermaid\ngraph LR\nA[Write] --> B[Preview]\nB --> C{Export}\nC -->|PDF| D[Share]\nC -->|HTML| E[Publish]\n\`\`\`\n\n---\n\nHappy writing!`;
|
| 70 |
+
easyMDE.value(sample);
|
| 71 |
+
}
|
| 72 |
+
renderPreview(getMarkdown());
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
function setupSplit() {
|
| 76 |
+
if (window.Split) {
|
| 77 |
+
Split(['#leftPane', '#rightPane'], { sizes: [50, 50], minSize: 280, gutterSize: 8, cursor: 'col-resize' });
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
function pickFile(inputEl, cb) {
|
| 83 |
+
inputEl.onchange = (e) => { const f = e.target.files[0]; if (f) cb(f); inputEl.value = ''; };
|
| 84 |
+
inputEl.click();
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
function readFileText(file) {
|
| 88 |
+
return new Promise((resolve) => {
|
| 89 |
+
const reader = new FileReader();
|
| 90 |
+
reader.onload = (e) => resolve(e.target.result);
|
| 91 |
+
reader.readAsText(file);
|
| 92 |
+
});
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
function bindActions() {
|
| 97 |
+
// Theme toggle
|
| 98 |
+
const themeBtn = document.getElementById('btn-theme');
|
| 99 |
+
const setDark = (on) => {
|
| 100 |
+
document.documentElement.classList.toggle('light', !on);
|
| 101 |
+
document.documentElement.classList.toggle('dark', !!on);
|
| 102 |
+
|
| 103 |
+
if (hljsTheme) {
|
| 104 |
+
hljsTheme.href = on
|
| 105 |
+
? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css'
|
| 106 |
+
: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css';
|
| 107 |
+
}
|
| 108 |
+
try { localStorage.setItem('md-theme-dark', on ? '1' : '0'); } catch {}
|
| 109 |
+
};
|
| 110 |
+
try {
|
| 111 |
+
const saved = localStorage.getItem('md-theme-dark');
|
| 112 |
+
const initialDark = saved === null ? true : saved === '1';
|
| 113 |
+
setDark(initialDark);
|
| 114 |
+
const icon = themeBtn.querySelector('i');
|
| 115 |
+
if (icon) icon.className = initialDark ? 'fa-solid fa-moon' : 'fa-solid fa-sun';
|
| 116 |
+
} catch { setDark(true); }
|
| 117 |
+
themeBtn.addEventListener('click', () => {
|
| 118 |
+
const isDark = document.documentElement.classList.contains('dark');
|
| 119 |
+
const nowDark = !isDark;
|
| 120 |
+
setDark(nowDark);
|
| 121 |
+
const icon = themeBtn.querySelector('i');
|
| 122 |
+
if (icon) icon.className = nowDark ? 'fa-solid fa-moon' : 'fa-solid fa-sun';
|
| 123 |
+
});
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
document.getElementById('btn-import-md').addEventListener('click', () => {
|
| 127 |
+
pickFile(document.getElementById('file-md'), async (file) => {
|
| 128 |
+
const text = await readFileText(file);
|
| 129 |
+
easyMDE.value(text);
|
| 130 |
+
renderPreview(text);
|
| 131 |
+
});
|
| 132 |
+
});
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
document.getElementById('btn-import-html').addEventListener('click', () => {
|
| 136 |
+
pickFile(document.getElementById('file-html'), async (file) => {
|
| 137 |
+
const text = await readFileText(file);
|
| 138 |
+
easyMDE.value(text);
|
| 139 |
+
renderPreview(text);
|
| 140 |
+
});
|
| 141 |
+
});
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
const samplesBtn = document.getElementById('btn-samples');
|
| 145 |
+
const samplesModal = new bootstrap.Modal(document.getElementById('samplesModal'));
|
| 146 |
+
samplesBtn.addEventListener('click', () => samplesModal.show());
|
| 147 |
+
|
| 148 |
+
const samples = {
|
| 149 |
+
cheatsheet: `# Markdown Cheat Sheet\n\n## Headings\n# H1\n## H2\n### H3\n\n## Emphasis\n*italic* **bold** ~~strike~~ \`code\`\n\n## Lists\n- unordered\n- list\n - nested\n1. ordered\n2. list\n\n## Links & Images\n[OpenAI](https://openai.com) \\n\n\n## Blockquote\n> A wise quote.\n\n## Table\n| Name | Role |\n|---|---|\n| Alice | Developer |\n| Bob | Designer |\n\n## Task List\n- [x] design\n- [ ] implement\n- [ ] test\n\n## Code Block\n\`\`\`python\nfrom math import sqrt\nprint(sqrt(9))\n\`\`\`\n\n---\n\n### Horizontal rule above`,
|
| 150 |
+
email: `**Subject:** Request to Schedule Project Kickoff Meeting\n\nDear [Recipient Name],\n\nI hope you're doing well. I'd like to schedule a 30‑minute kickoff meeting to align on [Project/Topic].\n\n**Proposed agenda:**\n- Objectives and success criteria\n- Scope, timeline, and roles\n- Risks and dependencies\n- Next steps\n\n**Suggested times (IST):**\n- [Option 1, e.g., Tue 3:00–3:30 PM]\n- [Option 2, e.g., Wed 11:00–11:30 AM]\n\nIf these don't work, please share alternative slots and your preferred meeting platform.\n\nThanks in advance, and I look forward to our discussion.\n\nBest regards,\n\n[Your Full Name]\n[Your Role], [Your Company]\nPhone: [Your Phone]\nEmail: you@example.com\n\n---`,
|
| 151 |
+
readme: `# Project Title\n\n[](LICENSE) \n\nA short description of your project.\n\n## Features\n- Awesome feature\n- Fast and reliable\n\n## Installation\n\`\`\`bash\npip install -r requirements.txt\npython app.py\n\`\`\`\n\n## Usage\nDescribe how to use it.\n\n## License\nMIT © You`,
|
| 152 |
+
github_readme_pro: `# Awesome App\n\n[](https://github.com/OWNER/REPO/actions) [](LICENSE)\n\n> One‑liner that explains what this project does.\n\n## Table of Contents\n- [Demo](#demo)\n- [Stack](#stack)\n- [Install](#install)\n- [Project Structure](#project-structure)\n- [CLI](#cli)\n- [Docs](#docs)\n\n## Demo\n\n\n## Stack\n| Layer | Tech |\n|---|---|\n| Frontend | React / Vite |\n| Backend | FastAPI |\n| DB | PostgreSQL |\n| Infra | Docker / GH Actions |\n\n## Install\n\`\`\`bash\n git clone https://github.com/OWNER/REPO && cd REPO\n cp .env.example .env\n docker compose up -d --build\n\`\`\`\n\n## Project Structure\n\`\`\`text\nREPO/\n ├─ apps/\n │ ├─ api/\n │ └─ web/\n ├─ packages/\n │ ├─ ui/\n │ └─ config/\n └─ tools/\n └─ scripts/\n\`\`\`\n\n## Docs\n### Data Flow (Mermaid)\n\`\`\`mermaid\ngraph TD\nA[Browser] --> B(Edge API)\nB --> C{Auth}\nC -->|pass| D[Service] --> E[(DB)]\nC -->|fail| F[401]\n\`\`\`\n\n## License\nMIT`,
|
| 153 |
+
math_notes: `# KaTeX Quick Examples\n\nInline: $E=mc^2$, $\\int_0^1 x^2\\,dx = \\tfrac{1}{3}$.\n\nBlock:\n\n$$\\sum_{i=1}^n i = \\frac{n(n+1)}{2}$$\n\nEuler integral for the gamma function:\n\n$$\\Gamma(z)=\\int_0^\\infty t^{z-1}e^{-t}\\,dt$$\n\n> Tip: See more syntax in the MathJax guide.`,
|
| 154 |
+
api_docs: `# HTTP API\n\n> Version 1\n\n## Authentication\nUse a Bearer token in the Authorization header.\n\n## Endpoints\n### GET /v1/users\nReturns a paginated list of users.\n\n\`\`\`http\nGET /v1/users?page=1&pageSize=20\nAuthorization: Bearer <token>\n\`\`\`\n\nResponse:\n\`\`\`json\n{\n "items": [{"id": 1, "name": "Ada"}],\n "nextPage": 2\n}\n\`\`\`\n\n### Errors\n| Code | Meaning |\n|---|---|\n| 400 | Bad request |\n| 401 | Unauthorized |\n| 404 | Not found |`,
|
| 155 |
+
meeting_notes: `# Meeting Notes\n\n**When**: 2025-08-28 14:00–14:45 IST \n**Where**: Zoom \n**Attendees**: Alex, Priya, Sam\n\n## Agenda\n1. Status updates\n2. Release checklist\n3. Risks\n\n## Decisions\n- Ship v1.2 on Monday\n- Freeze new features until post‑release\n\n## Action Items\n- [ ] Alex: finalize docs\n- [ ] Priya: run load tests\n- [ ] Sam: prepare rollout plan\n\n## Timeline\n\`\`\`mermaid\ngantt\n title Release plan\n dateFormat YYYY-MM-DD\n section Prep\n Docs :a1, 2025-08-28, 2d\n Load tests :a2, 2025-08-28, 2d\n section Ship\n Release :milestone, 2025-08-30, 1d\n\`\`\``
|
| 156 |
+
};
|
| 157 |
+
|
| 158 |
+
document.querySelectorAll('[data-sample]').forEach(btn => {
|
| 159 |
+
btn.addEventListener('click', () => {
|
| 160 |
+
const key = btn.getAttribute('data-sample');
|
| 161 |
+
easyMDE.value(samples[key] || '');
|
| 162 |
+
renderPreview(getMarkdown());
|
| 163 |
+
samplesModal.hide();
|
| 164 |
+
});
|
| 165 |
+
});
|
| 166 |
+
document.querySelectorAll('[data-sample-append]').forEach(btn => {
|
| 167 |
+
btn.addEventListener('click', () => {
|
| 168 |
+
const key = btn.getAttribute('data-sample-append');
|
| 169 |
+
const text = samples[key] || '';
|
| 170 |
+
easyMDE.value((getMarkdown() + '\n\n' + text).trim());
|
| 171 |
+
renderPreview(getMarkdown());
|
| 172 |
+
samplesModal.hide();
|
| 173 |
+
});
|
| 174 |
+
});
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
document.getElementById('btn-export-md').addEventListener('click', () => {
|
| 178 |
+
const content = easyMDE.value();
|
| 179 |
+
const filename = document.getElementById('doc-title-pill').textContent || 'document';
|
| 180 |
+
const blob = new Blob([content], { type: 'text/markdown' });
|
| 181 |
+
saveAs(blob, `${filename}.md`);
|
| 182 |
+
});
|
| 183 |
+
|
| 184 |
+
document.getElementById('btn-export-html').addEventListener('click', () => {
|
| 185 |
+
const content = easyMDE.value();
|
| 186 |
+
const html = marked.parse(content);
|
| 187 |
+
const filename = document.getElementById('doc-title-pill').textContent || 'document';
|
| 188 |
+
const blob = new Blob([html], { type: 'text/html' });
|
| 189 |
+
saveAs(blob, `${filename}.html`);
|
| 190 |
+
});
|
| 191 |
+
|
| 192 |
+
document.getElementById('btn-export-html-styled').addEventListener('click', () => {
|
| 193 |
+
const content = easyMDE.value();
|
| 194 |
+
const html = marked.parse(content);
|
| 195 |
+
const styledHtml = `
|
| 196 |
+
<!DOCTYPE html>
|
| 197 |
+
<html lang="en">
|
| 198 |
+
<head>
|
| 199 |
+
<meta charset="UTF-8">
|
| 200 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 201 |
+
<title>${document.getElementById('doc-title-pill').textContent || 'Document'}</title>
|
| 202 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@primer/css@21.0.7/dist/primer.css">
|
| 203 |
+
<style>
|
| 204 |
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif; line-height: 1.6; max-width: 800px; margin: 0 auto; padding: 2rem; }
|
| 205 |
+
.markdown-body { color: #24292f; }
|
| 206 |
+
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { color: #24292f; margin-top: 1.5rem; }
|
| 207 |
+
.markdown-body table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
|
| 208 |
+
.markdown-body th, .markdown-body td { border: 1px solid #d0d7de; padding: 0.5rem; text-align: left; }
|
| 209 |
+
.markdown-body th { background-color: #f6f8fa; font-weight: 600; }
|
| 210 |
+
.markdown-body tr:nth-child(even) td { background-color: #fafbfc; }
|
| 211 |
+
.markdown-body pre { background-color: #f6f8fa; border: 1px solid #d0d7de; border-radius: 6px; padding: 1rem; overflow-x: auto; }
|
| 212 |
+
.markdown-body code { background-color: #f6f8fa; padding: 0.2rem 0.4rem; border-radius: 3px; font-size: 0.9em; }
|
| 213 |
+
.markdown-body blockquote { border-left: 4px solid #d0d7de; margin: 1rem 0; padding: 0 1rem; color: #6a737d; }
|
| 214 |
+
</style>
|
| 215 |
+
</head>
|
| 216 |
+
<body>
|
| 217 |
+
<div class="markdown-body">
|
| 218 |
+
${html}
|
| 219 |
+
</div>
|
| 220 |
+
</body>
|
| 221 |
+
</html>`;
|
| 222 |
+
const filename = document.getElementById('doc-title-pill').textContent || 'document';
|
| 223 |
+
const blob = new Blob([styledHtml], { type: 'text/html' });
|
| 224 |
+
saveAs(blob, `${filename}-styled.html`);
|
| 225 |
+
});
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
document.getElementById('btn-export-pdf').addEventListener('click', () => {
|
| 229 |
+
const pdfOptionsModal = new bootstrap.Modal(document.getElementById('pdfOptionsModal'));
|
| 230 |
+
pdfOptionsModal.show();
|
| 231 |
+
});
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
document.getElementById('btn-generate-pdf').addEventListener('click', () => {
|
| 235 |
+
const orientation = document.querySelector('input[name="orientation"]:checked').value;
|
| 236 |
+
const paperSize = document.getElementById('paperSize').value;
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
const pdfOptionsModal = bootstrap.Modal.getInstance(document.getElementById('pdfOptionsModal'));
|
| 240 |
+
pdfOptionsModal.hide();
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
generatePDF(orientation, paperSize);
|
| 244 |
+
});
|
| 245 |
+
|
| 246 |
+
function generatePDF(orientation, paperSize, fullPage) {
|
| 247 |
+
const content = easyMDE.value();
|
| 248 |
+
const html = marked.parse(content);
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
const pageBreakStyles = fullPage ? '' : `
|
| 252 |
+
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
|
| 253 |
+
page-break-after: avoid;
|
| 254 |
+
}
|
| 255 |
+
.markdown-body table {
|
| 256 |
+
page-break-inside: avoid;
|
| 257 |
+
}
|
| 258 |
+
.markdown-body pre {
|
| 259 |
+
page-break-inside: avoid;
|
| 260 |
+
}
|
| 261 |
+
.markdown-body hr {
|
| 262 |
+
page-break-after: avoid;
|
| 263 |
+
}`;
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
const styledHtml = `
|
| 267 |
+
<!DOCTYPE html>
|
| 268 |
+
<html lang="en">
|
| 269 |
+
<head>
|
| 270 |
+
<meta charset="UTF-8">
|
| 271 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 272 |
+
<title>${document.getElementById('doc-title-pill').textContent || 'Document'}</title>
|
| 273 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@primer/css@21.0.7/dist/primer.css">
|
| 274 |
+
<style>
|
| 275 |
+
/* Force light theme for PDF */
|
| 276 |
+
body {
|
| 277 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
|
| 278 |
+
line-height: 1.6;
|
| 279 |
+
margin: 0;
|
| 280 |
+
padding: 1rem;
|
| 281 |
+
color: #24292f !important;
|
| 282 |
+
background: #ffffff !important;
|
| 283 |
+
}
|
| 284 |
+
.markdown-body {
|
| 285 |
+
color: #24292f !important;
|
| 286 |
+
background: #ffffff !important;
|
| 287 |
+
}
|
| 288 |
+
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
|
| 289 |
+
color: #24292f !important;
|
| 290 |
+
margin-top: 1.5rem;
|
| 291 |
+
margin-bottom: 1rem;
|
| 292 |
+
}
|
| 293 |
+
.markdown-body p {
|
| 294 |
+
color: #24292f !important;
|
| 295 |
+
margin-bottom: 1rem;
|
| 296 |
+
}
|
| 297 |
+
.markdown-body ul, .markdown-body ol {
|
| 298 |
+
color: #24292f !important;
|
| 299 |
+
margin-bottom: 1rem;
|
| 300 |
+
}
|
| 301 |
+
.markdown-body li {
|
| 302 |
+
color: #24292f !important;
|
| 303 |
+
margin-bottom: 0.5rem;
|
| 304 |
+
}
|
| 305 |
+
.markdown-body table {
|
| 306 |
+
border-collapse: collapse;
|
| 307 |
+
width: 100%;
|
| 308 |
+
margin: 1rem 0;
|
| 309 |
+
background: #ffffff !important;
|
| 310 |
+
}
|
| 311 |
+
.markdown-body th, .markdown-body td {
|
| 312 |
+
border: 1px solid #d0d7de;
|
| 313 |
+
padding: 0.5rem;
|
| 314 |
+
text-align: left;
|
| 315 |
+
color: #24292f !important;
|
| 316 |
+
background: #ffffff !important;
|
| 317 |
+
}
|
| 318 |
+
.markdown-body th {
|
| 319 |
+
background-color: #f6f8fa !important;
|
| 320 |
+
color: #24292f !important;
|
| 321 |
+
font-weight: 600;
|
| 322 |
+
}
|
| 323 |
+
.markdown-body tr:nth-child(even) td {
|
| 324 |
+
background-color: #fafbfc !important;
|
| 325 |
+
color: #24292f !important;
|
| 326 |
+
}
|
| 327 |
+
.markdown-body pre {
|
| 328 |
+
background-color: #f6f8fa !important;
|
| 329 |
+
border: 1px solid #d0d7de;
|
| 330 |
+
border-radius: 6px;
|
| 331 |
+
padding: 1rem;
|
| 332 |
+
overflow-x: auto;
|
| 333 |
+
color: #24292f !important;
|
| 334 |
+
}
|
| 335 |
+
.markdown-body code {
|
| 336 |
+
background-color: #f6f8fa !important;
|
| 337 |
+
padding: 0.2rem 0.4rem;
|
| 338 |
+
border-radius: 3px;
|
| 339 |
+
font-size: 0.9em;
|
| 340 |
+
color: #24292f !important;
|
| 341 |
+
}
|
| 342 |
+
.markdown-body blockquote {
|
| 343 |
+
border-left: 4px solid #d0d7de;
|
| 344 |
+
margin: 1rem 0;
|
| 345 |
+
padding: 0 1rem;
|
| 346 |
+
color: #6a737d !important;
|
| 347 |
+
background-color: #f6f8fa !important;
|
| 348 |
+
}
|
| 349 |
+
.markdown-body img {
|
| 350 |
+
max-width: 100%;
|
| 351 |
+
height: auto;
|
| 352 |
+
border: 1px solid #d0d7de;
|
| 353 |
+
}
|
| 354 |
+
.markdown-body a {
|
| 355 |
+
color: #0969da !important;
|
| 356 |
+
text-decoration: none;
|
| 357 |
+
}
|
| 358 |
+
.markdown-body a:hover {
|
| 359 |
+
text-decoration: underline;
|
| 360 |
+
}
|
| 361 |
+
.markdown-body hr {
|
| 362 |
+
border: 0;
|
| 363 |
+
border-top: 1px solid #d0d7de;
|
| 364 |
+
margin: 1.5rem 0;
|
| 365 |
+
}
|
| 366 |
+
/* Override any dark theme styles */
|
| 367 |
+
* { color: inherit !important; }
|
| 368 |
+
${pageBreakStyles}
|
| 369 |
+
</style>
|
| 370 |
+
</head>
|
| 371 |
+
<body>
|
| 372 |
+
<div class="markdown-body">
|
| 373 |
+
${html}
|
| 374 |
+
</div>
|
| 375 |
+
</body>
|
| 376 |
+
</html>`;
|
| 377 |
+
|
| 378 |
+
const filename = document.getElementById('doc-title-pill').textContent || 'document';
|
| 379 |
+
|
| 380 |
+
if (fullPage) {
|
| 381 |
+
|
| 382 |
+
const tempDiv = document.createElement('div');
|
| 383 |
+
tempDiv.innerHTML = styledHtml;
|
| 384 |
+
tempDiv.style.position = 'absolute';
|
| 385 |
+
tempDiv.style.left = '-9999px';
|
| 386 |
+
tempDiv.style.top = '0';
|
| 387 |
+
tempDiv.style.width = '800px';
|
| 388 |
+
tempDiv.style.background = '#ffffff';
|
| 389 |
+
tempDiv.style.padding = '20px';
|
| 390 |
+
tempDiv.style.overflow = 'visible';
|
| 391 |
+
tempDiv.style.fontSize = '14px';
|
| 392 |
+
tempDiv.style.lineHeight = '1.6';
|
| 393 |
+
document.body.appendChild(tempDiv);
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
setTimeout(() => {
|
| 397 |
+
const contentHeight = Math.max(tempDiv.scrollHeight, tempDiv.offsetHeight);
|
| 398 |
+
console.log('Content height:', contentHeight);
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
document.body.removeChild(tempDiv);
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
const opt = {
|
| 405 |
+
margin: [0, 0, 0, 0],
|
| 406 |
+
filename: `${filename}-fullpage.pdf`,
|
| 407 |
+
image: { type: 'jpeg', quality: 0.98 },
|
| 408 |
+
html2canvas: {
|
| 409 |
+
scale: 1,
|
| 410 |
+
useCORS: true,
|
| 411 |
+
letterRendering: true,
|
| 412 |
+
backgroundColor: '#ffffff',
|
| 413 |
+
width: 800,
|
| 414 |
+
height: contentHeight,
|
| 415 |
+
scrollY: 0,
|
| 416 |
+
scrollX: 0,
|
| 417 |
+
allowTaint: true,
|
| 418 |
+
foreignObjectRendering: true
|
| 419 |
+
},
|
| 420 |
+
jsPDF: {
|
| 421 |
+
unit: 'px',
|
| 422 |
+
format: [800, contentHeight + 40],
|
| 423 |
+
orientation: 'portrait',
|
| 424 |
+
compress: true
|
| 425 |
+
}
|
| 426 |
+
};
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
html2pdf().from(styledHtml).set(opt).save();
|
| 430 |
+
}, 300);
|
| 431 |
+
} else {
|
| 432 |
+
|
| 433 |
+
const opt = {
|
| 434 |
+
margin: [10, 10, 10, 10],
|
| 435 |
+
filename: `${filename}.pdf`,
|
| 436 |
+
image: { type: 'jpeg', quality: 0.98 },
|
| 437 |
+
html2canvas: {
|
| 438 |
+
scale: 2,
|
| 439 |
+
useCORS: true,
|
| 440 |
+
letterRendering: true,
|
| 441 |
+
backgroundColor: '#ffffff'
|
| 442 |
+
},
|
| 443 |
+
jsPDF: {
|
| 444 |
+
unit: 'mm',
|
| 445 |
+
format: paperSize,
|
| 446 |
+
orientation: orientation
|
| 447 |
+
}
|
| 448 |
+
};
|
| 449 |
+
|
| 450 |
+
html2pdf().from(styledHtml).set(opt).save();
|
| 451 |
+
}
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
const readBtn = document.getElementById('btn-readmode');
|
| 456 |
+
if (readBtn) {
|
| 457 |
+
let readMode = false;
|
| 458 |
+
readBtn.addEventListener('click', () => {
|
| 459 |
+
readMode = !readMode;
|
| 460 |
+
document.body.classList.toggle('read-mode', readMode);
|
| 461 |
+
const icon = readBtn.querySelector('i');
|
| 462 |
+
if (icon) icon.className = readMode ? 'fa-solid fa-book' : 'fa-solid fa-book-open';
|
| 463 |
+
readBtn.textContent = readMode ? ' Exit read mode' : ' Read mode';
|
| 464 |
+
readBtn.prepend(icon);
|
| 465 |
+
});
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
if (docTitlePill) {
|
| 470 |
+
const savedTitle = localStorage.getItem('md-doc-title') || 'Welcome file';
|
| 471 |
+
docTitlePill.textContent = savedTitle;
|
| 472 |
+
docTitlePill.addEventListener('input', () => {
|
| 473 |
+
const v = (docTitlePill.textContent || '').trim() || 'Untitled';
|
| 474 |
+
localStorage.setItem('md-doc-title', v);
|
| 475 |
+
});
|
| 476 |
+
}
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
window.addEventListener('DOMContentLoaded', () => {
|
| 480 |
+
|
| 481 |
+
const ensureCss = (href) => { if (!document.querySelector(`link[href="${href}"]`)) { const l=document.createElement('link'); l.rel='stylesheet'; l.href=href; document.head.appendChild(l);} };
|
| 482 |
+
const ensureScript = (src, cb) => { if (document.querySelector(`script[src="${src}"]`)) { cb && cb(); return; } const s=document.createElement('script'); s.src=src; s.defer=true; s.onload=() => cb && cb(); document.body.appendChild(s); };
|
| 483 |
+
|
| 484 |
+
ensureCss('https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css');
|
| 485 |
+
ensureScript('https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js', () => {
|
| 486 |
+
ensureScript('https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js', () => {
|
| 487 |
+
try { renderPreview(getMarkdown()); } catch {}
|
| 488 |
+
});
|
| 489 |
+
});
|
| 490 |
+
|
| 491 |
+
ensureScript('https://cdn.jsdelivr.net/npm/mermaid@10.9.1/dist/mermaid.min.js', () => { try { renderPreview(getMarkdown()); } catch {} });
|
| 492 |
+
setupEditor();
|
| 493 |
+
setupSplit();
|
| 494 |
+
bindActions();
|
| 495 |
+
|
| 496 |
+
const aboutBtn = document.getElementById('btn-about');
|
| 497 |
+
if (aboutBtn) {
|
| 498 |
+
const aboutEl = document.getElementById('aboutModal');
|
| 499 |
+
const aboutModal = new bootstrap.Modal(aboutEl);
|
| 500 |
+
aboutBtn.addEventListener('click', () => aboutModal.show());
|
| 501 |
+
|
| 502 |
+
aboutEl.addEventListener('hidden.bs.modal', () => {
|
| 503 |
+
document.body.classList.remove('modal-open');
|
| 504 |
+
document.body.style.removeProperty('padding-right');
|
| 505 |
+
document.querySelectorAll('.modal-backdrop').forEach(b => b.remove());
|
| 506 |
+
|
| 507 |
+
document.documentElement.style.overflow = '';
|
| 508 |
+
document.body.style.overflow = '';
|
| 509 |
+
});
|
| 510 |
+
}
|
| 511 |
+
});
|
static/style.css
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* General */
|
| 2 |
+
:root {
|
| 3 |
+
--bg: #0f172a; /* slate-900 */
|
| 4 |
+
--panel: #0b1220; /* deep card */
|
| 5 |
+
--panel-contrast: #0b1220f5;
|
| 6 |
+
--text: #e2e8f0; /* slate-200 */
|
| 7 |
+
--muted: #94a3b8; /* slate-400 */
|
| 8 |
+
--border: #1f2937; /* slate-800 */
|
| 9 |
+
--accent: #3b82f6; /* blue-500 */
|
| 10 |
+
--code-bg: #0b1220;
|
| 11 |
+
--code-border: #1f2937;
|
| 12 |
+
--blockquote-bg: rgba(255,255,255,0.03);
|
| 13 |
+
--toolbar-icon: #e5e7eb; /* brighter by default in dark */
|
| 14 |
+
--toolbar-icon-active: #ffffff; /* bright for active/hover */
|
| 15 |
+
--toolbar-bg: var(--panel);
|
| 16 |
+
--toolbar-hover: rgba(255,255,255,0.06);
|
| 17 |
+
--toolbar-shadow: 0 2px 8px rgba(0,0,0,.25);
|
| 18 |
+
}
|
| 19 |
+
.light {
|
| 20 |
+
--bg: #f6f8fa;
|
| 21 |
+
--panel: #ffffff;
|
| 22 |
+
--panel-contrast: #ffffffcc;
|
| 23 |
+
--text: #24292f;
|
| 24 |
+
--muted: #6b7280;
|
| 25 |
+
--border: #d0d7de;
|
| 26 |
+
--accent: #2563eb;
|
| 27 |
+
--code-bg: #f6f8fa;
|
| 28 |
+
--code-border: #d0d7de;
|
| 29 |
+
--blockquote-bg: #f8fafc;
|
| 30 |
+
--toolbar-icon: #1f2937; /* darker for light bg */
|
| 31 |
+
--toolbar-icon-active: #111827;
|
| 32 |
+
--toolbar-bg: #ffffff;
|
| 33 |
+
--toolbar-hover: rgba(0,0,0,0.06);
|
| 34 |
+
--toolbar-shadow: 0 2px 10px rgba(0,0,0,.08);
|
| 35 |
+
}
|
| 36 |
+
html, body { height: 100%; }
|
| 37 |
+
body {
|
| 38 |
+
background: radial-gradient(1200px 800px at 10% -10%, #1f2937 0%, var(--bg) 40%),
|
| 39 |
+
radial-gradient(800px 600px at 110% 10%, #0b1220 0%, var(--bg) 50%);
|
| 40 |
+
color: var(--text);
|
| 41 |
+
font-family: 'Inter', 'Roboto', 'Segoe UI', Arial, sans-serif;
|
| 42 |
+
}
|
| 43 |
+
.light body { background: var(--bg); }
|
| 44 |
+
|
| 45 |
+
/* Editor and preview */
|
| 46 |
+
#preview { background: transparent; }
|
| 47 |
+
|
| 48 |
+
.EasyMDEContainer { height: 100%; display: flex; flex-direction: column; }
|
| 49 |
+
.EasyMDEContainer .CodeMirror { flex: 1 1 auto; height: 100% !important; font-size: 0.98rem; background: var(--panel); color: var(--text); }
|
| 50 |
+
/* Enhanced toolbar layout for consistent alignment */
|
| 51 |
+
.editor-toolbar {
|
| 52 |
+
background: var(--toolbar-bg);
|
| 53 |
+
border-color: var(--border) !important;
|
| 54 |
+
border-radius: 12px 12px 0 0;
|
| 55 |
+
box-shadow: var(--toolbar-shadow);
|
| 56 |
+
padding: 6px 8px;
|
| 57 |
+
line-height: 1; /* prevent baseline drift */
|
| 58 |
+
}
|
| 59 |
+
.editor-toolbar a {
|
| 60 |
+
color: var(--toolbar-icon) !important;
|
| 61 |
+
display: inline-flex !important;
|
| 62 |
+
align-items: center !important;
|
| 63 |
+
justify-content: center !important;
|
| 64 |
+
height: 34px !important;
|
| 65 |
+
min-width: 34px !important;
|
| 66 |
+
padding: 0 8px !important;
|
| 67 |
+
margin: 2px 3px !important;
|
| 68 |
+
border-radius: 8px !important;
|
| 69 |
+
transition: background .15s ease, color .15s ease, transform .05s ease;
|
| 70 |
+
font-size: 1.05rem !important;
|
| 71 |
+
}
|
| 72 |
+
.editor-toolbar a:hover, .editor-toolbar a.active {
|
| 73 |
+
color: var(--toolbar-icon-active) !important;
|
| 74 |
+
background: var(--toolbar-hover) !important;
|
| 75 |
+
}
|
| 76 |
+
/* Ensure all icon glyphs inherit the color (FA4 + FA6 + SVGs) */
|
| 77 |
+
.editor-toolbar .fa, .editor-toolbar .fa:before,
|
| 78 |
+
.editor-toolbar svg { color: inherit !important; fill: currentColor !important; }
|
| 79 |
+
.editor-toolbar i.separator { border-color: var(--border) !important; opacity: .7; margin: 0 6px !important; }
|
| 80 |
+
.CodeMirror-cursor { border-left: 2px solid var(--accent) !important; }
|
| 81 |
+
.CodeMirror-gutters { background: var(--panel); border-right: 1px solid var(--border); }
|
| 82 |
+
.cm-s-default .CodeMirror-linenumber { color: var(--muted); }
|
| 83 |
+
|
| 84 |
+
/* Make the separators visible in dark */
|
| 85 |
+
.editor-toolbar i.separator { border-color: var(--border) !important; opacity: .7; }
|
| 86 |
+
/* Ensure FA4 icons inherit color properly */
|
| 87 |
+
.editor-toolbar .fa, .editor-toolbar .fa:before { color: inherit; }
|
| 88 |
+
/* make icons aligned */
|
| 89 |
+
.editor-toolbar .fa { width: 18px; text-align: center; }
|
| 90 |
+
|
| 91 |
+
.navbar .nav-link, .navbar-brand { color: inherit; }
|
| 92 |
+
.navbar .dropdown-item i { width: 1.25rem; }
|
| 93 |
+
/* Ensure brand has correct color in dark mode and on hover */
|
| 94 |
+
.dark .navbar .navbar-brand { color: #e5e7eb !important; }
|
| 95 |
+
.dark .navbar .navbar-brand:hover { color: #ffffff !important; background: transparent !important; }
|
| 96 |
+
|
| 97 |
+
/* Markdown content */
|
| 98 |
+
.markdown-body { max-width: 100%; color: var(--text); line-height: 1.7; }
|
| 99 |
+
.markdown-body h1, .markdown-body h2, .markdown-body h3,
|
| 100 |
+
.markdown-body h4, .markdown-body h5, .markdown-body h6 { color: var(--text); margin-top: 1.25rem; font-weight: 700; }
|
| 101 |
+
.markdown-body p, .markdown-body li { color: var(--text); }
|
| 102 |
+
.markdown-body a { color: var(--accent); text-decoration: none; }
|
| 103 |
+
.markdown-body a:hover { text-decoration: underline; }
|
| 104 |
+
.markdown-body hr { border: 0; border-top: 1px solid var(--border); margin: 1.25rem 0; }
|
| 105 |
+
.markdown-body blockquote { border-left: 4px solid var(--border); padding: .5rem 1rem; color: var(--muted); background: var(--blockquote-bg); }
|
| 106 |
+
.markdown-body pre, .markdown-body code { background: var(--code-bg); border: 1px solid var(--code-border); color: var(--text); }
|
| 107 |
+
.markdown-body pre { padding: .75rem; border-radius: 8px; overflow: auto; }
|
| 108 |
+
.markdown-body table { border-collapse: collapse; width: 100%; background: var(--panel); }
|
| 109 |
+
/* Stronger specificity to override Bootstrap's .table */
|
| 110 |
+
.markdown-body table th, .markdown-body table td { border: 1px solid var(--border); padding: .6rem .8rem; color: var(--text) !important; opacity: 1 !important; }
|
| 111 |
+
.markdown-body table thead th { color: var(--text) !important; }
|
| 112 |
+
.markdown-body table tbody td { color: var(--text) !important; }
|
| 113 |
+
.markdown-body table tr, .markdown-body table td, .markdown-body table th { filter: none !important; }
|
| 114 |
+
.markdown-body table th { background: #1b2433; color: #e2e8f0 !important; font-weight: 600; }
|
| 115 |
+
/* Dark mode body cell backgrounds for contrast */
|
| 116 |
+
.markdown-body table td { background: rgba(255,255,255,0.065); }
|
| 117 |
+
.markdown-body tr:nth-child(even) td { background: rgba(255,255,255,0.06); }
|
| 118 |
+
.markdown-body tr:hover td { background: rgba(59,130,246,0.12); }
|
| 119 |
+
.light .markdown-body th { background: #f0f3f6; color: #24292f; }
|
| 120 |
+
.light .markdown-body tr:nth-child(even) td { background: #fafbfc; }
|
| 121 |
+
#preview img { max-width: 100%; height: auto; }
|
| 122 |
+
|
| 123 |
+
/* Hard overrides for dark-mode table inside preview to defeat any framework table styles */
|
| 124 |
+
.dark #preview .markdown-body table { background: var(--panel) !important; border-color: var(--border) !important; }
|
| 125 |
+
.dark #preview .markdown-body table td,
|
| 126 |
+
.dark #preview .markdown-body table th {
|
| 127 |
+
color: var(--text) !important;
|
| 128 |
+
border-color: var(--border) !important;
|
| 129 |
+
opacity: 1 !important;
|
| 130 |
+
filter: none !important;
|
| 131 |
+
}
|
| 132 |
+
.dark #preview .markdown-body table td { background-color: rgba(255,255,255,0.07) !important; }
|
| 133 |
+
.dark #preview .markdown-body tr:nth-child(even) td { background-color: rgba(255,255,255,0.1) !important; }
|
| 134 |
+
.dark #preview .markdown-body tr:hover td { background-color: rgba(59,130,246,0.18) !important; }
|
| 135 |
+
|
| 136 |
+
/* Force bright text for all table content in dark mode */
|
| 137 |
+
.dark .markdown-body table,
|
| 138 |
+
.dark .markdown-body table * {
|
| 139 |
+
color: var(--text) !important;
|
| 140 |
+
opacity: 1 !important;
|
| 141 |
+
}
|
| 142 |
+
/* Extra specificity for body cells and their content */
|
| 143 |
+
.dark .markdown-body table td,
|
| 144 |
+
.dark .markdown-body table td * { color: var(--text) !important; }
|
| 145 |
+
/* Max specificity hammer for stubborn overrides */
|
| 146 |
+
.dark #preview .markdown-body table tbody td,
|
| 147 |
+
.dark #preview .markdown-body table tbody td * {
|
| 148 |
+
color: #e5e7eb !important;
|
| 149 |
+
opacity: 1 !important;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/* Utility */
|
| 153 |
+
button.nav-link { cursor: pointer; }
|
| 154 |
+
|
| 155 |
+
/* Modern shell */
|
| 156 |
+
.app-header {
|
| 157 |
+
backdrop-filter: blur(12px);
|
| 158 |
+
background: linear-gradient(180deg, var(--panel-contrast), transparent);
|
| 159 |
+
border-bottom: 1px solid var(--border);
|
| 160 |
+
}
|
| 161 |
+
.modal-open .app-header { backdrop-filter: none !important; background: var(--panel) !important; }
|
| 162 |
+
.app-shell { min-height: calc(100vh - 64px); }
|
| 163 |
+
.pane { display: flex; flex-direction: column; min-width: 0; }
|
| 164 |
+
.card-pane {
|
| 165 |
+
background: linear-gradient(180deg, var(--panel), var(--panel));
|
| 166 |
+
border: 1px solid var(--border);
|
| 167 |
+
border-radius: 14px;
|
| 168 |
+
box-shadow: 0 10px 30px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.02);
|
| 169 |
+
}
|
| 170 |
+
.light .card-pane { box-shadow: 0 10px 24px rgba(0,0,0,.08), inset 0 1px 0 rgba(255,255,255,.6); }
|
| 171 |
+
.pane-header {
|
| 172 |
+
padding: .75rem 1rem;
|
| 173 |
+
border-bottom: 1px solid var(--border);
|
| 174 |
+
color: var(--text);
|
| 175 |
+
}
|
| 176 |
+
.pane-header .text-muted { color: var(--muted) !important; }
|
| 177 |
+
|
| 178 |
+
/* List group harmonization in modal */
|
| 179 |
+
.list-group-item { background: var(--panel); color: var(--text); border-color: var(--border); }
|
| 180 |
+
.modal-content.bg-body { background: var(--panel); color: var(--text); border-color: var(--border); }
|
| 181 |
+
|
| 182 |
+
/* Theme toggle */
|
| 183 |
+
.form-switch .form-check-input { cursor: pointer; }
|
| 184 |
+
|
| 185 |
+
/* Slightly lighter CodeMirror caret & line-height for readability */
|
| 186 |
+
.EasyMDEContainer .CodeMirror { line-height: 1.55; }
|
| 187 |
+
|
| 188 |
+
/* Toolbar can wrap into multiple rows (left-aligned) */
|
| 189 |
+
.editor-toolbar {
|
| 190 |
+
display: flex !important;
|
| 191 |
+
flex-wrap: wrap !important; /* allow wrapping */
|
| 192 |
+
align-items: center !important;
|
| 193 |
+
white-space: normal !important;
|
| 194 |
+
overflow: visible !important; /* no horizontal scrollbar */
|
| 195 |
+
text-align: left !important;
|
| 196 |
+
gap: 2px 4px; /* spacing between rows/items */
|
| 197 |
+
}
|
| 198 |
+
.editor-toolbar a, .editor-toolbar i.separator {
|
| 199 |
+
display: inline-flex !important;
|
| 200 |
+
flex: 0 0 auto !important;
|
| 201 |
+
vertical-align: middle !important;
|
| 202 |
+
}
|
| 203 |
+
/* Keep table icon aligned */
|
| 204 |
+
.editor-toolbar .fa-table { line-height: 1 !important; font-size: 1.05rem !important; }
|
| 205 |
+
|
| 206 |
+
/* Fix Bootstrap conflict: EasyMDE 'table' toolbar button should not take full width */
|
| 207 |
+
.editor-toolbar a.table,
|
| 208 |
+
.editor-toolbar button.table {
|
| 209 |
+
display: inline-flex !important;
|
| 210 |
+
width: auto !important;
|
| 211 |
+
min-width: 0 !important;
|
| 212 |
+
height: 34px !important;
|
| 213 |
+
align-items: center !important;
|
| 214 |
+
justify-content: center !important;
|
| 215 |
+
margin: 2px 3px !important;
|
| 216 |
+
padding: 0 8px !important;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
/* Ensure navbar dropdown overlays editor panes */
|
| 220 |
+
.navbar { position: relative; z-index: 1500; }
|
| 221 |
+
.dropdown-menu { z-index: 2000; }
|
| 222 |
+
|
| 223 |
+
/* Force FA6 icons to render in dropdown */
|
| 224 |
+
.dropdown-menu .fa, .dropdown-menu .fa-solid, .dropdown-menu i.fa-solid {
|
| 225 |
+
font-family: "Font Awesome 6 Free" !important;
|
| 226 |
+
font-weight: 900 !important;
|
| 227 |
+
display: inline-block;
|
| 228 |
+
width: 1.25rem;
|
| 229 |
+
text-align: center;
|
| 230 |
+
}
|
| 231 |
+
/* Also ensure icon color follows text */
|
| 232 |
+
.dropdown-menu i { color: inherit; }
|
| 233 |
+
|
| 234 |
+
/* Dropdown theming */
|
| 235 |
+
.dropdown-menu { border-radius: 10px; border: 1px solid var(--border); box-shadow: 0 8px 24px rgba(0,0,0,.15); }
|
| 236 |
+
.dropdown-menu .dropdown-item { padding: .6rem .9rem; display: flex; align-items: center; gap: .6rem; }
|
| 237 |
+
.dropdown-menu .dropdown-item i { width: 1.1rem; text-align: center; }
|
| 238 |
+
|
| 239 |
+
.dark .dropdown-menu { background: #0f172a; color: #e5e7eb; border-color: #1f2937; box-shadow: 0 12px 28px rgba(0,0,0,.45); }
|
| 240 |
+
.dark .dropdown-menu .dropdown-item { color: #e5e7eb; }
|
| 241 |
+
.dark .dropdown-menu .dropdown-item:hover { background: rgba(255,255,255,0.08); color: #fff; }
|
| 242 |
+
/* caret contrast */
|
| 243 |
+
.dropdown-menu::before { border-bottom-color: var(--border); }
|
| 244 |
+
|
| 245 |
+
/* Dark mode overrides to make toolbar icons white */
|
| 246 |
+
.dark .editor-toolbar a { color: #ffffff !important; }
|
| 247 |
+
.dark .editor-toolbar a:hover, .dark .editor-toolbar a.active { color: #ffffff !important; background: rgba(255,255,255,0.12) !important; }
|
| 248 |
+
.dark .editor-toolbar .fa, .dark .editor-toolbar .fa:before, .dark .editor-toolbar svg { color: #ffffff !important; fill: #ffffff !important; }
|
| 249 |
+
|
| 250 |
+
/* Dark mode toolbar hover/focus */
|
| 251 |
+
.dark .editor-toolbar a:hover,
|
| 252 |
+
.dark .editor-toolbar a:focus,
|
| 253 |
+
.dark .editor-toolbar a.active {
|
| 254 |
+
background: rgba(255,255,255,0.18) !important;
|
| 255 |
+
outline: none !important;
|
| 256 |
+
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.25) !important;
|
| 257 |
+
}
|
| 258 |
+
.dark .editor-toolbar a:focus-visible {
|
| 259 |
+
box-shadow: inset 0 0 0 2px #60a5fa !important; /* blue ring for keyboard focus */
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
/* Dark mode toolbar buttons - unified look */
|
| 263 |
+
.dark .editor-toolbar a,
|
| 264 |
+
.dark .editor-toolbar button {
|
| 265 |
+
color: #ffffff !important;
|
| 266 |
+
border-radius: 8px !important;
|
| 267 |
+
transition: background .15s ease, box-shadow .15s ease, color .15s ease;
|
| 268 |
+
}
|
| 269 |
+
/* Softer hover/active (no solid white block) */
|
| 270 |
+
.dark .editor-toolbar a:hover,
|
| 271 |
+
.dark .editor-toolbar button:hover,
|
| 272 |
+
.dark .editor-toolbar a.active,
|
| 273 |
+
.dark .editor-toolbar button.active {
|
| 274 |
+
background: rgba(255,255,255,0.12) !important;
|
| 275 |
+
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.18) !important;
|
| 276 |
+
}
|
| 277 |
+
/* Remove any default white background from library styles */
|
| 278 |
+
.dark .editor-toolbar a:hover *,
|
| 279 |
+
.dark .editor-toolbar button:hover * { background: transparent !important; }
|
| 280 |
+
|
| 281 |
+
/* Navbar icons ensure FA6 solid */
|
| 282 |
+
.navbar .fa-solid { font-family: "Font Awesome 6 Free" !important; font-weight: 900 !important; }
|
| 283 |
+
|
| 284 |
+
/* Document title input */
|
| 285 |
+
.doc-title-input { width: 220px; background: transparent; color: var(--text); border-color: var(--border); }
|
| 286 |
+
.doc-title-input::placeholder { color: var(--muted); }
|
| 287 |
+
.dark .doc-title-input { background: rgba(255,255,255,0.06); color: #e5e7eb; border-color: #374151; }
|
| 288 |
+
.dark .doc-title-input:focus { background: rgba(255,255,255,0.09); color: #fff; }
|
| 289 |
+
|
| 290 |
+
/* Navbar button alignment */
|
| 291 |
+
.navbar .nav-link,
|
| 292 |
+
.navbar .dropdown-toggle {
|
| 293 |
+
display: inline-flex;
|
| 294 |
+
align-items: center;
|
| 295 |
+
gap: 6px;
|
| 296 |
+
padding: 6px 10px;
|
| 297 |
+
border-radius: 8px;
|
| 298 |
+
}
|
| 299 |
+
.navbar .nav-link i,
|
| 300 |
+
.navbar .dropdown-toggle i { color: inherit; }
|
| 301 |
+
/* Light hover */
|
| 302 |
+
.navbar .nav-link:hover,
|
| 303 |
+
.navbar .dropdown-toggle:hover { background: rgba(0,0,0,0.06); }
|
| 304 |
+
/* Dark hover */
|
| 305 |
+
.dark .navbar .nav-link,
|
| 306 |
+
.dark .navbar .dropdown-toggle { color: #e5e7eb; }
|
| 307 |
+
.dark .navbar .nav-link:hover,
|
| 308 |
+
.dark .navbar .dropdown-toggle:hover { background: rgba(255,255,255,0.12); color: #ffffff; }
|
| 309 |
+
/* Ensure export caret/icon position */
|
| 310 |
+
.navbar .dropdown-toggle::after { margin-left: 6px; }
|
| 311 |
+
|
| 312 |
+
.doc-title-pill {
|
| 313 |
+
display: inline-block;
|
| 314 |
+
max-width: 360px;
|
| 315 |
+
padding: 4px 10px;
|
| 316 |
+
border-radius: 10px;
|
| 317 |
+
background: rgba(0,0,0,0.04);
|
| 318 |
+
border: 1px solid var(--border);
|
| 319 |
+
color: var(--text);
|
| 320 |
+
cursor: text;
|
| 321 |
+
}
|
| 322 |
+
.doc-title-pill:focus { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
|
| 323 |
+
.dark .doc-title-pill { background: rgba(255,255,255,0.06); border-color: #374151; color: #e5e7eb; }
|
| 324 |
+
|
| 325 |
+
/* Theme button */
|
| 326 |
+
#btn-theme { display: inline-flex; align-items: center; gap: 6px; }
|
| 327 |
+
.dark #btn-theme { color: #e5e7eb; }
|
| 328 |
+
.dark #btn-theme:hover { background: rgba(255,255,255,0.12); border-radius: 8px; }
|
| 329 |
+
|
| 330 |
+
/* Prevent dropdown text cutoff and hide arrow caret */
|
| 331 |
+
.dropdown-menu { min-width: 260px; }
|
| 332 |
+
.dropdown-menu .dropdown-item { white-space: nowrap; }
|
| 333 |
+
.dropdown-menu::before, .dropdown-menu::after { display: none !important; }
|
| 334 |
+
|
| 335 |
+
/* Read mode helper: smooth layout change */
|
| 336 |
+
#leftPane, #rightPane { transition: all .2s ease; }
|
| 337 |
+
|
| 338 |
+
/* Read mode: preview full page, hide editor */
|
| 339 |
+
.read-mode .app-header { position: sticky; top: 0; z-index: 1500; }
|
| 340 |
+
.read-mode #leftPane { display: none !important; }
|
| 341 |
+
.read-mode #rightPane { width: 100% !important; flex: 1 1 auto; }
|
| 342 |
+
.read-mode #rightPane[class*="col-"] { float: none; max-width: 100%; }
|
| 343 |
+
/* Expand row height in read mode */
|
| 344 |
+
.read-mode .app-shell .row { height: calc(100vh - 72px) !important; }
|
| 345 |
+
|
| 346 |
+
/* Footer behavior: normal flow in default; fixed at bottom in read mode */
|
| 347 |
+
.read-mode .footer-credit-wrap {
|
| 348 |
+
position: fixed;
|
| 349 |
+
left: 0;
|
| 350 |
+
right: 0;
|
| 351 |
+
bottom: 20px;
|
| 352 |
+
z-index: 1500;
|
| 353 |
+
}
|
| 354 |
+
.read-mode .footer-credit-wrap .footer-credit {
|
| 355 |
+
background: rgba(0,0,0,0.8);
|
| 356 |
+
color: #ffffff;
|
| 357 |
+
border: 1px solid rgba(255,255,255,0.2);
|
| 358 |
+
}
|
| 359 |
+
.read-mode .footer-credit-wrap .footer-credit { pointer-events: auto; }
|
| 360 |
+
|
| 361 |
+
/* Samples modal theming */
|
| 362 |
+
#samplesModal .modal-content { border: 1px solid var(--border); }
|
| 363 |
+
#samplesModal .list-group-item { background: var(--panel); color: var(--text); border-color: var(--border); }
|
| 364 |
+
#samplesModal .list-group-item .fw-semibold { color: var(--text); }
|
| 365 |
+
#samplesModal .text-muted { color: var(--muted) !important; }
|
| 366 |
+
#samplesModal .btn.btn-outline-primary { color: var(--text); border-color: var(--border); }
|
| 367 |
+
#samplesModal .btn.btn-outline-primary:hover { background: var(--toolbar-hover); color: var(--text); }
|
| 368 |
+
#samplesModal .btn.btn-outline-secondary { color: var(--text); border-color: var(--border); }
|
| 369 |
+
#samplesModal .btn.btn-outline-secondary:hover { background: var(--toolbar-hover); color: var(--text); }
|
| 370 |
+
/* Light mode hover contrast */
|
| 371 |
+
.light #samplesModal .btn:hover { background: rgba(0,0,0,0.06); }
|
| 372 |
+
|
| 373 |
+
/* Global modal dark-mode fixes */
|
| 374 |
+
.dark .modal-content { background: var(--panel); color: var(--text); border-color: var(--border); }
|
| 375 |
+
.dark .modal-header, .dark .modal-footer { border-color: var(--border); }
|
| 376 |
+
.dark .modal-title { color: var(--text); }
|
| 377 |
+
.dark .btn-close { filter: invert(1) opacity(0.8); }
|
| 378 |
+
.dark .modal .btn-outline-primary, .dark .modal .btn-outline-secondary { color: var(--text); border-color: var(--border); }
|
| 379 |
+
.dark .modal .btn-outline-primary:hover, .dark .modal .btn-outline-secondary:hover { background: var(--toolbar-hover); color: #fff; }
|
| 380 |
+
/* About modal cards and links */
|
| 381 |
+
.dark .modal .card.bg-body-secondary { background: rgba(255,255,255,0.06); border: 1px solid var(--border); color: var(--text); }
|
| 382 |
+
.dark .modal .card.bg-body-secondary * { color: var(--text) !important; }
|
| 383 |
+
.dark .modal a { color: var(--accent); }
|
| 384 |
+
.dark .modal a:hover { text-decoration: underline; }
|
| 385 |
+
/* About modal: ensure strong contrast in dark mode */
|
| 386 |
+
.dark #aboutModal .modal-content { background: var(--panel); color: var(--text); border-color: var(--border); }
|
| 387 |
+
.dark #aboutModal .modal-header, .dark #aboutModal .modal-footer { border-color: var(--border); }
|
| 388 |
+
.dark #aboutModal .modal-title { color: var(--text); }
|
| 389 |
+
.dark #aboutModal .modal-body p,
|
| 390 |
+
.dark #aboutModal .modal-body li,
|
| 391 |
+
.dark #aboutModal .modal-body .fw-semibold { color: var(--text) !important; }
|
| 392 |
+
.dark #aboutModal .card { background: rgba(255,255,255,0.07) !important; border: 1px solid var(--border) !important; }
|
| 393 |
+
.dark #aboutModal .card * { color: var(--text) !important; }
|
| 394 |
+
.dark #aboutModal a { color: var(--accent) !important; }
|
| 395 |
+
.dark #aboutModal a:hover { text-decoration: underline; }
|
| 396 |
+
|
| 397 |
+
/* Footer credit box */
|
| 398 |
+
.footer-credit {
|
| 399 |
+
padding: 10px 18px;
|
| 400 |
+
border-radius: 14px;
|
| 401 |
+
background: linear-gradient(90deg, rgba(255,255,255,0.9), rgba(59,130,246,0.15));
|
| 402 |
+
color: #1f2937;
|
| 403 |
+
font-weight: 600;
|
| 404 |
+
box-shadow: 0 6px 18px rgba(0,0,0,.12);
|
| 405 |
+
border: 1px solid rgba(0,0,0,0.06);
|
| 406 |
+
}
|
| 407 |
+
.dark .footer-credit {
|
| 408 |
+
background: linear-gradient(90deg, rgba(17,24,39,0.9), rgba(59,130,246,0.25));
|
| 409 |
+
color: #e5e7eb;
|
| 410 |
+
border-color: rgba(255,255,255,0.08);
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
.footer-credit-wrap { padding: 16px 0 24px 0; }
|
| 414 |
+
.read-mode .footer-credit-wrap { display: none !important; }
|
templates/index.html
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% set title = 'Rich Markdown Editor' %}
|
| 2 |
+
<!DOCTYPE html>
|
| 3 |
+
<html lang="en">
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="UTF-8" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<title>{{ title }}</title>
|
| 8 |
+
<meta name="author" content="Aradhya Pavan H S" />
|
| 9 |
+
<link rel="author" href="https://github.com/aradhyapavan" />
|
| 10 |
+
<meta name="application-name" content="Rich Markdown Editor" />
|
| 11 |
+
<meta name="generator" content="Designgen developed by Aradhya Pavan" />
|
| 12 |
+
<meta name="description" content="Free Markdown editor and online Markdown previewer. Convert Markdown to HTML and export to PDF. Developed by Aradhya Pavan." />
|
| 13 |
+
<meta name="keywords" content="Free Markdown editor, Online Markdown Previewer, Markdown to HTML, Markdown to PDF, Markdown editor online, Markdown converter, Markdown viewer, HTML export, PDF export, EasyMDE, KaTeX, Mermaid, GitHub-style Markdown, Designgen, Aradhya Pavan H S, Aradhya Pavan" />
|
| 14 |
+
<meta name="robots" content="index, follow" />
|
| 15 |
+
<link rel="canonical" href="https://github.com/aradhyapavan" />
|
| 16 |
+
<meta property="og:title" content="{{ title }}" />
|
| 17 |
+
<meta property="og:description" content="Free Markdown editor and online Markdown previewer. Convert Markdown to HTML and export to PDF. Developed by Aradhya Pavan." />
|
| 18 |
+
<meta property="og:type" content="website" />
|
| 19 |
+
<meta property="og:url" content="https://github.com/aradhyapavan" />
|
| 20 |
+
<meta name="twitter:card" content="summary" />
|
| 21 |
+
<meta name="twitter:title" content="{{ title }}" />
|
| 22 |
+
<meta name="twitter:description" content="Free Markdown editor and online Markdown previewer. Convert Markdown to HTML and export to PDF. Developed by Aradhya Pavan." />
|
| 23 |
+
<meta name="theme-color" content="#0f172a" />
|
| 24 |
+
<link rel="icon" href="{{ url_for('static', filename='favicon/favicon.ico') }}" type="image/x-icon" />
|
| 25 |
+
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon/favicon.ico') }}" type="image/x-icon" />
|
| 26 |
+
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon/favicon.svg') }}" />
|
| 27 |
+
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='favicon/android-chrome-512x512.png') }}" />
|
| 28 |
+
<link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', filename='favicon/android-chrome-512x512.png') }}" />
|
| 29 |
+
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='favicon/android-chrome-512x512.png') }}" />
|
| 30 |
+
<link rel="manifest" href="{{ url_for('static', filename='favicon/site.webmanifest') }}" />
|
| 31 |
+
|
| 32 |
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" />
|
| 33 |
+
|
| 34 |
+
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 35 |
+
|
| 36 |
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet" />
|
| 37 |
+
|
| 38 |
+
<link href="https://unpkg.com/@primer/css@21.0.7/dist/primer.css" rel="stylesheet" />
|
| 39 |
+
|
| 40 |
+
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css" />
|
| 41 |
+
|
| 42 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
|
| 43 |
+
</head>
|
| 44 |
+
<body>
|
| 45 |
+
|
| 46 |
+
<nav class="navbar navbar-expand-lg navbar-light app-header px-3">
|
| 47 |
+
<span class="navbar-brand fw-bold d-flex align-items-center"><i class="fa-solid fa-file-lines me-2"></i>Markdown Editor</span>
|
| 48 |
+
<div class="collapse navbar-collapse show">
|
| 49 |
+
<div class="ms-3 me-3 d-flex align-items-center gap-2">
|
| 50 |
+
<span class="text-muted small">File name</span>
|
| 51 |
+
<span id="doc-title-pill" class="doc-title-pill" contenteditable="true" spellcheck="false" title="Click to rename" aria-label="File name">Welcome file</span>
|
| 52 |
+
</div>
|
| 53 |
+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
| 54 |
+
<li class="nav-item"><button class="btn btn-link nav-link" id="btn-import-md"><i class="fa-solid fa-upload"></i> Import Markdown</button></li>
|
| 55 |
+
<li class="nav-item"><button class="btn btn-link nav-link" id="btn-import-html"><i class="fa-solid fa-file-import"></i> Import HTML</button></li>
|
| 56 |
+
<li class="nav-item"><button class="btn btn-link nav-link" id="btn-samples"><i class="fa-solid fa-wand-magic-sparkles"></i> Samples</button></li>
|
| 57 |
+
<li class="nav-item dropdown">
|
| 58 |
+
<a class="nav-link dropdown-toggle" href="#" id="exportDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="fa-solid fa-download"></i> Export</a>
|
| 59 |
+
<ul class="dropdown-menu" aria-labelledby="exportDropdown">
|
| 60 |
+
<li><button class="dropdown-item" id="btn-export-md"><i class="fa-solid fa-file-lines"></i> Markdown</button></li>
|
| 61 |
+
<li><button class="dropdown-item" id="btn-export-html"><i class="fa-solid fa-code"></i> HTML (raw)</button></li>
|
| 62 |
+
<li><button class="dropdown-item" id="btn-export-html-styled"><i class="fa-solid fa-paintbrush"></i> HTML (with styles)</button></li>
|
| 63 |
+
<li><hr class="dropdown-divider"></li>
|
| 64 |
+
<li><button class="dropdown-item" id="btn-export-pdf"><i class="fa-solid fa-file-pdf"></i> PDF</button></li>
|
| 65 |
+
</ul>
|
| 66 |
+
</li>
|
| 67 |
+
</ul>
|
| 68 |
+
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
| 69 |
+
<li class="nav-item"><button class="btn btn-link nav-link" id="btn-readmode"><i class="fa-solid fa-book-open"></i> Read mode</button></li>
|
| 70 |
+
<li class="nav-item"><button class="btn btn-link nav-link" id="btn-theme"><i class="fa-solid fa-moon"></i> Theme</button></li>
|
| 71 |
+
<li class="nav-item"><button class="btn btn-link nav-link" id="btn-about"><i class="fa-solid fa-user"></i> About</button></li>
|
| 72 |
+
</ul>
|
| 73 |
+
</div>
|
| 74 |
+
</nav>
|
| 75 |
+
|
| 76 |
+
<div class="container-fluid p-3 app-shell">
|
| 77 |
+
<div class="row g-3">
|
| 78 |
+
<div id="leftPane" class="col-md-6 d-flex flex-column pane card-pane">
|
| 79 |
+
<div class="pane-header d-flex align-items-center justify-content-between"><span class="fw-semibold"><i class="fa-solid fa-pen"></i> Editor</span><span class="text-muted small" id="status-line">Ready</span></div>
|
| 80 |
+
<textarea id="editor"></textarea>
|
| 81 |
+
</div>
|
| 82 |
+
<div id="rightPane" class="col-md-6 d-flex flex-column pane card-pane">
|
| 83 |
+
<div class="pane-header d-flex align-items-center justify-content-between">
|
| 84 |
+
<span class="fw-semibold"><i class="fa-solid fa-eye"></i> Preview</span>
|
| 85 |
+
</div>
|
| 86 |
+
<div id="preview" class="p-4 flex-grow-1 overflow-auto"></div>
|
| 87 |
+
</div>
|
| 88 |
+
</div>
|
| 89 |
+
</div>
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
<div class="modal fade" id="aboutModal" tabindex="-1" aria-labelledby="aboutLabel" aria-hidden="true">
|
| 93 |
+
<div class="modal-dialog modal-lg modal-dialog-centered">
|
| 94 |
+
<div class="modal-content">
|
| 95 |
+
<div class="modal-header">
|
| 96 |
+
<h5 class="modal-title" id="aboutLabel"><i class="fa-solid fa-user-astronaut me-2"></i>About the Developer</h5>
|
| 97 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 98 |
+
</div>
|
| 99 |
+
<div class="modal-body">
|
| 100 |
+
<div class="d-flex flex-column gap-2">
|
| 101 |
+
<p class="mb-0">Hi, I'm <strong>Aradhya Pavan H S</strong> — a passionate techie who loves building useful tools and contributing to open source. I got fed up with online Markdown tools and their limitations, so I built this clean, fast editor with a great preview and rich exports (Markdown, HTML, styled HTML, PDF). I built it for myself—feel free to clone it and use it.</p>
|
| 102 |
+
<div class="row g-3 mt-1">
|
| 103 |
+
<div class="col-md-6">
|
| 104 |
+
<div class="card bg-body-secondary border-0">
|
| 105 |
+
<div class="card-body py-3">
|
| 106 |
+
<div class="fw-semibold mb-1"><i class="fa-solid fa-bolt me-2"></i>What I do</div>
|
| 107 |
+
<ul class="mb-0 small">
|
| 108 |
+
<li>UI engineering and component libraries</li>
|
| 109 |
+
<li>APIs, automation, and tooling</li>
|
| 110 |
+
<li>Docs, DX, and product polish</li>
|
| 111 |
+
<li>Contributing to open source and community tooling</li>
|
| 112 |
+
</ul>
|
| 113 |
+
</div>
|
| 114 |
+
</div>
|
| 115 |
+
</div>
|
| 116 |
+
<div class="col-md-6">
|
| 117 |
+
<div class="card bg-body-secondary border-0">
|
| 118 |
+
<div class="card-body py-3">
|
| 119 |
+
<div class="fw-semibold mb-1"><i class="fa-solid fa-link me-2"></i>Links</div>
|
| 120 |
+
<ul class="mb-0 small">
|
| 121 |
+
<li><a href="https://aradhyapavan.github.io/" target="_blank" rel="noopener">Personal website</a></li>
|
| 122 |
+
<li><a href="https://github.com/aradhyapavan" target="_blank" rel="noopener">GitHub</a></li>
|
| 123 |
+
</ul>
|
| 124 |
+
</div>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
</div>
|
| 128 |
+
<div class="row g-3 mt-1">
|
| 129 |
+
<div class="col-12">
|
| 130 |
+
<div class="card bg-body-secondary border-0">
|
| 131 |
+
<div class="card-body py-3">
|
| 132 |
+
<div class="fw-semibold mb-1"><i class="fa-solid fa-star me-2"></i>Features</div>
|
| 133 |
+
<ul class="mb-0 small">
|
| 134 |
+
<li>Rich Markdown editor toolbar (headings, lists, tables, code, images, links)</li>
|
| 135 |
+
<li>Live preview with GitHub-style rendering</li>
|
| 136 |
+
<li>KaTeX math typesetting and Mermaid diagrams (sequence, flow, gantt)</li>
|
| 137 |
+
<li>Exports: Markdown (.md), HTML (raw), HTML (with styles), and PDF</li>
|
| 138 |
+
<li>Read mode for distraction-free full-page preview</li>
|
| 139 |
+
<li>Dark/Light themes with persistent preference</li>
|
| 140 |
+
<li>Editable file name in navbar; used in exports</li>
|
| 141 |
+
<li>Sample templates (README, Math Notes, API Docs, Meeting Notes)</li>
|
| 142 |
+
<li>Import Markdown or HTML files</li>
|
| 143 |
+
</ul>
|
| 144 |
+
</div>
|
| 145 |
+
</div>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
</div>
|
| 149 |
+
</div>
|
| 150 |
+
<div class="modal-footer">
|
| 151 |
+
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
|
| 152 |
+
</div>
|
| 153 |
+
</div>
|
| 154 |
+
</div>
|
| 155 |
+
</div>
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
<div class="modal fade" id="pdfOptionsModal" tabindex="-1" aria-labelledby="pdfOptionsLabel" aria-hidden="true">
|
| 159 |
+
<div class="modal-dialog modal-dialog-centered">
|
| 160 |
+
<div class="modal-content">
|
| 161 |
+
<div class="modal-header">
|
| 162 |
+
<h5 class="modal-title" id="pdfOptionsLabel"><i class="fa-solid fa-file-pdf me-2"></i>PDF Export Options</h5>
|
| 163 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 164 |
+
</div>
|
| 165 |
+
<div class="modal-body">
|
| 166 |
+
<div class="row g-3">
|
| 167 |
+
<div class="col-md-6">
|
| 168 |
+
<label class="form-label fw-semibold">Page Orientation</label>
|
| 169 |
+
<div class="form-check">
|
| 170 |
+
<input class="form-check-input" type="radio" name="orientation" id="portrait" value="portrait" checked>
|
| 171 |
+
<label class="form-check-label" for="portrait">
|
| 172 |
+
<i class="fa-solid fa-arrow-down me-2"></i>Portrait
|
| 173 |
+
</label>
|
| 174 |
+
</div>
|
| 175 |
+
<div class="form-check">
|
| 176 |
+
<input class="form-check-input" type="radio" name="orientation" id="landscape" value="landscape">
|
| 177 |
+
<label class="form-check-label" for="landscape">
|
| 178 |
+
<i class="fa-solid fa-arrow-right me-2"></i>Landscape
|
| 179 |
+
</label>
|
| 180 |
+
</div>
|
| 181 |
+
</div>
|
| 182 |
+
<div class="col-md-6">
|
| 183 |
+
<label class="form-label fw-semibold">Paper Size</label>
|
| 184 |
+
<select class="form-select" id="paperSize">
|
| 185 |
+
<option value="a4">A4 (210 × 297 mm)</option>
|
| 186 |
+
<option value="letter">Letter (8.5 × 11 in)</option>
|
| 187 |
+
<option value="legal">Legal (8.5 × 14 in)</option>
|
| 188 |
+
<option value="a3">A3 (297 × 420 mm)</option>
|
| 189 |
+
<option value="a5">A5 (148 × 210 mm)</option>
|
| 190 |
+
</select>
|
| 191 |
+
</div>
|
| 192 |
+
</div>
|
| 193 |
+
</div>
|
| 194 |
+
<div class="modal-footer">
|
| 195 |
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
| 196 |
+
<button type="button" class="btn btn-primary" id="btn-generate-pdf">Generate PDF</button>
|
| 197 |
+
</div>
|
| 198 |
+
</div>
|
| 199 |
+
</div>
|
| 200 |
+
</div>
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
<input type="file" id="file-md" accept=".md,.markdown,.txt" hidden>
|
| 204 |
+
<input type="file" id="file-html" accept=".html,.htm" hidden>
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
<div class="modal fade" id="samplesModal" tabindex="-1" aria-labelledby="samplesLabel" aria-hidden="true">
|
| 208 |
+
<div class="modal-dialog modal-xl modal-dialog-centered">
|
| 209 |
+
<div class="modal-content bg-body">
|
| 210 |
+
<div class="modal-header">
|
| 211 |
+
<h5 class="modal-title" id="samplesLabel"><i class="fa-solid fa-wand-magic-sparkles me-2"></i>Insert Markdown Samples</h5>
|
| 212 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 213 |
+
</div>
|
| 214 |
+
<div class="modal-body">
|
| 215 |
+
<div class="list-group">
|
| 216 |
+
<div class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
| 217 |
+
<div>
|
| 218 |
+
<div class="fw-semibold">Full Cheat Sheet</div>
|
| 219 |
+
<div class="text-muted small">Headings, emphasis, lists, tables, code, tasks, links, images, quotes, rules.</div>
|
| 220 |
+
</div>
|
| 221 |
+
<div>
|
| 222 |
+
<button class="btn btn-outline-primary me-2" data-sample="cheatsheet">Insert (replace)</button>
|
| 223 |
+
<button class="btn btn-outline-secondary" data-sample-append="cheatsheet">Append</button>
|
| 224 |
+
</div>
|
| 225 |
+
</div>
|
| 226 |
+
<div class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
| 227 |
+
<div>
|
| 228 |
+
<div class="fw-semibold">Email Template</div>
|
| 229 |
+
<div class="text-muted small">Professional email with bullet lists and signature.</div>
|
| 230 |
+
</div>
|
| 231 |
+
<div>
|
| 232 |
+
<button class="btn btn-outline-primary me-2" data-sample="email">Insert (replace)</button>
|
| 233 |
+
<button class="btn btn-outline-secondary" data-sample-append="email">Append</button>
|
| 234 |
+
</div>
|
| 235 |
+
</div>
|
| 236 |
+
<div class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
| 237 |
+
<div>
|
| 238 |
+
<div class="fw-semibold">README Starter</div>
|
| 239 |
+
<div class="text-muted small">Project badges, install, usage, features, license.</div>
|
| 240 |
+
</div>
|
| 241 |
+
<div>
|
| 242 |
+
<button class="btn btn-outline-primary me-2" data-sample="readme">Insert (replace)</button>
|
| 243 |
+
<button class="btn btn-outline-secondary" data-sample-append="readme">Append</button>
|
| 244 |
+
</div>
|
| 245 |
+
</div>
|
| 246 |
+
<div class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
| 247 |
+
<div>
|
| 248 |
+
<div class="fw-semibold">Pro GitHub README</div>
|
| 249 |
+
<div class="text-muted small">Badges, stack table, structure tree, CLI, Mermaid diagram.</div>
|
| 250 |
+
</div>
|
| 251 |
+
<div>
|
| 252 |
+
<button class="btn btn-outline-primary me-2" data-sample="github_readme_pro">Insert (replace)</button>
|
| 253 |
+
<button class="btn btn-outline-secondary" data-sample-append="github_readme_pro">Append</button>
|
| 254 |
+
</div>
|
| 255 |
+
</div>
|
| 256 |
+
<div class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
| 257 |
+
<div>
|
| 258 |
+
<div class="fw-semibold">Math Notes</div>
|
| 259 |
+
<div class="text-muted small">KaTeX inline/block formulas with references.</div>
|
| 260 |
+
</div>
|
| 261 |
+
<div>
|
| 262 |
+
<button class="btn btn-outline-primary me-2" data-sample="math_notes">Insert (replace)</button>
|
| 263 |
+
<button class="btn btn-outline-secondary" data-sample-append="math_notes">Append</button>
|
| 264 |
+
</div>
|
| 265 |
+
</div>
|
| 266 |
+
<div class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
| 267 |
+
<div>
|
| 268 |
+
<div class="fw-semibold">API Docs</div>
|
| 269 |
+
<div class="text-muted small">HTTP examples, JSON responses, and error table.</div>
|
| 270 |
+
</div>
|
| 271 |
+
<div>
|
| 272 |
+
<button class="btn btn-outline-primary me-2" data-sample="api_docs">Insert (replace)</button>
|
| 273 |
+
<button class="btn btn-outline-secondary" data-sample-append="api_docs">Append</button>
|
| 274 |
+
</div>
|
| 275 |
+
</div>
|
| 276 |
+
<div class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
|
| 277 |
+
<div>
|
| 278 |
+
<div class="fw-semibold">Meeting Notes + Gantt</div>
|
| 279 |
+
<div class="text-muted small">Agenda, actions, and Mermaid timeline.</div>
|
| 280 |
+
</div>
|
| 281 |
+
<div>
|
| 282 |
+
<button class="btn btn-outline-primary me-2" data-sample="meeting_notes">Insert (replace)</button>
|
| 283 |
+
<button class="btn btn-outline-secondary" data-sample-append="meeting_notes">Append</button>
|
| 284 |
+
</div>
|
| 285 |
+
</div>
|
| 286 |
+
</div>
|
| 287 |
+
</div>
|
| 288 |
+
<div class="modal-footer">
|
| 289 |
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
| 290 |
+
</div>
|
| 291 |
+
</div>
|
| 292 |
+
</div>
|
| 293 |
+
</div>
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
<div class="footer-credit-wrap">
|
| 297 |
+
<div class="d-flex justify-content-center">
|
| 298 |
+
<div class="footer-credit">Designed and Developed by <strong>Aradhya Pavan H S</strong></div>
|
| 299 |
+
</div>
|
| 300 |
+
</div>
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
| 304 |
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
| 305 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
| 306 |
+
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
|
| 307 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
| 308 |
+
<link id="hljs-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
| 309 |
+
|
| 310 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
|
| 311 |
+
|
| 312 |
+
<script>window.FLASK_EXPORT_HTML_URL = "{{ url_for('export_html') }}"; window.FLASK_EXPORT_MD_URL = "{{ url_for('export_md') }}";</script>
|
| 313 |
+
<script>
|
| 314 |
+
document.addEventListener('DOMContentLoaded', function(){
|
| 315 |
+
const aboutBtn = document.getElementById('btn-about');
|
| 316 |
+
if (aboutBtn) {
|
| 317 |
+
const aboutModal = new bootstrap.Modal(document.getElementById('aboutModal'));
|
| 318 |
+
aboutBtn.addEventListener('click', () => aboutModal.show());
|
| 319 |
+
}
|
| 320 |
+
});
|
| 321 |
+
</script>
|
| 322 |
+
<script src="{{ url_for('static', filename='main.js') }}"></script>
|
| 323 |
+
</body>
|
| 324 |
+
</html>
|