From a9b46434f44f2de8b4aafcc8bbcb04a6e486f4a1 Mon Sep 17 00:00:00 2001 From: Jheison Martinez Bolivar Date: Mon, 2 Nov 2026 08:41:56 -0500 Subject: [PATCH 1/5] feat(canopy): add Remote and catalogue sections and fix hero CTAs --- public/_redirects | 10 +- public/llms.txt | 12 +- src/__tests__/canopy-remote-catalogue.test.ts | 150 +++++ src/components/Banner.astro | 6 + src/components/CataloguePlate.astro | 91 ++++ src/i18n/en-experiments.ts | 515 ++++++++++++++++++ src/i18n/en.ts | 492 +---------------- src/i18n/es-experiments.ts | 514 +++++++++++++++++ src/i18n/es.ts | 489 +---------------- src/layouts/ExperimentLayout.astro | 2 +- src/views/experiments/Canopy.astro | 206 ++++++- 11 files changed, 1489 insertions(+), 998 deletions(-) create mode 100644 src/__tests__/canopy-remote-catalogue.test.ts create mode 100644 src/components/CataloguePlate.astro create mode 100644 src/i18n/en-experiments.ts create mode 100644 src/i18n/es-experiments.ts diff --git a/public/_redirects b/public/_redirects index 9d09a6b..194b7aa 100644 --- a/public/_redirects +++ b/public/_redirects @@ -33,6 +33,14 @@ /canopy/docs/adr/0001-recipes/index.md /canopy/docs/ 301 /canopy/docs/agents.md /canopy/docs/agents/ 301 /canopy/docs/cli-reference.md /canopy/docs/cli-reference/ 301 +/canopy/docs/graphs-ensembles.md /canopy/docs/graphs-ensembles/ 301 +/canopy/docs/graphs-execution.md /canopy/docs/graphs-execution/ 301 +/canopy/docs/graphs-hooks-and-queues.md /canopy/docs/graphs-hooks-and-queues/ 301 +/canopy/docs/graphs-isolation.md /canopy/docs/graphs-isolation/ 301 +/canopy/docs/graphs-nodes.md /canopy/docs/graphs-nodes/ 301 +/canopy/docs/graphs-operations.md /canopy/docs/graphs-operations/ 301 +/canopy/docs/graphs-sharing.md /canopy/docs/graphs-sharing/ 301 +/canopy/docs/graphs-templates.md /canopy/docs/graphs-templates/ 301 /canopy/docs/graphs.md /canopy/docs/graphs/ 301 /canopy/docs/index.md /canopy/docs/ 301 /canopy/docs/installation.md /canopy/docs/installation/ 301 @@ -41,9 +49,7 @@ /canopy/docs/mcp-tools.md /canopy/docs/mcp-tools/ 301 /canopy/docs/quickstart.md /canopy/docs/quickstart/ 301 /canopy/docs/rag.md /canopy/docs/rag/ 301 -/canopy/docs/recipes.md /canopy/docs/recipes/ 301 /canopy/docs/tui.md /canopy/docs/tui/ 301 -/canopy/docs/usage-patterns.md /canopy/docs/usage-patterns/ 301 /canopy/docs/workflows/ /canopy/docs/graphs/ 301 /demostage/docs/commands.md /demostage/docs/commands/ 301 /demostage/docs/decisions/browser-events.md /demostage/docs/ 301 diff --git a/public/llms.txt b/public/llms.txt index e6d5ead..0eae83c 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -52,10 +52,16 @@ Different problems, one method: take work that lives in fragile, manual procedur - [Agents](https://univerlab.org/canopy/docs/agents/index.md): Interactive PTY agents, background cron/watcher agents, terminal sessions and seed identities. Canopy documentation — A graph engine for AI coding agents - [Intelligence & Sync](https://univerlab.org/canopy/docs/intelligence-and-sync/index.md): The project-scoped knowledge graph and multi-agent workspace coordination. Canopy documentation — A graph engine for AI coding agents — every harness, one - [Graphs](https://univerlab.org/canopy/docs/graphs/index.md): The DAG graph engine — specs, nodes, edges, gates and lifecycle. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. -- [Usage Patterns](https://univerlab.org/canopy/docs/usage-patterns/index.md): The shapes a graph can take, what each one is good at, and where each one breaks. Canopy documentation — A graph engine for AI coding agents — every harness -- [Recipes](https://univerlab.org/canopy/docs/recipes/index.md): Graphs built end to end — the actual calls, the actual prompts, and what breaks. Canopy documentation — A graph engine for AI coding agents — every harness, one +- [Template Variables](https://univerlab.org/canopy/docs/graphs-templates/index.md): Placeholders in agent prompts and check commands. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. +- [Ensembles](https://univerlab.org/canopy/docs/graphs-ensembles/index.md): Parallel, cascade and round-robin member groups, crews, and availability. Canopy documentation — A graph engine for AI coding agents — every harness, one +- [Execution](https://univerlab.org/canopy/docs/graphs-execution/index.md): Commit rights, self-reports, retries, the execution limit, lifecycle, deterministic commits and spec notes. Canopy documentation — A graph engine for AI coding +- [Isolation](https://univerlab.org/canopy/docs/graphs-isolation/index.md): Sandboxes, concurrency, and named mutexes. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. +- [Hooks and Queues](https://univerlab.org/canopy/docs/graphs-hooks-and-queues/index.md): Event-keyed hooks, the standalone spec backlog, and spec queues. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. +- [Sharing Graphs](https://univerlab.org/canopy/docs/graphs-sharing/index.md): Export and import, validate before you share, and install a graph. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. +- [Node Documents](https://univerlab.org/canopy/docs/graphs-nodes/index.md): Reusable node documents: install, list, add, remove. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. +- [Graph Operations](https://univerlab.org/canopy/docs/graphs-operations/index.md): Archive and restore, node run history, the event log, and engine tuning. Canopy documentation — A graph engine for AI coding agents — every harness, one - [RAG Pipeline](https://univerlab.org/canopy/docs/rag/index.md): Local-first semantic search over your own documents. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. -- [MCP Tools](https://univerlab.org/canopy/docs/mcp-tools/index.md): All 117 MCP tools exposed by the canopy daemon, by category. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. +- [MCP Tools](https://univerlab.org/canopy/docs/mcp-tools/index.md): All 34 MCP tools exposed by the canopy daemon, by category. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. - [CLI Reference](https://univerlab.org/canopy/docs/cli-reference/index.md): Every canopy command and flag. Canopy documentation — A graph engine for AI coding agents — every harness, one workflow. ### TeXForge diff --git a/src/__tests__/canopy-remote-catalogue.test.ts b/src/__tests__/canopy-remote-catalogue.test.ts new file mode 100644 index 0000000..f1c6caa --- /dev/null +++ b/src/__tests__/canopy-remote-catalogue.test.ts @@ -0,0 +1,150 @@ +/** + * Canopy's third pass: the Remote and catalogue sections, the hero CTAs and the + * banner clip fix. + * + * Req 5 (both new links visible without scrolling at 1280×800) has no e2e + * harness in this repo — there is no Playwright, no e2e directory and no + * browser in CI. The repo's convention is source pins plus real-`dist/` reads, + * so this file asserts the structure that makes the fold possible (the links + * are in the hero `.actions`, and the view orders `.actions` first) while the + * actual geometry is measured with Chrome/CDP in the change report and the + * screenshots. Do not "fix" this with Playwright. + * + * CI builds before it tests; a missing dist/ is a clear "run npm run build". + */ +import { existsSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { en } from '../i18n/en'; +import { es } from '../i18n/es'; + +const root = join(__dirname, '..', '..'); + +function built(page: string): string { + const file = join(root, 'dist', page); + if (!existsSync(file)) { + throw new Error(`dist/${page} not found — run \`npm run build\` before \`npm test\`.`); + } + return readFileSync(file, 'utf8'); +} + +const canopyEn = built('canopy/index.html'); +const canopyEs = built('es/canopy/index.html'); + +const view = readFileSync(join(root, 'src/views/experiments/Canopy.astro'), 'utf8'); +const layout = readFileSync(join(root, 'src/layouts/ExperimentLayout.astro'), 'utf8'); +const banner = readFileSync(join(root, 'src/components/Banner.astro'), 'utf8'); + +/** The hero's action row: anchors only, so the first `` closes it. */ +function actionsOf(html: string): string { + const m = html.match(/
]*>([\s\S]*?)<\/div>/); + if (!m) throw new Error('hero .actions block not found'); + return m[1]; +} + +/** A section's inner markup, matched by its unique class. */ +function sectionOf(html: string, cls: string): string { + const m = html.match(new RegExp(`
]*>([\\s\\S]*?)<\\/section>`)); + if (!m) throw new Error(`section .${cls} not found`); + return m[1]; +} + +describe('canopy i18n: Remote and catalogue strings in both languages', () => { + it('carries the spec-fixed labels verbatim', () => { + expect(en.experiments.canopy.remote.title).toBe('Your sessions and graphs from your phone'); + expect(en.experiments.canopy.remote.button).toBe('Open remote →'); + expect(es.experiments.canopy.remote.button).toBe('Abrir remote →'); + expect(en.experiments.canopy.catalogue.explore).toBe('Explore the catalogue →'); + expect(en.experiments.canopy.catalogue.patterns).toBe('Patterns →'); + expect(en.experiments.canopy.catalogue.transparency).toBe('Transparency →'); + expect(es.experiments.canopy.catalogue.explore).toBe('Explora el catálogo →'); + expect(es.experiments.canopy.catalogue.patterns).toBe('Patrones →'); + expect(es.experiments.canopy.catalogue.transparency).toBe('Transparencia →'); + expect(en.experiments.canopy.cta).toEqual({ catalogue: 'Graph catalogue', remote: 'Remote' }); + }); + + it('has three columns and the same keys in es as in en', () => { + for (const lang of [en, es]) { + const c = lang.experiments.canopy; + expect(c.remote.cols).toHaveLength(3); + expect(Object.keys(c.remote).sort()).toEqual(['button', 'cols', 'kicker', 'title']); + expect(Object.keys(c.catalogue).sort()).toEqual([ + 'caption', 'explore', 'kicker', 'patterns', 'transparency', + ]); + expect(Object.keys(c.cta).sort()).toEqual(['catalogue', 'remote']); + } + }); +}); + +describe.each([ + ['en', canopyEn], + ['es', canopyEs], +] as const)('built /%s canopy page', (lang, html) => { + const actions = actionsOf(html); + const c = lang === 'en' ? en.experiments.canopy : es.experiments.canopy; + + it('puts both site links in the hero actions, above the fold', () => { + expect(actions).toContain('href="https://remote.univerlab.org"'); + expect(actions).toContain('href="https://graphs.univerlab.org"'); + expect(actions).toContain(c.cta.remote); + expect(actions).toContain(c.cta.catalogue); + }); + + it('leads with the Install anchor and a label', () => { + expect(actions).toContain('href="#install"'); + expect(actions).toContain(lang === 'en' ? '>Install<' : '>Instalar<'); + }); + + it('demotes GitHub to a tertiary link, not a btn', () => { + const m = actions.match(/]*href="https:\/\/github\.com\/UniverLab\/harness-canopy"[^>]*>[^<]*<\/a>/); + if (!m) throw new Error('GitHub anchor not found in the actions block'); + expect(m[0]).toContain('class="tertiary-link"'); + expect(m[0]).not.toMatch(/class="btn\b/); + }); + + it('renders the Remote section with its command, three columns and CTA', () => { + const section = sectionOf(html, 'remote-section'); + expect(section).toContain(c.remote.title); + expect(section.match(/
{ + const section = sectionOf(html, 'catalogue-section'); + expect(section).toContain('data-catalogue-plate'); + expect(section).toContain('href="https://graphs.univerlab.org"'); + expect(section).toContain('href="https://graphs.univerlab.org/patterns"'); + expect(section).toContain('href="https://graphs.univerlab.org/transparency"'); + expect(section).toContain(c.catalogue.explore); + expect(section).toContain(c.catalogue.patterns); + expect(section).toContain(c.catalogue.transparency); + }); +}); + +describe('source pins: hero CTAs, fold order and the banner clip fix', () => { + it('hides the layout GitHub button and authors the three primary/secondary CTAs', () => { + expect(view).toContain(''); + expect(view).toContain('class="btn btn-primary" href="#install"'); + expect(view).toContain('href="https://graphs.univerlab.org"'); + expect(view).toContain('href="https://remote.univerlab.org"'); + expect(view).toContain('class="tertiary-link"'); + expect(view).toContain('#3fb8b0'); + }); + + it('orders the hero children so the actions precede the morph (global selectors)', () => { + expect(view).toContain(":global(html[data-surface='tui'] .exp .hero .actions) { order: 1; }"); + expect(view).toContain(":global(html[data-surface='tui'] .exp .hero .hero-visual) { order: 2; }"); + expect(view).toContain(":global(html[data-surface='tui'] .exp .hero .install) { order: 3; }"); + }); + + it('gives the install box an anchor', () => { + expect(layout).toContain('
'); + }); + + it('pads both banner boxes so the first and last ink rows survive the clip', () => { + expect(banner).toMatch(/\.banner \{[^}]*padding-block: 2px/); + expect(banner).toMatch(/\.banner code \{[^}]*padding-block: 2px/); + }); +}); diff --git a/src/components/Banner.astro b/src/components/Banner.astro index f631565..2316e01 100644 --- a/src/components/Banner.astro +++ b/src/components/Banner.astro @@ -68,6 +68,11 @@ const gradColor = (i: number) => { .banner { margin: 0; padding: 0; + /* 9 art lines of 10px each; DejaVu's block ink runs ~1px past each line box + and both this box and the code box clip. Two pixels of block padding give + the first and last rows their ink back without touching the horizontal + clip (scrollWidth still equals clientWidth). */ + padding-block: 2px; width: auto; max-width: 100%; min-width: 0; @@ -97,6 +102,7 @@ const gradColor = (i: number) => { .banner::-webkit-scrollbar { display: none; } .banner code { display: block; + padding-block: 2px; max-width: 100%; overflow: hidden; font-family: inherit; diff --git a/src/components/CataloguePlate.astro b/src/components/CataloguePlate.astro new file mode 100644 index 0000000..4daa796 --- /dev/null +++ b/src/components/CataloguePlate.astro @@ -0,0 +1,91 @@ +--- +// A static plate of the real "Gated implement" pattern — the catalogue's first +// exhibit, drawn with the same vocabulary as the hero (canopy-graph.ts): spec +// chip → implement agent → gates check → review agent → commit, with the two +// fail back-edges into implement. A plate is an exhibit: no animation, which +// also covers reduced motion for free. +import { GEOM, PALETTE, TYPE, edgePath, type Kind } from '../lib/canopy-graph'; + +interface Props { + /** Figure caption, e.g. "Pattern: Gated implement — one plate…". */ + caption: string; + /** Harness accent colours keyed by platform stem (from canopy-registry). */ + colors: Record; +} +const { caption, colors } = Astro.props; + +type PlateNode = { id: string; kind: Kind; x: number; y: number; label: string; harness?: string }; +const nodes: PlateNode[] = [ + { id: 'spec', kind: 'chip', x: 7, y: 31, label: 'spec' }, + { id: 'implement', kind: 'agent', x: 28, y: 31, label: 'implement', harness: 'claude' }, + { id: 'gates', kind: 'check', x: 49, y: 31, label: 'gates' }, + { id: 'review', kind: 'agent', x: 70, y: 31, label: 'review', harness: 'opencode' }, + { id: 'commit', kind: 'commit', x: 91, y: 31, label: 'commit' }, +]; +const edges: { from: string; to: string; cond: 'pass' | 'fail'; lift: number }[] = [ + { from: 'spec', to: 'implement', cond: 'pass', lift: 0 }, + { from: 'implement', to: 'gates', cond: 'pass', lift: 0 }, + { from: 'gates', to: 'review', cond: 'pass', lift: 0 }, + { from: 'review', to: 'commit', cond: 'pass', lift: 0 }, + { from: 'gates', to: 'implement', cond: 'fail', lift: 0 }, + { from: 'review', to: 'implement', cond: 'fail', lift: 7 }, +]; +const node = (id: string) => nodes.find((n) => n.id === id)!; +const path = (from: string, to: string) => { + const e = edges.find((x) => x.from === from && x.to === to)!; + return edgePath(node(from), node(to), e.cond === 'fail', e.lift); +}; +const accent = (n: PlateNode) => (n.harness ? colors[n.harness] ?? 'var(--canopy)' : 'var(--ink-dim)'); +--- +
+ +
{caption}
+
+ + diff --git a/src/i18n/en-experiments.ts b/src/i18n/en-experiments.ts new file mode 100644 index 0000000..b53174b --- /dev/null +++ b/src/i18n/en-experiments.ts @@ -0,0 +1,515 @@ +// Experiment copy (English) — split out of en.ts to keep the dictionary +// files readable. Spread into `en` unchanged. +export const experiments = { + canopy: { + need: 'One agent on one harness can\'t carry a project. A graph of them can — implement, check, review, commit, and route around every failure.', + tagline: + 'A graph engine for AI coding agents — every harness, one workflow.', + etymology: '(From canopy, the forest’s crown.)', + figure: 'A wooden hive where many agents work under one roof, each in its own cell and all in one flow. Like the forest canopy, it does not grow the trees: it covers and orders them so the light reaches every one.', + thread: { + problem: 'AI agents', + method: 'a graph engine above every harness', + artifact: 'specs carried all the way to a commit', + }, + title: + 'Multi-agent graph engine for AI coding agents — Canopy', + description: + 'An open-source graph engine for AI coding agents: specs walk agent, check and gate nodes, ensembles vote across harnesses, failures route back.', + koan: 'In a forest, the canopy is where the crowns touch — separate trees, one living layer.', + lede: + 'A Rust daemon and terminal UI with a graph engine at its core. Work enters as a spec and walks a graph you design: agent nodes on any harness, check nodes that run your real commands, ensembles that fan out to several models and keep a quorum, routers that pick the branch, and a resilience node that turns a quota death into a scheduled wake-up. Underneath: memory, scheduling and sync for every agent.', + morph: [ + ['Gated implement', 'checks run your real commands — red routes the work back, never to you'], + ['Ensemble · quorum', 'one spec, three models on three harnesses, two of three must agree'], + ['Cascade — one runs, the rest wait', 'the first member takes the spec; if it fails, the next takes it cold — the first to pass exits, the rest never run'], + ['Router', 'one decision node sends each spec down the branch that fits'], + ['Full pipeline', 'design, implement, gates, two reviews, commit — and a resilience branch for quota deaths'], + ] as [string, string][], + genesis: { + kicker: 'Genesis', + title: 'The one that started it all.', + body: + 'It started as a folder of skills for work. Then I noticed what nobody was talking about: agent harnesses shipped a headless mode, just sitting there unused. I wired cron jobs to fire tasks through it — too much for a skill, and the models of the day choked on the instructions. So it became an MCP: task-trigger. It worked, but it ran blind in the background; only the agent ever saw what happened. Not enough. I killed it and built a TUI — then scheduling, memory, sync, identities, and a new name. Canopy. By then the twist was complete: Canopy was building Canopy, and everything else in this lab.', + }, + layer: { + kicker: 'The graph engine', + cols: [ + ['Graphs, not prompts', 'Agent, check, gate and router nodes wired with pass, fail and always edges. A spec walks the graph from entry to commit; routing keys on real exit codes and verdicts, not on what a model claims.'], + ['Ensembles across harnesses', 'Fan one step out to several models — parallel with a quorum, cascade to the next on failure, or round-robin across a crew — each member on whatever harness and model you pick.'], + ['Failure is a route', 'Red gates send the work back with the output attached. A resilience node reads quota errors and schedules the graph to wake at the exact reset. Only a real verdict ever reaches you.'], + ] as [string, string][], + }, + platforms: { + kicker: 'Supported platforms', + }, + graphs: { + kicker: 'The runtime underneath', + cols: [ + ['Memory that persists', 'Every run writes facts, patterns and decisions to a project-scoped knowledge graph. The next session — on any harness — reads them instead of rediscovering the codebase.'], + ['Queues and scheduling', 'Specs live in ordered queues a graph works through in the background; graphs and agents also fire on cron schedules or file-change triggers, and hooks chain one graph into the next.'], + ['Multi-agent sync', 'Agents declare their mission, report status and message each other, so several sessions can share one workspace without stepping on each other.'], + ] as [string, string][], + }, + cta: { + catalogue: 'Graph catalogue', + remote: 'Remote', + }, + remote: { + kicker: 'Remote', + title: 'Your sessions and graphs from your phone', + cols: [ + ['Encrypted end to end', + 'Sessions travel over WebRTC with end-to-end encryption: the relay only carries signalling, and your screen never passes through it.'], + ['Pairing in one command', + 'Run the command below: it prints a one-time code with its link and a terminal QR code. Scan it and the browser becomes a revocable device of the host.'], + ['Live graphs, shared', + 'Watch specs walk the graph in real time from your phone, and hand single-use invite codes to guests you trust.'], + ] as [string, string][], + button: 'Open remote →', + }, + catalogue: { + kicker: 'Graph catalogue', + caption: 'Pattern: Gated implement — one plate of the live catalogue', + explore: 'Explore the catalogue →', + patterns: 'Patterns →', + transparency: 'Transparency →', + }, + builder: { + kicker: 'Autonomous graph', + pattern: 'Pattern: Ensemble · quorum + Gated implement', + title: 'It failed. It didn’t stop.', + stat: ['0', 'times it stopped'] as [string, string], + outro: 'Every node runs on the harness you pick, and the loop you just watched is the real engine doing its real job.', + steps: [ + ['One spec, three drafts', + 'Work enters as a spec — role, what, how. It fans out to three models on three harnesses, and a quorum keeps the consensus.'], + ['Implement from the consensus', + 'An implementer node picks up the agreed plan on your harness and starts writing.'], + ['It failed: cargo test, 2 red', + 'The check node runs your real commands. Red routes the token straight back to the implementer — it never comes to you.'], + ['Green — and a second opinion', + 'The retry passes the gates. A different harness reviews the diff against the spec before anything moves.'], + ['Committed. Pushed. Again.', + 'The branch is pushed and the run is remembered — the next spec is already walking the same graph.'], + ] as [string, string][], + }, + faq: [ + ['What does Canopy actually do?', + 'Canopy runs AI coding work as graphs. A spec enters, agent nodes do the work on the harnesses you choose — Claude, Codex, Gemini, OpenCode or any terminal agent — check nodes run your real tests, ensembles get several models to agree, and failures route back automatically until the spec is committed. One daemon, all your agents, all your free tiers.'], + ['How do I share work between Claude and Codex?', + 'Canopy gives each agent a shared knowledge graph and sync protocol. When Claude finishes a task, the facts and patterns it discovered are available to Codex in the next session. No manual context copying.'], + ['Can I run AI agents on a schedule?', + 'Yes. Canopy fires agents on cron schedules or file-change triggers via a background daemon. Set the schedule once; the daemon watches the workspace and runs tasks automatically.'], + ['How is Canopy different from just using Claude Code or Codex directly?', + 'Those are individual harnesses. Canopy is the layer that connects them — shared memory, background scheduling, and multi-agent coordination. You keep your agents; Canopy adds the infrastructure.'], + ] as [string, string][], + }, + texforge: { + need: 'Writing LaTeX should not require installing four gigabytes of toolchain.', + tagline: + 'A unified LaTeX workspace — writing, diagrams, and PDFs in one self-contained tool.', + etymology: '(From TeX and forge.)', + figure: 'A type press for the age of machines: the manuscript goes in, the printed sheet comes out. With it LaTeX stops being an installer\'s trade and becomes again what it always was, the art of setting a page well.', + thread: { + problem: 'LaTeX', + method: 'one self-contained toolchain', + artifact: 'reproducible documents', + }, + title: + 'LaTeX with Mermaid, Graphviz and D2 diagrams — TeXForge', + description: + 'One binary, no LaTeX distribution to install. Mermaid, Graphviz and D2 render straight from your .tex, and the errors are written to be read by agents.', + koan: 'Movable type once took a workshop. Now it takes one binary.', + figures: ['build pipeline', 'document graph', 'build map', 'code listing'], + lede: + 'A single Rust binary that scaffolds, lints, formats, proofreads and compiles your document, then lets you inspect what came out — the text a reader sees, the fonts, the metadata, and which section opens each page. The LaTeX engine arrives by itself on first build, and Mermaid, Graphviz or D2 diagrams render inside your .tex files with no browser and no Node.js. Code blocks come out highlighted the same way: the highlighter is compiled into the binary, so no Pygments and no shell-escape.', + genesis: { + kicker: 'Genesis', + title: 'Born from a thesis.', + body: + 'A master’s thesis, written with AI in the loop. Overleaf charged, TeXstudio was too heavy, VSCode wanted an extension for everything. All I wanted was Mermaid diagrams in my LaTeX — which of course meant Node and a pile of .mmd files. I duct-taped a latexmake to render the missing ones. It held the way duct tape holds. And every error sent the model scrolling a thousand-line build log to find one bad line. TeXForge condenses all of that into a binary and a skill.', + }, + press: { + kicker: 'The whole press', + items: [ + ['Compose', 'templates with placeholders that already know your name.'], + ['Proof', 'a linter that catches broken refs, missing files and unclosed environments before the press runs.'], + ['Proofread', 'spell-checking that reads the language from the document — Babel, polyglossia, or your configured default — and checks prose against the right dictionary.'], + ['Set', 'one canonical format, like rustfmt for .tex. Clean diffs forever.'], + ['Illustrate', 'Mermaid, Graphviz and D2 blocks become figures at build time, rendered in pure Rust.'], + ['Highlight', 'code blocks become framed listings with line numbers and captions, coloured at build time — light, dark or mono for print.'], + ['Inspect', 'the compiled PDF — text, fonts, metadata, page diffs and whether every source word survived.'], + ['Print', 'Tectonic compiles deterministically; watch mode reprints as you write.'], + ] as [string, string][], + }, + // The printed listing: a crop of the capabilities example as texforge + // compiled it, not a mock-up. + listing: { + caption: 'listing as printed · python', + alt: 'A Python code block typeset by texforge: a light frame, line numbers in the margin, keywords, strings and numbers in colour.', + }, + subproject: { + kicker: 'Subproject', + name: 'texforge-templates', + body: + 'The general template ships embedded in the binary so creating a document works even offline.', + link: 'https://github.com/UniverLab/texforge-templates', + }, + templates: { + kicker: 'Templates', + title: '{n} templates in the open registry', + cols: { name: 'name', description: 'description', version: 'version' }, + use: 'Every row copies the command; my-doc is your project name:', + copy: 'Copy', + copied: 'Copied', + copyLabel: 'Copy the command that starts a document with {name}', + }, + faq: [ + ['What problem does TeXForge solve?', + 'Compiling LaTeX normally requires installing TeX Live (4+ GB), then separate tools for Mermaid, Graphviz, and D2 diagrams — each with its own setup. TeXForge is a single ~15 MB binary that handles everything: scaffolding, linting, formatting, diagrams, and compilation.'], + ['Can AI agents use TeXForge to work with LaTeX?', + 'Yes. An agent can run texforge build without installing anything — the LaTeX engine downloads on first use. Errors are concise (not 1000-line logs), and diagrams render inside .tex files without Node.js or external tools.'], + ['Does TeXForge support Mermaid and D2 diagrams in LaTeX?', + 'Yes. Write a Mermaid, Graphviz, or D2 block directly in your .tex file. TeXForge renders it to a figure at build time, in pure Rust, with no browser or Node.js required.'], + ['How does spell-checking work in languages other than English?', + 'The language comes from the document itself — \\usepackage[spanish]{babel} or polyglossia — and falls back to your configured default when nothing is declared. Spanish is checked against a Hunspell dictionary with affix rules, so soluciones is recognised from the stem solución without being stored as its own entry. Dictionaries download on first use into ~/.texforge/dicts/.'], + ] as [string, string][], + }, + gitkit: { + need: 'Every new repository starts with the same setup ritual — done by hand, every time.', + tagline: + 'Guided git repository setup — hooks, ignores, attributes, and config in one flow.', + etymology: '(From git and kit, a case of tools.)', + figure: 'The craftsman\'s case that gets the most out of git: the hooks nobody sets up and the rules everybody forgets, kept in one case that opens the same way in every repository.', + thread: { + problem: 'repository setup', + method: 'saved, idempotent builds', + artifact: 'the same ritual everywhere', + }, + title: + 'Git hooks setup: conventional commits + secrets — GitKit', + description: + 'Git hooks setup in one guided, idempotent flow: conventional commits, secret detection, .gitignore, .gitattributes and config. One binary, no Node, no Python.', + koan: '// the ritual, automated', + hero: { + wizard: { + kicker: 'gitkit init', + steps: [ + 'Repository profile', + 'Hooks', + 'Ignore & attributes', + 'Config presets', + 'Save as build', + ], + }, + hooks: { + kicker: 'hook stack', + items: [ + ['conventional-commits', 'commit-msg'], + ['no-secrets', 'pre-commit'], + ['branch-naming', 'pre-push'], + ] as [string, string][], + }, + }, + lede: + 'One guided flow for hooks, .gitignore, .gitattributes and git config — then saved as a build you can re-apply to any project with a single command.', + genesis: { + kicker: 'Genesis', + title: 'A line-ending that wouldn’t behave.', + body: + 'It started with a commit that changed nothing — just line endings, silently rewritten somewhere between my work Mac, my Windows box and the agents on WSL. Chasing it down led me to .gitattributes. That led to git hooks: built into git, genuinely powerful, and ignored by almost everyone — because wiring them into every repo is a chore. So I deleted the chore. That’s gitkit.', + }, + features: { + kicker: 'One flow', + items: [ + 'Built-in hooks: conventional commits, secret detection, branch naming — embedded, offline.', + 'Every gitignore.io template plus curated git config presets, applied idempotently.', + 'gitkit clone bootstraps a repo the moment it lands on disk.', + 'Builds: save a setup once, apply it to every future project.', + 'Lock: block commits and pushes via the hooks already installed — --json for machine-readable status.', + ], + }, + faq: [ + ['How do I prevent AI agents from making bad commits?', + 'GitKit sets up git hooks in one flow — conventional commits, secret detection, branch naming. Hooks run offline, embedded in the repo. Agents can\'t push code that doesn\'t compile or contains secrets.'], + ['What are GitKit "builds"?', + 'A build saves your git configuration (hooks, ignore, attributes, config) as a reusable template. Apply it to any future project with one command — no need to reconfigure hooks for every repo.'], + ['How do I pause writes to a repository?', + 'Run gitkit lock to block commits and pushes through the hooks gitkit already installs. The repository stays readable but unwritable until you run gitkit unlock. Useful when an autonomous agent needs to stop writing while you rebase, reinstall, or inspect the tree.'], + ] as [string, string][], + }, + ghscaff: { + need: 'Creating a GitHub repository properly is a dozen forgettable steps.', + tagline: + 'An interactive wizard that scaffolds and enforces conventions on GitHub repositories.', + etymology: '(From GitHub and scaffold.)', + figure: 'Scaffolding raised before the building: it guards the branches, lifts the checks and leaves every repository standing from day one, so whatever is built on top never needs propping up later.', + thread: { + problem: 'GitHub repositories', + method: 'an idempotent wizard', + artifact: 'conventions enforced', + }, + title: + 'Create GitHub repos with conventions enforced — ghScaff', + description: + 'An interactive wizard that scaffolds a repository and applies labels, branch protection and status checks from the start — idempotent and safe to re-run.', + koan: 'A building is only as straight as its scaffold.', + lede: + 'ghScaff raises the whole structure in one interactive wizard — and because every operation is idempotent, it can re-level any existing repository without tearing it down.', + genesis: { + kicker: 'Genesis', + title: 'The same setup, again and again.', + body: + 'New Rust project. Same labels, same branch protection, same CI, same secrets. Again. And again. Replicable work you do by hand isn’t craft — it’s toil. ghScaff runs the whole ritual in one wizard, idempotently, so your conventions hold the line instead of drifting.', + }, + lifts: { + kicker: '7 lifts', + items: [ + 'Repository basics', + 'Visibility & ownership', + 'Team access', + 'Language template', + 'Branches', + 'Features & license', + 'Review & confirm', + ], + }, + details: { + kicker: 'Structural details', + items: [ + 'Tokens live in an encrypted vault (XSalsa20-Poly1305), bound to your user, host and binary — never in env vars or plain text.', + 'One atomic chore: init repository commit carries all boilerplate — no noisy file-by-file history.', + 'Seven standard labels enforced on every run; drift is corrected, not accumulated.', + '--dry-run previews every change without a single API call.', + 'Branch protection reads the workflow files ghscaff just committed and derives the required status checks from them — a mistyped check name silently guards nothing, and this removes that class of misconfiguration. Run ghscaff doctor to verify the setup.', + ], + }, + subproject: { + kicker: 'Subproject', + name: 'ghscaff-boilerplate', + link: 'https://github.com/UniverLab/ghscaff-boilerplate', + }, + boilerplates: { + kicker: 'Boilerplates', + title: '{n} boilerplates the wizard can lay down', + cols: { name: 'name', language: 'language', description: 'description', version: 'version' }, + use: 'Pick one at the wizard\'s language step:', + }, + faq: [ + ['How does ghScaff set up a GitHub repository?', + 'Run ghscaff — an interactive wizard that creates the repo, commits boilerplate (CI, README, license), sets branch protection, and enforces standard labels. One atomic commit, no manual steps.'], + ['Why does ghScaff use an encrypted vault for tokens?', + 'Environment variables with tokens are easily exploitable — any process on your machine can read them. ghScaff encrypts tokens with XSalsa20-Poly1305, bound to your OS user and hostname. The vault prevents ghScaff from becoming an attack vector.'], + ['How does ghScaff know which checks are required for branch protection?', + 'ghScaff reads the workflow files it just committed and derives the required status checks from the job names. A mistyped check name creates a protection rule that guards nothing — ghScaff removes that class of silent misconfiguration.'], + ] as [string, string][], + }, + cadspec: { + need: 'CAD drawings carry no semantics — just lines on a canvas, impossible to diff, review or automate.', + tagline: + 'CAD as code: declarative geometry compiled deterministically to DXF.', + etymology: '(From CAD and spec, specification.)', + figure: 'A drafting table that declared parts instead of drawing them. It was archived when the lab chose to wrap the best tools rather than reinvent them; its idea lives on in the tools that replaced it.', + thread: { + problem: 'CAD', + method: 'declarative geometry', + artifact: 'drawings an agent can verify', + }, + title: + 'CAD as code: from declaration to model — cadSpec', + description: + 'Declarative geometry in TOML you can diff and review, with live browser preview. Compiled deterministically — the same source always yields the same model.', + }, + 'astro-denoise': { + need: 'Noise reduction on an astronomical image can recover a faint galaxy — or invent one that was never there. There is no standard, reproducible way to tell which.', + tagline: + 'A research proposal for benchmarking astronomical noise reduction — evaluated on the science it recovers, not how clean it looks.', + etymology: '(From Greek ástron, star, and denoise.)', + figure: 'An eyepiece that parts light from noise: between the grain the sensor leaves and the star that is really there, a line of research that measures before it claims.', + thread: { + problem: 'noise reduction', + method: 'a benchmark scored by the science', + artifact: 'comparable methods', + }, + title: 'Astro Denoise — a noise-reduction benchmark on DC2 images', + description: + 'A reproducible benchmark of noise-reduction methods on simulated Vera Rubin Observatory DC2 images, scored by faint-source recovery, image quality and cost.', + koan: 'Frontier knowledge hides behind the noise…', + lede: + 'astro-denoise is a research proposal for a modular, reproducible benchmark of noise-reduction methods on simulated Vera Rubin Observatory (LSST DC2) images. Any method — classical filter, trained network — plugs into the same protocol and runs on the same patches, and is scored not by how clean the image looks, but by what it does to the science: the completeness and purity of the faint-source catalog, compared against the DC2 truth catalog. BM3D and a U-Net are the first two references being explored — the platform is designed to grow as more methods are added.', + genesis: { + kicker: 'Genesis', + title: "A master's thesis in progress.", + body: + "This began as a master's thesis and is still taking shape. The ecosystem has worked since July 2026: an orchestrator with a common command-line protocol (FITS image in and out, metadata in JSON), a no-processing control condition, and two separately packaged methods, BM3D for the classical family and a U-Net for deep learning. The experiments run in the open, right here, but there is work ahead before this is a finished benchmark.", + }, + objectives: { + kicker: 'Objectives', + thesisTitle: + 'Benchmarking noise-reduction methods on simulated DC2 images of the Vera Rubin Observatory', + general: + 'Develop a reproducible benchmarking platform that compares noise-reduction methods on simulated DC2 images of the Vera Rubin Observatory, evaluating for each method the recovery of faint sources, the resulting image quality and its computational cost.', + items: [ + { + state: 'complete', + text: 'A modular, extensible ecosystem with a common command-line protocol, where each method is packaged separately with its own environment and a new method joins without changing the orchestrator.', + }, + { + state: 'in progress', + text: 'Reliable source-recovery metrics (completeness, purity, completeness by magnitude and photometric accuracy, by one-to-one matching against the DC2 truth catalogue) and image-quality metrics (PSNR, SSIM and background noise), each checked against at least two independent sources in the literature.', + }, + { + state: 'in progress', + text: "Tools that measure each method's computational cost (time per region, peak memory and regions per unit of time) from outside its process, reported with a description of the hardware.", + }, + { + state: 'in progress', + text: 'The methods under test: BM3D, and a U-Net trained only on DC2 regions outside the evaluation set, with its training recipe and weights published.', + }, + { + state: 'pending', + text: 'A reproducible benchmark on a curated, versioned DESC DC2 dataset of multiband regions from several sky areas; each version is identified by checksums and declares its regions, and every result is reported against one version.', + }, + ], + }, + repos: { + kicker: 'Repositories', + items: [ + { + name: 'UniverLab/astro-denoise', + description: 'The bench: orchestrator, science metrics and the control condition.', + url: 'https://github.com/UniverLab/astro-denoise', + }, + { + name: 'UniverLab/astro-denoise-bm3d', + description: 'BM3D, the classical baseline.', + url: 'https://github.com/UniverLab/astro-denoise-bm3d', + }, + { + name: 'UniverLab/astro-denoise-unet', + description: 'The U-Net, the deep-learning baseline.', + url: 'https://github.com/UniverLab/astro-denoise-unet', + }, + ], + }, + proposalCta: 'Read the proposal (PDF) ↗', + questions: { + kicker: 'Open questions', + items: [ + 'Can a denoiser raise completeness without hurting purity — or does it invent sources that were never there?', + 'Which family of methods — classical filters or learned models — is more likely to help at LSST DC2 depth, and by how much?', + 'Can the whole benchmark be repeated with the same scientific results on the same version of data and code?', + ], + }, + faq: [ + ['What is astro-denoise?', + 'A research project benchmarking noise-reduction methods on simulated Vera Rubin Observatory (LSST DC2) images. It compares three conditions: a no-processing control, BM3D as the classical baseline and a U-Net as the deep-learning baseline. The goal is to recover faint galaxies without inventing ones that were never there — scored by science recovery (completeness + purity), not visual quality.'], + ['What is noise reduction in astronomy?', + 'Astronomical images contain unavoidable noise from the detector and sky. Noise reduction aims to lower this noise while preserving real sources. The risk: a method can smooth out real faint galaxies or invent fake ones that were never there.'], + ['What is LSST?', + 'The Legacy Survey of Space and Time — a 10-year astronomical survey conducted by the Vera Rubin Observatory in Chile. It will image the entire visible sky repeatedly, producing the deepest wide-field catalog of galaxies, stars, and transient events ever made.'], + ['What is DC2?', + 'Data Challenge 2 — a simulated dataset that mimics what LSST will produce, created by the Dark Energy Science Collaboration (DESC). It includes realistic noise, PSF, and atmospheric effects, with a truth catalog telling you exactly which sources are real.'], + ] as [string, string][], + bgCredit: 'Star chart: Yale Bright Star Catalogue (CDS V/50); constellation lines from d3-celestial by Olaf Frohn (BSD-3-Clause).', + }, + 'quorum': { + need: 'Planning poker usually means a server in the middle — an account to create, a room to host, one more tool between you and a number.', + tagline: 'Serverless planning poker — share a link, estimate together, no sign-up.', + etymology: '(From Latin quorum, ‘of whom’.)', + figure: 'A table of face-down cards: everyone plays their estimate at once and nobody sways the others; agreement appears when they turn over.', + thread: { + problem: 'estimation', + method: 'a peer-to-peer room', + artifact: 'disagreement you can see', + }, + title: + 'Free planning poker online — no sign-up, no server — Quorum', + description: + 'Free planning poker over WebRTC — peers talk directly, nothing is stored, no account to create. Share a room link and estimate together.', + koan: '// the estimate is already in the room', + bench: 'open the table → quorum.univerlab.org', + lede: + 'Quorum is serverless planning poker: each person plays a card, votes reveal with an animation once everyone has voted, and the disagreement is where the useful conversation starts. It runs peer-to-peer over WebRTC — a shared room link is the whole app, no cloud, no account.', + genesis: { + kicker: 'Genesis', + title: 'One link, no server.', + body: + 'I had been exploring the idea of building peer-to-peer collaborative computing in the browser, with no intermediary servers. Before scaling the architecture, I needed to validate the basics — can a P2P connection in the browser actually be smooth and robust? To find out, I built something with purpose: planning poker. Not because there aren\'t tools out there — there are excellent ones — but because this ritual captures the essence of teamwork: each participant contributes their estimate privately, in a decentralized and secure way. The result is Quorum: zero data leaving the browser, no servers, no friction. Free, open, secure.', + }, + how: { + kicker: 'P2P session', + items: [ + 'Share the room URL — no sign-up, no invite flow.', + 'Pick a card from the Fibonacci deck (0, 1, 2, 3, 5, 8, 13, 21, ?).', + 'Cards reveal with a multi-phase animation — particles, slide and flip — the moment everyone votes.', + 'Load stories from a CSV or add them one at a time, step through in order — drop out and reconnect, and state syncs back from any peer still in the room.', + ], + }, + faq: [ + ['Is Quorum free?', + 'Completely free. No accounts, no user limits, no premium tier. It\'s a P2P experiment in the browser — WebRTC connections, no server. Open the URL and start estimating.'], + ['How does planning poker work without a server?', + 'Quorum uses peer-to-peer WebRTC connections. Your browser connects directly to your teammates\' browsers — no cloud, no database. A BitTorrent tracker handles the initial handshake; after that, data flows straight between peers.'], + ['Can I use Quorum for remote sprint planning?', + 'Yes. Share the room URL with your team. Everyone picks a Fibonacci card (0, 1, 2, 3, 5, 8, 13, 21, ?). Cards reveal simultaneously once everyone votes — a multi-phase animation prevents premature reveals.'], + ['Does Quorum support user stories?', + 'Yes. Load a CSV or add stories one at a time. Step through stories in order, estimating each one. The session stays alive if someone disconnects — reconnect and state syncs from any peer still in the room.'], + ['What makes Quorum different from planningpokeronline.com?', + 'No accounts, no user limits, no data stored on a server. Quorum is open-source and runs entirely in the browser via WebRTC. Your estimation data never leaves your team\'s devices.'], + ] as [string, string][], + }, + 'demostage': { + need: 'Recording demos by hand is fiddly — typos, uneven pacing, dead air, and a prompt leaking your host.', + tagline: + 'Demos as Code — capture, record and export reproducible terminal demos.', + etymology: '(From demo and stage.)', + figure: 'A zoetrope for the terminal: every demo is written like a score and plays back the same, frame by frame, like the gallop Muybridge broke apart so it could finally be seen.', + thread: { + problem: 'terminal demos', + method: 'an event score', + artifact: 'demos you re-run', + }, + title: + 'Reproducible terminal demos as code — DemoStage', + description: + 'An asciinema alternative where the demo is a file, not a take. Re-record after every change and export gif, mp4 or svg — version-controlled, diffable.', + koan: '// the demo is the source', + lede: + 'DemoStage records a session as events, normalizes human imperfections into a clean demo.toml score, and compiles it to gif, mp4 or an animated svg — version-controlled, re-runnable and diffable.', + genesis: { + kicker: 'Genesis', + title: 'Born building this very page.', + body: + 'Building this landing, every experiment needed a demo — and every tool (asciinema, vhs, OBS, recording by hand) came out brittle or ugly, with a fresh re-record for each typo. So the demo stopped being a video and became a file: events you can prune, pace and replay. DemoStage records the rest of the lab — including the page you are reading.', + }, + pipeline: { + kicker: '5 commands', + items: [ + 'capture — live capture: record the session, auto-normalize into a clean score and faithful .rec.', + 'focus — switch the live view to one or two sources (terminal, repo page, docs, localhost) — full screen, split or stacked, composited into the demo.', + 'record — re-execute demo.toml cleanly, producing a humanized recording.', + 'export — pure playback: render to gif, mp4 or animated svg (no re-execution, ffmpeg/chromium auto-provisioned).', + 'edit — edit the timeline interactively; mark several steps and apply bulk changes.', + ], + }, + // The two genuine figures on this page — the tour as mp4 and the same + // take as its own svg export. Captions name no feature the pipeline + // doesn't already name. + demos: { + first: 'The tour — one score recorded in a real PTY, exported to gif and svg from a single take', + svg: 'The same take, exported as svg — 83 KB, font embedded, no video player', + svgAlt: 'The DemoStage tour as an animated SVG: a banner printed in the terminal, the score that demos it, record, export and doctor.', + }, + faq: [ + ['What is DemoStage?', + 'A tool for planning and recording multi-source demos — terminal, browser, and files in one scene. Not just screen recording: you configure typography, aspect ratio, fps, and terminal style. Output is optimized for web.'], + ['Can I re-record a demo if something changes?', + 'Yes. demostage capture records events, not video. If the UX changes, re-capture and the demo updates deterministically — no need to manually re-record the whole thing.'], + ['How is DemoStage different from asciinema?', + 'asciinema records raw terminal output. DemoStage records events, supports multiple sources (terminal + browser + files), normalizes imperfections, and compiles to gif, mp4 or animated svg. The source is a versionable TOML file.'], + ] as [string, string][], + bgCredit: 'Background: Eadweard Muybridge, The Horse in Motion, 1878 (public domain).', + }, +}; diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 9463320..b8c41cb 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -1,6 +1,8 @@ /** English copy. Single source of truth for all UI text. * Documentation pulled from repositories stays English-only and is not * part of this dictionary. */ +import { experiments } from './en-experiments.ts'; + export const en = { meta: { // The home is the brand hub — it is the one page that can only win the @@ -207,495 +209,7 @@ export const en = { contribute: 'Contribute on GitHub ↗', }, }, - experiments: { - canopy: { - need: 'One agent on one harness can\'t carry a project. A graph of them can — implement, check, review, commit, and route around every failure.', - tagline: - 'A graph engine for AI coding agents — every harness, one workflow.', - etymology: '(From canopy, the forest’s crown.)', - figure: 'A wooden hive where many agents work under one roof, each in its own cell and all in one flow. Like the forest canopy, it does not grow the trees: it covers and orders them so the light reaches every one.', - thread: { - problem: 'AI agents', - method: 'a graph engine above every harness', - artifact: 'specs carried all the way to a commit', - }, - title: - 'Multi-agent graph engine for AI coding agents — Canopy', - description: - 'An open-source graph engine for AI coding agents: specs walk agent, check and gate nodes, ensembles vote across harnesses, failures route back.', - koan: 'In a forest, the canopy is where the crowns touch — separate trees, one living layer.', - lede: - 'A Rust daemon and terminal UI with a graph engine at its core. Work enters as a spec and walks a graph you design: agent nodes on any harness, check nodes that run your real commands, ensembles that fan out to several models and keep a quorum, routers that pick the branch, and a resilience node that turns a quota death into a scheduled wake-up. Underneath: memory, scheduling and sync for every agent.', - morph: [ - ['Gated implement', 'checks run your real commands — red routes the work back, never to you'], - ['Ensemble · quorum', 'one spec, three models on three harnesses, two of three must agree'], - ['Cascade — one runs, the rest wait', 'the first member takes the spec; if it fails, the next takes it cold — the first to pass exits, the rest never run'], - ['Router', 'one decision node sends each spec down the branch that fits'], - ['Full pipeline', 'design, implement, gates, two reviews, commit — and a resilience branch for quota deaths'], - ] as [string, string][], - genesis: { - kicker: 'Genesis', - title: 'The one that started it all.', - body: - 'It started as a folder of skills for work. Then I noticed what nobody was talking about: agent harnesses shipped a headless mode, just sitting there unused. I wired cron jobs to fire tasks through it — too much for a skill, and the models of the day choked on the instructions. So it became an MCP: task-trigger. It worked, but it ran blind in the background; only the agent ever saw what happened. Not enough. I killed it and built a TUI — then scheduling, memory, sync, identities, and a new name. Canopy. By then the twist was complete: Canopy was building Canopy, and everything else in this lab.', - }, - layer: { - kicker: 'The graph engine', - cols: [ - ['Graphs, not prompts', 'Agent, check, gate and router nodes wired with pass, fail and always edges. A spec walks the graph from entry to commit; routing keys on real exit codes and verdicts, not on what a model claims.'], - ['Ensembles across harnesses', 'Fan one step out to several models — parallel with a quorum, cascade to the next on failure, or round-robin across a crew — each member on whatever harness and model you pick.'], - ['Failure is a route', 'Red gates send the work back with the output attached. A resilience node reads quota errors and schedules the graph to wake at the exact reset. Only a real verdict ever reaches you.'], - ] as [string, string][], - }, - platforms: { - kicker: 'Supported platforms', - }, - graphs: { - kicker: 'The runtime underneath', - cols: [ - ['Memory that persists', 'Every run writes facts, patterns and decisions to a project-scoped knowledge graph. The next session — on any harness — reads them instead of rediscovering the codebase.'], - ['Queues and scheduling', 'Specs live in ordered queues a graph works through in the background; graphs and agents also fire on cron schedules or file-change triggers, and hooks chain one graph into the next.'], - ['Multi-agent sync', 'Agents declare their mission, report status and message each other, so several sessions can share one workspace without stepping on each other.'], - ] as [string, string][], - }, - builder: { - kicker: 'Autonomous graph', - pattern: 'Pattern: Ensemble · quorum + Gated implement', - title: 'It failed. It didn’t stop.', - stat: ['0', 'times it stopped'] as [string, string], - outro: 'Every node runs on the harness you pick, and the loop you just watched is the real engine doing its real job.', - steps: [ - ['One spec, three drafts', - 'Work enters as a spec — role, what, how. It fans out to three models on three harnesses, and a quorum keeps the consensus.'], - ['Implement from the consensus', - 'An implementer node picks up the agreed plan on your harness and starts writing.'], - ['It failed: cargo test, 2 red', - 'The check node runs your real commands. Red routes the token straight back to the implementer — it never comes to you.'], - ['Green — and a second opinion', - 'The retry passes the gates. A different harness reviews the diff against the spec before anything moves.'], - ['Committed. Pushed. Again.', - 'The branch is pushed and the run is remembered — the next spec is already walking the same graph.'], - ] as [string, string][], - }, - faq: [ - ['What does Canopy actually do?', - 'Canopy runs AI coding work as graphs. A spec enters, agent nodes do the work on the harnesses you choose — Claude, Codex, Gemini, OpenCode or any terminal agent — check nodes run your real tests, ensembles get several models to agree, and failures route back automatically until the spec is committed. One daemon, all your agents, all your free tiers.'], - ['How do I share work between Claude and Codex?', - 'Canopy gives each agent a shared knowledge graph and sync protocol. When Claude finishes a task, the facts and patterns it discovered are available to Codex in the next session. No manual context copying.'], - ['Can I run AI agents on a schedule?', - 'Yes. Canopy fires agents on cron schedules or file-change triggers via a background daemon. Set the schedule once; the daemon watches the workspace and runs tasks automatically.'], - ['How is Canopy different from just using Claude Code or Codex directly?', - 'Those are individual harnesses. Canopy is the layer that connects them — shared memory, background scheduling, and multi-agent coordination. You keep your agents; Canopy adds the infrastructure.'], - ] as [string, string][], - }, - texforge: { - need: 'Writing LaTeX should not require installing four gigabytes of toolchain.', - tagline: - 'A unified LaTeX workspace — writing, diagrams, and PDFs in one self-contained tool.', - etymology: '(From TeX and forge.)', - figure: 'A type press for the age of machines: the manuscript goes in, the printed sheet comes out. With it LaTeX stops being an installer\'s trade and becomes again what it always was, the art of setting a page well.', - thread: { - problem: 'LaTeX', - method: 'one self-contained toolchain', - artifact: 'reproducible documents', - }, - title: - 'LaTeX with Mermaid, Graphviz and D2 diagrams — TeXForge', - description: - 'One binary, no LaTeX distribution to install. Mermaid, Graphviz and D2 render straight from your .tex, and the errors are written to be read by agents.', - koan: 'Movable type once took a workshop. Now it takes one binary.', - figures: ['build pipeline', 'document graph', 'build map', 'code listing'], - lede: - 'A single Rust binary that scaffolds, lints, formats, proofreads and compiles your document, then lets you inspect what came out — the text a reader sees, the fonts, the metadata, and which section opens each page. The LaTeX engine arrives by itself on first build, and Mermaid, Graphviz or D2 diagrams render inside your .tex files with no browser and no Node.js. Code blocks come out highlighted the same way: the highlighter is compiled into the binary, so no Pygments and no shell-escape.', - genesis: { - kicker: 'Genesis', - title: 'Born from a thesis.', - body: - 'A master’s thesis, written with AI in the loop. Overleaf charged, TeXstudio was too heavy, VSCode wanted an extension for everything. All I wanted was Mermaid diagrams in my LaTeX — which of course meant Node and a pile of .mmd files. I duct-taped a latexmake to render the missing ones. It held the way duct tape holds. And every error sent the model scrolling a thousand-line build log to find one bad line. TeXForge condenses all of that into a binary and a skill.', - }, - press: { - kicker: 'The whole press', - items: [ - ['Compose', 'templates with placeholders that already know your name.'], - ['Proof', 'a linter that catches broken refs, missing files and unclosed environments before the press runs.'], - ['Proofread', 'spell-checking that reads the language from the document — Babel, polyglossia, or your configured default — and checks prose against the right dictionary.'], - ['Set', 'one canonical format, like rustfmt for .tex. Clean diffs forever.'], - ['Illustrate', 'Mermaid, Graphviz and D2 blocks become figures at build time, rendered in pure Rust.'], - ['Highlight', 'code blocks become framed listings with line numbers and captions, coloured at build time — light, dark or mono for print.'], - ['Inspect', 'the compiled PDF — text, fonts, metadata, page diffs and whether every source word survived.'], - ['Print', 'Tectonic compiles deterministically; watch mode reprints as you write.'], - ] as [string, string][], - }, - // The printed listing: a crop of the capabilities example as texforge - // compiled it, not a mock-up. - listing: { - caption: 'listing as printed · python', - alt: 'A Python code block typeset by texforge: a light frame, line numbers in the margin, keywords, strings and numbers in colour.', - }, - subproject: { - kicker: 'Subproject', - name: 'texforge-templates', - body: - 'The general template ships embedded in the binary so creating a document works even offline.', - link: 'https://github.com/UniverLab/texforge-templates', - }, - templates: { - kicker: 'Templates', - title: '{n} templates in the open registry', - cols: { name: 'name', description: 'description', version: 'version' }, - use: 'Every row copies the command; my-doc is your project name:', - copy: 'Copy', - copied: 'Copied', - copyLabel: 'Copy the command that starts a document with {name}', - }, - faq: [ - ['What problem does TeXForge solve?', - 'Compiling LaTeX normally requires installing TeX Live (4+ GB), then separate tools for Mermaid, Graphviz, and D2 diagrams — each with its own setup. TeXForge is a single ~15 MB binary that handles everything: scaffolding, linting, formatting, diagrams, and compilation.'], - ['Can AI agents use TeXForge to work with LaTeX?', - 'Yes. An agent can run texforge build without installing anything — the LaTeX engine downloads on first use. Errors are concise (not 1000-line logs), and diagrams render inside .tex files without Node.js or external tools.'], - ['Does TeXForge support Mermaid and D2 diagrams in LaTeX?', - 'Yes. Write a Mermaid, Graphviz, or D2 block directly in your .tex file. TeXForge renders it to a figure at build time, in pure Rust, with no browser or Node.js required.'], - ['How does spell-checking work in languages other than English?', - 'The language comes from the document itself — \\usepackage[spanish]{babel} or polyglossia — and falls back to your configured default when nothing is declared. Spanish is checked against a Hunspell dictionary with affix rules, so soluciones is recognised from the stem solución without being stored as its own entry. Dictionaries download on first use into ~/.texforge/dicts/.'], - ] as [string, string][], - }, - gitkit: { - need: 'Every new repository starts with the same setup ritual — done by hand, every time.', - tagline: - 'Guided git repository setup — hooks, ignores, attributes, and config in one flow.', - etymology: '(From git and kit, a case of tools.)', - figure: 'The craftsman\'s case that gets the most out of git: the hooks nobody sets up and the rules everybody forgets, kept in one case that opens the same way in every repository.', - thread: { - problem: 'repository setup', - method: 'saved, idempotent builds', - artifact: 'the same ritual everywhere', - }, - title: - 'Git hooks setup: conventional commits + secrets — GitKit', - description: - 'Git hooks setup in one guided, idempotent flow: conventional commits, secret detection, .gitignore, .gitattributes and config. One binary, no Node, no Python.', - koan: '// the ritual, automated', - hero: { - wizard: { - kicker: 'gitkit init', - steps: [ - 'Repository profile', - 'Hooks', - 'Ignore & attributes', - 'Config presets', - 'Save as build', - ], - }, - hooks: { - kicker: 'hook stack', - items: [ - ['conventional-commits', 'commit-msg'], - ['no-secrets', 'pre-commit'], - ['branch-naming', 'pre-push'], - ] as [string, string][], - }, - }, - lede: - 'One guided flow for hooks, .gitignore, .gitattributes and git config — then saved as a build you can re-apply to any project with a single command.', - genesis: { - kicker: 'Genesis', - title: 'A line-ending that wouldn’t behave.', - body: - 'It started with a commit that changed nothing — just line endings, silently rewritten somewhere between my work Mac, my Windows box and the agents on WSL. Chasing it down led me to .gitattributes. That led to git hooks: built into git, genuinely powerful, and ignored by almost everyone — because wiring them into every repo is a chore. So I deleted the chore. That’s gitkit.', - }, - features: { - kicker: 'One flow', - items: [ - 'Built-in hooks: conventional commits, secret detection, branch naming — embedded, offline.', - 'Every gitignore.io template plus curated git config presets, applied idempotently.', - 'gitkit clone bootstraps a repo the moment it lands on disk.', - 'Builds: save a setup once, apply it to every future project.', - 'Lock: block commits and pushes via the hooks already installed — --json for machine-readable status.', - ], - }, - faq: [ - ['How do I prevent AI agents from making bad commits?', - 'GitKit sets up git hooks in one flow — conventional commits, secret detection, branch naming. Hooks run offline, embedded in the repo. Agents can\'t push code that doesn\'t compile or contains secrets.'], - ['What are GitKit "builds"?', - 'A build saves your git configuration (hooks, ignore, attributes, config) as a reusable template. Apply it to any future project with one command — no need to reconfigure hooks for every repo.'], - ['How do I pause writes to a repository?', - 'Run gitkit lock to block commits and pushes through the hooks gitkit already installs. The repository stays readable but unwritable until you run gitkit unlock. Useful when an autonomous agent needs to stop writing while you rebase, reinstall, or inspect the tree.'], - ] as [string, string][], - }, - ghscaff: { - need: 'Creating a GitHub repository properly is a dozen forgettable steps.', - tagline: - 'An interactive wizard that scaffolds and enforces conventions on GitHub repositories.', - etymology: '(From GitHub and scaffold.)', - figure: 'Scaffolding raised before the building: it guards the branches, lifts the checks and leaves every repository standing from day one, so whatever is built on top never needs propping up later.', - thread: { - problem: 'GitHub repositories', - method: 'an idempotent wizard', - artifact: 'conventions enforced', - }, - title: - 'Create GitHub repos with conventions enforced — ghScaff', - description: - 'An interactive wizard that scaffolds a repository and applies labels, branch protection and status checks from the start — idempotent and safe to re-run.', - koan: 'A building is only as straight as its scaffold.', - lede: - 'ghScaff raises the whole structure in one interactive wizard — and because every operation is idempotent, it can re-level any existing repository without tearing it down.', - genesis: { - kicker: 'Genesis', - title: 'The same setup, again and again.', - body: - 'New Rust project. Same labels, same branch protection, same CI, same secrets. Again. And again. Replicable work you do by hand isn’t craft — it’s toil. ghScaff runs the whole ritual in one wizard, idempotently, so your conventions hold the line instead of drifting.', - }, - lifts: { - kicker: '7 lifts', - items: [ - 'Repository basics', - 'Visibility & ownership', - 'Team access', - 'Language template', - 'Branches', - 'Features & license', - 'Review & confirm', - ], - }, - details: { - kicker: 'Structural details', - items: [ - 'Tokens live in an encrypted vault (XSalsa20-Poly1305), bound to your user, host and binary — never in env vars or plain text.', - 'One atomic chore: init repository commit carries all boilerplate — no noisy file-by-file history.', - 'Seven standard labels enforced on every run; drift is corrected, not accumulated.', - '--dry-run previews every change without a single API call.', - 'Branch protection reads the workflow files ghscaff just committed and derives the required status checks from them — a mistyped check name silently guards nothing, and this removes that class of misconfiguration. Run ghscaff doctor to verify the setup.', - ], - }, - subproject: { - kicker: 'Subproject', - name: 'ghscaff-boilerplate', - link: 'https://github.com/UniverLab/ghscaff-boilerplate', - }, - boilerplates: { - kicker: 'Boilerplates', - title: '{n} boilerplates the wizard can lay down', - cols: { name: 'name', language: 'language', description: 'description', version: 'version' }, - use: 'Pick one at the wizard\'s language step:', - }, - faq: [ - ['How does ghScaff set up a GitHub repository?', - 'Run ghscaff — an interactive wizard that creates the repo, commits boilerplate (CI, README, license), sets branch protection, and enforces standard labels. One atomic commit, no manual steps.'], - ['Why does ghScaff use an encrypted vault for tokens?', - 'Environment variables with tokens are easily exploitable — any process on your machine can read them. ghScaff encrypts tokens with XSalsa20-Poly1305, bound to your OS user and hostname. The vault prevents ghScaff from becoming an attack vector.'], - ['How does ghScaff know which checks are required for branch protection?', - 'ghScaff reads the workflow files it just committed and derives the required status checks from the job names. A mistyped check name creates a protection rule that guards nothing — ghScaff removes that class of silent misconfiguration.'], - ] as [string, string][], - }, - cadspec: { - need: 'CAD drawings carry no semantics — just lines on a canvas, impossible to diff, review or automate.', - tagline: - 'CAD as code: declarative geometry compiled deterministically to DXF.', - etymology: '(From CAD and spec, specification.)', - figure: 'A drafting table that declared parts instead of drawing them. It was archived when the lab chose to wrap the best tools rather than reinvent them; its idea lives on in the tools that replaced it.', - thread: { - problem: 'CAD', - method: 'declarative geometry', - artifact: 'drawings an agent can verify', - }, - title: - 'CAD as code: from declaration to model — cadSpec', - description: - 'Declarative geometry in TOML you can diff and review, with live browser preview. Compiled deterministically — the same source always yields the same model.', - }, - 'astro-denoise': { - need: 'Noise reduction on an astronomical image can recover a faint galaxy — or invent one that was never there. There is no standard, reproducible way to tell which.', - tagline: - 'A research proposal for benchmarking astronomical noise reduction — evaluated on the science it recovers, not how clean it looks.', - etymology: '(From Greek ástron, star, and denoise.)', - figure: 'An eyepiece that parts light from noise: between the grain the sensor leaves and the star that is really there, a line of research that measures before it claims.', - thread: { - problem: 'noise reduction', - method: 'a benchmark scored by the science', - artifact: 'comparable methods', - }, - title: 'Astro Denoise — a noise-reduction benchmark on DC2 images', - description: - 'A reproducible benchmark of noise-reduction methods on simulated Vera Rubin Observatory DC2 images, scored by faint-source recovery, image quality and cost.', - koan: 'Frontier knowledge hides behind the noise…', - lede: - 'astro-denoise is a research proposal for a modular, reproducible benchmark of noise-reduction methods on simulated Vera Rubin Observatory (LSST DC2) images. Any method — classical filter, trained network — plugs into the same protocol and runs on the same patches, and is scored not by how clean the image looks, but by what it does to the science: the completeness and purity of the faint-source catalog, compared against the DC2 truth catalog. BM3D and a U-Net are the first two references being explored — the platform is designed to grow as more methods are added.', - genesis: { - kicker: 'Genesis', - title: "A master's thesis in progress.", - body: - "This began as a master's thesis and is still taking shape. The ecosystem has worked since July 2026: an orchestrator with a common command-line protocol (FITS image in and out, metadata in JSON), a no-processing control condition, and two separately packaged methods, BM3D for the classical family and a U-Net for deep learning. The experiments run in the open, right here, but there is work ahead before this is a finished benchmark.", - }, - objectives: { - kicker: 'Objectives', - thesisTitle: - 'Benchmarking noise-reduction methods on simulated DC2 images of the Vera Rubin Observatory', - general: - 'Develop a reproducible benchmarking platform that compares noise-reduction methods on simulated DC2 images of the Vera Rubin Observatory, evaluating for each method the recovery of faint sources, the resulting image quality and its computational cost.', - items: [ - { - state: 'complete', - text: 'A modular, extensible ecosystem with a common command-line protocol, where each method is packaged separately with its own environment and a new method joins without changing the orchestrator.', - }, - { - state: 'in progress', - text: 'Reliable source-recovery metrics (completeness, purity, completeness by magnitude and photometric accuracy, by one-to-one matching against the DC2 truth catalogue) and image-quality metrics (PSNR, SSIM and background noise), each checked against at least two independent sources in the literature.', - }, - { - state: 'in progress', - text: "Tools that measure each method's computational cost (time per region, peak memory and regions per unit of time) from outside its process, reported with a description of the hardware.", - }, - { - state: 'in progress', - text: 'The methods under test: BM3D, and a U-Net trained only on DC2 regions outside the evaluation set, with its training recipe and weights published.', - }, - { - state: 'pending', - text: 'A reproducible benchmark on a curated, versioned DESC DC2 dataset of multiband regions from several sky areas; each version is identified by checksums and declares its regions, and every result is reported against one version.', - }, - ], - }, - repos: { - kicker: 'Repositories', - items: [ - { - name: 'UniverLab/astro-denoise', - description: 'The bench: orchestrator, science metrics and the control condition.', - url: 'https://github.com/UniverLab/astro-denoise', - }, - { - name: 'UniverLab/astro-denoise-bm3d', - description: 'BM3D, the classical baseline.', - url: 'https://github.com/UniverLab/astro-denoise-bm3d', - }, - { - name: 'UniverLab/astro-denoise-unet', - description: 'The U-Net, the deep-learning baseline.', - url: 'https://github.com/UniverLab/astro-denoise-unet', - }, - ], - }, - proposalCta: 'Read the proposal (PDF) ↗', - questions: { - kicker: 'Open questions', - items: [ - 'Can a denoiser raise completeness without hurting purity — or does it invent sources that were never there?', - 'Which family of methods — classical filters or learned models — is more likely to help at LSST DC2 depth, and by how much?', - 'Can the whole benchmark be repeated with the same scientific results on the same version of data and code?', - ], - }, - faq: [ - ['What is astro-denoise?', - 'A research project benchmarking noise-reduction methods on simulated Vera Rubin Observatory (LSST DC2) images. It compares three conditions: a no-processing control, BM3D as the classical baseline and a U-Net as the deep-learning baseline. The goal is to recover faint galaxies without inventing ones that were never there — scored by science recovery (completeness + purity), not visual quality.'], - ['What is noise reduction in astronomy?', - 'Astronomical images contain unavoidable noise from the detector and sky. Noise reduction aims to lower this noise while preserving real sources. The risk: a method can smooth out real faint galaxies or invent fake ones that were never there.'], - ['What is LSST?', - 'The Legacy Survey of Space and Time — a 10-year astronomical survey conducted by the Vera Rubin Observatory in Chile. It will image the entire visible sky repeatedly, producing the deepest wide-field catalog of galaxies, stars, and transient events ever made.'], - ['What is DC2?', - 'Data Challenge 2 — a simulated dataset that mimics what LSST will produce, created by the Dark Energy Science Collaboration (DESC). It includes realistic noise, PSF, and atmospheric effects, with a truth catalog telling you exactly which sources are real.'], - ] as [string, string][], - bgCredit: 'Star chart: Yale Bright Star Catalogue (CDS V/50); constellation lines from d3-celestial by Olaf Frohn (BSD-3-Clause).', - }, - 'quorum': { - need: 'Planning poker usually means a server in the middle — an account to create, a room to host, one more tool between you and a number.', - tagline: 'Serverless planning poker — share a link, estimate together, no sign-up.', - etymology: '(From Latin quorum, ‘of whom’.)', - figure: 'A table of face-down cards: everyone plays their estimate at once and nobody sways the others; agreement appears when they turn over.', - thread: { - problem: 'estimation', - method: 'a peer-to-peer room', - artifact: 'disagreement you can see', - }, - title: - 'Free planning poker online — no sign-up, no server — Quorum', - description: - 'Free planning poker over WebRTC — peers talk directly, nothing is stored, no account to create. Share a room link and estimate together.', - koan: '// the estimate is already in the room', - bench: 'open the table → quorum.univerlab.org', - lede: - 'Quorum is serverless planning poker: each person plays a card, votes reveal with an animation once everyone has voted, and the disagreement is where the useful conversation starts. It runs peer-to-peer over WebRTC — a shared room link is the whole app, no cloud, no account.', - genesis: { - kicker: 'Genesis', - title: 'One link, no server.', - body: - 'I had been exploring the idea of building peer-to-peer collaborative computing in the browser, with no intermediary servers. Before scaling the architecture, I needed to validate the basics — can a P2P connection in the browser actually be smooth and robust? To find out, I built something with purpose: planning poker. Not because there aren\'t tools out there — there are excellent ones — but because this ritual captures the essence of teamwork: each participant contributes their estimate privately, in a decentralized and secure way. The result is Quorum: zero data leaving the browser, no servers, no friction. Free, open, secure.', - }, - how: { - kicker: 'P2P session', - items: [ - 'Share the room URL — no sign-up, no invite flow.', - 'Pick a card from the Fibonacci deck (0, 1, 2, 3, 5, 8, 13, 21, ?).', - 'Cards reveal with a multi-phase animation — particles, slide and flip — the moment everyone votes.', - 'Load stories from a CSV or add them one at a time, step through in order — drop out and reconnect, and state syncs back from any peer still in the room.', - ], - }, - faq: [ - ['Is Quorum free?', - 'Completely free. No accounts, no user limits, no premium tier. It\'s a P2P experiment in the browser — WebRTC connections, no server. Open the URL and start estimating.'], - ['How does planning poker work without a server?', - 'Quorum uses peer-to-peer WebRTC connections. Your browser connects directly to your teammates\' browsers — no cloud, no database. A BitTorrent tracker handles the initial handshake; after that, data flows straight between peers.'], - ['Can I use Quorum for remote sprint planning?', - 'Yes. Share the room URL with your team. Everyone picks a Fibonacci card (0, 1, 2, 3, 5, 8, 13, 21, ?). Cards reveal simultaneously once everyone votes — a multi-phase animation prevents premature reveals.'], - ['Does Quorum support user stories?', - 'Yes. Load a CSV or add stories one at a time. Step through stories in order, estimating each one. The session stays alive if someone disconnects — reconnect and state syncs from any peer still in the room.'], - ['What makes Quorum different from planningpokeronline.com?', - 'No accounts, no user limits, no data stored on a server. Quorum is open-source and runs entirely in the browser via WebRTC. Your estimation data never leaves your team\'s devices.'], - ] as [string, string][], - }, - 'demostage': { - need: 'Recording demos by hand is fiddly — typos, uneven pacing, dead air, and a prompt leaking your host.', - tagline: - 'Demos as Code — capture, record and export reproducible terminal demos.', - etymology: '(From demo and stage.)', - figure: 'A zoetrope for the terminal: every demo is written like a score and plays back the same, frame by frame, like the gallop Muybridge broke apart so it could finally be seen.', - thread: { - problem: 'terminal demos', - method: 'an event score', - artifact: 'demos you re-run', - }, - title: - 'Reproducible terminal demos as code — DemoStage', - description: - 'An asciinema alternative where the demo is a file, not a take. Re-record after every change and export gif, mp4 or svg — version-controlled, diffable.', - koan: '// the demo is the source', - lede: - 'DemoStage records a session as events, normalizes human imperfections into a clean demo.toml score, and compiles it to gif, mp4 or an animated svg — version-controlled, re-runnable and diffable.', - genesis: { - kicker: 'Genesis', - title: 'Born building this very page.', - body: - 'Building this landing, every experiment needed a demo — and every tool (asciinema, vhs, OBS, recording by hand) came out brittle or ugly, with a fresh re-record for each typo. So the demo stopped being a video and became a file: events you can prune, pace and replay. DemoStage records the rest of the lab — including the page you are reading.', - }, - pipeline: { - kicker: '5 commands', - items: [ - 'capture — live capture: record the session, auto-normalize into a clean score and faithful .rec.', - 'focus — switch the live view to one or two sources (terminal, repo page, docs, localhost) — full screen, split or stacked, composited into the demo.', - 'record — re-execute demo.toml cleanly, producing a humanized recording.', - 'export — pure playback: render to gif, mp4 or animated svg (no re-execution, ffmpeg/chromium auto-provisioned).', - 'edit — edit the timeline interactively; mark several steps and apply bulk changes.', - ], - }, - // The two genuine figures on this page — the tour as mp4 and the same - // take as its own svg export. Captions name no feature the pipeline - // doesn't already name. - demos: { - first: 'The tour — one score recorded in a real PTY, exported to gif and svg from a single take', - svg: 'The same take, exported as svg — 83 KB, font embedded, no video player', - svgAlt: 'The DemoStage tour as an animated SVG: a banner printed in the terminal, the score that demos it, record, export and doctor.', - }, - faq: [ - ['What is DemoStage?', - 'A tool for planning and recording multi-source demos — terminal, browser, and files in one scene. Not just screen recording: you configure typography, aspect ratio, fps, and terminal style. Output is optimized for web.'], - ['Can I re-record a demo if something changes?', - 'Yes. demostage capture records events, not video. If the UX changes, re-capture and the demo updates deterministically — no need to manually re-record the whole thing.'], - ['How is DemoStage different from asciinema?', - 'asciinema records raw terminal output. DemoStage records events, supports multiple sources (terminal + browser + files), normalizes imperfections, and compiles to gif, mp4 or animated svg. The source is a versionable TOML file.'], - ] as [string, string][], - bgCredit: 'Background: Eadweard Muybridge, The Horse in Motion, 1878 (public domain).', - }, - }, + experiments, manifesto: { kicker: 'Manifesto', title: 'Pensamiento Cósmico', diff --git a/src/i18n/es-experiments.ts b/src/i18n/es-experiments.ts new file mode 100644 index 0000000..f26aec9 --- /dev/null +++ b/src/i18n/es-experiments.ts @@ -0,0 +1,514 @@ +import type { Dict } from './en.ts'; + +// Copy de los experimentos (español) — separada de es.ts para que los +// diccionarios sigan legibles. Se integra en `es` sin cambios. +export const experiments: Dict['experiments'] = { + canopy: { + need: 'Un agente en un solo harness no carga un proyecto. Un grafo de ellos sí — implementa, verifica, revisa, commitea, y esquiva cada fallo.', + tagline: + 'Un motor de grafos para agentes de IA — todos los harness, un solo flujo.', + etymology: '(Del ingl. canopy, ‘dosel del bosque’.)', + figure: 'Colmena de madera donde muchos agentes trabajan bajo un mismo techo, cada uno en su celda y todos en un solo flujo. Como el dosel del bosque, no hace crecer los árboles: los cubre y los ordena para que la luz llegue a todos.', + thread: { + problem: 'agentes de IA', + method: 'un motor de grafos sobre cada harness', + artifact: 'specs llevados hasta el commit', + }, + title: + 'Motor de grafos multi-agente para agentes de IA — Canopy', + description: + 'Motor de grafos de código abierto para agentes de IA: los specs recorren nodos de agente, chequeo y gate, los ensembles votan y los fallos vuelven solos.', + koan: 'En un bosque, el dosel es donde las copas se tocan — árboles separados, una sola capa viva.', + lede: + 'Un daemon en Rust y una interfaz de terminal con un motor de grafos en el centro. El trabajo entra como spec y recorre un grafo que tú diseñas: nodos de agente en cualquier harness, nodos de chequeo que corren tus comandos reales, ensembles que reparten un paso entre varios modelos y exigen quórum, routers que eligen la rama, y un nodo de resiliencia que convierte una cuota agotada en un despertar programado. Por debajo: memoria, scheduling y sync para todos tus agentes.', + morph: [ + ['Implementación con gates', 'los chequeos corren tus comandos reales — el rojo devuelve el trabajo, nunca a ti'], + ['Ensemble · quórum', 'un spec, tres modelos en tres harness, dos de tres deben coincidir'], + ['Cascada — corre uno, los demás esperan', 'el primer miembro toma el spec; si falla, el siguiente lo toma en frío — el primero que pasa sale, el resto nunca corre'], + ['Router', 'un nodo de decisión manda cada spec por la rama que le corresponde'], + ['Pipeline completo', 'diseño, implementación, gates, dos revisiones, commit — y una rama de resiliencia para cuando se acaba la cuota'], + ] as [string, string][], + genesis: { + kicker: 'Génesis', + title: 'El que empezó todo.', + body: + 'Empezó como una carpeta de skills para el trabajo. Entonces noté lo que nadie mencionaba: los harness de agentes traían un modo headless, ahí, sin que nadie lo usara. Conecté tareas con cron para dispararlas por ese modo — demasiado para un skill, y los modelos de entonces se atragantaban con las instrucciones. Así que se volvió un MCP: task-trigger. Funcionaba, pero corría a ciegas en segundo plano; solo el agente veía lo que pasaba. No bastaba. Lo maté y construí una TUI — luego scheduling, memoria, sync, identidades, y un nombre nuevo. Canopy. Para entonces el giro estaba completo: Canopy construía a Canopy, y a todo lo demás en este laboratorio.', + }, + layer: { + kicker: 'El motor de grafos', + cols: [ + ['Grafos, no prompts', 'Nodos de agente, chequeo, gate y router unidos por aristas de pass, fail y always. Un spec recorre el grafo de la entrada al commit; el enrutamiento depende de códigos de salida y veredictos reales, no de lo que un modelo dice haber hecho.'], + ['Ensembles entre harness', 'Reparte un paso entre varios modelos — en paralelo con quórum, en cascada al siguiente si falla, o en round-robin por un equipo — cada miembro en el harness y modelo que elijas.'], + ['El fallo es una ruta', 'Un gate en rojo devuelve el trabajo con la salida adjunta. Un nodo de resiliencia lee los errores de cuota y programa el grafo para despertar justo al reinicio. Solo un veredicto real llega a ti.'], + ], + }, + platforms: { + kicker: 'Plataformas soportadas', + }, + graphs: { + kicker: 'El runtime por debajo', + cols: [ + ['Memoria que persiste', 'Cada ejecución escribe hechos, patrones y decisiones en un grafo de conocimiento por proyecto. La siguiente sesión — en cualquier harness — los lee en vez de redescubrir el código.'], + ['Colas y scheduling', 'Los specs viven en colas ordenadas que un grafo recorre en segundo plano; grafos y agentes también se disparan por cron o por cambios de archivo, y los hooks encadenan un grafo con el siguiente.'], + ['Sincronización multiagente', 'Los agentes declaran su misión, reportan su estado y se mandan mensajes, para que varias sesiones compartan un espacio de trabajo sin pisarse.'], + ], + }, + cta: { + catalogue: 'Catálogo de grafos', + remote: 'Remote', + }, + remote: { + kicker: 'Remote', + title: 'Tus sesiones y tus grafos desde el teléfono', + cols: [ + ['Cifrado de extremo a extremo', + 'Tus sesiones viajan por WebRTC con cifrado de extremo a extremo: el relay solo lleva la señalización y tu pantalla nunca pasa por él.'], + ['Emparejamiento en un comando', + 'Corre el comando de abajo: imprime un código de un solo uso con su enlace y un QR en la terminal. Escanéalo y el navegador queda como un dispositivo revocable de tu host.'], + ['Grafos en vivo, compartidos', + 'Mira los specs recorrer el grafo en tiempo real desde tu teléfono y reparte códigos de invitación de un solo uso entre invitados de confianza.'], + ] as [string, string][], + button: 'Abrir remote →', + }, + catalogue: { + kicker: 'Catálogo de grafos', + caption: 'Patrón: Implementación con gates — una lámina del catálogo en vivo', + explore: 'Explora el catálogo →', + patterns: 'Patrones →', + transparency: 'Transparencia →', + }, + builder: { + kicker: 'Grafo autónomo', + pattern: 'Patrón: Ensemble · quórum + Implementación con gates', + title: 'Falló. No se detuvo.', + stat: ['0', 'veces que se detuvo'] as [string, string], + outro: 'Cada nodo corre en el harness que elijas, y el loop que acabas de ver es el motor real haciendo su trabajo real.', + steps: [ + ['Un spec, tres borradores', + 'El trabajo entra como spec — rol, qué, cómo. Se reparte entre tres modelos en tres harnesses y un quorum se queda con el consenso.'], + ['Implementar desde el consenso', + 'Un nodo implementador toma el plan acordado en tu harness y empieza a escribir.'], + ['Falló: cargo test, 2 rojos', + 'El nodo de chequeo corre tus comandos reales. El rojo devuelve el token directo al implementador — nunca llega a ti.'], + ['Verde — y una segunda opinión', + 'El reintento pasa las puertas. Un harness distinto revisa el diff contra el spec antes de que algo avance.'], + ['Commiteado. Pusheado. Otra vez.', + 'La rama se pushea y la corrida queda en memoria — el próximo spec ya está recorriendo el mismo grafo.'], + ] as [string, string][], + }, + faq: [ + ['¿Qué hace Canopy exactamente?', + 'Canopy corre el trabajo de programación con IA como grafos. Entra un spec, los nodos de agente hacen el trabajo en los harness que elijas — Claude, Codex, Gemini, OpenCode o cualquier agente de terminal —, los nodos de chequeo corren tus tests reales, los ensembles ponen de acuerdo a varios modelos, y los fallos vuelven solos hasta que el spec queda commiteado. Un daemon, todos tus agentes, todos tus free tiers.'], + ['¿Cómo comparto trabajo entre Claude y Codex?', + 'Canopy le da a cada agente un grafo de conocimiento compartido y un protocolo de sincronización. Cuando Claude termina una tarea, los hechos y patrones que descubrió están disponibles para Codex en la siguiente sesión. Sin copia manual de contexto.'], + ['¿Puedo ejecutar agentes de IA en un horario?', + 'Sí. Canopy dispara agentes por cron o por eventos de cambio de archivo a través de un daemon en segundo plano. Configuras el horario una vez; el daemon vigila el workspace y ejecuta tareas automáticamente.'], + ['¿En qué se diferencia Canopy de usar Claude Code o Codex directamente?', + 'Esos son harnesses individuales. Canopy es la capa que los conecta — memoria compartida, programación en segundo plano y coordinación multi-agente. Tú conservas tus agentes; Canopy añade la infraestructura.'], + ] as [string, string][], + }, + texforge: { + need: 'Escribir LaTeX no debería exigir instalar cuatro gigabytes de toolchain.', + tagline: + 'Un espacio de trabajo LaTeX unificado — escritura, diagramas y PDFs en una sola herramienta autocontenida.', + etymology: '(De TeX y el ingl. forge, ‘forja’.)', + figure: 'Prensa de tipos para la época de las máquinas: entra el manuscrito, sale el pliego. Con ella LaTeX deja de ser un oficio de instaladores y vuelve a ser lo que siempre fue, el arte de componer bien una página.', + thread: { + problem: 'LaTeX', + method: 'una sola cadena de herramientas autocontenida', + artifact: 'documentos reproducibles', + }, + title: + 'LaTeX con diagramas Mermaid, Graphviz y D2 — TeXForge', + description: + 'Un solo binario, sin distribución de LaTeX que instalar. Mermaid, Graphviz y D2 se renderizan desde tu .tex, y los errores están escritos para un agente.', + koan: 'Los tipos móviles antes requerían un taller. Ahora requieren un solo binario.', + figures: ['flujo de compilación', 'grafo del documento', 'mapa de build', 'listado de código'], + lede: + 'Un único binario en Rust que arma, revisa, formatea, corrige y compila tu documento, y después te deja inspeccionar lo que salió — el texto que ve un lector, las fuentes, los metadatos y qué sección abre cada página. El motor LaTeX llega solo en la primera compilación, y los diagramas Mermaid, Graphviz o D2 se renderizan dentro de tus archivos .tex sin navegador ni Node.js. Los bloques de código salen resaltados de la misma forma: el resaltador viene compilado en el binario, sin Pygments ni shell-escape.', + genesis: { + kicker: 'Génesis', + title: 'Nació de una tesis.', + body: + 'Una tesis de maestría, escrita con IA en el flujo. Overleaf cobraba, TeXstudio pesaba demasiado, VSCode pedía una extensión por cada cosa. Solo quería diagramas Mermaid en mi LaTeX — lo que por supuesto significaba Node y una pila de archivos .mmd. Pegué con cinta un latexmake para renderizar los que faltaban. Aguantaba como aguanta la cinta. Y cada error mandaba al modelo a recorrer un log de compilación de mil líneas para hallar una sola línea mala. TeXForge condensa todo eso en un binario y un skill.', + }, + press: { + kicker: 'Toda la imprenta', + items: [ + ['Componer', 'plantillas con placeholders que ya saben tu nombre.'], + ['Corregir', 'un linter que detecta referencias rotas, archivos faltantes y entornos sin cerrar antes de imprimir.'], + ['Revisar', 'ortografía y estilo que leen el idioma del documento — Babel, polyglossia o tu configuración por defecto — y revisan el texto contra el diccionario correcto.'], + ['Ajustar', 'un formato canónico, como rustfmt para .tex. Diffs limpios para siempre.'], + ['Ilustrar', 'los bloques Mermaid, Graphviz y D2 se vuelven figuras en compilación, renderizados en Rust puro.'], + ['Resaltar', 'los bloques de código se vuelven listados con marco, números de línea y leyenda, coloreados en compilación — claro, oscuro o mono para imprimir.'], + ['Inspeccionar', 'el PDF compilado — texto, fuentes, metadatos, diffs de página y si cada palabra del fuente sobrevivió.'], + ['Imprimir', 'Tectonic compila de forma determinista; el modo watch reimprime mientras escribes.'], + ], + }, + // El listado impreso: un recorte del ejemplo de capacidades tal como lo + // compiló texforge, no una maqueta. + listing: { + caption: 'listado impreso · python', + alt: 'Un bloque de código Python compuesto por texforge: marco claro, números de línea al margen, palabras clave, cadenas y números en color.', + }, + subproject: { + kicker: 'Subproyecto', + name: 'texforge-templates', + body: + 'La plantilla general va embebida en el binario, así que crear un documento funciona incluso sin conexión.', + link: 'https://github.com/UniverLab/texforge-templates', + }, + templates: { + kicker: 'Plantillas', + title: '{n} plantillas en el registro abierto', + cols: { name: 'nombre', description: 'descripción', version: 'versión' }, + use: 'Cada fila copia el comando; my-doc es el nombre de tu proyecto:', + copy: 'Copiar', + copied: 'Copiado', + copyLabel: 'Copiar el comando que empieza un documento con {name}', + }, + faq: [ + ['¿Qué problema resuelve TeXForge?', + 'Compilar LaTeX normalmente requiere instalar TeX Live (4+ GB), luego herramientas separadas para Mermaid, Graphviz y D2 — cada una con su propia configuración. TeXForge es un solo binario de ~15 MB que hace todo: scaffolding, linting, formateo, diagramas y compilación.'], + ['¿Pueden los agentes de IA usar TeXForge para trabajar con LaTeX?', + 'Sí. Un agente puede ejecutar texforge build sin instalar nada — el motor de LaTeX se descarga en el primer uso. Los errores son concisos (no logs de 1000 líneas), y los diagramas se renderizan dentro de archivos .tex sin Node.js ni herramientas externas.'], + ['¿TeXForge soporta diagramas Mermaid y D2 en LaTeX?', + 'Sí. Escribe un bloque de Mermaid, Graphviz o D2 directamente en tu archivo .tex. TeXForge lo renderiza como figura en el build, en Rust puro, sin navegador ni Node.js.'], + ['¿Cómo funciona la corrección ortográfica en idiomas distintos del inglés?', + 'El idioma sale del propio documento — \\usepackage[spanish]{babel} o polyglossia — y recurre a tu configuración por defecto cuando no se declara ninguno. El español se revisa con un diccionario Hunspell y reglas de afijos, de modo que soluciones se reconoce a partir de la raíz solución sin necesidad de almacenarlo como entrada propia. Los diccionarios se descargan en el primer uso en ~/.texforge/dicts/.'], + ] as [string, string][], + }, + gitkit: { + need: 'Cada repositorio nuevo arranca con el mismo ritual de configuración — a mano, cada vez.', + tagline: + 'Configuración guiada de repositorios git — hooks, ignores, attributes y config en un solo flujo.', + etymology: '(De git y el ingl. kit, ‘estuche de herramientas’.)', + figure: 'Estuche del artesano que le saca a git todo el jugo: los ganchos que nadie configura y las reglas que todos olvidan, guardados en un solo estuche que se abre igual en cada repositorio.', + thread: { + problem: 'configuración de repos', + method: 'builds guardados e idempotentes', + artifact: 'el mismo ritual en todas partes', + }, + title: + 'Setup de git hooks: commits y secretos — GitKit', + description: + 'Setup de git hooks guiado e idempotente: commits convencionales, detección de secretos, .gitignore, .gitattributes y config. Un binario, sin Node ni Python.', + koan: '// el ritual, automatizado', + hero: { + wizard: { + kicker: 'gitkit init', + steps: [ + 'Perfil del repositorio', + 'Hooks', + 'Ignore & attributes', + 'Presets de config', + 'Guardar como build', + ], + }, + hooks: { + kicker: 'pila de hooks', + items: [ + ['conventional-commits', 'commit-msg'], + ['no-secrets', 'pre-commit'], + ['branch-naming', 'pre-push'], + ] as [string, string][], + }, + }, + lede: + 'Un flujo guiado para hooks, .gitignore, .gitattributes y configuración de git — y luego guardado como un build que puedes reaplicar a cualquier proyecto con un solo comando.', + genesis: { + kicker: 'Génesis', + title: 'Un salto de línea que no se portaba bien.', + body: + 'Empezó con un commit que no cambiaba nada — solo los saltos de línea, reescritos en silencio entre mi Mac del trabajo, mi Windows y los agentes en WSL. Perseguirlo me llevó a .gitattributes. Eso me llevó a los git hooks: integrados en git, de verdad potentes, e ignorados por casi todos — porque configurarlos en cada repo es trabajo manual que se repite una y otra vez. Así que lo resolví de raíz. Eso es gitkit.', + }, + features: { + kicker: 'Un solo flujo', + items: [ + 'Hooks integrados: conventional commits, detección de secretos, nomenclatura de ramas — embebidos, sin conexión.', + 'Todas las plantillas de gitignore.io más presets curados de configuración, aplicados de forma idempotente.', + 'gitkit clone arranca un repo en el instante en que toca el disco.', + 'Builds: guarda una configuración una vez, aplícala a todo proyecto futuro.', + 'Lock: bloquea commits y pushes a través de los hooks ya instalados — --json para estado legible por máquinas.', + ], + }, + faq: [ + ['¿Cómo evito que los agentes de IA hagan commits feos?', + 'GitKit configura git hooks en un solo flujo — conventional commits, detección de secretos, nomenclatura de ramas. Los hooks corren offline, embebidos en el repo. Los agentes no pueden pushear código que no compila o contiene secretos.'], + ['¿Qué son los "builds" de GitKit?', + 'Un build guarda tu configuración de git (hooks, ignore, attributes, config) como una plantilla reutilizable. Aplícala a cualquier proyecto futuro con un solo comando — sin reconfigurar hooks en cada repo.'], + ['¿Cómo pauso la escritura en un repositorio?', + 'Ejecuta gitkit lock para bloquear commits y pushes a través de los hooks que gitkit ya instaló. El repositorio se puede leer, pero no escribir, hasta que ejecutes gitkit unlock. Útil cuando un agente autónomo tiene que dejar de escribir mientras haces un rebase, reinstalas o inspeccionas el árbol.'], + ] as [string, string][], + }, + ghscaff: { + need: 'Crear un repositorio de GitHub como se debe son una docena de pasos que se olvidan.', + tagline: + 'Un asistente interactivo que arma y hace cumplir convenciones en repositorios de GitHub.', + etymology: '(De GitHub y el ingl. scaffold, ‘andamio’.)', + figure: 'Andamio que se levanta antes que la obra: protege las ramas, alza los chequeos y deja cada repositorio en pie desde el primer día, para que lo que se construya encima no haya que apuntalarlo después.', + thread: { + problem: 'repositorios de GitHub', + method: 'un asistente idempotente', + artifact: 'convenciones aplicadas', + }, + title: + 'Repos de GitHub con convenciones aplicadas — ghScaff', + description: + 'Un wizard interactivo que arma el repositorio y aplica labels, branch protection y status checks desde el primer commit — idempotente y seguro de reaplicar.', + koan: 'Un edificio es tan recto como su andamio.', + lede: + 'Crear un repositorio como se debe son una docena de pasos olvidables. ghScaff levanta toda la estructura en un wizard interactivo — y como cada operación es idempotente, puede renivelar cualquier repositorio existente sin derribarlo.', + genesis: { + kicker: 'Génesis', + title: 'La misma configuración, una y otra vez.', + body: + 'Proyecto nuevo en Rust. Las mismas etiquetas, la misma protección de ramas, el mismo CI, los mismos secretos. Otra vez. Y otra. El trabajo replicable hecho a mano no es oficio — es desgaste. ghScaff corre el ritual entero en un asistente, de forma idempotente, para que tus convenciones se sostengan en vez de desviarse.', + }, + lifts: { + kicker: '7 niveles', + items: [ + 'Datos del repositorio', + 'Visibilidad y propiedad', + 'Acceso de equipos', + 'Plantilla de lenguaje', + 'Ramas', + 'Características y licencia', + 'Revisar y confirmar', + ], + }, + details: { + kicker: 'Detalles estructurales', + items: [ + 'Los tokens viven en una bóveda cifrada (XSalsa20-Poly1305), ligada a tu usuario, host y binario — nunca en variables de entorno ni en texto plano.', + 'Un único commit atómico chore: init repository lleva todo el boilerplate — sin historial ruidoso archivo por archivo.', + 'Siete etiquetas estándar forzadas en cada ejecución; la desviación se corrige, no se acumula.', + '--dry-run previsualiza cada cambio sin una sola llamada a la API.', + 'La protección de ramas lee los workflows que ghscaff acaba de commitear y deriva de ellos los checks requeridos — un nombre mal escrito produce una regla que no protege nada, y no avisa; esto elimina esa clase de error de configuración. ghscaff doctor verifica cómo quedó.', + ], + }, + subproject: { + kicker: 'Subproyecto', + name: 'ghscaff-boilerplate', + link: 'https://github.com/UniverLab/ghscaff-boilerplate', + }, + boilerplates: { + kicker: 'Boilerplates', + title: '{n} boilerplates que el asistente puede crear', + cols: { name: 'nombre', language: 'lenguaje', description: 'descripción', version: 'versión' }, + use: 'Elige uno en el paso de lenguaje del asistente:', + }, + faq: [ + ['¿Cómo configura ghScaff un repositorio de GitHub?', + 'Ejecuta ghscaff — un asistente interactivo que crea el repo, commitea el boilerplate (CI, README, licencia), configura branch protection y aplica etiquetas estándar. Un commit atómico, sin pasos manuales.'], + ['¿Por qué ghScaff usa una bóveda encriptada para tokens?', + 'Las variables de entorno con tokens son fácilmente explotables — cualquier proceso en tu máquina puede leerlas. ghScaff encripta tokens con XSalsa20-Poly1305, ligado a tu usuario de OS y hostname. La bóveda evita que ghScaff se convierta en un vector de ataque.'], + ['¿Cómo sabe ghScaff qué checks son requeridos para la protección de ramas?', + 'ghScaff lee los workflows que acaba de commitear y deriva los checks requeridos de los nombres de los jobs. Un nombre mal escrito crea una regla de protección que no protege nada y no lo dice — ghScaff elimina esa clase de error silencioso.'], + ] as [string, string][], + }, + cadspec: { + need: 'Los dibujos CAD no tienen semántica — solo líneas en un lienzo, imposibles de versionar, revisar o automatizar.', + tagline: + 'CAD como código: geometría declarativa compilada de forma determinista a DXF.', + etymology: '(De CAD y el ingl. spec, ‘especificación’.)', + figure: 'Mesa de dibujo que declaraba las piezas en lugar de trazarlas. Se archivó cuando el laboratorio prefirió envolver las mejores herramientas antes que reinventarlas; su idea sigue en las que la reemplazaron.', + thread: { + problem: 'CAD', + method: 'geometría declarativa', + artifact: 'planos que un agente puede verificar', + }, + title: + 'CAD as code: de la declaración al modelo — cadSpec', + description: + 'Geometría declarativa en TOML que puedes revisar en un diff, con preview en el navegador. Compilación determinista: el mismo fuente siempre da el mismo modelo.', + }, + 'astro-denoise': { + need: 'Aplicar una reducción de ruido a una imagen astronómica puede recuperar una galaxia tenue — o inventar una que nunca estuvo ahí. No hay forma estándar y reproducible de saber cuál de las dos.', + tagline: + 'Una propuesta de investigación para benchmarking de reducción de ruido astronómico — evaluada por la ciencia que recupera, no por lo limpia que se vea.', + etymology: '(Del gr. ástron, ‘astro’, y el ingl. denoise, ‘quitar el ruido’.)', + figure: 'Ocular que separa la luz del ruido: entre el grano que deja el sensor y la estrella que de verdad está ahí, una investigación que mide antes de afirmar.', + thread: { + problem: 'reducción de ruido', + method: 'un benchmark puntuado por la ciencia', + artifact: 'métodos comparables', + }, + title: 'Astro Denoise — benchmark de reducción de ruido en DC2', + description: + 'Benchmark reproducible de métodos de reducción de ruido en imágenes DC2 del Vera Rubin Observatory, por recuperación de fuentes débiles, calidad y coste.', + koan: 'El conocimiento de frontera se oculta tras el ruido…', + lede: + 'astro-denoise es una propuesta de investigación para un benchmark modular y reproducible de métodos de reducción de ruido sobre imágenes simuladas del Vera Rubin Observatory (LSST DC2). La idea es directa: cualquier método — un filtro clásico, una red entrenada — se enchufa al mismo protocolo y corre sobre los mismos parches, y se evalúa no por una imagen más limpia sino por lo que le hace a la ciencia: la completitud y la pureza del catálogo de fuentes débiles, contrastadas con el truth catalog de DC2. BM3D y una U-Net son las dos primeras referencias que se están explorando — la plataforma está diseñada para crecer a medida que se añadan más métodos.', + genesis: { + kicker: 'Génesis', + title: 'Una tesis de maestría en curso.', + body: + 'Esto empezó como tesis de maestría y aún está tomando forma. El ecosistema funciona desde julio de 2026: un orquestador con un protocolo común de línea de comandos (imagen FITS de entrada y de salida, metadatos en JSON), una condición de control sin procesamiento y dos métodos empaquetados por separado, BM3D como representante de los métodos clásicos y una U-Net como representante del aprendizaje profundo. Los experimentos corren en abierto, aquí mismo, pero queda trabajo antes de que esto sea un benchmark terminado.', + }, + objectives: { + kicker: 'Objetivos', + thesisTitle: + 'Benchmarking de métodos de reducción de ruido en imágenes simuladas DC2 del Vera Rubin Observatory', + general: + 'Desarrollar una plataforma de benchmarking reproducible que compare métodos de reducción de ruido en imágenes simuladas DC2 del Vera Rubin Observatory, evaluando en cada método la recuperación de fuentes débiles, la calidad de imagen resultante y su coste computacional.', + items: [ + { + state: 'completo', + text: 'Un ecosistema modular y extensible con un protocolo común de línea de comandos, en el que cada método se empaqueta por separado con su propio entorno y un método nuevo se incorpora sin modificar el orquestador.', + }, + { + state: 'en curso', + text: 'Métricas confiables de recuperación de fuentes (completitud, pureza, completitud por magnitud y precisión fotométrica, por cruce uno a uno con el catálogo de verdad de DC2) y de calidad de imagen (PSNR, SSIM y ruido de fondo), cada una verificada contra al menos dos fuentes independientes de la literatura.', + }, + { + state: 'en curso', + text: 'Herramientas que miden el coste computacional de cada método (tiempo por región, memoria máxima y regiones por unidad de tiempo) desde fuera de su proceso, reportadas con la descripción del equipo.', + }, + { + state: 'en curso', + text: 'Los métodos evaluados: BM3D y una U-Net entrenada solo con regiones de DC2 que no están en el conjunto de evaluación, con su receta de entrenamiento y sus pesos publicados.', + }, + { + state: 'pendiente', + text: 'Un benchmark reproducible sobre un conjunto de datos curado y versionado de DESC DC2, con regiones multibanda de varias zonas del cielo; cada versión se identifica por checksums y declara sus regiones, y todo resultado se reporta contra una versión concreta.', + }, + ], + }, + repos: { + kicker: 'Repositorios', + items: [ + { + name: 'UniverLab/astro-denoise', + description: 'El banco: orquestador, métricas científicas y la condición de control.', + url: 'https://github.com/UniverLab/astro-denoise', + }, + { + name: 'UniverLab/astro-denoise-bm3d', + description: 'BM3D, la referencia clásica.', + url: 'https://github.com/UniverLab/astro-denoise-bm3d', + }, + { + name: 'UniverLab/astro-denoise-unet', + description: 'La U-Net, la referencia de aprendizaje profundo.', + url: 'https://github.com/UniverLab/astro-denoise-unet', + }, + ], + }, + proposalCta: 'Leer la propuesta (PDF) ↗', + questions: { + kicker: 'Preguntas abiertas', + items: [ + '¿Puede un denoiser subir la completitud sin dañar la pureza — o inventa fuentes que nunca estuvieron ahí?', + '¿Qué familia de métodos — filtros clásicos o modelos aprendidos — tiene más probabilidad de ayudar a la profundidad de LSST DC2, y cuánto?', + '¿Puede el benchmark completo repetirse con los mismos resultados científicos sobre la misma versión de datos y de código?', + ], + }, + faq: [ + ['¿Qué es astro-denoise?', + 'Un proyecto de investigación que evalúa métodos de reducción de ruido en imágenes simuladas del Observatorio Vera Rubin (LSST DC2). Compara tres condiciones: un control sin procesamiento, BM3D como referencia clásica y una U-Net como referencia de aprendizaje profundo. El objetivo es recuperar galaxias tenues sin inventar las que nunca estuvieron — evaluado por recuperación científica (completitud + pureza), no por calidad visual.'], + ['¿Qué es la reducción de ruido en astronomía?', + 'Las imágenes astronómicas tienen ruido inevitable del detector y del cielo. La reducción de ruido busca bajar ese ruido preservando las fuentes reales. El riesgo: un método puede suavizar galaxias tenues reales o inventar falsas que nunca existieron.'], + ['¿Qué es LSST?', + 'El Legacy Survey of Space and Time — un relevamiento astronómico de 10 años que realizará el Observatorio Vera Rubin en Chile. Fotografiará todo el cielo visible repetidamente, produciendo el catálogo profundo de campo amplio más grande jamás hecho.'], + ['¿Qué es DC2?', + 'Data Challenge 2 — un conjunto de datos simulados que imita lo que LSST producirá, creado por la Dark Energy Science Collaboration (DESC). Incluye ruido realista, PSF y efectos atmosféricos, con un catálogo de verdad que dice exactamente qué fuentes son reales.'], + ] as [string, string][], + bgCredit: 'Carta celeste: Yale Bright Star Catalogue (CDS V/50); líneas de constelaciones de d3-celestial, de Olaf Frohn (BSD-3-Clause).', + }, + 'quorum': { + need: 'El planning poker casi siempre implica un servidor en medio — una cuenta que crear, una sala que hospedar, una herramienta más entre tú y un número.', + tagline: 'Planning poker sin servidor — comparte un enlace, estima juntos, sin registro.', + etymology: '(Del lat. quorum, ‘de los cuales’.)', + figure: 'Mesa de cartas boca abajo: cada quien juega su estimación a la vez y nadie arrastra a los demás; el acuerdo aparece cuando se voltean.', + thread: { + problem: 'estimación', + method: 'una sala peer-to-peer', + artifact: 'desacuerdo visible', + }, + title: + 'Planning poker online gratis, sin registro — Quorum', + description: + 'Planning poker gratis por WebRTC — los peers hablan directamente, no se guarda nada, no hay cuenta que crear. Comparte el link de la sala y estimen juntos.', + koan: '// la estimación ya está en la sala', + bench: 'abre la mesa → quorum.univerlab.org', + lede: + 'Quorum es planning poker sin servidor: cada quien juega una carta, los votos se revelan con una animación cuando todos votan, y el desacuerdo es donde arranca la conversación útil. Corre punto a punto sobre WebRTC — un enlace de sala es toda la app, sin nube, sin cuenta.', + genesis: { + kicker: 'Génesis', + title: 'Un enlace, sin servidor.', + body: + 'Llevaba tiempo con la idea de construir cómputo colaborativo peer-to-peer en el navegador, sin servidores de intermediarios. Antes de escalar la arquitectura, necesitaba validar lo básico — ¿puede una conexión P2P en el browser ser fluida y robusta? Para comprobarlo, construí algo con propósito: planning poker. No porque no existieran herramientas — hay excelentes opciones — sino porque este ritual refleja la esencia del trabajo en equipo: cada participante aporta su estimación en privado, de forma descentralizada y segura. El resultado es Quorum: cero datos saliendo del navegador, sin servidores, sin fricción. Libre, abierto, seguro.', + }, + how: { + kicker: 'Sesión P2P', + items: [ + 'Comparte la URL de la sala — sin registro, sin flujo de invitación.', + 'Elige una carta del mazo Fibonacci (0, 1, 2, 3, 5, 8, 13, 21, ?).', + 'Las cartas se revelan con una animación multi-fase en cuanto todos votan — partículas, slide y flip sincronizados.', + 'Carga historias desde un CSV o agrégalas una a una, avanza por ellas en orden — desconéctate y vuelve a conectar: el estado se sincroniza desde cualquier par que siga en la sala.', + ], + }, + faq: [ + ['¿Es Quorum gratis?', + 'Completamente gratis. Sin cuentas, sin límites de usuarios, sin tier premium. Es un experimento P2P en el navegador — conexiones WebRTC, sin servidor. Abre la URL y empieza a estimar.'], + ['¿Cómo funciona planning poker sin servidor?', + 'Quorum usa conexiones peer-to-peer WebRTC. Tu navegador se conecta directamente a los de tus compañeros — sin nube, sin base de datos. Un tracker de BitTorrent maneja el handshake inicial; después, los datos fluyen directamente entre pares.'], + ['¿Puedo usar Quorum para sprint planning remoto?', + 'Sí. Comparte la URL de la sala con tu equipo. Cada uno elige una carta Fibonacci (0, 1, 2, 3, 5, 8, 13, 21, ?). Las cartas se revelan simultáneamente cuando todos votan — una cuenta regresiva de 3 segundos previene revelaciones prematuras.'], + ['¿Quorum soporta historias de usuario?', + 'Sí. Pega una lista de historias o carga un CSV. Avanza por ellas en orden, estimando cada una. La sesión se mantiene viva si alguien se desconecta — reconéctate y el estado se sincroniza desde cualquier par que siga en la sala.'], + ['¿Qué hace diferente Quorum de planningpokeronline.com?', + 'Sin cuentas, sin límites de usuarios, sin datos almacenados en un servidor. Quorum es open-source y corre completamente en el navegador vía WebRTC. Tus datos de estimación nunca salen de los dispositivos de tu equipo.'], + ] as [string, string][], + }, + 'demostage': { + need: 'Grabar demos a mano es tedioso: errores, ritmo desigual, tiempos muertos y un terminal que expone tu hostname.', + tagline: + 'Demos como Código — captura, graba y exporta demos de terminal reproducibles.', + etymology: '(Del ingl. demo y stage, ‘escenario’.)', + figure: 'Zoótropo de la terminal: cada demostración se escribe como una partitura y se reproduce idéntica, cuadro a cuadro, como el galope que Muybridge descompuso para que por fin pudiera verse.', + thread: { + problem: 'demos de terminal', + method: 'una partitura de eventos', + artifact: 'demos que se vuelven a correr', + }, + title: + 'Demos de terminal reproducibles, como código — DemoStage', + description: + 'Una alternativa a asciinema donde el demo es un archivo, no una toma. Regrábalo tras cada cambio; exporta gif, mp4 o svg — versionado y comparable.', + koan: '// el demo es el código fuente', + lede: + 'DemoStage graba una sesión como eventos, corrige las imperfecciones humanas en un demo.toml limpio — una partitura — y lo compila a gif, mp4 o un svg animado: versionado, re-ejecutable y comparable.', + genesis: { + kicker: 'Génesis', + title: 'Nació construyendo esta misma página.', + body: + 'Al construir esta landing, cada experimento necesitaba un demo — y cada herramienta (asciinema, vhs, OBS, grabar a mano) dejaba algo por fuera: el ritmo, el encuadre, la legibilidad. Entonces el demo dejó de ser un video y pasó a ser un archivo: eventos que puedes podar, ritmar y reproducir. DemoStage graba el resto del laboratorio — incluida la página que estás leyendo.', + }, + pipeline: { + kicker: '5 comandos', + items: [ + 'capture — captura en vivo: graba la sesión, la normaliza en una partitura limpia y un .rec fiel.', + 'focus — cambia la vista en vivo a una o dos fuentes (terminal, repo, docs, localhost): pantalla completa, dividida o apilada, compuesta en el demo.', + 'record — re-ejecuta demo.toml de forma limpia, produciendo una grabación humanizada.', + 'export — reproducción pura: renderiza a gif, mp4 o svg animado sin re-ejecución, con ffmpeg y chromium autoprovisionados.', + 'edit — edita la línea de tiempo de forma interactiva: marca varios pasos y aplica cambios en bloque.', + ], + }, + demos: { + first: 'El tour — un score grabado en un PTY real, exportado a gif y svg desde una sola toma', + svg: 'La misma toma, exportada a svg — 83 KB, fuente incrustada, sin reproductor de video', + svgAlt: 'El tour de DemoStage como SVG animado: un banner impreso en la terminal, el score que lo demuestra, record, export y doctor.', + }, + faq: [ + ['¿Qué es DemoStage?', + 'Una herramienta para planificar y grabar demos multi-fuente — terminal, navegador y archivos en una sola escena. No es solo grabación de pantalla: configuras tipografía, aspect ratio, fps y estilo de terminal. El resultado está optimizado para web.'], + ['¿Puedo re-grabar una demo si algo cambia?', + 'Sí. demostage capture graba eventos, no video. Si la UX cambia, vuelves a capturar y la demo se actualiza de forma determinista — sin necesidad de re-grabar todo manualmente.'], + ['¿En qué se diferencia DemoStage de asciinema?', + 'asciinema graba la salida cruda del terminal. DemoStage graba eventos, soporta múltiples fuentes (terminal + navegador + archivos), normaliza imperfecciones y compila a gif, mp4 o svg animado. El fuente es un archivo TOML versionable.'], + ] as [string, string][], + bgCredit: 'Fondo: Eadweard Muybridge, The Horse in Motion, 1878 (dominio público).', + }, +}; diff --git a/src/i18n/es.ts b/src/i18n/es.ts index 1e7889d..876f7b2 100644 --- a/src/i18n/es.ts +++ b/src/i18n/es.ts @@ -2,6 +2,8 @@ import type { Dict } from './en'; /** Copy en español. Misma forma que `en`. La documentación que se extrae de * los repositorios permanece solo en inglés. */ +import { experiments } from './es-experiments.ts'; + export const es: Dict = { meta: { // 54 chars: "de código abierto" is the site's own term for open-source @@ -205,492 +207,7 @@ export const es: Dict = { contribute: 'Colabora en GitHub ↗', }, }, - experiments: { - canopy: { - need: 'Un agente en un solo harness no carga un proyecto. Un grafo de ellos sí — implementa, verifica, revisa, commitea, y esquiva cada fallo.', - tagline: - 'Un motor de grafos para agentes de IA — todos los harness, un solo flujo.', - etymology: '(Del ingl. canopy, ‘dosel del bosque’.)', - figure: 'Colmena de madera donde muchos agentes trabajan bajo un mismo techo, cada uno en su celda y todos en un solo flujo. Como el dosel del bosque, no hace crecer los árboles: los cubre y los ordena para que la luz llegue a todos.', - thread: { - problem: 'agentes de IA', - method: 'un motor de grafos sobre cada harness', - artifact: 'specs llevados hasta el commit', - }, - title: - 'Motor de grafos multi-agente para agentes de IA — Canopy', - description: - 'Motor de grafos de código abierto para agentes de IA: los specs recorren nodos de agente, chequeo y gate, los ensembles votan y los fallos vuelven solos.', - koan: 'En un bosque, el dosel es donde las copas se tocan — árboles separados, una sola capa viva.', - lede: - 'Un daemon en Rust y una interfaz de terminal con un motor de grafos en el centro. El trabajo entra como spec y recorre un grafo que tú diseñas: nodos de agente en cualquier harness, nodos de chequeo que corren tus comandos reales, ensembles que reparten un paso entre varios modelos y exigen quórum, routers que eligen la rama, y un nodo de resiliencia que convierte una cuota agotada en un despertar programado. Por debajo: memoria, scheduling y sync para todos tus agentes.', - morph: [ - ['Implementación con gates', 'los chequeos corren tus comandos reales — el rojo devuelve el trabajo, nunca a ti'], - ['Ensemble · quórum', 'un spec, tres modelos en tres harness, dos de tres deben coincidir'], - ['Cascada — corre uno, los demás esperan', 'el primer miembro toma el spec; si falla, el siguiente lo toma en frío — el primero que pasa sale, el resto nunca corre'], - ['Router', 'un nodo de decisión manda cada spec por la rama que le corresponde'], - ['Pipeline completo', 'diseño, implementación, gates, dos revisiones, commit — y una rama de resiliencia para cuando se acaba la cuota'], - ] as [string, string][], - genesis: { - kicker: 'Génesis', - title: 'El que empezó todo.', - body: - 'Empezó como una carpeta de skills para el trabajo. Entonces noté lo que nadie mencionaba: los harness de agentes traían un modo headless, ahí, sin que nadie lo usara. Conecté tareas con cron para dispararlas por ese modo — demasiado para un skill, y los modelos de entonces se atragantaban con las instrucciones. Así que se volvió un MCP: task-trigger. Funcionaba, pero corría a ciegas en segundo plano; solo el agente veía lo que pasaba. No bastaba. Lo maté y construí una TUI — luego scheduling, memoria, sync, identidades, y un nombre nuevo. Canopy. Para entonces el giro estaba completo: Canopy construía a Canopy, y a todo lo demás en este laboratorio.', - }, - layer: { - kicker: 'El motor de grafos', - cols: [ - ['Grafos, no prompts', 'Nodos de agente, chequeo, gate y router unidos por aristas de pass, fail y always. Un spec recorre el grafo de la entrada al commit; el enrutamiento depende de códigos de salida y veredictos reales, no de lo que un modelo dice haber hecho.'], - ['Ensembles entre harness', 'Reparte un paso entre varios modelos — en paralelo con quórum, en cascada al siguiente si falla, o en round-robin por un equipo — cada miembro en el harness y modelo que elijas.'], - ['El fallo es una ruta', 'Un gate en rojo devuelve el trabajo con la salida adjunta. Un nodo de resiliencia lee los errores de cuota y programa el grafo para despertar justo al reinicio. Solo un veredicto real llega a ti.'], - ], - }, - platforms: { - kicker: 'Plataformas soportadas', - }, - graphs: { - kicker: 'El runtime por debajo', - cols: [ - ['Memoria que persiste', 'Cada ejecución escribe hechos, patrones y decisiones en un grafo de conocimiento por proyecto. La siguiente sesión — en cualquier harness — los lee en vez de redescubrir el código.'], - ['Colas y scheduling', 'Los specs viven en colas ordenadas que un grafo recorre en segundo plano; grafos y agentes también se disparan por cron o por cambios de archivo, y los hooks encadenan un grafo con el siguiente.'], - ['Sincronización multiagente', 'Los agentes declaran su misión, reportan su estado y se mandan mensajes, para que varias sesiones compartan un espacio de trabajo sin pisarse.'], - ], - }, - builder: { - kicker: 'Grafo autónomo', - pattern: 'Patrón: Ensemble · quórum + Implementación con gates', - title: 'Falló. No se detuvo.', - stat: ['0', 'veces que se detuvo'] as [string, string], - outro: 'Cada nodo corre en el harness que elijas, y el loop que acabas de ver es el motor real haciendo su trabajo real.', - steps: [ - ['Un spec, tres borradores', - 'El trabajo entra como spec — rol, qué, cómo. Se reparte entre tres modelos en tres harnesses y un quorum se queda con el consenso.'], - ['Implementar desde el consenso', - 'Un nodo implementador toma el plan acordado en tu harness y empieza a escribir.'], - ['Falló: cargo test, 2 rojos', - 'El nodo de chequeo corre tus comandos reales. El rojo devuelve el token directo al implementador — nunca llega a ti.'], - ['Verde — y una segunda opinión', - 'El reintento pasa las puertas. Un harness distinto revisa el diff contra el spec antes de que algo avance.'], - ['Commiteado. Pusheado. Otra vez.', - 'La rama se pushea y la corrida queda en memoria — el próximo spec ya está recorriendo el mismo grafo.'], - ] as [string, string][], - }, - faq: [ - ['¿Qué hace Canopy exactamente?', - 'Canopy corre el trabajo de programación con IA como grafos. Entra un spec, los nodos de agente hacen el trabajo en los harness que elijas — Claude, Codex, Gemini, OpenCode o cualquier agente de terminal —, los nodos de chequeo corren tus tests reales, los ensembles ponen de acuerdo a varios modelos, y los fallos vuelven solos hasta que el spec queda commiteado. Un daemon, todos tus agentes, todos tus free tiers.'], - ['¿Cómo comparto trabajo entre Claude y Codex?', - 'Canopy le da a cada agente un grafo de conocimiento compartido y un protocolo de sincronización. Cuando Claude termina una tarea, los hechos y patrones que descubrió están disponibles para Codex en la siguiente sesión. Sin copia manual de contexto.'], - ['¿Puedo ejecutar agentes de IA en un horario?', - 'Sí. Canopy dispara agentes por cron o por eventos de cambio de archivo a través de un daemon en segundo plano. Configuras el horario una vez; el daemon vigila el workspace y ejecuta tareas automáticamente.'], - ['¿En qué se diferencia Canopy de usar Claude Code o Codex directamente?', - 'Esos son harnesses individuales. Canopy es la capa que los conecta — memoria compartida, programación en segundo plano y coordinación multi-agente. Tú conservas tus agentes; Canopy añade la infraestructura.'], - ] as [string, string][], - }, - texforge: { - need: 'Escribir LaTeX no debería exigir instalar cuatro gigabytes de toolchain.', - tagline: - 'Un espacio de trabajo LaTeX unificado — escritura, diagramas y PDFs en una sola herramienta autocontenida.', - etymology: '(De TeX y el ingl. forge, ‘forja’.)', - figure: 'Prensa de tipos para la época de las máquinas: entra el manuscrito, sale el pliego. Con ella LaTeX deja de ser un oficio de instaladores y vuelve a ser lo que siempre fue, el arte de componer bien una página.', - thread: { - problem: 'LaTeX', - method: 'una sola cadena de herramientas autocontenida', - artifact: 'documentos reproducibles', - }, - title: - 'LaTeX con diagramas Mermaid, Graphviz y D2 — TeXForge', - description: - 'Un solo binario, sin distribución de LaTeX que instalar. Mermaid, Graphviz y D2 se renderizan desde tu .tex, y los errores están escritos para un agente.', - koan: 'Los tipos móviles antes requerían un taller. Ahora requieren un solo binario.', - figures: ['flujo de compilación', 'grafo del documento', 'mapa de build', 'listado de código'], - lede: - 'Un único binario en Rust que arma, revisa, formatea, corrige y compila tu documento, y después te deja inspeccionar lo que salió — el texto que ve un lector, las fuentes, los metadatos y qué sección abre cada página. El motor LaTeX llega solo en la primera compilación, y los diagramas Mermaid, Graphviz o D2 se renderizan dentro de tus archivos .tex sin navegador ni Node.js. Los bloques de código salen resaltados de la misma forma: el resaltador viene compilado en el binario, sin Pygments ni shell-escape.', - genesis: { - kicker: 'Génesis', - title: 'Nació de una tesis.', - body: - 'Una tesis de maestría, escrita con IA en el flujo. Overleaf cobraba, TeXstudio pesaba demasiado, VSCode pedía una extensión por cada cosa. Solo quería diagramas Mermaid en mi LaTeX — lo que por supuesto significaba Node y una pila de archivos .mmd. Pegué con cinta un latexmake para renderizar los que faltaban. Aguantaba como aguanta la cinta. Y cada error mandaba al modelo a recorrer un log de compilación de mil líneas para hallar una sola línea mala. TeXForge condensa todo eso en un binario y un skill.', - }, - press: { - kicker: 'Toda la imprenta', - items: [ - ['Componer', 'plantillas con placeholders que ya saben tu nombre.'], - ['Corregir', 'un linter que detecta referencias rotas, archivos faltantes y entornos sin cerrar antes de imprimir.'], - ['Revisar', 'ortografía y estilo que leen el idioma del documento — Babel, polyglossia o tu configuración por defecto — y revisan el texto contra el diccionario correcto.'], - ['Ajustar', 'un formato canónico, como rustfmt para .tex. Diffs limpios para siempre.'], - ['Ilustrar', 'los bloques Mermaid, Graphviz y D2 se vuelven figuras en compilación, renderizados en Rust puro.'], - ['Resaltar', 'los bloques de código se vuelven listados con marco, números de línea y leyenda, coloreados en compilación — claro, oscuro o mono para imprimir.'], - ['Inspeccionar', 'el PDF compilado — texto, fuentes, metadatos, diffs de página y si cada palabra del fuente sobrevivió.'], - ['Imprimir', 'Tectonic compila de forma determinista; el modo watch reimprime mientras escribes.'], - ], - }, - // El listado impreso: un recorte del ejemplo de capacidades tal como lo - // compiló texforge, no una maqueta. - listing: { - caption: 'listado impreso · python', - alt: 'Un bloque de código Python compuesto por texforge: marco claro, números de línea al margen, palabras clave, cadenas y números en color.', - }, - subproject: { - kicker: 'Subproyecto', - name: 'texforge-templates', - body: - 'La plantilla general va embebida en el binario, así que crear un documento funciona incluso sin conexión.', - link: 'https://github.com/UniverLab/texforge-templates', - }, - templates: { - kicker: 'Plantillas', - title: '{n} plantillas en el registro abierto', - cols: { name: 'nombre', description: 'descripción', version: 'versión' }, - use: 'Cada fila copia el comando; my-doc es el nombre de tu proyecto:', - copy: 'Copiar', - copied: 'Copiado', - copyLabel: 'Copiar el comando que empieza un documento con {name}', - }, - faq: [ - ['¿Qué problema resuelve TeXForge?', - 'Compilar LaTeX normalmente requiere instalar TeX Live (4+ GB), luego herramientas separadas para Mermaid, Graphviz y D2 — cada una con su propia configuración. TeXForge es un solo binario de ~15 MB que hace todo: scaffolding, linting, formateo, diagramas y compilación.'], - ['¿Pueden los agentes de IA usar TeXForge para trabajar con LaTeX?', - 'Sí. Un agente puede ejecutar texforge build sin instalar nada — el motor de LaTeX se descarga en el primer uso. Los errores son concisos (no logs de 1000 líneas), y los diagramas se renderizan dentro de archivos .tex sin Node.js ni herramientas externas.'], - ['¿TeXForge soporta diagramas Mermaid y D2 en LaTeX?', - 'Sí. Escribe un bloque de Mermaid, Graphviz o D2 directamente en tu archivo .tex. TeXForge lo renderiza como figura en el build, en Rust puro, sin navegador ni Node.js.'], - ['¿Cómo funciona la corrección ortográfica en idiomas distintos del inglés?', - 'El idioma sale del propio documento — \\usepackage[spanish]{babel} o polyglossia — y recurre a tu configuración por defecto cuando no se declara ninguno. El español se revisa con un diccionario Hunspell y reglas de afijos, de modo que soluciones se reconoce a partir de la raíz solución sin necesidad de almacenarlo como entrada propia. Los diccionarios se descargan en el primer uso en ~/.texforge/dicts/.'], - ] as [string, string][], - }, - gitkit: { - need: 'Cada repositorio nuevo arranca con el mismo ritual de configuración — a mano, cada vez.', - tagline: - 'Configuración guiada de repositorios git — hooks, ignores, attributes y config en un solo flujo.', - etymology: '(De git y el ingl. kit, ‘estuche de herramientas’.)', - figure: 'Estuche del artesano que le saca a git todo el jugo: los ganchos que nadie configura y las reglas que todos olvidan, guardados en un solo estuche que se abre igual en cada repositorio.', - thread: { - problem: 'configuración de repos', - method: 'builds guardados e idempotentes', - artifact: 'el mismo ritual en todas partes', - }, - title: - 'Setup de git hooks: commits y secretos — GitKit', - description: - 'Setup de git hooks guiado e idempotente: commits convencionales, detección de secretos, .gitignore, .gitattributes y config. Un binario, sin Node ni Python.', - koan: '// el ritual, automatizado', - hero: { - wizard: { - kicker: 'gitkit init', - steps: [ - 'Perfil del repositorio', - 'Hooks', - 'Ignore & attributes', - 'Presets de config', - 'Guardar como build', - ], - }, - hooks: { - kicker: 'pila de hooks', - items: [ - ['conventional-commits', 'commit-msg'], - ['no-secrets', 'pre-commit'], - ['branch-naming', 'pre-push'], - ] as [string, string][], - }, - }, - lede: - 'Un flujo guiado para hooks, .gitignore, .gitattributes y configuración de git — y luego guardado como un build que puedes reaplicar a cualquier proyecto con un solo comando.', - genesis: { - kicker: 'Génesis', - title: 'Un salto de línea que no se portaba bien.', - body: - 'Empezó con un commit que no cambiaba nada — solo los saltos de línea, reescritos en silencio entre mi Mac del trabajo, mi Windows y los agentes en WSL. Perseguirlo me llevó a .gitattributes. Eso me llevó a los git hooks: integrados en git, de verdad potentes, e ignorados por casi todos — porque configurarlos en cada repo es trabajo manual que se repite una y otra vez. Así que lo resolví de raíz. Eso es gitkit.', - }, - features: { - kicker: 'Un solo flujo', - items: [ - 'Hooks integrados: conventional commits, detección de secretos, nomenclatura de ramas — embebidos, sin conexión.', - 'Todas las plantillas de gitignore.io más presets curados de configuración, aplicados de forma idempotente.', - 'gitkit clone arranca un repo en el instante en que toca el disco.', - 'Builds: guarda una configuración una vez, aplícala a todo proyecto futuro.', - 'Lock: bloquea commits y pushes a través de los hooks ya instalados — --json para estado legible por máquinas.', - ], - }, - faq: [ - ['¿Cómo evito que los agentes de IA hagan commits feos?', - 'GitKit configura git hooks en un solo flujo — conventional commits, detección de secretos, nomenclatura de ramas. Los hooks corren offline, embebidos en el repo. Los agentes no pueden pushear código que no compila o contiene secretos.'], - ['¿Qué son los "builds" de GitKit?', - 'Un build guarda tu configuración de git (hooks, ignore, attributes, config) como una plantilla reutilizable. Aplícala a cualquier proyecto futuro con un solo comando — sin reconfigurar hooks en cada repo.'], - ['¿Cómo pauso la escritura en un repositorio?', - 'Ejecuta gitkit lock para bloquear commits y pushes a través de los hooks que gitkit ya instaló. El repositorio se puede leer, pero no escribir, hasta que ejecutes gitkit unlock. Útil cuando un agente autónomo tiene que dejar de escribir mientras haces un rebase, reinstalas o inspeccionas el árbol.'], - ] as [string, string][], - }, - ghscaff: { - need: 'Crear un repositorio de GitHub como se debe son una docena de pasos que se olvidan.', - tagline: - 'Un asistente interactivo que arma y hace cumplir convenciones en repositorios de GitHub.', - etymology: '(De GitHub y el ingl. scaffold, ‘andamio’.)', - figure: 'Andamio que se levanta antes que la obra: protege las ramas, alza los chequeos y deja cada repositorio en pie desde el primer día, para que lo que se construya encima no haya que apuntalarlo después.', - thread: { - problem: 'repositorios de GitHub', - method: 'un asistente idempotente', - artifact: 'convenciones aplicadas', - }, - title: - 'Repos de GitHub con convenciones aplicadas — ghScaff', - description: - 'Un wizard interactivo que arma el repositorio y aplica labels, branch protection y status checks desde el primer commit — idempotente y seguro de reaplicar.', - koan: 'Un edificio es tan recto como su andamio.', - lede: - 'Crear un repositorio como se debe son una docena de pasos olvidables. ghScaff levanta toda la estructura en un wizard interactivo — y como cada operación es idempotente, puede renivelar cualquier repositorio existente sin derribarlo.', - genesis: { - kicker: 'Génesis', - title: 'La misma configuración, una y otra vez.', - body: - 'Proyecto nuevo en Rust. Las mismas etiquetas, la misma protección de ramas, el mismo CI, los mismos secretos. Otra vez. Y otra. El trabajo replicable hecho a mano no es oficio — es desgaste. ghScaff corre el ritual entero en un asistente, de forma idempotente, para que tus convenciones se sostengan en vez de desviarse.', - }, - lifts: { - kicker: '7 niveles', - items: [ - 'Datos del repositorio', - 'Visibilidad y propiedad', - 'Acceso de equipos', - 'Plantilla de lenguaje', - 'Ramas', - 'Características y licencia', - 'Revisar y confirmar', - ], - }, - details: { - kicker: 'Detalles estructurales', - items: [ - 'Los tokens viven en una bóveda cifrada (XSalsa20-Poly1305), ligada a tu usuario, host y binario — nunca en variables de entorno ni en texto plano.', - 'Un único commit atómico chore: init repository lleva todo el boilerplate — sin historial ruidoso archivo por archivo.', - 'Siete etiquetas estándar forzadas en cada ejecución; la desviación se corrige, no se acumula.', - '--dry-run previsualiza cada cambio sin una sola llamada a la API.', - 'La protección de ramas lee los workflows que ghscaff acaba de commitear y deriva de ellos los checks requeridos — un nombre mal escrito produce una regla que no protege nada, y no avisa; esto elimina esa clase de error de configuración. ghscaff doctor verifica cómo quedó.', - ], - }, - subproject: { - kicker: 'Subproyecto', - name: 'ghscaff-boilerplate', - link: 'https://github.com/UniverLab/ghscaff-boilerplate', - }, - boilerplates: { - kicker: 'Boilerplates', - title: '{n} boilerplates que el asistente puede crear', - cols: { name: 'nombre', language: 'lenguaje', description: 'descripción', version: 'versión' }, - use: 'Elige uno en el paso de lenguaje del asistente:', - }, - faq: [ - ['¿Cómo configura ghScaff un repositorio de GitHub?', - 'Ejecuta ghscaff — un asistente interactivo que crea el repo, commitea el boilerplate (CI, README, licencia), configura branch protection y aplica etiquetas estándar. Un commit atómico, sin pasos manuales.'], - ['¿Por qué ghScaff usa una bóveda encriptada para tokens?', - 'Las variables de entorno con tokens son fácilmente explotables — cualquier proceso en tu máquina puede leerlas. ghScaff encripta tokens con XSalsa20-Poly1305, ligado a tu usuario de OS y hostname. La bóveda evita que ghScaff se convierta en un vector de ataque.'], - ['¿Cómo sabe ghScaff qué checks son requeridos para la protección de ramas?', - 'ghScaff lee los workflows que acaba de commitear y deriva los checks requeridos de los nombres de los jobs. Un nombre mal escrito crea una regla de protección que no protege nada y no lo dice — ghScaff elimina esa clase de error silencioso.'], - ] as [string, string][], - }, - cadspec: { - need: 'Los dibujos CAD no tienen semántica — solo líneas en un lienzo, imposibles de versionar, revisar o automatizar.', - tagline: - 'CAD como código: geometría declarativa compilada de forma determinista a DXF.', - etymology: '(De CAD y el ingl. spec, ‘especificación’.)', - figure: 'Mesa de dibujo que declaraba las piezas en lugar de trazarlas. Se archivó cuando el laboratorio prefirió envolver las mejores herramientas antes que reinventarlas; su idea sigue en las que la reemplazaron.', - thread: { - problem: 'CAD', - method: 'geometría declarativa', - artifact: 'planos que un agente puede verificar', - }, - title: - 'CAD as code: de la declaración al modelo — cadSpec', - description: - 'Geometría declarativa en TOML que puedes revisar en un diff, con preview en el navegador. Compilación determinista: el mismo fuente siempre da el mismo modelo.', - }, - 'astro-denoise': { - need: 'Aplicar una reducción de ruido a una imagen astronómica puede recuperar una galaxia tenue — o inventar una que nunca estuvo ahí. No hay forma estándar y reproducible de saber cuál de las dos.', - tagline: - 'Una propuesta de investigación para benchmarking de reducción de ruido astronómico — evaluada por la ciencia que recupera, no por lo limpia que se vea.', - etymology: '(Del gr. ástron, ‘astro’, y el ingl. denoise, ‘quitar el ruido’.)', - figure: 'Ocular que separa la luz del ruido: entre el grano que deja el sensor y la estrella que de verdad está ahí, una investigación que mide antes de afirmar.', - thread: { - problem: 'reducción de ruido', - method: 'un benchmark puntuado por la ciencia', - artifact: 'métodos comparables', - }, - title: 'Astro Denoise — benchmark de reducción de ruido en DC2', - description: - 'Benchmark reproducible de métodos de reducción de ruido en imágenes DC2 del Vera Rubin Observatory, por recuperación de fuentes débiles, calidad y coste.', - koan: 'El conocimiento de frontera se oculta tras el ruido…', - lede: - 'astro-denoise es una propuesta de investigación para un benchmark modular y reproducible de métodos de reducción de ruido sobre imágenes simuladas del Vera Rubin Observatory (LSST DC2). La idea es directa: cualquier método — un filtro clásico, una red entrenada — se enchufa al mismo protocolo y corre sobre los mismos parches, y se evalúa no por una imagen más limpia sino por lo que le hace a la ciencia: la completitud y la pureza del catálogo de fuentes débiles, contrastadas con el truth catalog de DC2. BM3D y una U-Net son las dos primeras referencias que se están explorando — la plataforma está diseñada para crecer a medida que se añadan más métodos.', - genesis: { - kicker: 'Génesis', - title: 'Una tesis de maestría en curso.', - body: - 'Esto empezó como tesis de maestría y aún está tomando forma. El ecosistema funciona desde julio de 2026: un orquestador con un protocolo común de línea de comandos (imagen FITS de entrada y de salida, metadatos en JSON), una condición de control sin procesamiento y dos métodos empaquetados por separado, BM3D como representante de los métodos clásicos y una U-Net como representante del aprendizaje profundo. Los experimentos corren en abierto, aquí mismo, pero queda trabajo antes de que esto sea un benchmark terminado.', - }, - objectives: { - kicker: 'Objetivos', - thesisTitle: - 'Benchmarking de métodos de reducción de ruido en imágenes simuladas DC2 del Vera Rubin Observatory', - general: - 'Desarrollar una plataforma de benchmarking reproducible que compare métodos de reducción de ruido en imágenes simuladas DC2 del Vera Rubin Observatory, evaluando en cada método la recuperación de fuentes débiles, la calidad de imagen resultante y su coste computacional.', - items: [ - { - state: 'completo', - text: 'Un ecosistema modular y extensible con un protocolo común de línea de comandos, en el que cada método se empaqueta por separado con su propio entorno y un método nuevo se incorpora sin modificar el orquestador.', - }, - { - state: 'en curso', - text: 'Métricas confiables de recuperación de fuentes (completitud, pureza, completitud por magnitud y precisión fotométrica, por cruce uno a uno con el catálogo de verdad de DC2) y de calidad de imagen (PSNR, SSIM y ruido de fondo), cada una verificada contra al menos dos fuentes independientes de la literatura.', - }, - { - state: 'en curso', - text: 'Herramientas que miden el coste computacional de cada método (tiempo por región, memoria máxima y regiones por unidad de tiempo) desde fuera de su proceso, reportadas con la descripción del equipo.', - }, - { - state: 'en curso', - text: 'Los métodos evaluados: BM3D y una U-Net entrenada solo con regiones de DC2 que no están en el conjunto de evaluación, con su receta de entrenamiento y sus pesos publicados.', - }, - { - state: 'pendiente', - text: 'Un benchmark reproducible sobre un conjunto de datos curado y versionado de DESC DC2, con regiones multibanda de varias zonas del cielo; cada versión se identifica por checksums y declara sus regiones, y todo resultado se reporta contra una versión concreta.', - }, - ], - }, - repos: { - kicker: 'Repositorios', - items: [ - { - name: 'UniverLab/astro-denoise', - description: 'El banco: orquestador, métricas científicas y la condición de control.', - url: 'https://github.com/UniverLab/astro-denoise', - }, - { - name: 'UniverLab/astro-denoise-bm3d', - description: 'BM3D, la referencia clásica.', - url: 'https://github.com/UniverLab/astro-denoise-bm3d', - }, - { - name: 'UniverLab/astro-denoise-unet', - description: 'La U-Net, la referencia de aprendizaje profundo.', - url: 'https://github.com/UniverLab/astro-denoise-unet', - }, - ], - }, - proposalCta: 'Leer la propuesta (PDF) ↗', - questions: { - kicker: 'Preguntas abiertas', - items: [ - '¿Puede un denoiser subir la completitud sin dañar la pureza — o inventa fuentes que nunca estuvieron ahí?', - '¿Qué familia de métodos — filtros clásicos o modelos aprendidos — tiene más probabilidad de ayudar a la profundidad de LSST DC2, y cuánto?', - '¿Puede el benchmark completo repetirse con los mismos resultados científicos sobre la misma versión de datos y de código?', - ], - }, - faq: [ - ['¿Qué es astro-denoise?', - 'Un proyecto de investigación que evalúa métodos de reducción de ruido en imágenes simuladas del Observatorio Vera Rubin (LSST DC2). Compara tres condiciones: un control sin procesamiento, BM3D como referencia clásica y una U-Net como referencia de aprendizaje profundo. El objetivo es recuperar galaxias tenues sin inventar las que nunca estuvieron — evaluado por recuperación científica (completitud + pureza), no por calidad visual.'], - ['¿Qué es la reducción de ruido en astronomía?', - 'Las imágenes astronómicas tienen ruido inevitable del detector y del cielo. La reducción de ruido busca bajar ese ruido preservando las fuentes reales. El riesgo: un método puede suavizar galaxias tenues reales o inventar falsas que nunca existieron.'], - ['¿Qué es LSST?', - 'El Legacy Survey of Space and Time — un relevamiento astronómico de 10 años que realizará el Observatorio Vera Rubin en Chile. Fotografiará todo el cielo visible repetidamente, produciendo el catálogo profundo de campo amplio más grande jamás hecho.'], - ['¿Qué es DC2?', - 'Data Challenge 2 — un conjunto de datos simulados que imita lo que LSST producirá, creado por la Dark Energy Science Collaboration (DESC). Incluye ruido realista, PSF y efectos atmosféricos, con un catálogo de verdad que dice exactamente qué fuentes son reales.'], - ] as [string, string][], - bgCredit: 'Carta celeste: Yale Bright Star Catalogue (CDS V/50); líneas de constelaciones de d3-celestial, de Olaf Frohn (BSD-3-Clause).', - }, - 'quorum': { - need: 'El planning poker casi siempre implica un servidor en medio — una cuenta que crear, una sala que hospedar, una herramienta más entre tú y un número.', - tagline: 'Planning poker sin servidor — comparte un enlace, estima juntos, sin registro.', - etymology: '(Del lat. quorum, ‘de los cuales’.)', - figure: 'Mesa de cartas boca abajo: cada quien juega su estimación a la vez y nadie arrastra a los demás; el acuerdo aparece cuando se voltean.', - thread: { - problem: 'estimación', - method: 'una sala peer-to-peer', - artifact: 'desacuerdo visible', - }, - title: - 'Planning poker online gratis, sin registro — Quorum', - description: - 'Planning poker gratis por WebRTC — los peers hablan directamente, no se guarda nada, no hay cuenta que crear. Comparte el link de la sala y estimen juntos.', - koan: '// la estimación ya está en la sala', - bench: 'abre la mesa → quorum.univerlab.org', - lede: - 'Quorum es planning poker sin servidor: cada quien juega una carta, los votos se revelan con una animación cuando todos votan, y el desacuerdo es donde arranca la conversación útil. Corre punto a punto sobre WebRTC — un enlace de sala es toda la app, sin nube, sin cuenta.', - genesis: { - kicker: 'Génesis', - title: 'Un enlace, sin servidor.', - body: - 'Llevaba tiempo con la idea de construir cómputo colaborativo peer-to-peer en el navegador, sin servidores de intermediarios. Antes de escalar la arquitectura, necesitaba validar lo básico — ¿puede una conexión P2P en el browser ser fluida y robusta? Para comprobarlo, construí algo con propósito: planning poker. No porque no existieran herramientas — hay excelentes opciones — sino porque este ritual refleja la esencia del trabajo en equipo: cada participante aporta su estimación en privado, de forma descentralizada y segura. El resultado es Quorum: cero datos saliendo del navegador, sin servidores, sin fricción. Libre, abierto, seguro.', - }, - how: { - kicker: 'Sesión P2P', - items: [ - 'Comparte la URL de la sala — sin registro, sin flujo de invitación.', - 'Elige una carta del mazo Fibonacci (0, 1, 2, 3, 5, 8, 13, 21, ?).', - 'Las cartas se revelan con una animación multi-fase en cuanto todos votan — partículas, slide y flip sincronizados.', - 'Carga historias desde un CSV o agrégalas una a una, avanza por ellas en orden — desconéctate y vuelve a conectar: el estado se sincroniza desde cualquier par que siga en la sala.', - ], - }, - faq: [ - ['¿Es Quorum gratis?', - 'Completamente gratis. Sin cuentas, sin límites de usuarios, sin tier premium. Es un experimento P2P en el navegador — conexiones WebRTC, sin servidor. Abre la URL y empieza a estimar.'], - ['¿Cómo funciona planning poker sin servidor?', - 'Quorum usa conexiones peer-to-peer WebRTC. Tu navegador se conecta directamente a los de tus compañeros — sin nube, sin base de datos. Un tracker de BitTorrent maneja el handshake inicial; después, los datos fluyen directamente entre pares.'], - ['¿Puedo usar Quorum para sprint planning remoto?', - 'Sí. Comparte la URL de la sala con tu equipo. Cada uno elige una carta Fibonacci (0, 1, 2, 3, 5, 8, 13, 21, ?). Las cartas se revelan simultáneamente cuando todos votan — una cuenta regresiva de 3 segundos previene revelaciones prematuras.'], - ['¿Quorum soporta historias de usuario?', - 'Sí. Pega una lista de historias o carga un CSV. Avanza por ellas en orden, estimando cada una. La sesión se mantiene viva si alguien se desconecta — reconéctate y el estado se sincroniza desde cualquier par que siga en la sala.'], - ['¿Qué hace diferente Quorum de planningpokeronline.com?', - 'Sin cuentas, sin límites de usuarios, sin datos almacenados en un servidor. Quorum es open-source y corre completamente en el navegador vía WebRTC. Tus datos de estimación nunca salen de los dispositivos de tu equipo.'], - ] as [string, string][], - }, - 'demostage': { - need: 'Grabar demos a mano es tedioso: errores, ritmo desigual, tiempos muertos y un terminal que expone tu hostname.', - tagline: - 'Demos como Código — captura, graba y exporta demos de terminal reproducibles.', - etymology: '(Del ingl. demo y stage, ‘escenario’.)', - figure: 'Zoótropo de la terminal: cada demostración se escribe como una partitura y se reproduce idéntica, cuadro a cuadro, como el galope que Muybridge descompuso para que por fin pudiera verse.', - thread: { - problem: 'demos de terminal', - method: 'una partitura de eventos', - artifact: 'demos que se vuelven a correr', - }, - title: - 'Demos de terminal reproducibles, como código — DemoStage', - description: - 'Una alternativa a asciinema donde el demo es un archivo, no una toma. Regrábalo tras cada cambio; exporta gif, mp4 o svg — versionado y comparable.', - koan: '// el demo es el código fuente', - lede: - 'DemoStage graba una sesión como eventos, corrige las imperfecciones humanas en un demo.toml limpio — una partitura — y lo compila a gif, mp4 o un svg animado: versionado, re-ejecutable y comparable.', - genesis: { - kicker: 'Génesis', - title: 'Nació construyendo esta misma página.', - body: - 'Al construir esta landing, cada experimento necesitaba un demo — y cada herramienta (asciinema, vhs, OBS, grabar a mano) dejaba algo por fuera: el ritmo, el encuadre, la legibilidad. Entonces el demo dejó de ser un video y pasó a ser un archivo: eventos que puedes podar, ritmar y reproducir. DemoStage graba el resto del laboratorio — incluida la página que estás leyendo.', - }, - pipeline: { - kicker: '5 comandos', - items: [ - 'capture — captura en vivo: graba la sesión, la normaliza en una partitura limpia y un .rec fiel.', - 'focus — cambia la vista en vivo a una o dos fuentes (terminal, repo, docs, localhost): pantalla completa, dividida o apilada, compuesta en el demo.', - 'record — re-ejecuta demo.toml de forma limpia, produciendo una grabación humanizada.', - 'export — reproducción pura: renderiza a gif, mp4 o svg animado sin re-ejecución, con ffmpeg y chromium autoprovisionados.', - 'edit — edita la línea de tiempo de forma interactiva: marca varios pasos y aplica cambios en bloque.', - ], - }, - demos: { - first: 'El tour — un score grabado en un PTY real, exportado a gif y svg desde una sola toma', - svg: 'La misma toma, exportada a svg — 83 KB, fuente incrustada, sin reproductor de video', - svgAlt: 'El tour de DemoStage como SVG animado: un banner impreso en la terminal, el score que lo demuestra, record, export y doctor.', - }, - faq: [ - ['¿Qué es DemoStage?', - 'Una herramienta para planificar y grabar demos multi-fuente — terminal, navegador y archivos en una sola escena. No es solo grabación de pantalla: configuras tipografía, aspect ratio, fps y estilo de terminal. El resultado está optimizado para web.'], - ['¿Puedo re-grabar una demo si algo cambia?', - 'Sí. demostage capture graba eventos, no video. Si la UX cambia, vuelves a capturar y la demo se actualiza de forma determinista — sin necesidad de re-grabar todo manualmente.'], - ['¿En qué se diferencia DemoStage de asciinema?', - 'asciinema graba la salida cruda del terminal. DemoStage graba eventos, soporta múltiples fuentes (terminal + navegador + archivos), normaliza imperfecciones y compila a gif, mp4 o svg animado. El fuente es un archivo TOML versionable.'], - ] as [string, string][], - bgCredit: 'Fondo: Eadweard Muybridge, The Horse in Motion, 1878 (dominio público).', - }, - }, + experiments, manifesto: { kicker: 'Manifiesto', title: 'Pensamiento Cósmico', diff --git a/src/layouts/ExperimentLayout.astro b/src/layouts/ExperimentLayout.astro index 86d317f..6d79331 100644 --- a/src/layouts/ExperimentLayout.astro +++ b/src/layouts/ExperimentLayout.astro @@ -106,7 +106,7 @@ const ogImage = `/og/${id}.png`;
{exp.install && ( -
+
{exp.install && ( <>
diff --git a/src/views/experiments/Canopy.astro b/src/views/experiments/Canopy.astro index 63eda8e..3fe6d0b 100644 --- a/src/views/experiments/Canopy.astro +++ b/src/views/experiments/Canopy.astro @@ -5,6 +5,7 @@ import { byId } from '../../lib/experiments'; import { readFileSync, existsSync, readdirSync } from 'node:fs'; import { resolve } from 'node:path'; import GraphMorph from '../../components/GraphMorph.astro'; +import CataloguePlate from '../../components/CataloguePlate.astro'; import { GEOM, PALETTE, TYPE, edgePath, type Kind } from '../../lib/canopy-graph'; const lang = getLang(Astro.url); @@ -136,7 +137,16 @@ const morphColors: Record = { muse: '#e7b04b', }; --- - + + + {t.common.install} + {e.cta.catalogue} + {e.cta.remote} + + {t.common.repo}
@@ -173,6 +183,40 @@ const morphColors: Record = {
+ +
+ {e.remote.kicker} +

{e.remote.title}

+
+ {e.remote.cols.map(([h, b]) => ( +

{h}

{b}

+ ))} +
+
+ canopy remote pair + +
+ {e.remote.button} +
+ + +
+ {e.catalogue.kicker} + + +
+