mcpField
P
pretext

pretext

github.com/nousresearchクリエイティブ・メディア258.1k317 回インストール今日更新
リポジトリ

pretext について

pretextは、DOMの読み取りなしで複数行テキストを測定・配置するTypeScriptライブラリ兼クリエイティブスキルです。描画前に行や書記素の幾何情報を得て、ブラウザデモやUIに活用できます。

主な機能

  1. 01canvasによるテキストと行幅の測定
  2. 02DOM再レイアウトなしの複数行配置
  3. 03書記素ごとの位置情報
  4. 04行ごとに幅を変える配置
  5. 05rich-inline段落ヘルパー
  6. 06配置データをアニメーションやゲームに活用

活用シーン

  1. 01動く図形を避ける段落の配置
  2. 02テキスト形状を使ったゲームの制作
  3. 03個々の字形のアニメーションや飛散
  4. 04複数行UIの最小寸法の算出

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/nousresearch/hermes-agent --skill pretext

インストール後、Claude Code で/pretextを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
patterns.md
templates/
donut-orbit.html
hello-orb-flow.html

詳細

Pretext のクリエイティブデモ

概要

@chenglou/pretext は、Cheng Lou(React コア、ReasonML、Midjourney)による、依存関係のない 15KB の TypeScript ライブラリで、DOM を使わない複数行テキストの測定とレイアウトを行います。機能は 1 つです。(text, font, width) を指定すると、改行位置、行ごとの幅、書記素ごとの位置、合計の高さを返します。すべてキャンバスで測定し、リフローは発生しません。

一見すると、地味な裏方仕事に思えるかもしれません。しかし、そうではありません。高速で幾何学的に扱えるため、クリエイティブな基本要素になります。60fps で動くスプライトの周囲に段落をリフローしたり、実際の単語でレベルの地形を作るゲームを構築したり、散文の中に ASCII ロゴを動かしたり、書記素ごとの正確な開始位置からテキストを粒子に砕いたり、getBoundingClientRect を何度も呼び出さずに、複数行の UI をぴったり収まる幅にしたりできます。

このスキルは、Hermes がこれを使ってクールなデモを作れるようにするものです。人々が X に投稿するようなデモです。コミュニティのデモ集は pretext.cool と chenglou.me/pretext をご覧ください。

使用する場面

次のような依頼に使用してください。

  • 「pretext のデモ」/「クールな pretext のやつ」/「テキストを X にする」
  • 動く図形の周囲を流れるテキスト(ヒーローセクション、エディトリアルレイアウト、アニメーション付きの長文ページ)
  • 実際の単語や散文を使った ASCII アート風の効果。等幅フォントのラスター画像ではありません
  • プレイフィールド/障害物/ブロックがテキストでできたゲーム(文字のテトリス、散文のブレイクアウト)
  • 書体ごとの物理挙動を使ったキネティックタイポグラフィ(粉砕、散乱、群れ、流れ)
  • タイポグラフィを使ったジェネレーティブアート。特に、非ラテン文字や複数の文字体系が混在する場合
  • 複数行の「シュリンクラップ」UI(テキストが収まる最小のコンテナー幅)
  • 描画前に改行位置を知る必要があるもの

次の用途には使用しないでください。

  • CSS でレイアウトを解決できる静的な SVG/HTML ページ。CSS を使ってください
  • リッチテキストエディターや汎用インライン書式エンジン(pretext は意図的に用途を絞っています)
  • 画像 → テキスト(ascii-art/ascii-video スキルを使ってください)
  • テキストが関与しない純粋なキャンバスのジェネレーティブアート。p5js を使ってください

クリエイティブの基準

これはブラウザー上に描画するビジュアルアートです。Pretext が返すのは数値です。描画するのはあなたです。

  • 「Hello World」デモを公開しないでください。 hello-orb-flow.html のテンプレートはあくまで出発点です。納品するデモにはすべて、意図的な色使い、動き、構図に加え、ユーザーが求めてはいないものの喜ばれるビジュアル上の工夫を 1 つ加えてください。
  • 暗い背景、温かみのある中心、考え抜かれた配色。 定番の黒地にアンバー(CRT/ターミナル)もよいですが、チャコール地に冷たい白(エディトリアル)や、彩度を落としたパステル(リソグラフ)も使えます。どれかを選んだら、最後まで一貫させてください。
  • プロポーショナルフォントこそがポイントです。 Pretext の持ち味は「等幅ではない」ことです。それを活かしてください。Iowan Old Style、Inter、JetBrains Mono、Helvetica Neue、またはバリアブルフォントを使ってください。デフォルトのサンセリフ体は使わないでください。
  • 実際の出典テキストを使い、ダミーテキストは使わないでください。 コーパスには意味があるべきです。短いマニフェスト、詩、実際のソースコード、見つけた文章、ライブラリ自身の README などを使い、lorem ipsum は使わないでください。
  • 最初の描画から完成度を高くしてください。 読み込み中の状態や空白のフレームを表示しないでください。デモは開いた瞬間から公開できる品質である必要があります。

技術スタック

デモごとに、自己完結した HTML ファイルを 1 つ用意します。ビルド工程はありません。

層ツール目的
コアesm.sh CDN 経由の @chenglou/pretextテキストの測定と行レイアウト
レンダリングHTML5 Canvas 2D書記素の描画、フレームごとの合成
分割Intl.Segmenter(組み込み)絵文字/CJK/結合文字に対応した書記素分割
インタラクション生の DOM イベントマウス/タッチ/ホイール。フレームワークは不要
<script type="module">
import {
  prepare, layout,                   // use-case 1: simple height
  prepareWithSegments, layoutWithLines,  // use-case 2a: fixed-width lines
  layoutNextLineRange, materializeLineRange, // use-case 2b: streaming / variable width
  measureLineStats, walkLineRanges,  // stats without string allocation
} from "https://esm.sh/@chenglou/[email protected]";
</script>

バージョンを固定してください。執筆時点では @0.0.6 です。デモの動作がおかしい場合は、最新バージョンを npm で確認してください。

2 つのユースケース

ほとんどすべての用途は、この 2 つの形のどちらかに当てはまります。両方を覚えてください。

ユースケース 1 — 計測してから、CSS/DOM で描画する
const prepared = prepare(text, "16px Inter");
const { height, lineCount } = layout(prepared, 320, 20);

テキストの描画は引き続きブラウザーに任せます。Pretext は DOM を読み取らずに、指定した幅でボックスがどれくらいの高さになるかを教えてくれます。次のような用途に使います。

  • 折り返されるテキストを含む行を扱う仮想化リスト
  • カードの高さを正確にそろえる Masonry
  • 「このラベルは収まるか?」を開発時に確認する
  • リモートのテキストの読み込み時にレイアウトシフトを防ぐ

font と letterSpacing は CSS と完全に同期させてください。 Canvas の ctx.font 形式(例:"16px Inter"、"500 17px 'JetBrains Mono'")は、描画される CSS と一致させる必要があります。一致しないと計測にずれが生じます。

ユースケース 2 — 計測して、自分で描画する
const prepared = prepareWithSegments(text, FONT);
const { lines } = layoutWithLines(prepared, 320, 26);
for (let i = 0; i < lines.length; i++) {
  ctx.fillText(lines[i].text, 0, i * 26);
}

ここから創造的な作業が始まります。描画を自分で行うため、次のことができます。

  • Canvas、SVG、WebGL、または任意の座標系に描画する
  • グリフごとの変換(回転、ジッター、拡大縮小、不透明度)を適用する
  • 行のメタデータ(幅、書記素の位置)をジオメトリとして使う

行ごとに幅が異なるフロー(図形の周囲に回り込むテキスト、ドーナツ状の帯の中のテキスト、矩形ではない段組み内のテキスト)には、次を使います。

let cursor = { segmentIndex: 0, graphemeIndex: 0 };
let y = 0;
while (true) {
  const lineWidth = widthAtY(y);  // your function: how wide is the corridor at this y?
  const range = layoutNextLineRange(prepared, cursor, lineWidth);
  if (!range) break;
  const line = materializeLineRange(prepared, range);
  ctx.fillText(line.text, leftEdgeAtY(y), y);
  cursor = range.end;
  y += lineHeight;
}

これは、このライブラリ全体で最も重要なパターンです。「ドラッグしたスプライトの周囲にテキストを回り込ませる」を実現するもので、X で話題になったデモにも使われています。

知っておくと便利なヘルパー
  • measureLineStats(prepared, maxWidth) → { lineCount, maxLineWidth } — 最も幅の広い行、つまり複数行テキストを縮めて囲む際の幅です。
  • walkLineRanges(prepared, maxWidth, callback) — 文字列を割り当てずに行を反復処理します。文字が不要で、書記素に関する統計や物理演算を行う場合に使います。
  • @chenglou/pretext/rich-inline — フォント、チップ、メンションが混在する段落向けの同じシステムです。サブパスからインポートします。

デモのレシピパターン

コミュニティのコーパス(references/patterns.md を参照)には、いくつかの有力なパターンがまとめられています。どれかを選んで発展させましょう。依頼がない限り、新しいカテゴリを作らないでください。

パターン主な APIアイデアの例
障害物の周囲でリフローlayoutNextLineRange + 行ごとの幅関数ドラッグしたカーソルスプライトを避けて折り返すエディトリアル段落
ジオメトリとしてのテキストゲームlayoutWithLines + 行ごとの衝突矩形計測した単語が各ブリックになるブロック崩し
粉砕/パーティクルwalkLineRanges → 書記素ごとの (x,y) → 物理演算クリックすると文字に分かれて爆発する文
ASCII 障害物タイポグラフィlayoutNextLineRange + 行ごとに計測した障害物の範囲ビットマップ ASCII ロゴ、形状の変形、実際のジオメトリに合わせてテキストを開かせるドラッグ可能なワイヤーオブジェクト
エディトリアルな複数段組みlayoutNextLineRange を段ごとに使用 + 共通カーソル引用文を配したアニメーション雑誌見開き
キネティックタイポグラフィlayoutWithLines + 時間経過に伴う行ごとの変換Star Wars のクロール、波、バウンス、グリッチ
複数行テキストの縮約measureLineStats最も小さいコンテナーに合わせて自動サイズ調整する引用カード

動作する単一ファイルのスターターについては、templates/donut-orbit.html と templates/hello-orb-flow.html を参照してください。

ワークフロー

  1. ユーザーの依頼に基づいて、上の表からパターンを選びます。
  2. テンプレートから始めます。
    • templates/hello-orb-flow.html — 動く球体の周囲でテキストがリフローする例(障害物の周囲でリフローするパターン)
    • templates/donut-orbit.html — 発展的な例:計測済み ASCII ロゴの障害物、ドラッグ可能なワイヤー球体/立方体、変形する形状フィールド、選択可能な DOM テキスト、開発時専用コントロール
    • write_file を使い、~/.hermes/cache/scratch/ またはユーザーのワークスペースに新しい .html ファイルを作成します。
  3. 依頼内容に合った、意図のあるコンテンツに差し替えます。実際の文章を 10〜100 文使用し、lorem は使わないでください。
  4. フォント、配色、構図、インタラクションを調整します。ここが作業の肝です。省略しないでください。
  5. ローカルで確認します。
    cd <dir-with-html> && python -m http.server 8765
    # then open http://localhost:8765/<file>.html
    
  6. コンソールを確認します。prepareWithSegments に不正なフォント文字列を渡すと pretext はエラーをスローします。Intl.Segmenter はすべての最新ブラウザーで利用できます。
  7. コードだけでなく、ユーザーにファイルのパスを伝えます。ユーザーはファイルを開きたいからです。

パフォーマンスに関する注意点

  • prepare()/prepareWithSegments() は処理コストの高い呼び出しです。テキストとフォントの組み合わせごとに一度だけ実行してください。ハンドルはキャッシュします。
  • リサイズ時は layout()/layoutWithLines() のみを再実行し、再準備はしないでください。
  • テキストは変わらずジオメトリだけが変化するフレーム単位のアニメーションでは、通常の長さの段落なら、layoutNextLineRange をタイトなループで毎秒60フレーム実行しても十分軽量です。
  • フレームごとに ASCII マスクを描画する場合は、セルバッファー(Uint8Array/型付き配列)を保持し、セルまたは投影したジオメトリから行ごとの測定済み障害物範囲を算出して範囲をマージし、テキストを描画する前にその範囲を layoutNextLineRange に渡します。
  • ビジュアルアニメーションとレイアウトアニメーションを連動させてください。球体が立方体に変形する場合は、同じ値を使って、描画するセルバッファーと障害物範囲の両方をトゥイーンします。そうしないと、デモが物理的にリフローしているのではなく、表面に描き足しているように見えます。
  • フェードには、グリフの強度や障害物のスケールを変えるのではなく、レイヤーの不透明度を使うのがおすすめです。一時的な ASCII スプライトは独自のキャンバスに配置し、CSS/GSAP の不透明度でキャンバスをフェードさせると、ジオメトリが縮んで見えることはありません。
  • Canvas の ctx.font の設定は意外に時間がかかります。フォントが変わらない場合は、fillText の呼び出しごとではなく、フレームごとに一度だけ設定してください。

よくある落とし穴

  1. CSS とキャンバスのフォント文字列がずれる。 ctx.font = "16px Inter" で測定していても、CSS が font-family: Inter, sans-serif; font-size: 16px なら問題ありません。ただし、Inter が読み込まれている場合に限ります。Inter が 404 になると、CSS は sans-serif にフォールバックし、測定値が 5~20%ずれます。フォントを必ず preload するか、ウェブセーフなフォントファミリーを使用してください。

  2. アニメーションループ内で再準備する。 軽量なのは layout* だけです。フレームごとに prepare を再呼び出しすると、パフォーマンスが大幅に低下します。準備済みハンドルはモジュールスコープに保持してください。

  3. グラフェム分割に Intl.Segmenter を使い忘れる。 絵文字、結合文字、CJK では、"é".split("") は2文字を返します。個々の表示グリフをサンプリングするときは、new Intl.Segmenter(undefined, { granularity: "grapheme" }) を使用してください。

  4. extraWidth を指定せずに break: 'never' のチップを使う。 rich-inline でアトミックなチップ/メンションに break: 'never' を使う場合、ピルのパディング分の extraWidth も指定する必要があります。そうしないと、チップの装飾がコンテナーからはみ出します。

  5. TypeScript 専用エントリーポイントの @chenglou/pretext を unpkg から使う。 esm.sh を使ってください。TypeScript のエクスポートがブラウザー対応の ESM に自動でコンパイルされます。unpkg では 404 になるか、生の TS が配信されます。

  6. 等幅フォントへのフォールバックで、肝心な点が知らないうちに失われる。 等幅に見える出力が表示される場合、CSS の font-family が monospace にフォールバックしていることがよくあります。DevTools で実際に描画されているフォントを確認してください。

  7. 形状を避けてテキストを流し込む際に、幅を調整せず行をスキップする。 その行の通路が行全体を収めるには狭すぎる場合、layoutNextLineRange に極端に小さな maxWidth を渡すのではなく、行をスキップしてください(y += lineHeight; continue;)。pretext は1つのグラフェムだけの行を返し、表示が崩れてしまいます。

  8. 作り込みが足りないデモを公開する。 デフォルトの初回描画はチュートリアル程度の見栄えです。ビネット、控えめな走査線、アイドル時の自動モーション、慎重に選んだインタラクション(ドラッグ、ホバー、スクロール、クリック)を加えてください。これらがないと、「かっこいい pretext のデモ」が「README をそのまま再現したインターンの成果物」に見えてしまいます。

検証チェックリスト

  • デモは自己完結した単一の .html ファイルで、ダブルクリックまたは python -m http.server で開ける
  • @chenglou/pretext はバージョンを固定して esm.sh 経由でインポートしている
  • 使用する文章は Lorem Ipsum ではなく実際の文章で、デモのコンセプトに合っている
  • prepare に渡すフォント文字列が CSS のフォントと完全に一致している
  • prepare()/prepareWithSegments() はフレームごとではなく、一度だけ呼び出している
  • 暗い背景とよく考えられた配色を使っており、デフォルトの白いキャンバスではない
  • 少なくとも1つのインタラクション(ドラッグ/ホバー/スクロール/クリック)またはアイドル時の自動モーションがある
  • python -m http.server を使ってローカルでテストし、コンソールエラーがないことを確認している
  • ミドルレンジのノート PC で毎秒60フレームを維持できる(または、適切なフォールバックについて説明している)
  • ユーザーに頼まれていない「ひと手間」の工夫がある

参考:コミュニティのデモ

インスピレーションやパターンの参考に、これらをクローンしてください(すべてMITに近いライセンスで、pretext.coolからリンクされています)。

  • Pretext Breaker — 単語ブロックのブレイクアウト — github.com/rinesh/pretext-breaker
  • Tetris × Pretext — github.com/shinichimochizuki/tetris-pretext
  • ドラゴンのアニメーション — github.com/qtakmalay/PreTextExperiments
  • Somnaiのエディトリアルエンジン — github.com/somnai-dreams/pretext-demos
  • Bad Apple!! ASCII — github.com/frmlinn/bad-apple-pretext
  • ドラッグ可能なスプライトのリフロー — github.com/dokobot/pretext-demo
  • Alarmyのエディトリアル時計 — github.com/SmisLee/alarmy-pretext-demo

公式プレイグラウンド:chenglou.me/pretext — accordion、bubbles、dynamic-layout、editorial-engine、justification-comparison、masonry、markdown-chat、rich-note。

コメント