Project Plateau
Project Plateau: four glass plates, one living valley, and the road back to camp.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗小学 2 年生が、学校で九九が始まる前に全部言える状態になることを目的とした PWA。
報酬をアプリの中に置かない。教室で「これ知ってる」となる体験を報酬とする。
設計の方針と判断の根拠は docs/design-notes.md を参照。
ストーリーモードの設計と判断の根拠は docs/story-mode-design.md。
ランクと見た目の判断は docs/rank-design.md。
子どもが話を注文する仕組みの判断は docs/story-authoring-design.md。
話の追加手順は docs/story-prompt.md(親が LLM に投げる雛形)。
| 項目 | 状態 |
|---|---|
| モードの切り替え(たたかう / ぼうけん) | 実装済み |
| 九九のタイムアタック(10 問) | 実装済み |
| 段ごとの解放(順序は設定で変更可能) | 実装済み |
| 問題の音声読み上げ | 実装済み |
| 親子で同じ問題セット。親の記録が常に表示 | 実装済み |
| 段ごとの遅さ・誤答の可視化(親のみ) | 実装済み |
| 問題文・世界観の差し替え | 実装済み(JSON 4 テーマ) |
| 親子で端末が分かれた場合の記録・設定の同期 | 実装済み・実プロジェクトで動作確認ずみ |
| ストーリーモード(街・散策・道の選択・戦い) | 実装済み(2 エリア同梱・各 720 通り) |
| タイムに応じたランク(段ごとに目標が違う) | 実装済み |
| 習熟度による段の解放 | 未実装(設計のみ) |
| 子どもが話を注文して作ってもらう仕組み | 実装済み(要 Supabase。生成は親の Claude) |
| プッシュ通知 | 未実装 |
npm install
npm run dev # http://localhost:3000
npm run build # 本番ビルド
npm run typecheck # 型チェック
npm test # 単体テスト
npm run layout-check # ホームが端末の高さに収まっているかを 6 サイズで確認
npm run stories # 注文された話を作る道具(手順は docs/story-task.md)
npm run icons # アイコン PNG の再生成
ブラウザのブックマークでは開かれない。ホーム画面に置くこと。
親の端末で、デプロイ先の URL から親画面を開き、「べつの端末とつなぐ」の 「子どもの端末で開く QR を出す」を押す。子どもの端末のカメラで読み取ると、アプリが開く。
# 以降)に入れており、サーバへは送られない。
読み取った側では、読み込み直後にアドレスバーから消すアイコンと名称は意図的にゲーム側へ振っている。 「勉強」「ドリル」「学習」という語は画面のどこにも出していない。
src/
app/
page.tsx ホーム。相手の選択と、親の記録の常時表示
play/ タイムアタック本体
story/ ストーリーモード
parent/ 親専用画面(段の解放、弱点、着せ替え)
join/ 親画面の QR から開く入口。合言葉を受け取る
manifest.ts PWA マニフェスト
components/
PlayScreen.tsx 出題・判定・計測
Result.tsx 結果。答えをタップで聞き返せる
StoryScreen.tsx 街・散策・道の選択の進行
StoryBattle.tsx ストーリー中の戦い
StageInsights.tsx 子どもの状態(親のみ)
RankBadge.tsx ランクの表示
ParentGate.tsx 親画面の入口
lib/
kuku.ts 出題の組み立てと重み付け、入力判定
rank.ts 段ごとのランクのしきい値と判定
yomi.ts 九九の唱え方テーブル
speech.ts Web Speech API のラッパ
themes.ts 着せ替えの読み込み
story.ts ストーリーの型と検証
stories.ts 散策の組み立てと読み込み
storage/ 保存層(local / supabase)
data/themes/*.json テーマ定義
data/stories/*.json ストーリー
実装を変更する際は以下を壊さないこと。いずれも設計メモ上の判断に対応する。
lib/kuku.ts の weightOf)にのみ反映する。.app[data-fit] と .scrollArea)。角を斜めに落とした HUD(アーケード筐体の読み方)で統一している。
判断の根拠は docs/rank-design.md の判断 7。
clip-path による切り欠き)・発光・地の質感・量の刻み方で出すarcade(シアン)だが、緑・紫・青でも成立する触るときに踏みやすい点が 3 つある。
clip-path は box-shadow を切り落とす。 角を落とした要素の外側の光は
filter: drop-shadow() で出すことbody::before / body::after)は z-index: 0、中身は 1。
body に不透明な背景を戻すと地の格子が隠れる(描画順の問題).panel の切り欠きは 14px ある。
答えの札のような小さい箱には .factCell のように専用の値を持たせるタイムでゴールド・シルバー・ブロンズが付く。判断の根拠は docs/rank-design.md。
src/lib/rank.ts)ブロンズの境目が 1 分を超えないことは単体テストで固定してある。 「1 回 1 分で終わる長さ」をランクの境目が壊さないため。
ホームは外枠を画面の高さに固定し(.app[data-fit='true'])、
あふれる可能性があるのは中身(.scrollArea)だけにしてある。
開いて最初に押す「はじめる」が画面の外に出ないことを構造で保証するため。
画面の高さに応じた圧縮を 3 段(790px / 620px / 580px 以下)置いている。 レイアウトを変えたら 1 つの画面サイズで確かめて終わりにしないこと。 320×568 から 430×932 までで、次が崩れていないことを確認する。
目視では分からないので npm run layout-check で測る。
npm run build && npm start -- -p 3210 &
npm run layout-check
崩れていたら終了コード 1 と、どのサイズでどの条件が落ちたかを出す。
Chromium の場所が違う場合は CHROMIUM_PATH で指定する。
このアプリで刺さったのは「話を読むこと」ではなく「話を作らせること」だった。 実機で確認したところ、同梱の話は使われていなかった。元々 AI に話を作らせて遊んでいた子なので、 楽しんでいたのは注文する側であって、出来上がったものを読む側ではなかった。
設計メモは最初からそう書いていた。
報酬は「話の続きが読めること」ではなく「自分の考えた世界が動いていること」。
判断の根拠は docs/story-authoring-design.md。
子どもが注文する → Supabase に残る → 親の Claude が作る →「ぼうけん」に並ぶ
(アプリ) (status: ordered) (スケジュールタスク) (status: ready)
LLM を API として呼ばない。 話を書くのは、スケジュールタスクを実行している Claude 自身である。Anthropic の API は従量課金で、claude.ai のサブスクリプションとは 別請求になる。子どもが注文するたびに課金される構造は、家庭用のアプリには重い。
アプリ側は注文を置くだけで、生成には一切関わらない。生成を叩ける URL も存在しない。
publish が検査に通らなければ書き込まない。
読む側(アプリ)も読み込んだ話を毎回検査し、通らないものは遊べる一覧に出さない。
Supabase は手でも触れる以上、壊れた話が子どもの画面に出る経路を残さないdocs/story-prompt.md から実行時に読む。 親が手で LLM に投げるときと
同じ文面を使う。二重に持つと必ず片方が古くなるfailed として残し、
子どもの画面に「うまく できなかったみたい」と出す。
注文が消えたように見えると、次を注文しなくなるThis is the beginning of the README. Read the rest at the original. Read the full README ↗