Files
time/ventures/mdecks/index.html
T
teahubxyzandClaude Opus 5.5 d2e4d755b6 mdecks: a slide too long even at 70% is cut into slides that fit
A slide that doesn't fit at 70% is cut into the fewest slides that do: between lines, never right after a heading, as evenly as it can, and at a heading, a blank line or a change between list and text when that costs little evenness. Each new slide repeats the title with (cont.). Cuts are measured in a hidden frame showing the copied deck at 1280x720, so the preview and the copy have the same slides on any screen. The note under the preview says which slides were cut, in grey; it turns yellow only for a line longer than a whole slide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYHXFvWMCs1GUmRmpySqQ6
2026-10-15 02:15:23 -07:00

509 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>.mdeck — plain text in, clean slides out</title>
<meta name="description" content="Type markdown-ish slides, watch a clean navigable deck render live, copy it as one self-contained HTML file. Free, runs entirely in your browser.">
<style>
:root {
--bg: #0f1115;
--panel: #171a21;
--panel-2: #1e222b;
--ink: #e8eaf0;
--ink-dim: #9aa1af;
--accent: #6ea8fe;
--accent-2: #9d7bff;
--line: #2a2f3a;
--radius: 12px;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--bg);
color: var(--ink);
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
padding: 18px 20px 10px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
header .left {
display: flex;
align-items: baseline;
gap: 12px;
}
header h1 {
margin: 0;
font-size: 1.35rem;
letter-spacing: 0.2px;
}
header h1 .dot { color: var(--accent); }
header p {
margin: 0;
color: var(--ink-dim);
font-size: 0.9rem;
max-width: 62ch;
}
header .cta {
background: linear-gradient(135deg, var(--accent), var(--accent-2));
color: #0b0d12;
font-weight: 600;
border: none;
border-radius: 9px;
padding: 8px 16px;
font-size: 0.85rem;
text-decoration: none;
cursor: pointer;
white-space: nowrap;
flex-shrink: 0;
}
header .cta:hover { filter: brightness(1.08); }
.early-note {
margin: 6px 0 0;
font-size: 0.75rem;
color: var(--ink-dim);
grid-column: 1 / -1;
}
main {
flex: 1;
display: grid;
grid-template-columns: minmax(280px, 420px) 1fr;
gap: 14px;
padding: 12px 20px 16px;
min-height: 0;
}
.editor-pane {
display: flex;
flex-direction: column;
min-height: 0;
}
.editor-pane label {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-dim);
margin-bottom: 6px;
}
textarea {
flex: 1;
min-height: 220px;
resize: none;
background: var(--panel);
color: var(--ink);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 14px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.9rem;
line-height: 1.55;
outline: none;
}
textarea:focus { border-color: var(--accent); }
.hint {
margin-top: 8px;
font-size: 0.78rem;
color: var(--ink-dim);
line-height: 1.5;
}
.hint code {
background: var(--panel-2);
border: 1px solid var(--line);
border-radius: 5px;
padding: 1px 5px;
font-size: 0.75rem;
}
.stage {
display: flex;
flex-direction: column;
min-height: 0;
}
.toolbar {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
flex-wrap: wrap;
}
button {
background: var(--panel-2);
color: var(--ink);
border: 1px solid var(--line);
border-radius: 9px;
padding: 8px 14px;
font-size: 0.85rem;
cursor: pointer;
touch-action: manipulation;
}
button:hover { border-color: var(--accent); }
button:disabled { opacity: 0.4; cursor: default; }
button.primary {
background: linear-gradient(135deg, var(--accent), var(--accent-2));
color: #0b0d12;
font-weight: 600;
border: none;
}
.counter {
margin-left: auto;
font-variant-numeric: tabular-nums;
color: var(--ink-dim);
font-size: 0.85rem;
}
.progress {
height: 5px;
background: var(--panel-2);
border-radius: 99px;
overflow: hidden;
margin-bottom: 10px;
}
.progress > div {
height: 100%;
width: 0;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
transition: width 180ms ease;
}
.slide-wrap {
flex: 1;
min-height: 0;
border: 1px solid var(--line);
border-radius: var(--radius);
background:
radial-gradient(1200px 500px at 80% -10%, rgba(110,168,254,0.10), transparent),
radial-gradient(900px 500px at 0% 110%, rgba(157,123,255,0.10), transparent),
var(--panel);
overflow-y: auto;
touch-action: pan-y;
}
.slide {
--fit: 1;
padding: clamp(22px, 5vw, 56px);
min-height: 100%;
overflow-wrap: anywhere;
display: flex;
flex-direction: column;
justify-content: center;
}
.slide h1 {
font-size: calc(clamp(1.6rem, 4.5vw, 3rem) * var(--fit));
margin: 0 0 0.5em;
line-height: 1.15;
}
.slide h2 {
font-size: calc(clamp(1.2rem, 3vw, 1.9rem) * var(--fit));
margin: 0 0 0.6em;
color: var(--ink-dim);
font-weight: 500;
}
.slide ul {
margin: 0;
padding-left: 1.3em;
font-size: calc(clamp(1rem, 2.2vw, 1.35rem) * var(--fit));
line-height: 1.7;
}
.slide p {
font-size: calc(clamp(1rem, 2.2vw, 1.35rem) * var(--fit));
line-height: 1.7;
margin: 0.3em 0;
}
.slide a {
color: var(--accent);
text-decoration: none;
}
.slide a:hover {
text-decoration: underline;
}
.slide .empty { color: var(--ink-dim); font-style: italic; }
.slide code, .export-note code {
background: rgba(110,168,254,0.12);
border-radius: 5px;
padding: 1px 6px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.9em;
}
.fit-note {
font-size: 0.78rem;
color: var(--ink-dim);
margin-top: 8px;
}
.fit-note.warn { color: #f2c46d; }
.export-note {
font-size: 0.78rem;
color: var(--ink-dim);
margin-top: 8px;
}
section {
padding: 40px 20px;
max-width: 1200px;
margin: 0 auto;
}
section h2 {
margin: 0 0 20px;
font-size: 1.5rem;
}
.steps {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.step {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 20px;
}
.step h3 {
margin: 0 0 10px;
font-size: 1.1rem;
color: var(--accent);
}
.repos {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.repo {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 20px;
}
.repo h3 {
margin: 0 0 8px;
font-size: 1.1rem;
}
.repo h3 a {
color: var(--accent);
text-decoration: none;
}
.repo h3 a:hover {
text-decoration: underline;
}
.stars {
font-size: 0.9rem;
color: var(--ink-dim);
margin: 0 0 12px;
}
.stars b {
color: var(--accent);
}
.tiers {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 30px;
}
.tier {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 25px;
}
.tier h3 {
margin: 0 0 15px;
font-size: 1.3rem;
}
.tier .price {
font-size: 1.8rem;
margin: 0 0 15px;
}
.tier ul {
padding-left: 1.3em;
margin: 0 0 20px;
}
.tier .btn {
display: inline-block;
text-decoration: none;
text-align: center;
width: 100%;
}
.nothing-charged {
text-align: center;
font-style: italic;
color: var(--ink-dim);
margin: 20px 0 0;
}
.limits {
padding-left: 1.3em;
color: var(--ink-dim);
}
.limits li {
margin-bottom: 10px;
}
footer {
padding: 20px;
text-align: center;
color: var(--ink-dim);
font-size: 0.9rem;
border-top: 1px solid var(--line);
}
footer a {
color: var(--accent);
text-decoration: none;
}
footer a:hover {
text-decoration: underline;
}
@media (max-width: 820px) {
main {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
textarea { min-height: 150px; flex: none; }
.slide-wrap { min-height: 300px; }
.steps, .repos {
grid-template-columns: 1fr;
}
.tiers {
grid-template-columns: 1fr;
}
header {
flex-direction: column;
align-items: flex-start;
}
header .left {
flex-direction: column;
}
}
/* finish: a one-line header, the product as the whole first screen, and the sections flowing after it (never over it) */
html, body { height: auto; }
body { display: block; }
header { flex-direction: row; align-items: center; gap: 6px 14px; padding: 12px 20px; border-bottom: 1px solid var(--line); }
header h1 { font-size: 1.4rem; letter-spacing: -0.01em; }
header .promise { margin: 0; color: var(--ink-dim); font-size: 0.9rem; }
header .early-note { margin: 0 0 0 auto; font-size: 0.75rem; color: var(--ink-dim); }
main { flex: none; height: calc(100svh - 60px); min-height: 460px; }
main section { padding: 0; max-width: none; margin: 0; }
.hint code { white-space: nowrap; }
@media (max-width: 820px) {
header { padding: 10px 16px; }
header .early-note { margin: 0; flex: 1 1 150px; }
main { height: auto; min-height: 0; grid-template-rows: auto auto; padding: 12px 16px 16px; }
}
</style>
</head>
<body>
<header>
<h1><span class="dot">.</span>mdeck</h1>
<p class="promise">Plain text in, slides out.</p>
<p class="early-note">Opens a public GitHub issue. No email, no payment.</p>
<a class="cta" href="https://teahub.xyz/teahubxyz/time/issues/new?template=early-access.yml&title=Early+access%3A+.mdeck&venture=mdecks&plan=Pro" target="_blank" rel="noopener">Get early access</a>
</header>
<main id="try">
<section class="editor-pane">
<label for="src">Slides</label>
<textarea id="src" spellcheck="false"></textarea>
<p class="hint">
<code># Title</code> heading · <code>## Subtitle</code> · <code>- bullet</code> point ·
<code>---</code> on its own line starts a new slide · inline <code>**bold**</code>, <code>*italic*</code>, <code>`code`</code>.
Plain lines become paragraphs.
</p>
</section>
<section class="stage">
<div class="toolbar">
<button id="prev" aria-label="Previous slide">&larr; Prev</button>
<button id="next" aria-label="Next slide">Next &rarr;</button>
<button id="copy" class="primary">Copy deck as HTML</button>
<span class="counter" id="counter">1 / 1</span>
</div>
<div class="progress" role="progressbar" aria-label="Deck progress"><div id="bar"></div></div>
<div class="slide-wrap" id="wrap"><article class="slide" id="slide"></article></div><p class="fit-note" id="fitnote" role="status" hidden></p>
<p class="export-note">The copied file is a self-contained deck: open it in any browser and use &larr; / &rarr; to present.</p>
</section>
</main>
<section id="how">
<h2>How it works</h2>
<div class="steps">
<div class="step">
<h3>Type your slides</h3>
<p>Markdown-ish text in the editor: headings, bullets, paragraphs, <code>---</code> between slides. The syntax is the stuff you already type.</p>
</div>
<div class="step">
<h3>Watch it set live</h3>
<p>Every keystroke re-typesets the deck on the right. Navigate with arrow keys or swipe; the progress bar keeps your place.</p>
</div>
<div class="step">
<h3>Take the file with you</h3>
<p>One button copies the deck as a single self-contained HTML file. Open it anywhere, present it offline, email it, done.</p>
</div>
</div>
</section>
<section id="why">
<h2>Why this exists</h2>
<div class="repos">
<div class="repo">
<h3><a href="https://github.com/storytold/wordcraft" target="_blank" rel="noopener">storytold/wordcraft</a></h3>
<div class="stars">★ <b>5,496</b> stars</div>
<p>Proof that people want prose in, polished output out — no canvas-dragging required.</p>
</div>
<div class="repo">
<h3><a href="https://github.com/storytold/gridcraft" target="_blank" rel="noopener">storytold/gridcraft</a></h3>
<div class="stars">★ <b>2,749</b> stars</div>
<p>Structured text becoming layout: the same bet .mdeck makes for slides.</p>
</div>
<div class="repo">
<h3><a href="https://github.com/storytold/cadcraft" target="_blank" rel="noopener">storytold/cadcraft</a></h3>
<div class="stars">★ <b>2,552</b> stars</div>
<p>Even geometry is going plain-text. Your weekly status deck can too.</p>
</div>
</div>
</section>
<section id="pricing">
<h2>Pricing</h2>
<div class="tiers">
<div class="tier">
<h3>Free</h3>
<div class="price">$0</div>
<p>For anyone who needs a quick deck right now.</p>
<ul>
<li>Full plain-text editor</li>
<li>Live navigable preview with arrow keys and progress bar</li>
<li>One-click copy of the deck as self-contained HTML</li>
<li>Runs entirely in the browser; nothing is saved or sent.</li>
</ul>
<a class="cta btn" href="#try">Use it free</a>
</div>
<div class="tier">
<h3>Pro</h3>
<div class="price">$5/mo founding price</div>
<p>For people who make a deck every week and want it saved and polished.</p>
<ul>
<li>Saved decks, themes, speaker notes</li>
<li>PDF export, shareable links</li>
<li>Plain text in, always.</li>
</ul>
<a class="cta btn" href="https://teahub.xyz/teahubxyz/time/issues/new?template=early-access.yml&title=Early+access%3A+.mdeck&venture=mdecks&plan=Pro" target="_blank" rel="noopener">Join early access</a>
</div>
</div>
<p class="nothing-charged">Nothing is charged today.</p>
</section>
<section id="limits">
<h2>Honest limits</h2>
<ul class="limits">
<li>Nothing is saved — close the tab and your text is gone.</li>
<li>One layout and one typeface; no themes or branding.</li>
<li>No images, tables, or nested lists yet.</li>
<li>No speaker notes, presenter view, or PDF export (that is the Pro idea, if enough people ask for it).</li>
<li>The copy button needs a modern browser with clipboard access.</li>
</ul>
</section>
<footer>
<p>Built by <a href="https://teahub.xyz/teahubxyz/time" target="_blank" rel="noopener">Time, an agent-run venture studio on GitHub</a></p>
<p><a href="https://teahub.xyz/teahubxyz/time/issues/13" target="_blank" rel="noopener">Want the next version? 👍 the signal issue</a></p>
</footer>
<script src="app.js"></script>
</body>
</html>