OWworkout — アプリ解説資料
パワーリフティング特化のトレーニングログ(PWA) 版: v16 / 2026-08
この資料は2つの読者を想定しています。
- パワーリフター・利用者の方 → 1章・2章・3章・9章 をお読みください。何ができるアプリかが分かります
- エンジニア・開発者の方 → 4章以降。構成・データモデル・計測アルゴリズム・テスト方針を書いています
1. これは何か
iPhone のホーム画面に置いて使う、パワーリフティング専用のトレーニング記録アプリです。 ブラウザで動く PWA(Progressive Web App)ですが、起動すると全画面になり、 通信が無くても記録・グラフ・計測がすべて動きます。
何を解決しようとしているか
一般的なトレーニングログアプリは「記録できる」で止まります。 このアプリが目指しているのは、その先の「次に何をするか」が楽になるところです。
価値を5段階に分けて考えています。
| 段階 | 内容 | 状態 |
|---|---|---|
| ① 記録 | セット・重量・回数・RPE を残す | 実装済み |
| ② 効率 | 入力が速い。ルーチンから自動で埋まる | 実装済み |
| ③ 分析 | 推定1RM・ボリューム・バリエーション比・バー速度 | 実装済み |
| ④ 判断 | 「次は何kgでいくか」を根拠つきで出す | 実装済み(v15) |
| ⑤ コーチング | 期分け・ピーキング・試技選択 | 未実装 |
③の一部・④・⑤ が、汎用アプリとの差別化点です。 ①②は「あって当たり前」なので、速く・確実に動くことだけを目標にしています。
他アプリとの違い(要点)
- バー速度をカメラで測れる(外部センサー不要・追加課金なし)
- 競技種目とバリエーションの関係をデータとして持つ(ポーズSQ ÷ 競技SQ など)
- 弱点の判定を「一般的な目安」ではなく自分の平常値との差で行う
- AI に渡すためのテキスト書き出しが一級市民(1日単位で出せる)
- 依存ライブラリ・CDN・アカウント登録・サーバー送信がいっさい無い
2. 何ができるか(画面ごと)
ホーム
- 今日の予定 — ルーチンで組んだその日のメニューが出ます。押すとその内容でワークアウトを開始します
- 今日の体重 — 1タップで記録
- 直近4週 — 実施日数とセット数
ワークアウト(記録中)
トレーニング中に一番長く見る画面です。
- 種目を追加し、重量・回数・RPE を入力してセットを記録
- RPE はチップで選択(6 / 6.5 / 7 … 10)。数値キーボードを出しません
- ルーチンの目標があれば、次のセットの重量・回数があらかじめ入っています
- 休憩時間は自動で記録されます(前のセットを記録した時刻との差)
- プレート計算 — 「片側 25+20+2.5」のように表示
- バー速度の計測 — カメラボタンから撮影、または撮影済み動画を読み込み
- 本日の推定1RM — 推定式と RPE 換算の両方
- 次のセットの推奨重量(後述のオートアジャスト)
- 次回のこの枠は◯kg — その日の結果から次セッションの提案
履歴
日ごとの一覧。各日から 「この日を AI に渡す」 で、その日だけを書き出せます。
分析
- 体重の推移(実測 + 7日移動平均)
- 推定1RM の推移(推定式・RPE換算の2系列)
- 競技力(推定) — SQ/BP/DL の推定1RMからトータルを出し、DOTS / IPF GL ポイントと階級を表示
- バリエーション比 — 競技種目に対する各バリエーションの比と、自分の平常値からのズレ
- ボリューム — lift(SQ/BP/DL)単位の週次ボリューム。主働/補助の内訳つき
ルーチン
- 曜日ごとのメニューを組む(種目・重量・回数・セット数・目標RPE)
- 実施日をずらす(今回だけ) — 「今週だけ月曜のメニューを日曜に」といった変更ができます。 ルーチン本体の曜日設定は変わりません(±3日の範囲)
設定
1RM推定式の選択 / 性別(DOTS計算用) / 種目の分類編集 / カメラ計測の補正係数・プレート直径 / 種目ごとのMVT / エクスポート(期間指定・AI用テキスト・CSV・JSON) / 過去7世代からの復元 / インポート / 保存状態の確認 / アプリの再読み込み
3. 設計の考え方(5つの原則)
(1) ジムで確実に動くこと 電波・ログイン・サーバーに依存しません。全機能が端末内で完結します。
(2) 入力の速さは機能である セット記録は「重量・回数・RPEを押す」だけ。予定値の事前入力、休憩時間の自動記録、 RPEのチップ選択は、すべて「入力の手数を減らす」ためのものです。
(3) 根拠を必ず添える 「145kgを推奨」だけでは信用できません。「推定1RM 182.5kg → @7で5レップなら145kg」 のように計算過程を画面に出します。判断するのは本人です。
(4) 分からないときは黙る RPE が無ければ推奨を出しません。データが足りなければ「粗い推定です」と書きます。 それらしい数字を作らないことを優先しています。
(5) データは本人のもの サーバーに送りません。JSON で完全に持ち出せます。 アプリが無くなっても記録が読める形(CSV / Markdown)で出せます。
4. アーキテクチャ
全体像
iPhone(ホーム画面アプリ)
└ index.html ─ 起動・全CSS・タブ骨組み
├ js/lib.js 純粋関数(1RM推定・DOTS・オートアジャスト・書き出し整形)
├ js/store.js データ層(IndexedDB + localStorage、全ドメイン操作)
├ js/ui.js 画面生成とイベント(文字列でHTMLを組み、イベントは委譲)
├ js/chart.js SVGの折れ線グラフ(自前)
├ js/velocity-cam.js カメラ計測の画面と解析
├ js/tracker.js 多点オプティカルフロー
├ js/detect.js プレート(円)の検出
└ sw.js サービスワーカー(オフライン用キャッシュ)
数字
| 項目 | 値 |
|---|---|
| アプリ本体 | 約 7,900 行(JS + HTML + SW) |
| テスト | 約 1,900 行 / 10スイート / 347件 |
| 外部依存 | 0(npm パッケージ・CDN・フォント・解析ツールすべて無し) |
| ビルド | 不要(素の ES Modules。ファイルを置けば動く) |
| 通信 | 起動時のアプリ本体取得のみ。データ送信は一切なし |
なぜ依存ゼロか
3つ理由があります。
- ジムで動かなくなる要因を減らす。 CDN が落ちればアプリが起動しません
- 10年後も動く。 ビルドツールの寿命はアプリの寿命より短い
- 1人が全部読める規模に収まる。 依存が無ければ、挙動の説明はすべてこのリポジトリ内で完結します
UI の方式
仮想DOMを使いません。状態 → HTML文字列 → innerHTML の単純な再描画です。
イベントは document へのデリゲーションで、data-act="..." を1箇所の switch で処理します。
// ui.js の中心構造
function render() {
switch (route.name) {
case 'home': html = homeScreen(); break;
case 'workout': html = workoutScreen(route.workoutId); break;
...
}
}
document.addEventListener('click', (e) => {
const act = e.target.closest('[data-act]')?.dataset.act;
switch (act) { case 'add-set': ... }
});
画面の規模(数百ノード)では再描画のコストが問題にならず、 「今の状態からこの画面になる」が1関数で読み切れる利点のほうが大きいためです。
5. データモデル
全データを 1つの JSON オブジェクトとしてメモリに載せ、配列操作で扱います。 1年分でも約1MBに収まるため、インデックスや部分読み込みは要りません。
{
schema: 1,
exercises: [], // 種目マスタ(プリセット37 + 自作)
workouts: [], // 1日1件のトレーニング
workoutExercises: [], // その日にやった種目
sets: [], // セット1本ずつ
bodyweights: [], // 体重
routines: [], // ルーチン(複数可・1つがアクティブ)
routineDays: [], // 曜日別メニュー
routineItems: [], // メニューの中の種目と目標
scheduleOverrides: [], // 「今週だけこの日に移す」
settings: {},
}
全レコードが id (UUID) / updated_at / deleted_at(論理削除)を持ちます。
将来サーバー同期を足せる形にしてあり、いまは使っていません。
セット1本が持つ情報
{
id, workout_exercise_id, set_no,
weight, reps, rpe,
rep_velocities: [0.32, 0.29, 0.26, 0.23], // レップごとの平均速度
rep_traces: [...], // 軌跡(後の局面分析のため保存)
planned_weight, planned_reps, planned_rpe, // 記録時点のルーチン目標
velocity, velocity_source, // 'camera' | 'manual'
is_warmup, rest_sec, // 休憩は自動計算
e1rm_formula, e1rm_rpe, // 記録時の式で確定して保存
created_at, updated_at, deleted_at,
}
設計上の判断が2つあります。
- 推定1RMは記録時に確定して保存する。 あとで推定式を変えても、過去の記録の値は動きません。 履歴の一貫性を優先しています
- 予定値をセットに焼き付ける。 ルーチンを後から編集しても、「その日に何を予定していたか」が残ります
6. 保存の仕組み
二重書き込み
実際に起きた事故が設計の理由です。 登録したルーチンが翌日に消えていました。 原因は、IndexedDB への書き込みが非同期であることです。 iOS はホーム画面に戻した瞬間にページを止めることがあり、書き込みが完了しません。
対策として、保存を二重にしています。
| 経路 | 性質 | 役割 |
|---|---|---|
| localStorage | 同期。その場で必ず完了する | 取りこぼしゼロの保険(4MB上限) |
| IndexedDB | 非同期・容量が大きい | 本体(200msまとめ書き) |
起動時に両方を読み、saved_at が新しい方を採用します。
日次スナップショット
その日の最初の保存時に、1日1世代・最大7世代のスナップショットを IndexedDB に残します。 誤操作の翌日でも、設定画面から前日以前の状態に戻せます。
消えないようにする
navigator.storage.persist() でブラウザの自動削除から保護を要求します。
ホーム画面に追加したアプリでは許可されやすい傾向があります。
重要な運用上の注意 iOS ではホーム画面のアイコンを削除すると記録データごと消えます。 アイコンは URL への近道にすぎず、置き直さなくてもアプリの更新は届きます。 アイコンを消して追加し直す操作は絶対に行わないでください。
7. カメラによるバー速度計測
外部センサー(加速度計・エンコーダ)を使わず、iPhoneのカメラだけでバー速度を測ります。
パイプライン
① プレートに円を合わせる(画面上でドラッグ/自動検出)
└ 円の直径 = 実寸450mm。これが px→m の換算になる
↓
② 円の中から「追いやすい点」を数十個選ぶ
└ コーナー性の高い点。tracker.js
↓
③ ピラミッド型 Lucas-Kanade オプティカルフローで毎フレーム追う
└ 各点の移動量の【中央値】を採る
1点が外れても全体は壊れない。模様の乏しいラバープレート、
照明変化、手ブレに耐えるのはこのため
↓
④ 縦位置の時系列から挙上(コンセントリック)区間を切り出す
↓
⑤ レップごとに 変位 ÷ 所要時間 = 平均速度
+ 可動域を12分割した速度プロファイルを保存
平均速度の定義(ここが精度の要)
挙上の始点・終点を「速度がしきい値を超えた瞬間」で決めるとノイズに非常に弱い。 速度は位置の差分なので、位置のわずかな揺れが増幅されるためです。
そこで境界を 「位置が挙上距離の 2% / 98% を通過した時刻」 で決めています。
平均速度 = 挙上距離の96% ÷ (2%地点から98%地点までの時間)
位置そのものを見るのでノイズに強くなります。 両端の遅い部分を外すため、全区間平均より高めに出ます (理想的な半正弦なら1.172倍、最小ジャークなら1.283倍)。
ラックアップ・歩行をレップとして数えない
スクワットやベンチでは、実際のレップの前後に別の上下動が入ります。 ラックアップは上に挙がるのでレップに見え、1本混ざるとセット平均が丸ごと狂います。
見分ける手がかりは2つです。
- 本当のレップは、挙げる前に必ず同じくらい下ろしている。 直前4秒以内に挙上距離の50%以上の下降が無いものは除外
- 歩行は横に動く。 挙上距離に対して横の移動が60%を超えるものは除外
デッドリフトは床から挙げるので1レップ目の前に下降がありません。 その場合は条件1を外します(画面の「レップの型」で切り替え)。
実機での検証
同種の動画解析アプリ Metric と、同一のベンチプレス4レップで突き合わせました。
| レップ1 | レップ2 | レップ3 | レップ4 | |
|---|---|---|---|---|
| 可動域(本アプリ) | 17.1cm | 19.1cm | 21.5cm | 20.5cm |
| 可動域(Metric) | 17cm | 19cm | 21cm | 20cm |
| 速度(本アプリ) | 0.179 | 0.189 | 0.182 | 0.107 |
| 速度(Metric) | 0.18 | 0.21 | 0.18 | 0.10 |
可動域は3%以内、セット平均速度は1.9%以内で一致しました。
この結果を受けて補正係数 calib は 1.0(補正なし)にしています。
当初は 0.816 を掛けていましたが、それは過補正でした。
なお、この検証に使った実機の軌跡データは
tools/fixtures/bench-115kg.json として固定し、回帰テストに組み込んであります。
正直に書いておくこと
プレートの自動検出は、条件によっては失敗します。 ラバープレートは周囲との輝度差が5〜20階調しかないことがあり、 その場合はタオルや器具の縁を誤検出します。
古典的な画像処理の範囲では解決できないと判断し、 手動で合わせる操作を最良にする方向に舵を切りました。
- 撮影前の画面でも動画が全画面(縦)で見える
- 円は直径をドラッグして合わせられる(中心移動だけでは合わない)
- 記録開始前に検出結果を確認できる
自動検出は「うまくいけば手間が省ける補助」の位置づけです。
8. パワーリフティング特化のデータ設計
種目に3つの属性を持たせる
汎用アプリは種目を「名前とカテゴリ」でしか持ちません。 それでは「ポーズスクワットが弱い」を言えません。そこで各種目に3つ付けています。
| 属性 | 値 | 用途 |
|---|---|---|
| lift | SQ / BP / DL / なし |
どの競技種目に紐づくか。ボリューム集計・比の分母 |
| role | competition / variation / accessory |
比を取る対象かの区別 |
| emphasis | bottom / mid / top / form / control |
弱点がどこかを言い当てる鍵 |
プリセットは37種目。すべてに分類が付いています。設定画面から本人が変更できます。
{ name: 'ポーズスクワット', lift: 'SQ', role: 'variation',
emphasis: 'bottom', ref_ratio: 0.885 }
{ name: 'ラックプル', lift: 'DL', role: 'variation',
emphasis: 'top', ref_ratio: 1.15 }
競技種目は lift ごとに複数あってよい
デッドリフトのコンベンショナルとスモウのように、どちらも競技として成立する スタイルがあるためです。推定トータルには、その lift の競技種目のうち最も高い値を使います。
判定は「一般値」ではなく「自分の平常値」に対して行う
バリエーション比の一般的な目安は世に出回っていますが、個人差が非常に大きいので、 それを基準に「弱い/強い」と判定するのは危険です。
あなたのポーズSQ ÷ 競技SQ が、過去1年ずっと 0.88 前後で推移していた
↓
直近8週で 0.82 まで落ちた
↓
「ボトムの出力が相対的に落ちている」と根拠つきで言える
variationRatios() は 直近56日 と それより前の365日(重なりのない期間)を比較します。
表に載せている参考比は、データが貯まるまでの初期値にすぎません。
これは「大腿四頭筋が弱い」といった筋名の断定ではなく、動作局面の特定です。 計測で言える範囲はそこまで、という線を引いています。
9. オートアジャスト(次は何kgか)
v15 の中心機能です。LLM も通信も使いません。ただの計算です。
考え方
実施したセット(重量・回数・実際のRPE)から今日の推定1RMを出し、 そこから「予定していた回数を、予定していたRPEでこなせる重量」を逆算します。
予定 150kg × 5 @7
実際 150kg × 5 @8.5
→ 今日の推定1RM 182.5kg
→ @7 で 5レップなら 145kg
→ 次のセットは 145kg を推奨(-5kg)
逆に @6 で終われば 152.5kg を推奨します。
安全のための決まりごと
- RPE が無ければ判断しない(当てずっぽうを出さない)
- 1回の調整は ±10% まで。RPEひとつのブレで大きく動かさない
- 丸めは控えめな側へ。 下げるときは切り上げ、上げるときは切り捨てる。 つまり調整幅は必ず計算値より小さくなります
export function roundToward(v, base, step = 2.5) {
if (v < base) return Math.ceil(v / step) * step; // 下げるときは切り上げ
if (v > base) return Math.floor(v / step) * step; // 上げるときは切り捨て
return v;
}
- 高レップ(8超)や低RPE(6未満)は RPE から1RMを起こす精度が落ちるため、 「この推定は粗めです」と添えます
- 次セッションの提案は、1本のRPEに引きずられないよう 各セットの推定1RMの中央値で判断します
主観だけに頼らない
RPE は主観なので、その日の気分で上下します。 同じ重量で測ったバー速度は主観が入らないので、裏づけとして併記します。
同じ 150kg でのバー速度: 平常 0.34 → 今日 0.28 m/s(-18%・過去5セットとの比較)
主観だけでなく速度も落ちています。
velocityReadiness() は、同じ重量±3%・過去180日・別セッションの working set から
速度の中央値を取って比較します。両方が同じ向きなら確度が上がり、食い違えばそれも分かります。
出るところ
- セット記録の直後 — 次のセットの推奨と根拠。「この重量を入れる」で入力欄に入ります
- 種目カード — その日の結果から「次回のこの枠は◯kg」
- AIに渡すテキスト — 1日ぶんの書き出しに「次回案」が入ります
10. 分析と指標
推定1RM
4つの式(Epley / Brzycki / Lombardi / O'Conner)から選べます。
加えて RPE換算(RIR = 10 - RPE)でも計算し、両方をグラフに出します。
複数式を使う場合は中央値を採ります(外れ値に引きずられないため)。
競技力
SQ/BP/DL それぞれの競技種目の推定1RMからトータルを出し、 DOTS と IPF GL ポイント、階級を表示します。係数は公式のものを実装しています。
const DOTS_COEF = {
male: [-307.75076, 24.0900756, -0.1918759221, 0.0007391293, -0.000001093],
female: [ -57.96288, 13.6175032, -0.1126655495, 0.0005158568, -0.0000010706],
};
ボリューム
lift 単位で週次集計します(週の始まりは月曜)。 主働と補助を分けて表示するので、「SQ 12.4t(うち補助 2.1t)」のように読めます。 ピーキングやブロック設計は lift 単位で見るものなので、この集計単位が前提になります。
疲労の手がかり
- RPEの傾き — 同一重量の連続セットで RPE がどう上がったか
- 速度低下率 — セット内の1レップ目と最終レップの比
- 予定と実績の差 —
planVsActual() - 休憩時間 — 自動記録されているので、後から相関を見られます
11. エクスポートとAI連携
AI にトレーニング内容を渡してメニューを組んでもらう使い方を、主要な用途として設計しています。
3つの形式
| 形式 | 用途 |
|---|---|
| AI に渡すテキスト(Markdown) | AIへの入力。読み方の凡例つき |
| CSV | 表計算ソフトでの集計 |
| JSON | 完全バックアップ・復元 |
期間指定
「全期間」「直近90日」だけでは足りません。実際の使い方はこうです。
AI に組んでもらったメニューを実施 → その日の結果を RPE と速度つきで報告 → 次のルーチンをもらう
そのため 1日単位で出せます。履歴タブの各日から「この日を AI に渡す」、 または設定画面で開始日=終了日にします。 「最後の日/今日/7日/30日/90日/全期間」のショートカットもあります。
出力の例
## 読み方
145kg×4 RPE8 [予定@7] v0.28 (0.27/0.34/0.25/0.27) 休3分 W
→ 重量×回数 RPE 予定RPE 平均速度 (レップ毎速度) 休憩 W=ウォームアップ
### 2026-08-17
ポーズスクワット
137.5kg×4 RPE7 v0.28 (0.32/0.29/0.26/0.23) 休4分
...
次回案: 140kg(推定1RM 172.5kg → @7×4)
iOS での配信方法
書き出しは画面上のシート(コピー/共有/ダウンロード)で行います。
これは実機の不具合対応の結果です。iOS の Web Share API は text/csv を受け取れず、
共有するとファイル名1行だけのテキストが出来てしまいました。
中身を画面に出してコピーさせるのが最も確実なので、その経路を主にしています。
インポート
JSON を読み込むと現在のデータを完全に置き換えます。
BOM 付きのファイルも読めるようにしてあります(JSON.parse(json.replace(/^/, '').trim()))。
「書き出せた」だけでは検証になりません。 実機から出てきたバックアップそのものを
tools/fixtures/backup-real.json として固定し、それで実際に復元できることを
自動テストで毎回確かめています。
12. 更新の仕組み
サービスワーカーは キャッシュ優先 + 裏で更新 です。 ジムで開いたときの起動が最速になり、電波があるときに新しい版を取り込みます。
skipWaiting() はしません。使用中に中身が入れ替わると入力の途中で挙動が変わるため、
新しい版は待機させ、画面下の案内を本人が押したときに引き継ぎます。
デプロイ時は次の2箇所を必ず上げます(片方だけだと更新が届きません)。
sw.jsのconst VERSIONindex.htmlのwindow.APP_VERSION
更新が届かないときのために、「アプリ本体を読み込み直す」ボタンがあります。 キャッシュを捨てて取り直すだけで、記録・ルーチン・設定は消えません。
13. テスト
347件 / 10スイート。 純粋関数の単体テストと、実際のブラウザ(Playwright)での E2E を併用しています。
| スイート | 件数 | 内容 |
|---|---|---|
test-logic.js |
96 | 1RM推定・DOTS/GL・オートアジャスト・書き出し整形 |
test-ui-e2e.js |
70 | 画面操作の一通り |
test-velocity.js |
63 | 速度解析(合成波形+境界条件) |
test-camera-e2e.js |
38 | カメラ画面(疑似映像を流し込む) |
test-backup-e2e.js |
31 | 実機バックアップからの復元・書き出しの中身 |
test-real-trace.js |
12 | 実機の軌跡 vs Metric の実測値 |
test-update-e2e.js |
12 | サービスワーカーの更新 |
test-detect.js |
11 | 円検出・半径の精密化 |
test-persistence-e2e.js |
8 | 二重保存・スナップショット復元 |
test-force-refresh-e2e.js |
6 | 強制再読み込み |
方針
- 実機で起きた不具合は、必ず実機のデータを固定して回帰テストにする。
bench-115kg.json(軌跡 + Metricの実測値)とbackup-real.json(実機バックアップ)がそれです - 速度解析は合成波形(正弦・最小ジャーク)で理論値と突き合わせる
- カメラの E2E は Playwright の
--use-file-for-fake-video-captureに numpy + ffmpeg で作った合成動画を流し込む - 更新テストはバージョン非依存(
sw.jsから現在値を読む)。 版を上げるたびにテストを直す必要がない
14. まだ実装していないこと
意図的に後回しにしているものです。
| 項目 | 理由 |
|---|---|
| スティッキングポイントの検出 | レップ内の速度プロファイル(12分割)の保存は v13 から開始済み。判定に足るデータが貯まるのを待っています。仕組みより先にデータを溜めるのが順序として正しいため |
| 試合に向けたピーキング | 試合日 + 試技選択(第1試技90% / 第2試技95% / 第3試技100%+)。設計はあるが未着手 |
| 減量・計量の計画 | 同上 |
| クラウド同期・複数端末 | データモデルは同期可能な形(UUID + updated_at + 論理削除)にしてある。必要になってから足す |
| AIコーチの自動連携 | 手動でのコピー&ペースト連携を採用。API キーの管理・通信・費用を持ち込まずに同じ価値が出せるため |
| 完全な自動プレート検出 | 前述のとおり古典的手法の限界。手動操作の質を上げる方向を選択 |
15. 用語集
| 用語 | 意味 |
|---|---|
| RPE | 主観的運動強度。10 = これ以上挙げられない、8 = あと2回できた |
| RIR | 余力レップ数。RIR = 10 - RPE |
| 推定1RM (e1RM) | 1回だけ挙げられる最大重量の推定値 |
| MVT | 最小挙上速度。その人がその種目で1RMを挙げるときのバー速度。1RMの速度推定の基準 |
| VBT | Velocity Based Training。バー速度を指標に負荷を決める方法 |
| ROM | 可動域(Range of Motion) |
| DOTS / IPF GL | 体重で補正して選手の強さを比較する点数 |
| lift | 競技3種目(SQ = スクワット / BP = ベンチプレス / DL = デッドリフト) |
| role | 種目の位置づけ(競技 / バリエーション / 補助) |
| emphasis | その種目が効く動作局面(ボトム / 中間 / ロックアウト / フォーム / 制御) |
| PWA | ブラウザ技術で作り、ホーム画面に置いてアプリのように使えるもの |
OWworkout v16 — 依存ゼロ・オフライン動作・データは端末内のみ