尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证食べたものを写真で残し、食べすぎたかを○×で振り返る食事日記PWA(Next.js / TypeScript / IndexedDB)
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗写真とかんたんな記録を日付ごとに残し、月のカレンダーで振り返るスマホ向けの日記アプリを、共通の土台(packages/journal/)の上に2つ作りました。
どちらも記録は端末内(IndexedDB)にだけ保存し、サーバーには送りません。ホーム画面に追加してアプリのように使えます(PWA)。
| アプリ | 内容 | デモ |
|---|---|---|
食事日記(apps/meal/) | 食べたものを写真で残し、その日に「食べすぎたか」を ○ / × で付ける。1日1件 | https://meal-diary-sable.vercel.app |
釣果ノート(apps/fishing/) | 釣行ごとに釣果・釣り場・天気・潮・風・仕掛けを記録する。1日に複数件 | https://fishing-log-chi.vercel.app |
スマホで開き、ホーム画面に追加して使うのがおすすめです。
| 分類 | 使用技術 |
|---|---|
| フレームワーク | Next.js 16(App Router)、React 19、TypeScript |
| スタイル | SCSS(CSS Modules)、デザイントークン(primitive / semantic の2層) |
| データ保存 | IndexedDB(Dexie.js 4、dexie-react-hooks) |
| テスト | Vitest、fake-indexeddb、Playwright(画面と画像処理の検証) |
| ホスティング | Vercel |
汎用的な部分(packages/journal/)とアプリに固有の部分(apps/meal/、apps/fishing/)を分け、1つのリポジトリで複数のアプリを管理する構成(npm workspaces)にしています。釣果ノートは、この土台の上に後から作りました。
Entry<TFields> 型で表し、アプリ固有の項目は型引数で渡します。食事日記では { overate: boolean | null }、釣果ノートでは釣り場・天気・潮・風・仕掛けと、魚種ごとの釣果の配列ですgetBadge 関数で、その日の記録の一覧から印(○× や合計匹数)を作って渡します。1日1件の食事日記も、1日に複数件の釣果ノートも同じ部品で表示できますJournalRepository インターフェースだけです。将来クラウド保存(Supabase)に移るときは、その実装を差し替えるだけで済みます保存ボタンは置かず、入力が止まって0.5秒たったら保存します(useDebouncedSave)。画面を離れるときと、アプリが裏に回ったとき(visibilitychange)は待たずに保存するので、入力の途中でアプリを閉じても消えません。釣果ノートの匹数・サイズ・風速は、入力中は文字列のまま持ち、保存するときに数値に直します。数値で持つと、「23.」のような入力の途中の値が「23」に戻ってしまうためです。
createdAt / updatedAt で持ちます。将来クラウドと同期するときに、ID が衝突せず、どちらが新しいかを比べられますundefined ではなく null で表します。値が入っていない状態と、項目そのものがない状態を区別するためです撮った写真は createImageBitmap で読み込み、canvas に描き直してから JPEG で保存します。描き直した画素だけが残るので、位置情報などの EXIF は一緒に保存されません。回転情報は描画の前に反映されるため、向きもずれません。この挙動は、位置情報と回転情報を埋め込んだ画像を使って実際のブラウザで確かめています。
日付は "YYYY-MM-DD" のローカル日付で扱います。toISOString() は UTC で計算するため、日本時間の朝9時より前に記録すると前日の日付になってしまいます。これを避けるためです。「今日」はサーバー(UTC・ビルド時刻)では決めず、端末側で useSyncExternalStore を使って求めます。
| 起きた問題 | 対応 |
|---|---|
| メモを入力してキーボードを閉じると、画面が押し上げられたまま戻らない | body に overflow: hidden を付けて内側だけスクロールさせる作りをやめ、ページ全体をスクロールさせる。タブバーと操作バーは sticky で画面下に固定する |
| 16px 未満の入力欄にフォーカスすると、画面が拡大される | 入力欄の文字は 16px 以上にする |
capture 属性を付けると、アルバムから選べなくなる | 「撮る」(capture あり)と「選ぶ」(capture なし)でボタンを分ける |
| Safari とホーム画面のアプリで、データが別々に保存される | Safari で開いたときにホーム画面への追加を案内し、記録を始める場所をアプリに揃える |
| 7日間使わないとデータが消えることがある | ホーム画面に追加すると、この削除の対象外になる。あわせて navigator.storage.persist() を呼ぶ |
packages/journal/ # 汎用層:型、Repository、Dexie の実装、フック、部品(カレンダー・写真・メモ・タブバーなど)、日付・画像処理
apps/meal/src/
├─ app/ # ルーティング(今日 / 日付指定 / カレンダー)、manifest
├─ meal/ # 食事日記に固有の部分:項目の型、○× の切り替え、画面
└─ styles/tokens/ # デザイントークン(primitive / semantic)。オレンジ系
apps/fishing/src/
├─ app/ # ルーティング(今日 / 日付指定 / 釣行 / カレンダー)、manifest
├─ fishing/ # 釣果ノートに固有の部分:項目の型、入力値の変換、並べ替え・集計、画面
└─ styles/tokens/ # デザイントークン。青系
共通の部品は semantic トークン(CSS 変数)だけを参照しています。2つのアプリは同じ変数名で値だけを変えているので、同じ部品のまま見た目が変わります。
npm install # リポジトリ直下で実行(全ワークスペースが入る)
npm run dev:meal # http://localhost:3000
npm run dev:fishing # http://localhost:3001
npm run dev:lan --workspace=apps/meal # 同じ Wi-Fi のスマホから http://<MacのIP>:3000 で開く(釣果ノートは apps/fishing、3001)
npm test # Vitest(日付計算・Repository・画像の寸法計算・釣果の入力変換と集計)
npm run typecheck # 各ワークスペースの型チェック
npm run build
LAN の http から開いた場合、HTTPS でしか使えない API(storage.persist() など)は動きません。ホーム画面への追加もうまく確認できません。これらの確認は、デプロイした後に本番の URL で行ってください。
スクリーンショットの料理写真には、Wikimedia Commons で CC0(パブリックドメイン)として公開されている画像を使い、実際にアプリへ読み込ませて撮影しました。