ontoref/assets/presentation/.tmp-regenerate-reader-docs.mjs

200 lines
9.8 KiB
JavaScript
Raw Normal View History

import fs from 'node:fs'
import { resolve } from 'node:path'
import { parser } from '@slidev/cli'
const TOTAL_SECONDS = 30 * 60
const data = await parser.load(resolve('.'), resolve('slides.md'))
const slides = data.slides.map((s, i) => ({
n: i + 1,
name: s.frontmatter?.name || null,
raw: s.content || '',
}))
function stripText(raw) {
let t = raw
t = t.replace(/<!--[\s\S]*?-->/g, ' ')
t = t.replace(/<style[\s\S]*?<\/style>/gi, ' ')
t = t.replace(/```[\s\S]*?```/g, ' [code example] ')
t = t.replace(/<[^>]+>/g, ' ')
t = t.replace(/^\s*::.*::\s*$/gm, ' ')
t = t.replace(/^\s*#\s*/gm, '')
t = t.replace(/^\s*[-*]\s+/gm, '')
t = t.replace(/^\s*\d+\.\s+/gm, '')
t = t.replace(/\[(.*?)\]\((.*?)\)/g, '$1')
t = t.replace(/[`*_~]/g, '')
t = t.replace(/\|/g, ' ')
t = t.replace(/\n{3,}/g, '\n\n')
const lines = t.split(/\r?\n/).map(l => l.trim()).filter(Boolean)
return lines
}
function simplify(line) {
let t = line
const repl = [
[/\binfrastructure\b/gi, 'infra'],
[/\bconfiguration\b/gi, 'config'],
[/\bvalidation\b/gi, 'check'],
[/\breliability\b/gi, 'stable work'],
[/\bdeclarative\b/gi, 'declared'],
[/\borchestrator\b/gi, 'controller'],
[/\bdistributed\b/gi, 'spread out'],
[/\bparadigm\b/gi, 'approach'],
[/\bpragmatism\b/gi, 'practical work'],
[/\bguarantees\b/gi, 'safety rules'],
[/\bprovisioning\b/gi, 'setup'],
[/\bcompiler\b/gi, 'compiler check'],
]
for (const [re, to] of repl) t = t.replace(re, to)
t = t.replace(/\bIaC\b/g, 'I A C').replace(/\bCI\/CD\b/g, 'C I slash C D').replace(/24×7×365/g, 'all day, every day')
t = t.replace(/\s+—\s+/g, '. ').replace(/\s+->\s+/g, '. then ').replace(/,\s*/g, '. ')
t = t.replace(/\s{2,}/g, ' ').trim()
return t
}
function say(line) {
const rules = [
[/\bInfrastructure\b/gi, 'Infrastructure (IN-fruh-STRUHK-cher)'],
[/\bautomation\b/gi, 'automation (aw-tuh-MAY-shun)'],
[/\bconfiguration\b/gi, 'configuration (kun-fig-yuh-RAY-shun)'],
[/\bprovisioning\b/gi, 'provisioning (pruh-VIH-zhuh-ning)'],
[/\bcompiler\b/gi, 'compiler (kum-PIE-ler)'],
[/\breliability\b/gi, 'reliability (rih-lai-uh-BIH-luh-tee)'],
[/\bvalidation\b/gi, 'validation (val-ih-DAY-shun)'],
[/\bdeclarative\b/gi, 'declarative (dih-KLAR-uh-tiv)'],
[/\borchestrator\b/gi, 'orchestrator (OR-kes-tray-ter)'],
[/\bparadigm\b/gi, 'paradigm (PAIR-uh-dym)'],
[/\bRust\b/g, 'Rust (rahst)'],
[/\bSerde\b/g, 'Serde (SER-dee)'],
[/\bOption\b/g, 'Option (OP-shun)'],
[/\benum\b/gi, 'enum (EE-num)'],
[/\bKubernetes\b/gi, 'Kubernetes (koo-ber-NET-eez)'],
[/\bAnsible\b/gi, 'Ansible (AN-suh-buhl)'],
[/\bTerraform\b/gi, 'Terraform (TEH-ruh-form)'],
[/\bCI\/CD\b/g, 'CI/CD (see-eye / see-dee)'],
[/\bIaC\b/g, 'IaC (eye-ay-see)'],
]
let t = line
for (const [re, rep] of rules) t = t.replace(re, rep)
t = t.replace(/\s+—\s+/g, ' / ').replace(/\.\s+/g, '. / ').replace(/:\s+/g, ': / ')
return t
}
function phase(n) {
if (n <= 3) return ['Act 1 - Hook & Promise', 'Low -> Medium', 'Credibilidad y dolor real', 'Calma, pausas limpias']
if (n <= 7) return ['Act 2 - Escalation', 'Medium -> High', 'Crecimiento de complejidad sin control', 'Ritmo creciente']
if (n <= 14) return ['Act 3 - Problem Anatomy', 'High', 'Causa raiz y costo de fallar tarde', 'Didactico, frases cortas']
if (n <= 18) return ['Act 4 - Turning Point', 'Peak', 'No faltan tools, falta paradigma', 'Silencios intencionales']
if (n <= 23) return ['Act 5 - Resolution', 'High -> Medium', 'Types y compiler como respuesta', 'Claro y tecnico']
if (n <= 31) return ['Act 6 - Proof in Production', 'Medium -> High', 'Casos reales e impacto operativo', 'Energetico y concreto']
return ['Act 7 - Close & CTA', 'Medium -> Low', 'Cierre emocional con accion', 'Lento y memorable']
}
const slideTexts = slides.map(s => ({ ...s, lines: stripText(s.raw) }))
// 1) reader-script-en.md
let out = '# Rustikon 2026 - Slide Text (Reader Version)\n\n'
out += 'One section per real Slidev slide (parser-based count).\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
if (!s.lines.length) out += '[No readable text]\n\n'
else out += s.lines.join('\n') + '\n\n'
}
fs.writeFileSync('reader-script-en.md', out)
// 2) pronunciation
out = '# Rustikon 2026 - Reader Script with Pronunciation Guide\n\n'
out += 'Use this for practice. Read the SAY line.\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
if (!s.lines.length) {
out += 'EN: [No readable text]\nSAY: [No readable text]\n\n'
continue
}
for (const l of s.lines) out += `EN: ${l}\nSAY: ${say(l)}\n\n`
}
fs.writeFileSync('reader-script-en-pronunciation.md', out)
// 3) simple
out = '# Rustikon 2026 - Simple Reader Script (Easy English)\n\n'
out += 'Short and clear lines for easy pronunciation.\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
if (!s.lines.length) {
out += '[No readable text]\n\n'
continue
}
for (const l of s.lines) out += `${simplify(l)}\n\n`
}
fs.writeFileSync('reader-script-en-simple.md', out)
// 4) live
out = '# Rustikon 2026 - Live Reader Script (Speech Only)\n\n'
out += 'Only lines to speak. Legend: `/` short pause, `//` long pause.\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
const spoken = s.lines.map(simplify).filter(l => /[A-Za-z]/.test(l) && !/^[-=]+$/.test(l))
if (!spoken.length) {
out += '(No spoken text. Move forward.)\n\n'
continue
}
for (const l of spoken) out += `${l.replace(/\.\s+/g, '. / ').replace(/\s+—\s+/g, ' / ')}\n\n`
}
fs.writeFileSync('reader-script-en-live.md', out)
// 5) live 30-min
const weights = slideTexts.map(s => {
const words = s.lines.join(' ').split(/\s+/).filter(Boolean).length
return 6 + words
})
let secs = weights.map(w => Math.round((w / weights.reduce((a,b)=>a+b,0)) * TOTAL_SECONDS))
const minSec=15,maxSec=90
secs = secs.map(v => Math.max(minSec, Math.min(maxSec, v)))
let diff = TOTAL_SECONDS - secs.reduce((a,b)=>a+b,0)
while (diff !== 0) {
let changed = false
for (let i=0; i<secs.length && diff!==0; i++) {
if (diff > 0 && secs[i] < maxSec) { secs[i]++; diff--; changed = true }
else if (diff < 0 && secs[i] > minSec) { secs[i]--; diff++; changed = true }
}
if (!changed) break
}
const mmss = (t)=>`${String(Math.floor(t/60)).padStart(2,'0')}:${String(t%60).padStart(2,'0')}`
out = '# Rustikon 2026 - Live Reader (30-Min Timing)\n\n'
out += 'Total target: 30:00 (1800s)\n\n'
out += 'How to use:\n- Keep your pace near each slide target.\n- If you are late, cut one example line and move on.\n- If you are early, add one short emphasis line.\n\n'
let cum = 0
for (let i=0;i<slideTexts.length;i++) {
const s = slideTexts[i]
cum += secs[i]
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''} [target: ${secs[i]}s | cumulative: ${mmss(cum)}]\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
const spoken = s.lines.map(simplify).filter(l => /[A-Za-z]/.test(l) && !/^[-=]+$/.test(l))
if (!spoken.length) out += '(No spoken text. Move forward.)\n\n'
else for (const l of spoken) out += `${l.replace(/\.\s+/g, '. / ')}\n\n`
}
out += '## Checkpoints\n\n'
for (const mark of [5,10,15,20,25,30]) {
const target = mark * 60
let c = 0, slideN = 1
for (let i=0;i<secs.length;i++) { c += secs[i]; if (c >= target) { slideN = i + 1; break } }
out += `- ${String(mark).padStart(2,'0')}:00 -> around Slide ${slideN}\n`
}
fs.writeFileSync('reader-script-en-live-30min.md', out)
// 6) key moments for 35 slides
const key = `# Rustikon 2026 - Key Story Moments\n\nUse this as your minimal narrative map for a 30-minute talk.\n\n## Moment 1 - Hook (Slides 1-3)\nPurpose: Win attention and establish credibility.\n\nAnchor line:\n"I have lived this problem for decades, and I wanted one thing: to sleep."\n\n## Moment 2 - Rising Tension (Slides 4-7)\nPurpose: Show complexity growing faster than control.\n\nAnchor line:\n"We changed tools many times, but not the model."\n\n## Moment 3 - Crisis / Root Cause (Slides 8-14)\nPurpose: Make the cost of late failure undeniable.\n\nAnchor line:\n"Fail late is expensive. Fail early is cheap."\n\n## Moment 4 - Turning Point (Slides 15-18)\nPurpose: Reframe the problem.\n\nAnchor line:\n"The problem was not the tools. The problem was the paradigm."\n\n## Moment 5 - Proof in Production (Slides 24-31)\nPurpose: Show real operational evidence.\n\nAnchor line:\n"At 3 AM, the system responds first. Not me."\n\n## Moment 6 - Emotional Close + CTA (Slides 33-35)\nPurpose: Close with trust and clear action.\n\nAnchor line:\n"Rust gave me deterministic systems and better sleep."\n\n## One-Line Backup Version\n1. I lived this problem for decades.\n2. Complexity grew; certainty did not.\n3. Late failures are the real tax.\n4. Types changed the paradigm.\n5. Production proof: lower MTTR and deterministic workflows.\n6. Start small: model infrastructure as types.\n`
fs.writeFileSync('key-moments-storytelling.md', key)
console.log('Real Slidev slides:', slideTexts.length)
console.log('Docs regenerated with parser-based slide count.')