fixed some things. and did some UI changes

This commit is contained in:
2025-09-24 10:18:04 +02:00
parent 5e5ab6c6cc
commit 508624febc
11 changed files with 150 additions and 133 deletions

View File

@@ -122,7 +122,10 @@ def render_markdown(md_path: Path):
<title>{title}</title>
<link rel="stylesheet" href="../css/main.css">
<link rel="icon" type="image/x-icon" href="../css/favicon/favicon.ico">
<script src="../js/post/normal.js"></script>
<script src="../js/post/download.js" defer></script>
<style>
a {{ text-decoration: none; color: #0066cc; }}
</style>
</head>
<body style="display:flex; flex-direction:column; min-height:100%; margin:0;">
<main class="container" style="flex:1;">
@@ -144,6 +147,8 @@ def render_markdown(md_path: Path):
Hash 1 (<b>UTF-8</b>)<i>:{base64.b64encode(hash1.encode("utf-8")).decode("utf-8")}</i><br />
<img src="../css/icons/magnifier.webp" width="16" height="16" alt="Hash2" loading="lazy" style="display:inline; vertical-align:middle;" />
Hash 2 (<b>Windows-1252</b>)<i>:{base64.b64encode(hash2.encode("windows-1252")).decode("windows-1252")}</i><br />
<img src="../css/icons/save.webp" width="16" height="16" alt="Hash2" loading="lazy" style="display:inline; vertical-align:middle;" />
<a id="download-md">Download as Markdown</a>
</footer>
</body>
</html>"""
@@ -162,11 +167,11 @@ def render_markdown(md_path: Path):
# Create parent directories if needed
out_path.parent.mkdir(parents=True, exist_ok=True)
if obfuscate:
out_path.write_text(obfuscated_html, encoding="utf-8")
else:
out_path.write_text(clean_html, encoding="utf-8")
# if obfuscate:
# out_path.write_text(obfuscated_html, encoding="utf-8")
# else:
# out_path.write_text(clean_html, encoding="utf-8")
out_path.write_text(clean_html, encoding="utf-8")
Logger.log_debug(f"Rendered: {md_path} -> {out_path}")
def remove_html(md_path: Path):
@@ -184,7 +189,6 @@ def initial_scan(markdown_dir: Path):
def build_rust_parser() -> bool:
"""Attempt to build the Rust parser using cargo."""
fastmd_dir = ROOT / "fastmd"
if not fastmd_dir.exists():
@@ -269,34 +273,6 @@ if __name__ == "__main__":
else:
Logger.log_warning("Using Python parser for all files")
import argparse
parser = argparse.ArgumentParser(description="Monitor markdown directory and convert to HTML with dynamic parser selection.")
# This stores True when passed, but means "no obfuscation"
parser.add_argument(
"--no-obfuscate",
action="store_false",
help="Disable HTML obfuscation."
)
parser.add_argument(
"--rust-threshold",
type=int,
default=500,
help=f"Line count threshold for using Rust parser (default: {RUST_PARSER_THRESHOLD})"
)
args = parser.parse_args()
# Invert it to get the obfuscate flag
obfuscate = not args.no_obfuscate
# Update threshold if specified
RUST_PARSER_THRESHOLD = args.rust_threshold
Logger.log_obfuscation_info(f"Obfuscation is {obfuscate}",obfuscate)
initial_scan(MARKDOWN_DIR)
event_handler = Handler()
observer = Observer()

BIN
css/icons/save.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 KiB

BIN
css/icons/search.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -7,14 +7,18 @@
h1 { color: #333; }
li { list-style: none; background: url("../../css/icons/item.webp") no-repeat left center; background-size: 15px 20px; padding-left: 25px; transition: font-size 0.5s cubic-bezier(0.075, 0.82, 0.165, 1); padding-bottom: 5px; }
li:hover { font-size: larger; }
#available { padding-left: 40px; margin-bottom: 0.1em;}
ul { padding-left: 100px; margin-top: 0.2em;}
#available { padding-left: 40px; margin-bottom: 0.1em;}
ul { padding-left: 100px; margin-top: 0.2em;}
#nojs { display: inline-block;color: red;transition: transform 0.7s cubic-bezier(0.215, 0.610, 0.355, 1); }
/*#nojs:hover { transform: skewX(-12deg);}*/
#nonenormalul { list-style: disc inside; margin: 1em 0; padding-left: 40px; background: none; }
#nonenormalul li { list-style: inherit; margin: 0; padding: 0; background: none; transition: font-size 0.5s cubic-bezier(0.075, 0.82, 0.165, 1); }
#nonenormalul li:hover { font-size: larger; }
</style>
<script>
console.log("javascript is enabled! good!")
document.write('<h1 id="nojs" style="color:black; display: flex; align-items: center;"><img src="../../css/icons/folder.webp" width="45" height="45" style="vertical-align: middle; margin-right: 8px;" />Index of PyPost</h1>');
</script>
<link rel="icon" type="image/x-icon" href="../../css/favicon/favicon.ico">
</head>
<body>
@@ -32,77 +36,18 @@
</ul>
</p>
</noscript>
<script>
// we just repurpouse the nojs from css
// much easier than adding a new element
console.log("javascript is enabled! good!")
document.write('<h1 id="nojs" style="color:black; display: flex; align-items: center;"><img src="../../css/icons/folder.webp" width="45" height="45" style="vertical-align: middle; margin-right: 8px;" />Index of PyPost</h1>');
</script>
<p id="available">
<img src="../../css/icons/available.webp" width="40" height="40" style="vertical-align: middle; display: inline; /*margin-right: 8px;*/ padding-right: 5px;" />
Available pages:
</p>
<!-- CONTENT -->
<!-- load scripts needed for indexer -->
<script src="../../js/normal.js"></script>
<script type="text/javascript">
function search_ul_items() {
const query = document.getElementById('searchbox').value.toLowerCase();
const ul = document.querySelector('ul'); // only one UL
if (!ul) return;
<script src="../../js/search.js" defer></script>
const items = ul.querySelectorAll('li');
items.forEach(li => {
if (li.textContent.toLowerCase().includes(query)) {
li.style.display = 'list-item';
} else {
li.style.display = 'none';
}
});
}
// Create search box and insert before the available pages paragraph
window.addEventListener('DOMContentLoaded', function() {
const searchDiv = document.createElement('div');
searchDiv.style.marginBottom = '16px';
searchDiv.style.paddingLeft = '19px';
searchDiv.innerHTML = `
<input
type="text"
id="searchbox"
placeholder="Search pages..."
style="
padding: 6px 6px 6px 28px;
font-size: 1em;
width: 220px;
background: url('../../css/icons/search.png') no-repeat 6px center;
background-size: 20px 20px;
"
title="Search for pages (italic)"
/>
`;
const available = document.getElementById('available');
available.parentNode.insertBefore(searchDiv, available);
const searchbox = document.getElementById('searchbox');
if (searchbox) {
searchbox.title = "Search for pages";
searchbox.style.fontStyle = "italic";
}
document.getElementById('searchbox').addEventListener('input', search_ul_items);
});
</script>
<footer style="
position: absolute;
bottom: 0;
width: 100%;
">
<p>
</p>
<footer style=" position: absolute; bottom: 0; width: 100%;">
<p></p>
</footer>
</body>
</html>

File diff suppressed because one or more lines are too long

18
js/post/download.js Normal file
View File

@@ -0,0 +1,18 @@
document.addEventListener("DOMContentLoaded", () => {
// current page URL
let url = window.location.href;
// replace `/html/` with `/markdown/`
url = url.replace("/html/", "/markdown/");
// replace `.html` with `.md`
url = url.replace(/\.html$/, ".md");
// assign to <a>
const a = document.getElementById("download-md");
a.href = url;
// suggest filename
const filename = url.split("/").pop(); // e.g. markdowntest.md
a.download = filename;
});

File diff suppressed because one or more lines are too long

View File

@@ -1,31 +0,0 @@
// Theme toggling script for PyPost
function toggleTheme() {
const darkStyles = document.getElementById('dark-styles');
const lightStyles = document.getElementById('light-styles');
const currentlyLight = !lightStyles.disabled;
document.body.classList.add('theme-transitioning');
if (currentlyLight) {
// Switch to dark
lightStyles.disabled = true;
darkStyles.disabled = false;
} else {
// Switch to light
lightStyles.disabled = false;
darkStyles.disabled = true;
}
setTimeout(() => {
document.body.classList.remove('theme-transitioning');
}, 400);
}
document.addEventListener('DOMContentLoaded', function() {
const darkStyles = document.getElementById('dark-styles');
const lightStyles = document.getElementById('light-styles');
// Always start in light mode
lightStyles.disabled = false;
darkStyles.disabled = true;
});

46
js/search.js Normal file
View File

@@ -0,0 +1,46 @@
function search_ul_items() {
const query = document.getElementById('searchbox').value.toLowerCase();
const ul = document.querySelector('ul'); // only one UL
if (!ul) return;
const items = ul.querySelectorAll('li');
items.forEach(li => {
if (li.textContent.toLowerCase().includes(query)) {
li.style.display = 'list-item';
} else {
li.style.display = 'none';
}
});
}
// Create search box and insert before the available pages paragraph
window.addEventListener('DOMContentLoaded', function() {
const searchDiv = document.createElement('div');
searchDiv.style.marginBottom = '16px';
searchDiv.style.paddingLeft = '19px';
searchDiv.innerHTML = `
<input
type="text"
id="searchbox"
placeholder="Search pages..."
style="
padding: 6px 6px 6px 28px;
font-size: 1em;
width: 220px;
background: url('../../css/icons/search.webp') no-repeat 6px center;
background-size: 20px 20px;
"
title="Search for pages"
/>
`;
const available = document.getElementById('available');
available.parentNode.insertBefore(searchDiv, available);
const searchbox = document.getElementById('searchbox');
if (searchbox) {
searchbox.title = "Search for pages";
searchbox.style.fontStyle = "bold";
}
document.getElementById('searchbox').addEventListener('input', search_ul_items);
});

View File

@@ -6,6 +6,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
import mimetypes
from functools import lru_cache
from jsmin import jsmin # pip install jsmin
import time
from log.Logger import *
logger = Logger()
@@ -13,6 +14,7 @@ import PyPost
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
HTML_DIR = os.path.join(PROJECT_ROOT, "html")
MARKDOWN_DIR = os.path.join(PROJECT_ROOT, "markdown")
BASE_FILE = os.path.join(HTML_DIR, "base", "index.html")
def get_html_files(directory=HTML_DIR):
@@ -23,6 +25,18 @@ def get_html_files(directory=HTML_DIR):
html_files.append(entry)
return html_files
def get_markdown_files():
"""Get list of .md files from the markdown directory."""
if not os.path.exists(MARKDOWN_DIR):
return []
markdown_files = []
for entry in os.listdir(MARKDOWN_DIR):
full_path = os.path.join(MARKDOWN_DIR, entry)
if os.path.isfile(full_path) and entry.endswith(".md"):
markdown_files.append(entry)
return markdown_files
def build_index_page():
with open(BASE_FILE, "r", encoding="utf-8") as f:
base_html = f.read()
@@ -63,6 +77,8 @@ def index_footer():
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
req_path = self.path.lstrip("/")
# Handle root/index
if req_path == "" or req_path == "index.html":
content = build_index_page()
self.send_response(200)
@@ -71,6 +87,55 @@ class MyHandler(BaseHTTPRequestHandler):
self.wfile.write(content.encode("utf-8"))
return
# Handle markdown file downloads
if req_path.startswith("markdown/"):
markdown_filename = req_path[9:] # Remove "markdown/" prefix
# Security check: only allow .md files and prevent directory traversal
if not markdown_filename.endswith(".md") or ".." in markdown_filename or "/" in markdown_filename:
self.send_response(403)
self.end_headers()
self.wfile.write(b"403 - Forbidden: Only .md files allowed")
return
markdown_file_path = os.path.join(MARKDOWN_DIR, markdown_filename)
# Check if file exists and is within markdown directory
if not os.path.exists(markdown_file_path) or not os.path.isfile(markdown_file_path):
self.send_response(404)
self.end_headers()
self.wfile.write(b"404 - Markdown file not found")
return
# Verify the resolved path is still within the markdown directory (extra security)
resolved_path = os.path.realpath(markdown_file_path)
resolved_markdown_dir = os.path.realpath(MARKDOWN_DIR)
if not resolved_path.startswith(resolved_markdown_dir):
self.send_response(403)
self.end_headers()
self.wfile.write(b"403 - Forbidden")
return
try:
with open(markdown_file_path, "rb") as f:
content = f.read()
self.send_response(200)
self.send_header("Content-type", "text/markdown")
self.send_header("Content-Disposition", f'attachment; filename="{markdown_filename}"')
self.end_headers()
self.wfile.write(content)
logger.log_info(f"Served markdown file: {markdown_filename}")
return
except Exception as e:
logger.log_error(f"Error serving markdown file {markdown_filename}: {e}")
self.send_response(500)
self.end_headers()
self.wfile.write(b"500 - Internal Server Error")
return
# Handle other files (existing functionality)
file_path = os.path.normpath(os.path.join(PROJECT_ROOT, req_path))
if not file_path.startswith(PROJECT_ROOT):
self.send_response(403)