Uitgave 2: licentie op alles, presentatie erbij, README kaal

- CC BY-SA 4.0 voor documenten, dia's en beelden, MIT voor scripts; colofon met
  eigenaar en contact in elk gebouwd document en op de slotdia
- Presentatie (22 dia's) als bron, PDF en sprekersnotities in 00-workshop/presentatie
- README en CHANGELOG zonder procesnotities; instructieblokken heten Voor de bewerker
- Lokaal extractiescript uit de uitgave gehaald

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido van Dijk
2026-09-20 20:40:31 +02:00
parent 659e4c7e76
commit a47195722d
46 changed files with 887 additions and 131 deletions
+31 -2
View File
@@ -19,6 +19,12 @@ import markdown
TOOLS = Path(__file__).resolve().parent
LOGO = TOOLS / "logo-ai-netwerk-limburg.png"
# Licentie en contact, komen als colofon onderaan elk document (HTML, PDF en Word).
EIGENAAR = "Guido van Dijk, LeX Consultancy B.V. en AI Netwerk Limburg"
CONTACT = "guido@l-e-x.nl · guido@ai-netwerk-limburg.nl"
BRON = "https://git.lexconsultancy.nl/guido/dify-handleidingen"
LICENTIE_URL = "https://creativecommons.org/licenses/by-sa/4.0/deed.nl"
CSS = """
@page { size: A4; margin: 2cm 2cm 2.2cm 2cm; }
html { font-size: 11pt; }
@@ -56,6 +62,9 @@ ol li { margin: .25em 0; }
page-break-inside: avoid; break-inside: avoid; }
.typ-label { font-size: .75em; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
color: #334155; background: #e2e8f0; padding: .15em .7em; }
.colofon { margin-top: 2.2em; padding: .7em .9em; border: 1px solid #cbd5e1; border-radius: 4px;
background: #f8fafc; font-size: .88em; color: #334155; page-break-inside: avoid; break-inside: avoid; }
.colofon p { margin: .2em 0; }
.schema { margin: .6em 0 .9em; padding: .5em .7em; border: 1px dashed #94a3b8; border-radius: 4px;
background: #fff; overflow-x: auto; page-break-inside: avoid; break-inside: avoid; }
.schema pre { margin: 0; white-space: pre; font-family: "DejaVu Sans Mono", Consolas, monospace; font-size: .8em; line-height: 1.3; }
@@ -69,7 +78,7 @@ ol li { margin: .25em 0; }
def read_source(src: Path) -> str:
text = src.read_text(encoding="utf-8")
# Het instructieblok voor Claude Code bovenin hoort niet in de uitgave.
# Het instructieblok voor de bewerker bovenin hoort niet in de uitgave.
return re.sub(r"^\s*<!--.*?-->\s*", "", text, count=1, flags=re.S)
@@ -82,7 +91,7 @@ def logo_src(embed: bool) -> str:
COMPACT_CSS = """
html { font-size: 9.5pt; } h1 { font-size: 18pt; } h2 { margin-top: 1em; }
p { margin: .35em 0; } table { margin: .5em 0; } th, td { padding: .25em .5em; }
.voet { margin-top: .6em; } .voet img { width: 1.1cm; } .kop img { width: 2.8cm; } ul, ol { margin: .3em 0 .3em 1.3em; }
.colofon { margin-top: .5em; padding: .3em .5em; font-size: .75em; border: 0; border-top: 1px solid #ddd; border-radius: 0; background: none; } .voet { margin-top: .6em; } .voet img { width: 1.1cm; } .kop img { width: 2.8cm; } ul, ol { margin: .3em 0 .3em 1.3em; }
"""
@@ -156,6 +165,26 @@ def to_html(md_text: str, here: Path, embed_images: bool, compact: bool = False)
f'<img src="{logo}" alt="AI Netwerk Limburg" /></div>'),
body, count=1, flags=re.S)
colofon = (
f'<div class="colofon">'
f'<p><strong>Licentie:</strong> CC BY-SA 4.0 ({LICENTIE_URL}). Je mag dit document kopiëren, bewerken en '
f'hergebruiken, ook op je eigen school, als je de bron noemt ({EIGENAAR}, {BRON}) en je bewerking onder '
f'dezelfde licentie deelt. Het logo van AI Netwerk Limburg valt buiten de licentie; namen van producten '
f'zijn merken van hun eigenaren. Scholen, personen en documenten in de voorbeelden zijn verzonnen.</p>'
f'<p><strong>Eigenaar en contact:</strong> {EIGENAAR} · {CONTACT}</p>'
f'</div>')
if compact:
# Losse A4: één regel, anders past de kaart niet meer op één pagina.
colofon = (f'<div class="colofon"><p>CC BY-SA 4.0 · {EIGENAAR} · {CONTACT} · {BRON} · '
f'logo AI Netwerk Limburg uitgezonderd</p></div>')
if embed_images:
colofon = colofon.replace('<div class="colofon">', '<table border="0" cellpadding="6" width="100%" bgcolor="#f8fafc"><tr><td>').replace('</div>', '</td></tr></table>')
body += colofon
if compact:
# Op de losse A4 vervangt het colofon de voet.
return (f'<!DOCTYPE html><html lang="nl"><head><meta charset="utf-8">'
f"<title>{title}</title><style>{CSS}{COMPACT_CSS}</style></head><body>{body}</body></html>")
voet = (f'<div class="voet"><img src="{logo}" alt="" />'
f'<span>AI Netwerk Limburg · ai-netwerk-limburg.nl · {title}</span></div>')
-39
View File
@@ -1,39 +0,0 @@
#!/usr/bin/env python3
"""Haalt een screenshot uit het Claude Code-sessietranscript.
Gebruik: extract.py <screenshot-id zoals ss_1923g3a1u> <doelpad.jpg>
Let op: JSONL hieronder wijst naar de Claude Code-sessie van 20 september 2026;
bij een nieuwe sessie het pad aanpassen."""
import base64, json, sys
from pathlib import Path
JSONL = Path("/home/guido/.claude/projects/-home-guido-projecten-lexconsultancybv-nl-server/f0263769-3318-4702-826c-1957526cfde0.jsonl")
def find(ss_id):
for line in JSONL.read_text().splitlines():
if ss_id not in line:
continue
rec = json.loads(line)
content = rec.get("message", {}).get("content", [])
for block in content if isinstance(content, list) else []:
if block.get("type") != "tool_result":
continue
inner = block.get("content", [])
if not isinstance(inner, list):
continue
# In een browser_batch staan meerdere screenshots in één resultaat:
# neem het beeld dat direct volgt op de tekst met dit ID.
for i, b in enumerate(inner):
if b.get("type") == "text" and ss_id in b.get("text", ""):
for nb in inner[i + 1:]:
if nb.get("type") == "image":
return base64.b64decode(nb["source"]["data"])
if nb.get("type") == "text" and "ss_" in nb.get("text", ""):
break
return None
ss_id, dest = sys.argv[1], Path(sys.argv[2])
data = find(ss_id)
if not data:
sys.exit(f"geen beeld gevonden voor {ss_id}")
dest.write_bytes(data)
print(f"{dest.name}: {len(data)//1024} kB")