Compare commits

...

2 Commits

Author SHA1 Message Date
a74d482d7a typing to know what returns and needs to be passed, styling fix 2025-10-10 13:21:21 +02:00
ea3da89d18 cdn loaded latex math 2025-10-10 13:20:32 +02:00
9 changed files with 298 additions and 112 deletions

View File

@@ -7,7 +7,6 @@ from pathlib import Path
from jinja2 import Environment, FileSystemLoader
import base64
import random
import time
import yaml
import marko
@@ -19,6 +18,9 @@ from hashes.hashes import hash_list
from htmlhandler import htmlhandler as Handler
from lua import plugin_manager
# Import your LaTeX extension
from hashes.util.LaTeXRenderer import LaTeXExtension
plugin_manager = plugin_manager.PluginManager()
plugin_manager.load_all() # load plugins
@@ -35,8 +37,8 @@ RUST_PARSER_PATH = ROOT / "fastmd" / "target" / "release" / f"fastmd{exe_ext}"
if not RUST_PARSER_PATH.exists():
RUST_PARSER_PATH = ROOT / "fastmd" / "target" / "debug" / f"fastmd{exe_ext}"
# Python Markdown parser with table support
markdown_parser = marko.Markdown(extensions=[GFM])
# Python Markdown parser with table support AND LaTeX extension
markdown_parser = marko.Markdown(extensions=[GFM, LaTeXExtension()])
# Threshold for switching to Rust parser (number of lines)
RUST_PARSER_THRESHOLD = 1000

View File

@@ -361,7 +361,7 @@ body.dark-mode td {
border-color: #444;
}
body.dark-mode th {
background: #121212;
background: #2a2a2a;
}
body.dark-mode code,

View File

@@ -0,0 +1,42 @@
# latex_extension.py
import marko
import marko.block
import marko.inline
from marko.md_renderer import MarkdownRenderer
import re
from log.Logger import Logger
logger = Logger()
class BlockFormula(marko.block.BlockElement):
pattern = re.compile(r"\$\$ *\n([\s\S]+?)^\$\$ *$", re.MULTILINE)
def __init__(self, match):
logger.log_debug("Did shit at __init__ for blockformula")
self.children = [marko.inline.RawText(match.group(1))]
@classmethod
def match(cls, source):
return source.expect_re(cls.pattern)
@classmethod
def parse(cls, source):
logger.log_debug("Did some shit with Latex")
match = source.match
source.consume()
return match
class Paragraph(marko.block.Paragraph):
override = True
@classmethod
def break_paragraph(cls, source, lazy=False):
if BlockFormula.match(source):
return True
return super().break_paragraph(source, lazy=lazy)
class Renderer:
def render_block_formula(self, element):
# Render as HTML with MathJax-compatible format
return '\n<div class="math-block">$$\n' + self.render_children(element) + '$$</div>\n'
class LaTeXExtension:
logger.log_debug("Did shit at __init__ for latexextension")
elements = [BlockFormula, Paragraph]
parser_mixins = []
renderer_mixins = [Renderer]

View File

@@ -20,6 +20,8 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.11.0/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script"></script>
<!-- remove if causing issues -->
<script src="../js/post/lazyimg.js"></script>
<script src="../js/shared/theme.js"></script>

104
lua/luarails.lua Normal file
View File

@@ -0,0 +1,104 @@
-- Guardrails and safe patterns for plugin development
-- Safe string operations
function safe_concat(...)
local result = {}
for i, v in ipairs({...}) do
if v ~= nil then
table.insert(result, tostring(v))
end
end
return table.concat(result)
end
-- Safe table operations
function table_contains(tbl, value)
for _, v in ipairs(tbl) do
if v == value then return true end
end
return false
end
function table_keys(tbl)
local keys = {}
for k, _ in pairs(tbl) do
table.insert(keys, k)
end
return keys
end
function table_values(tbl)
local values = {}
for _, v in pairs(tbl) do
table.insert(values, v)
end
return values
end
-- Safe string escaping
function escape_html(str)
if str == nil then return "" end
local s = tostring(str)
s = string.gsub(s, "&", "&amp;")
s = string.gsub(s, "<", "&lt;")
s = string.gsub(s, ">", "&gt;")
s = string.gsub(s, '"', "&quot;")
s = string.gsub(s, "'", "&#39;")
return s
end
-- Pattern validation
function is_valid_filename(name)
if name == nil or name == "" then return false end
-- Block directory traversal
if string.match(name, "%.%.") then return false end
if string.match(name, "/") or string.match(name, "\\\\") then return false end
return true
end
-- Safe error handling wrapper
function try_catch(fn, catch_fn)
local status, err = pcall(fn)
if not status and catch_fn then
catch_fn(err)
end
return status
end
-- Request validation
function validate_request(req, required_fields)
if type(req) ~= "table" then return false, "Request must be a table" end
for _, field in ipairs(required_fields) do
if req[field] == nil then
return false, "Missing required field: " .. field
end
end
return true, nil
end
-- Rate limiting helper (simple in-memory)
_rate_limits = _rate_limits or {}
function check_rate_limit(key, max_calls, window_seconds)
local now = os.time()
if _rate_limits[key] == nil then
_rate_limits[key] = {count = 1, window_start = now}
return true
end
local rl = _rate_limits[key]
if now - rl.window_start > window_seconds then
-- Reset window
rl.count = 1
rl.window_start = now
return true
end
if rl.count >= max_calls then
return false
end
rl.count = rl.count + 1
return true
end
log("Lua guardrails initialized")

View File

@@ -1,106 +1,14 @@
guardrails_code = """
-- Guardrails and safe patterns for plugin development
import os
-- Safe string operations
function safe_concat(...)
local result = {}
for i, v in ipairs({...}) do
if v ~= nil then
table.insert(result, tostring(v))
end
end
return table.concat(result)
end
def get_file_contents(filename : str) -> str | None:
""" Gets the Contents of a File and returns it in str format """
script_dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(script_dir, filename)
-- Safe table operations
function table_contains(tbl, value)
for _, v in ipairs(tbl) do
if v == value then return true end
end
return false
end
if not os.path.isfile(path):
print(f"File '{filename}' not found in {script_dir}")
return None
function table_keys(tbl)
local keys = {}
for k, _ in pairs(tbl) do
table.insert(keys, k)
end
return keys
end
with open(path, 'r', encoding='utf-8') as file:
return file.read()
function table_values(tbl)
local values = {}
for _, v in pairs(tbl) do
table.insert(values, v)
end
return values
end
-- Safe string escaping
function escape_html(str)
if str == nil then return "" end
local s = tostring(str)
s = string.gsub(s, "&", "&amp;")
s = string.gsub(s, "<", "&lt;")
s = string.gsub(s, ">", "&gt;")
s = string.gsub(s, '"', "&quot;")
s = string.gsub(s, "'", "&#39;")
return s
end
-- Pattern validation
function is_valid_filename(name)
if name == nil or name == "" then return false end
-- Block directory traversal
if string.match(name, "%.%.") then return false end
if string.match(name, "/") or string.match(name, "\\\\") then return false end
return true
end
-- Safe error handling wrapper
function try_catch(fn, catch_fn)
local status, err = pcall(fn)
if not status and catch_fn then
catch_fn(err)
end
return status
end
-- Request validation
function validate_request(req, required_fields)
if type(req) ~= "table" then return false, "Request must be a table" end
for _, field in ipairs(required_fields) do
if req[field] == nil then
return false, "Missing required field: " .. field
end
end
return true, nil
end
-- Rate limiting helper (simple in-memory)
_rate_limits = _rate_limits or {}
function check_rate_limit(key, max_calls, window_seconds)
local now = os.time()
if _rate_limits[key] == nil then
_rate_limits[key] = {count = 1, window_start = now}
return true
end
local rl = _rate_limits[key]
if now - rl.window_start > window_seconds then
-- Reset window
rl.count = 1
rl.window_start = now
return true
end
if rl.count >= max_calls then
return false
end
rl.count = rl.count + 1
return true
end
log("Lua guardrails initialized")
"""

View File

@@ -8,7 +8,7 @@ from watchdog.events import FileSystemEventHandler
from log.Logger import Logger
from .PluginFSHandler import PluginFSHandler
import json
from .luarails import guardrails_code
from .luarails import get_file_contents
PLUGINS_DIR = Path(__file__).parent / "plugins"
HTML_DIR = Path(__file__).parent / "../html"
@@ -112,7 +112,7 @@ class PluginManager:
def _setup_lua_guardrails(self):
try:
self.lua.execute(guardrails_code)
self.lua.execute(get_file_contents("luarails.lua"))
except LuaError as e:
Logger.log_lua_error(f"Failed to initialize Lua guardrails: {e}")

128
markdown/Rotation.md Normal file
View File

@@ -0,0 +1,128 @@
---
summary: "Würfelrotation mit Matrizen die Multipliziert werden erkläret"
---
# Rotation eines Würfels um die x-Achse
Wir wollen verstehen, wie man einen Würfel im Raum um die x-Achse dreht.
## 1. Punkte eines Würfels
Ein Würfel hat 8 Eckpunkte. Wenn wir den Würfel in der Mitte des Koordinatensystems platzieren, können wir die Punkte als Vektoren schreiben:
$$
\mathbf{A} = (1, 1, 1), \quad
\mathbf{B} = (1, 1, -1), \quad
\mathbf{C} = (1, -1, 1), \quad
\mathbf{D} = (1, -1, -1)
$$
$$
\mathbf{E} = (-1, 1, 1), \quad
\mathbf{F} = (-1, 1, -1), \quad
\mathbf{G} = (-1, -1, 1), \quad
\mathbf{H} = (-1, -1, -1)
$$
Jeder Punkt hat drei Koordinaten
$$
(x', y', z')
$$
## 2. Rotationsmatrix um die x-Achse
Wenn wir einen Punkt
$$
\mathbf{v} = \begin{pmatrix} x \\ y \\ z \end{pmatrix}
$$
um die x-Achse um einen Winkel $\theta$ drehen wollen, benutzen wir die Rotationsmatrix:
$$
R_x(\theta) =
\begin{pmatrix}
1 & 0 & 0 \\
0 & \cos\theta & -\sin\theta \\
0 & \sin\theta & \cos\theta
\end{pmatrix}
$$
**Hinweis:**
- Die x-Koordinate bleibt gleich, weil wir um die x-Achse drehen.
- y und z verändern sich je nach Winkel $\theta$.
## 3. Berechnung des neuen Punktes
Der neue Punkt $\mathbf{v}'$ nach der Drehung ist:
$$
\mathbf{v}' = R_x(\theta) \mathbf{v} =
\begin{pmatrix}
1 & 0 & 0 \\
0 & \cos\theta & -\sin\theta \\
0 & \sin\theta & \cos\theta
\end{pmatrix}
\begin{pmatrix} x \\ y \\ z \end{pmatrix} =
\begin{pmatrix}
x \\
y \cos\theta - z \sin\theta \\
y \sin\theta + z \cos\theta
\end{pmatrix}
$$
## 4. Beispiel
Drehen wir den Punkt
$$
\mathbf{A} = (1,1,1)
$$
um
$$
\theta = 90^\circ = \frac{\pi}{2}
$$
Dann gilt:
$$
\cos \theta = 0, \quad \sin \theta = 1
$$
$$
\mathbf{A}' =
\begin{pmatrix}
1 \\
1 \cdot 0 - 1 \cdot 1 \\
1 \cdot 1 + 1 \cdot 0
\end{pmatrix} =
\begin{pmatrix}
1 \\ -1 \\ 1
\end{pmatrix}
$$
## 5. Tabelle aller Punkte nach Rotation
$$
\begin{array}{c|c}
\text{Originalpunkt} & \text{Punkt nach Rotation} \\
\hline
A (1,1,1) & (1,-1,1) \\
B (1,1,-1) & (1,-1,-1) \\
C (1,-1,1) & (1,-1,-1) \\
D (1,-1,-1) & (1,1,-1) \\
E (-1,1,1) & (-1,-1,1) \\
F (-1,1,-1) & (-1,-1,-1) \\
G (-1,-1,1) & (-1,1,1) \\
H (-1,-1,-1) & (-1,1,-1) \\
\end{array}
$$
## Fazit
- x bleibt unverändert
- y und z ändern sich je nach Winkel
- Rotationsmatrizen sind ein mächtiges Werkzeug, um Objekte im 3D-Raum zu bewegen

View File

@@ -31,7 +31,7 @@ def get_html_files(directory=HTML_DIR):
return html_files
def build_index_page():
def build_index_page() -> str:
with open(BASE_FILE, "r", encoding="utf-8") as f:
base_html = f.read()
@@ -80,7 +80,7 @@ H2_CANDIDATES = [h for h in hash_list if h != H1]
H2 = random.choice(H2_CANDIDATES) if H2_CANDIDATES else H1
# cahcing was a bad, idea, servertime got stuck. it is now a variable ;)
def index_footer():
def index_footer() -> str:
tor_link = "http://7uhuxits7qfmiagkmpazxvh3rtk6aijs6pbawge3fl77y4xqjixlhkqd.onion/"
return f"""
<!-- Footer styling doesnt need to work with
@@ -107,7 +107,7 @@ def index_footer():
</div>
"""
class MyHandler(BaseHTTPRequestHandler):
class WebServerHTTPRequestHandler(BaseHTTPRequestHandler):
# This is a Helper Function for the POST Endpoints
def _parse_post_data(self):
"""Parse POST request body"""
@@ -331,7 +331,7 @@ if __name__ == "__main__":
logger.log_debug("Started PyPost.py in background watcher thread.")
server_address = ("localhost", 8000)
httpd: HTTPServer = HTTPServer(server_address, MyHandler) # type: ignore[arg-type]
httpd: HTTPServer = HTTPServer(server_address, WebServerHTTPRequestHandler) # type: ignore[arg-type]
logger.log_info(f"Serving on http://{server_address[0]}:{server_address[1]}")
httpd.serve_forever()
except (Exception, KeyboardInterrupt) as e: