authoring server 4
All checks were successful
Build Org Website / build (push) Successful in 44s

This commit is contained in:
2026-05-07 13:53:45 +01:00
parent d33e1771af
commit 2fd5a098b1
7 changed files with 75 additions and 11 deletions

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -6,7 +6,9 @@ from __future__ import annotations
import json import json
import mimetypes import mimetypes
import os import os
import posixpath
import re import re
import struct
import subprocess import subprocess
import sys import sys
import threading import threading
@@ -455,7 +457,61 @@ def save_page(data: dict[str, Any]) -> dict[str, Any]:
return page_to_dict(read_page(target)) return page_to_dict(read_page(target))
def save_upload(filename: str, payload: bytes) -> dict[str, str]: def image_dimensions(payload: bytes, ext: str) -> tuple[int, int] | None:
if ext == ".png" and payload.startswith(b"\x89PNG\r\n\x1a\n") and len(payload) >= 24:
width, height = struct.unpack(">II", payload[16:24])
return width, height
if ext == ".gif" and payload[:6] in {b"GIF87a", b"GIF89a"} and len(payload) >= 10:
width, height = struct.unpack("<HH", payload[6:10])
return width, height
if ext in {".jpg", ".jpeg"} and payload.startswith(b"\xff\xd8"):
i = 2
while i + 9 < len(payload):
if payload[i] != 0xFF:
i += 1
continue
marker = payload[i + 1]
i += 2
if marker in {0xD8, 0xD9}:
continue
if i + 2 > len(payload):
break
size = int.from_bytes(payload[i:i + 2], "big")
if size < 2:
break
if marker in {0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF}:
if i + 7 <= len(payload):
height = int.from_bytes(payload[i + 3:i + 5], "big")
width = int.from_bytes(payload[i + 5:i + 7], "big")
return width, height
break
i += size
return None
def relative_asset_path(page_path: str, asset_path: str) -> str:
page = safe_relative_path(page_path) if page_path else LIMA_DIR / "index.md"
if page.suffix != ".md" or not page.is_relative_to(LIMA_DIR):
page = LIMA_DIR / "index.md"
page_rel = page.relative_to(ROOT).as_posix()
page_output_dir = posixpath.dirname(page_rel)
return posixpath.relpath(asset_path, page_output_dir or ".")
def gallery_image_html(filename: str, asset_path: str, page_path: str, payload: bytes, ext: str) -> str:
absolute_url = f"https://zainezq.com/{asset_path}"
relative_url = relative_asset_path(page_path, asset_path)
dims = image_dimensions(payload, ext)
width, height = dims if dims else (1920, 1080)
alt = html_escape(filename)
return (
f'<a href="{absolute_url}" data-img="{absolute_url}" data-alt="{alt}" '
f'data-width="{width}" data-height="{height}">'
f'<img src="{relative_url}" alt="{alt}" style="cursor: zoom-in;"></a>'
)
def save_upload(filename: str, payload: bytes, page_path: str = "") -> dict[str, str]:
original = Path(filename or "attachment").name original = Path(filename or "attachment").name
ext = Path(original).suffix.lower() ext = Path(original).suffix.lower()
if ext not in ALLOWED_UPLOAD_EXTENSIONS: if ext not in ALLOWED_UPLOAD_EXTENSIONS:
@@ -471,14 +527,20 @@ def save_upload(filename: str, payload: bytes) -> dict[str, str]:
counter += 1 counter += 1
target.write_bytes(payload) target.write_bytes(payload)
rel = target.relative_to(ROOT).as_posix() rel = target.relative_to(ROOT).as_posix()
url = f"/{rel}" absolute_url = f"https://zainezq.com/{rel}"
relative_url = relative_asset_path(page_path, rel)
mime = mimetypes.guess_type(target.name)[0] or "" mime = mimetypes.guess_type(target.name)[0] or ""
alt = Path(original).stem.replace("-", " ").replace("_", " ").strip() or "attachment"
if mime.startswith("video/") or ext in {".mp4", ".webm", ".mov"}: if mime.startswith("video/") or ext in {".mp4", ".webm", ".mov"}:
markdown = f'<video controls src="{url}"></video>' markdown = f'<video controls src="{relative_url}"></video>'
else: else:
markdown = f"![{alt}]({url})" markdown = gallery_image_html(target.name, rel, page_path, payload, ext)
return {"url": url, "path": rel, "markdown": markdown, "filename": target.name} return {
"url": absolute_url,
"relativeUrl": relative_url,
"path": rel,
"markdown": markdown,
"filename": target.name,
}
def run_build_commands() -> tuple[bool, str, str]: def run_build_commands() -> tuple[bool, str, str]:
@@ -893,6 +955,7 @@ APP_HTML = r"""<!doctype html>
saveMessage.textContent = "Uploading attachment."; saveMessage.textContent = "Uploading attachment.";
const body = new FormData(); const body = new FormData();
body.append("attachment", file); body.append("attachment", file);
body.append("pagePath", state.currentPath || editor.targetPath.value || "lima/index.md");
try { try {
const res = await fetch("/api/upload", { method: "POST", body }); const res = await fetch("/api/upload", { method: "POST", body });
const data = await res.json(); const data = await res.json();
@@ -974,7 +1037,10 @@ class Handler(BaseHTTPRequestHandler):
payload = field.file.read() payload = field.file.read()
if not payload: if not payload:
raise ValueError("Attachment is empty.") raise ValueError("Attachment is empty.")
self.send_json(save_upload(field.filename, payload)) page_path = ""
if "pagePath" in form:
page_path = str(form["pagePath"].value or "")
self.send_json(save_upload(field.filename, payload, page_path))
except Exception as exc: except Exception as exc:
self.send_json({"error": str(exc)}, HTTPStatus.BAD_REQUEST) self.send_json({"error": str(exc)}, HTTPStatus.BAD_REQUEST)
return return

View File

@@ -11,7 +11,5 @@ I made quite a few changes. It all started when I realised that not everyone kno
- One other thing, a more personal one: what do you think of all this? am i doing too much? am i doing too little? am i overengineering things? am i forcing you to do something you dont wanna do? these little trinkets work for me, but im not sure if it would work for someone else, so at any point if you have reservations, let me know okay? - One other thing, a more personal one: what do you think of all this? am i doing too much? am i doing too little? am i overengineering things? am i forcing you to do something you dont wanna do? these little trinkets work for me, but im not sure if it would work for someone else, so at any point if you have reservations, let me know okay?
# **This is a test**
<a href="https://zainezq.com/assets/images/hzone/2026/05/20260507-135058-example-png-image.png" data-img="https://zainezq.com/assets/images/hzone/2026/05/20260507-135058-example-png-image.png" data-alt="20260507-135058-example-png-image.png" data-width="512" data-height="512"><img src="../assets/images/hzone/2026/05/20260507-135058-example-png-image.png" alt="20260507-135058-example-png-image.png" style="cursor: zoom-in;"></a>
![Example PNG Image](/assets/images/hzone/2026/05/20260507-134236-example-png-image.png)

View File

@@ -4,7 +4,7 @@
See the categories: @@html:<a href="../home/categories.html">Categories</a>@@ See the categories: @@html:<a href="../home/categories.html">Categories</a>@@
* Posts: * Posts:
- [[file:career/career-list.org][Career List]] @@html:<span class="post-date">07-05-2026 13:46</span>@@ - [[file:career/career-list.org][Career List]] @@html:<span class="post-date">07-05-2026 13:53</span>@@
- [[file:posts-list.sync-conflict-20260417-233432-VT6366A.org][Posts List]] @@html:<span class="post-date">14-04-2026 16:36</span>@@ - [[file:posts-list.sync-conflict-20260417-233432-VT6366A.org][Posts List]] @@html:<span class="post-date">14-04-2026 16:36</span>@@
- [[file:career/ha-dr.org][High Availability, Disaster Recovery and Business Continuity]] @@html:<span class="post-date">11-03-2026 17:18</span>@@ @@html:<a href="/tags/learning.html"><span class="post-tag">learning</span></a>@@ @@html:<a href="/tags/notes.html"><span class="post-tag">notes</span></a>@@ - [[file:career/ha-dr.org][High Availability, Disaster Recovery and Business Continuity]] @@html:<span class="post-date">11-03-2026 17:18</span>@@ @@html:<a href="/tags/learning.html"><span class="post-tag">learning</span></a>@@ @@html:<a href="/tags/notes.html"><span class="post-tag">notes</span></a>@@
- [[file:career/javascript.org][Understands the Javascript language]] @@html:<span class="post-date">11-03-2026 16:52</span>@@ @@html:<a href="/tags/learning.html"><span class="post-tag">learning</span></a>@@ @@html:<a href="/tags/notes.html"><span class="post-tag">notes</span></a>@@ - [[file:career/javascript.org][Understands the Javascript language]] @@html:<span class="post-date">11-03-2026 16:52</span>@@ @@html:<a href="/tags/learning.html"><span class="post-tag">learning</span></a>@@ @@html:<a href="/tags/notes.html"><span class="post-tag">notes</span></a>@@