Compare commits
2
Commits
ad18d79c18
...
cfa5985f44
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfa5985f44 | ||
|
|
a6abe939e8 |
@@ -0,0 +1,348 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Computer Historyz - Progressive Klick-Decryption</title>
|
||||
<style>
|
||||
/* --- KERNFARBEN AUS DESIGN-DOKU: MASCHINEN-INSTANZEN --- */
|
||||
:root {
|
||||
--bg-deep: #05070a;
|
||||
--bg-panel: #0b0e14;
|
||||
|
||||
--accent-cyan: #00f2ff;
|
||||
--accent-green: #00ff73;
|
||||
--accent-amber: #ffb700;
|
||||
--accent-red: #ff3e3e;
|
||||
|
||||
--text-dim: #888899;
|
||||
--font-mono: "JetBrains Mono", "Fira Code", "Courier Prime", monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-deep);
|
||||
color: var(--text-dim);
|
||||
font-family: var(--font-mono);
|
||||
margin: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--text-dim);
|
||||
backdrop-filter: blur(4px);
|
||||
padding: 15px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 0 6px rgba(0, 242, 255, 0.1);
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
color: var(--accent-green);
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px dashed var(--text-dim);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
input, textarea, button {
|
||||
background: #07090e;
|
||||
border: 1px solid var(--text-dim);
|
||||
color: var(--accent-green);
|
||||
padding: 8px;
|
||||
font-family: var(--font-mono);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-cyan);
|
||||
box-shadow: 0 0 6px var(--accent-cyan);
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
background: #0c161a;
|
||||
border-color: var(--accent-cyan);
|
||||
color: var(--accent-cyan);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--accent-cyan);
|
||||
color: var(--bg-deep);
|
||||
box-shadow: 0 0 6px var(--accent-cyan);
|
||||
}
|
||||
|
||||
/* --- CRYPTO BLÖCKE SYSTEM --- */
|
||||
.lock-chain {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
background: #020407;
|
||||
padding: 12px;
|
||||
border-left: 3px solid var(--text-dim);
|
||||
margin: 15px 0;
|
||||
color: #555566;
|
||||
transition: all 0.3s ease;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Zustand: Block wartet darauf, geklickt zu werden */
|
||||
.lock-chain.is-active {
|
||||
border-left: 3px solid var(--accent-amber);
|
||||
color: var(--accent-cyan);
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
box-shadow: 0 0 6px rgba(255, 183, 0, 0.2);
|
||||
animation: pulse-active 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
/* Zustand: Erfolgreich decodiert */
|
||||
.lock-chain.is-decrypted {
|
||||
border-left: 3px solid var(--accent-green);
|
||||
color: var(--accent-green);
|
||||
background: transparent;
|
||||
white-space: normal;
|
||||
opacity: 1;
|
||||
animation: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Zustand: Fehler bei der Entschlüsselung */
|
||||
.lock-chain.error {
|
||||
border-left: 3px solid var(--accent-red);
|
||||
color: var(--accent-red);
|
||||
opacity: 1;
|
||||
animation: blink-error 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-active {
|
||||
0% { background: #020407; }
|
||||
50% { background: #0a121c; }
|
||||
100% { background: #020407; }
|
||||
}
|
||||
|
||||
@keyframes blink-error {
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
font-size: 0.85em;
|
||||
background: #111;
|
||||
border: 1px solid var(--text-dim);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="panel control-panel">
|
||||
<div class="panel-title">Decryption Control Deck</div>
|
||||
<label for="master-pass">Master-Passwort (Key 0):</label>
|
||||
<input type="password" id="master-pass" value="OortscheWolke2448">
|
||||
<button onclick="initializeCryptoChain()">Krypto-Kette initialisieren</button>
|
||||
<div id="deck-status" class="status-badge" style="color: var(--accent-amber);">Status: Warte auf Initialisierung...</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-title">Dokument-Struktur (Instanzen-Protokoll)</div>
|
||||
|
||||
<h3>Knotenpunkt-Datenstrom</h3>
|
||||
<p><em>Dieser Textbereich liegt außerhalb der Kette und bleibt dauerhaft sichtbar.</em></p>
|
||||
|
||||
<div class="lock-chain" id="block-0" onclick="triggerBlockDecryption(0)">
|
||||
4a21184a44f9deeb8db93ee5c56dfba2d28fbfb3970b880197d19e9eb10a
|
||||
a011de36bdfc1103f6f14066094ef5ad0554ba739cae9dfc01991a0c0f99
|
||||
8ee2954aef83fbdf9842a2acb119cb
|
||||
</div>
|
||||
|
||||
<div class="lock-chain" id="block-1" onclick="triggerBlockDecryption(1)">
|
||||
108b688d01a3579ab5a6c117b38d388f8d689b9d3ff02102554dfa45bbca
|
||||
c0a7cc02ccff5d8ee9d110f01ccba1dd010ea4fbc11e22778da5a6c1deba
|
||||
99a9cf588b
|
||||
</div>
|
||||
|
||||
<div class="lock-chain" id="block-2" onclick="triggerBlockDecryption(2)">
|
||||
92da21085ba1f43a99e7d9b9d62da3ffde4bb8df115dd0ca31bdaef9d877
|
||||
bbad84ff4da92da74e99a4c11100bb17dca5ffca188e7d22cb54fb77ac90
|
||||
d22bb488de1a56daff8e9b
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-title">Ketten-Generator (Text-zu-Klick-Block)</div>
|
||||
<textarea id="gen-input" rows="4">Das Ur-Ding der Erbauer war die Energie.
|
||||
Die Hauptmaschinen sind monumentale Datenbanken.
|
||||
Das Nina-Protokoll verhindert den Ressourcen-Kollaps.</textarea>
|
||||
<button onclick="generateNewChain()">Neue Krypto-Kette generieren</button>
|
||||
<div id="gen-output" style="white-space: pre; font-family: var(--font-mono); color: var(--accent-cyan); margin-top: 10px; font-size: 0.9em;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- STATE MACHINE FÜR DIE KLICK-KETTE ---
|
||||
let currentActiveIndex = -1;
|
||||
let nextKeyString = "";
|
||||
|
||||
// --- UTILITIES (Native Web Crypto) ---
|
||||
function bufToHex(buf) {
|
||||
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
function hexToBuf(hex) {
|
||||
const bytes = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
async function deriveKey(passwordString) {
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(passwordString);
|
||||
const hash = await crypto.subtle.digest('SHA-256', data);
|
||||
return await crypto.subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']);
|
||||
}
|
||||
|
||||
// --- INTERAKTIVE LOGIK ---
|
||||
|
||||
// Schritt 1: Master-Passwort laden und Block 0 scharf schalten
|
||||
function initializeCryptoChain() {
|
||||
const masterPassword = document.getElementById('master-pass').value;
|
||||
if (!masterPassword) {
|
||||
updateStatus("FEHLER: Passwort fehlt", "var(--accent-red)");
|
||||
return;
|
||||
}
|
||||
|
||||
nextKeyString = masterPassword;
|
||||
currentActiveIndex = 0;
|
||||
|
||||
// Alle UI-Klassen zurücksetzen
|
||||
const blocks = document.querySelectorAll('.lock-chain');
|
||||
blocks.forEach((block, idx) => {
|
||||
block.classList.remove('is-active', 'is-decrypted', 'error');
|
||||
// Wenn es der erste Block ist, aktiv schalten
|
||||
if(idx === 0) {
|
||||
block.classList.add('is-active');
|
||||
}
|
||||
});
|
||||
|
||||
updateStatus("Kette bereit. Klicke auf Block 1...", "var(--accent-cyan)");
|
||||
}
|
||||
|
||||
// Schritt 2: Klick-Handler für die einzelnen Blöcke
|
||||
async function triggerBlockDecryption(blockIndex) {
|
||||
// Ignoriere Klicks, wenn der Block noch nicht an der Reihe ist
|
||||
if (blockIndex !== currentActiveIndex) return;
|
||||
|
||||
const block = document.getElementById(`block-${blockIndex}`);
|
||||
let rawContent = block.innerText.replace(/\r?\n|\r/g, "");
|
||||
|
||||
// Format-Sicherheitscheck (\x02 STX und \x03 ETX)
|
||||
if (!rawContent.startsWith('\x02') || !rawContent.endsWith('\x03')) {
|
||||
markError(block, "Formatierungsfehler (Steuerzeichen fehlen)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Payload-Filterung
|
||||
const payload = rawContent.substring(1, rawContent.length - 1).replace(/\s/g, "");
|
||||
const ivHex = payload.substring(0, 24);
|
||||
const cipherHex = payload.substring(24);
|
||||
|
||||
try {
|
||||
const iv = hexToBuf(ivHex);
|
||||
const cipherData = hexToBuf(cipherHex);
|
||||
const key = await deriveKey(nextKeyString);
|
||||
|
||||
const decryptedBuf = await crypto.subtle.decrypt(
|
||||
{ name: 'AES-GCM', iv: iv },
|
||||
key,
|
||||
cipherData
|
||||
);
|
||||
|
||||
const plaintext = new TextDecoder().decode(decryptedBuf);
|
||||
|
||||
// DOM-Austausch unter Beibehaltung der Struktur
|
||||
block.innerHTML = plaintext;
|
||||
block.classList.remove('is-active', 'error');
|
||||
block.classList.add('is-decrypted');
|
||||
|
||||
// HIER PASSIERT DIE FORTLAUFENDE SCHLÜSSEL-ÜBERGABE:
|
||||
nextKeyString = plaintext;
|
||||
|
||||
// Nächsten Block in der Kette freischalten
|
||||
currentActiveIndex++;
|
||||
const nextBlock = document.getElementById(`block-${currentActiveIndex}`);
|
||||
|
||||
if (nextBlock) {
|
||||
nextBlock.classList.add('is-active');
|
||||
updateStatus(`Block ${blockIndex + 1} gelöst. Nächster Block freigegeben!`, "var(--accent-cyan)");
|
||||
} else {
|
||||
updateStatus("System vollständig decodiert. Kette beendet.", "var(--accent-green)");
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
markError(block, `Kettenschluss fehlgeschlagen. Falscher Key.`);
|
||||
}
|
||||
}
|
||||
|
||||
function markError(element, message) {
|
||||
element.classList.remove('is-active');
|
||||
element.classList.add('error');
|
||||
element.innerText = `[CRITICAL CODE CORRUPTION: ${message}]`;
|
||||
updateStatus("Kette dauerhaft unterbrochen.", "var(--accent-red)");
|
||||
currentActiveIndex = -1; // Blockiert weitere Klicks
|
||||
}
|
||||
|
||||
function updateStatus(text, color) {
|
||||
const badge = document.getElementById('deck-status');
|
||||
badge.innerText = `Status: ${text}`;
|
||||
badge.style.color = color;
|
||||
badge.style.borderColor = color;
|
||||
}
|
||||
|
||||
// --- CHIP-GENERATOR FÜR DEINE TEXTE ---
|
||||
async function generateNewChain() {
|
||||
const masterPassword = document.getElementById('master-pass').value;
|
||||
const lines = document.getElementById('gen-input').value.split('\n').filter(l => l.trim() !== "");
|
||||
let currentKeyString = masterPassword;
|
||||
let htmlOutput = "";
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const plaintext = lines[i];
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const key = await deriveKey(currentKeyString);
|
||||
|
||||
const encryptedBuf = await crypto.subtle.encrypt(
|
||||
{ name: 'AES-GCM', iv: iv },
|
||||
key,
|
||||
new TextEncoder().encode(plaintext)
|
||||
);
|
||||
|
||||
const fullPayloadHex = bufToHex(iv) + bufToHex(encryptedBuf);
|
||||
|
||||
let formattedHex = "";
|
||||
for (let j = 0; j < fullPayloadHex.length; j += 60) {
|
||||
formattedHex += "\n" + fullPayloadHex.substring(j, j + 60);
|
||||
}
|
||||
|
||||
// Generiert direkt die korrekten Klick-IDs und Handler für deinen Quelltext
|
||||
htmlOutput += `<div class="lock-chain" id="block-${i}" onclick="triggerBlockDecryption(${i})">${formattedHex}\n</div>\n\n`;
|
||||
|
||||
currentKeyString = plaintext;
|
||||
}
|
||||
|
||||
document.getElementById('gen-output').innerHTML = htmlOutput;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/bash
|
||||
BASEDIR=$(pwd)
|
||||
INPUT_DIR="$BASEDIR/input"
|
||||
OUTPUT_DIR="$BASEDIR/91_sonnensystem"
|
||||
TEMPLATES="$BASEDIR/templates"
|
||||
|
||||
chown -R appuser:appgroup "$OUTPUT_DIR"
|
||||
|
||||
function convert_adoc() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "-> Konvertiere $1 zu HTML..."
|
||||
asciidoctor -b html5 -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left "$INPUT_DIR/$1" -o "$OUTPUT_DIR/$2"
|
||||
else
|
||||
echo " [INFO] $1 nicht gefunden, überspringe..."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
echo '--- Baue README ---'
|
||||
convert_adoc README.adoc README.html
|
||||
|
||||
echo '--- Baue Lore (Master-Dokument) ---'
|
||||
LORE_SRC="$INPUT_DIR/index.adoc"
|
||||
if [ -f "$INPUT_DIR/master.adoc" ]; then LORE_SRC="$INPUT_DIR/master.adoc"; fi
|
||||
|
||||
if [ -f "$LORE_SRC" ]; then
|
||||
echo "-> Generiere Lore-Dokument (HTML & Markdown) aus $LORE_SRC..."
|
||||
asciidoctor -b html5 -d book -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left "$LORE_SRC" -o "$OUTPUT_DIR/lore.html"
|
||||
asciidoctor -b docbook -d book "$LORE_SRC" -o "$OUTPUT_DIR/temp_lore.xml"
|
||||
pandoc -f docbook -t markdown_strict "$OUTPUT_DIR/temp_lore.xml" -o "$OUTPUT_DIR/lore.md"
|
||||
rm "$OUTPUT_DIR/temp_lore.xml"
|
||||
else
|
||||
echo " [WARNUNG] Weder index.adoc noch master.adoc gefunden!"
|
||||
fi
|
||||
|
||||
echo '--- Baue Fließtexte / Geschichten ---'
|
||||
if [ -f "$INPUT_DIR/darkCenter.adoc" ]; then
|
||||
echo "-> Konvertiere darkCenter.adoc zu HTML..."
|
||||
asciidoctor -b html5 -d book -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left "$INPUT_DIR/darkCenter.adoc" -o "$OUTPUT_DIR/darkCenter.html"
|
||||
fi
|
||||
|
||||
rm -rf ./export
|
||||
mkdir "$OUTPUT_DIR/css"
|
||||
cp "$TEMPLATES/retro-dark.css" "$OUTPUT_DIR/css/"
|
||||
cp "$TEMPLATES/index.html" "$OUTPUT_DIR/"
|
||||
sed -i 's/\/workspace\/templates/css/g' "$OUTPUT_DIR/darkCenter.html"
|
||||
chown -R appuser:appgroup "$OUTPUT_DIR"
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM debian:stable-slim
|
||||
|
||||
RUN apt-get update && apt-get upgrade && apt-get -y install asciidoctor pandoc bash
|
||||
|
||||
RUN groupadd --gid 1000 appgroup && \
|
||||
useradd --uid 1000 --gid appgroup --shell /bin/bash --create-home appuser
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
ADD ./90_builder/templates templates
|
||||
ADD ./90_builder/bookFold.sh ./
|
||||
|
||||
RUN chown -R appuser:appgroup /workspace
|
||||
|
||||
|
||||
CMD ["/usr/bin/bash" "./bookFold.sh"]
|
||||
@@ -1,15 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Name des lokalen Docker-Images
|
||||
IMAGE_NAME="lore-builder"
|
||||
IMAGE_NAME="ian-lore-builder"
|
||||
# Input Ordner
|
||||
INP_DIR=$(pwd)
|
||||
# Build Ordner
|
||||
BID_DIR="90_builder"
|
||||
# BID_DIR="90_builder"
|
||||
# Build templares
|
||||
TEMPLATES="$BID_DIR/templates"
|
||||
# TEMPLATES="$BID_DIR/templates"
|
||||
# Ausgabeordner
|
||||
OUT_DIR="91_sonnensystem"
|
||||
OUT_DIR="$INP_DIR/91_sonnensystem"
|
||||
|
||||
# Farben für Output
|
||||
GREEN='\033[0;32m'
|
||||
@@ -26,11 +26,7 @@ echo -e "Output-Ordner '${GREEN}$OUT_DIR${NC}' bereinigt und bereit."
|
||||
# 2. Prüfen, ob das Docker-Image existiert, ansonsten on-the-fly bauen
|
||||
if [[ "$(docker images -q $IMAGE_NAME 2> /dev/null)" == "" ]]; then
|
||||
echo -e "${BLUE}Baue flüchtiges Docker-Image '$IMAGE_NAME' (Alpine, Asciidoctor, Pandoc)...${NC}"
|
||||
docker build -t $IMAGE_NAME - <<EOF
|
||||
FROM alpine:latest
|
||||
RUN apk add --no-cache asciidoctor pandoc bash
|
||||
WORKDIR /workspace
|
||||
EOF
|
||||
docker build -t $IMAGE_NAME -f ./90_builder/container/deb_Dockerfile .
|
||||
fi
|
||||
|
||||
# 3. CSS Datei für Retro Space Dark Theme in den Export-Ordner generieren
|
||||
@@ -39,40 +35,8 @@ fi
|
||||
echo -e "${BLUE}Starte Build-Container und generiere Dokumente...${NC}"
|
||||
|
||||
# 4. Container starten und Build-Schritte im Container ausführen
|
||||
docker run --rm -v "$INP_DIR:/workspace" $IMAGE_NAME bash -c "
|
||||
function convert_adoc() {
|
||||
if [ -f \"\$1\" ]; then
|
||||
echo \"-> Konvertiere \$1 zu HTML...\"
|
||||
asciidoctor -b html5 -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left \"\$1\" -o \"$OUT_DIR/\$2\"
|
||||
else
|
||||
echo \" [INFO] \$1 nicht gefunden, überspringe...\"
|
||||
fi
|
||||
}
|
||||
|
||||
echo '--- Baue README ---'
|
||||
convert_adoc README.adoc README.html
|
||||
|
||||
echo '--- Baue Lore (Master-Dokument) ---'
|
||||
LORE_SRC=\"index.adoc\"
|
||||
if [ -f master.adoc ]; then LORE_SRC=\"master.adoc\"; fi
|
||||
|
||||
if [ -f \"\$LORE_SRC\" ]; then
|
||||
echo \"-> Generiere Lore-Dokument (HTML & Markdown) aus \$LORE_SRC...\"
|
||||
asciidoctor -b html5 -d book -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left \"\$LORE_SRC\" -o \"$OUT_DIR/lore.html\"
|
||||
asciidoctor -b docbook -d book \"\$LORE_SRC\" -o \"$OUT_DIR/temp_lore.xml\"
|
||||
pandoc -f docbook -t markdown_strict \"$OUT_DIR/temp_lore.xml\" -o \"$OUT_DIR/lore.md\"
|
||||
rm \"$OUT_DIR/temp_lore.xml\"
|
||||
else
|
||||
echo \" [WARNUNG] Weder index.adoc noch master.adoc gefunden!\"
|
||||
fi
|
||||
|
||||
echo '--- Baue Fließtexte / Geschichten ---'
|
||||
if [ -f darkCenter.adoc ]; then
|
||||
echo \"-> Konvertiere darkCenter.adoc zu HTML...\"
|
||||
asciidoctor -b html5 -d book -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left darkCenter.adoc -o \"$OUT_DIR/darkCenter.html\"
|
||||
fi
|
||||
|
||||
"
|
||||
# docker run --rm -v "$INP_DIR:/workspace" $IMAGE_NAME /workspace/bookFold.sh
|
||||
docker run --rm -v "$(pwd):/workspace/input" -v "$(pwd)/91_sonnensystem:/workspace/91_sonnensystem" $IMAGE_NAME
|
||||
|
||||
# 5. index.html vollautomatisch und direkt generieren
|
||||
# moved to $BID_OUT/templates/index.html
|
||||
|
||||
Reference in New Issue
Block a user