書くことから届けることまで、ツールはひとつ ── 執筆・プレビュー・発表・PDF出力・公開まですべて peitho で完結。途中でツールを切り替えない。
内容は自分の手で、デザインはAIに任せる ── 書くのはMarkdownだけ。デザインは平文のHTML/CSSでLLMが生成しやすく、人間はgitのdiffとレビューで舵を取れる。
内容とデザインは分離する ── 内容はMarkdown、デザインはレイアウトHTMLとテーマCSS。混ぜない。
バージョン管理できるデザイン ── レイアウトもテーマも平文のHTML/CSS。diffが取れて、レビューできる。
型付きスロット契約 ── 内容がレイアウトに合わないとき、こっそり捨てられることはなく、行番号付きのビルドエラーで止まる。
書く ── 内容は deck.md に Markdown で書く。デザインはレイアウトHTMLとテーマCSSに閉じ込め、Markdownには一切漏らさない。
プレビューする ── peitho preview が保存のたびにビルドし、ブラウザをリロードする。
発表する ── peitho present が外部ディスプレイにスライドを、手元マシンに発表者ビューを配置する。
PeithoはHomebrewで配布しています。レイアウトも、ベーステーマも、発表者シェルも全部バイナリに埋め込まれているので、Node.jsもnpmもランタイム依存はありません。deck.mdが1枚あれば、どのディレクトリでも始められます。
brew install mizzy/tap/peitho
スライドは --- で区切ります。最も浅い見出しがタイトル、フェンス付きコードブロックがコード、それ以外は本文 ── それぞれが対応するスロットに落ちていきます。スロット自体はレイアウトHTMLが宣言していて、これは「デザインする」パートで詳しく見ます。
# 最初のスライド 本文の段落。 - 箇条書きも - 使えます --- # 次のスライド
デッキ先頭のYAMLでは7つのキーを受け付けます: time、aspect_ratio、resolution、layouts、css、syntaxes、fonts。省略した項目はデッキ隣接の同名ディレクトリ、次に組み込みのデフォルトへとフォールバックします。存在しないパスや未知のキーは行番号付きのビルドエラーになります。
--- time: 23m # 発表予定時間 aspect_ratio: 16:9 # 16:9 か 4:3 layouts: ./layouts # レイアウトHTML(ファイルかディレクトリ) css: ./css # テーマCSS(ファイルかディレクトリ) syntaxes: ./syntaxes # 追加のsublime-syntax定義 fonts: ./fonts # 同梱するwebフォント ---
スライドの先頭にある <!-- {...} --> がそのスライドのページ設定です。layout は名前でレイアウトを指定、key はスライドにCSSで狙える安定した識別子を付け、section と time はアジェンダのセクションを宣言します。
<!-- {"key":"arch-1","layout":"code-demo","section":"詳細","time":"5m"} -->
# アーキテクチャ
JSONでないHTMLコメントは、そのスライドの発表者ノートになります。複数書くと連結されます。ノートは発表者ビューにだけ乗り、公開する dist/ には決して漏れません。
# 締めのスライド <!-- ここで一呼吸置いて質問を受ける。 --> <!-- デモが死んでいたら録画に切り替える。 -->
画像1枚だけの段落を書くと、Peithoは accepts="image" スロットを持つレイアウトへスライドを振り分けます。画像はデッキ相対のローカルパスで、拡張子は png、jpg、jpeg、gif、webp に限られます。リモートURLや絶対パス、親ディレクトリ脱出はビルドエラーです。

ページ設定コメントで section を宣言すると、そのスライドから次の宣言までがひとつのアジェンダセクションになり、time はそのセクションの持ち時間です。合計がフロントマターの time と合わなければ、行番号付きのビルドエラーになります。発表中は、発表者ビューが計画と実測を並べて見せてくれます。
---
time: 15m
---
<!-- {"section":"準備","time":"3m"} -->
# 準備
---
<!-- {"section":"本題","time":"12m"} -->
# 本題
デザインの単位はレイアウト ── 平文のHTMLファイルです。<slot> 要素がスロットの名前、受け付ける型、個数を宣言し、Peithoはビルド時にそれを契約として読みます。layouts/ ディレクトリをデッキの隣に置けば自動で拾われます。
<section class="peitho-slide"> <h1><slot name="title" accepts="inline" arity="1"></slot></h1> <slot name="body" accepts="blocks" arity="0..*"></slot> <slot name="code" accepts="code" arity="0..1"></slot> </section>
明示指定が最強 ── ページ設定に {"layout":"cover"} があればそれを使う。未知の名前は候補一覧つきビルドエラー。
1個しかなければ、いつもそれ ── 契約違反は通常どおりエラーになります。
それ以外は型駆動ディスパッチ ── スライドの中身の形と各レイアウトのスロット契約を突き合わせる。一致がちょうど1件のときだけ通り、0件や複数件はビルドエラーです。
スロット過不足、型の食い違い、存在しないキーを狙ったCSS、合計がずれたセクション時間 ── どれも行番号付きのビルドエラーで止まります。こっそり捨てられることはありません。
error: slide 2 ('code-slide'), line 7: slot 'code' got 2 item(s),
but layout 'title-body-code' allows 0..1
= help: use a layout with more code capacity or remove one code block
key を宣言したスライドはHTMLで data-slide-key 属性を持つので、CSSでそのスライドだけを狙えます。タイトルを直してもCSSは壊れず、存在しないkeyを狙ったセレクタはビルドエラーで捕まえられます。
<!-- {"key":"arch-1"} -->
# アーキテクチャ
peitho preview はデッキとレイアウトとCSSを監視します。保存のたびにリビルドしブラウザをリロードしますが、表示中のスライドとオーバービュー状態はリロード後も維持されるので、いま調整しているスライドが視界から消えることはありません。
o、Enter、Escで単一スライドとタイル表示を切り替えられます。タイル表示中は矢印キーでグリッド移動、クリックかEnterでそのスライドが開きます。全体像の確認とジャンプの両方に使えます。
peitho present は外部ディスプレイにフルスクリーンでスライドを、手元マシンには発表者ビューを配置します。現在スライド、次スライド、発表者ノート、タイマー、セクション別の計画対実測がひとつの画面に載ります。スペースでタイマー開始、Escで全部閉じます。
peitho present --host は /remote をLAN(TailscaleのようなVPNがあれば優先)で公開し、ターミナルにQRコードを表示、ポートは 6173 で固定するのでURLは毎回同じです。SafariでQRを一度読み、共有シートから「ホーム画面に追加」をタップすると、次からはSafariのアドレスバーなしでリモコンが立ち上がります。
peitho present --host
PDF出力はコマンド1つです。公開は peitho publish が dist/ を検品したあと、-- の右に置いたあなたのデプロイコマンドへ処理を渡します。Peithoはデプロイを再発明しません。
peitho export pdf -o deck.pdf peitho publish -- aws s3 sync dist/ s3://your-bucket/