Problem
既存の記録アプリには、3 つの摩擦がある。
筋トレの記録は「重量 × 回数 × セット数」を高速に入れ続ける作業で、1 セット間の 数秒のひっかかり が集中力を切ってしまう。既存アプリを使い込むほど、摩擦の正体が 3 つあることが見えてきた。
入力摩擦 — OS キーボードが立ち上がるたびに画面がジャンプし、タップ位置がずれる。片手では操作しづらい。
電波依存 — ジムの地下や混雑時、サーバー依存のアプリは同期やログインで簡単に詰まる。
サブスク疲れ — 月額課金モデルが主流で、使用中に挟まる広告がストレスだった。
Approach
Local-First × BYOS × 入力経路の一元化。
IndexedDB を Dexie.js 経由でプライマリストレージに据え、ネットワークを前提から外した。
バックアップはユーザー自身の Google Drive に置き、BYOS (Bring Your Own Storage) として LWW + Tombstone で衝突解決する。運営側にデータが一切流れないまま、端末間同期が成立する構成。
入力側は OS キーボードを完全に排除し、カスタムテンキー (Numpad) を自前実装。全数値入力を uiStore.openNumpad() の 1 本に集約して、フォーカス喪失・ビューポート移動・二重送信をまとめて抑えた。
負荷提案は RPE (自覚的運動強度) を入力として、次セットの重量・回数を自動算出する Progressive Overload エンジンを内蔵。入力の手間を最小化するため、過去ログからプリフィルも行う。
カスタムテンキー (Numpad)
OS キーボードを完全排除する専用ボトムシート。aria-modal + フォーカストラップ付きで、汗ばんだ指でも取りこぼさない。
Smart Prefill
前回セッションの値をワンタップで再利用。新セット追加時は直前セットをコピーし、タップ数を限界まで削減。
Progressive Overload エンジン
RPE ベースで次セットの負荷を自動算出。「重量↑ / レップ↑ / 維持 / ディロード」の 4 状態で判定する。
アナリティクス
ContributionCalendar・MuscleHeatmap・VolumeChart で継続状況と部位別ボリュームを可視化。
Rest Timer
セット間の休憩を自動計測。バックグラウンド動作・通知対応で、タイマーを気にせず次セットへ。
BYOS 同期 (Google Drive)
任意機能。運営サーバーを経由せず、ユーザー自身の Drive に保存。LWW + Tombstone で競合解決する。
完全オフライン対応
PWA としてインストール可能。Service Worker + IndexedDB により、地下ジムでもゼロレイテンシで動作。
Result
電波ゼロでも "書ける・提案される" 状態へ。
ネットワーク依存を前提から外したことで、ジムでの体験が根本的に安定した。Google Drive バックアップはオプションのまま置かれ、使わない人でも一切困らない設計に。サーバー運用コストは引き続きゼロ。
最新版は nextrep.c12o.net で公開中。
Learnings
作ってみて分かったこと。
"オフライン対応" は機能名ではなく、前提
後から載せるのはほぼ不可能に近い。
最初にネットワーク依存をコードベースから追い出しておくと、その後の判断が簡単になる。
入力経路を 1 本に集約する
モバイル Web の最大ハマりどころは「入力」。
Numpad に一元化したことで、フォーカス喪失・ビューポート移動・二重送信といった細部のバグがまとめて消えた。
LWW は「弱い一貫性で十分」というドメイン判断とセット
筋トレ記録のように 1 ユーザー 1 デバイス同時使用が前提のドメインなら、CRDT を持ち込まずとも Tombstone + LWW で実用的に成立する。
どこまで割り切れるかの線引きが設計の要だった。
プライバシーをデフォルトにする設計
Google Drive を "自分が鍵を持つ保管庫" として扱う BYOS 構成は、SubCutter とも共通する思想。
アプリを跨いで同じ感覚で設計できるのは発見だった。