🤤

かっこいいポモドーロタイマー「Iteract」を公開しました

に公開

こんにちは、trhrです!
現在無職期間なのでなにか取り組んでみようということで、ポモドーロタイマーを作ってみました。
作成したアプリの紹介と、得た知見などを共有できればと思います。

モチベ

以下のモチベーションをもとに、ポモドーロタイマーを自作することにしました。

  • 既存のポモドーロタイマーに不満がある
    • タブが非アクティブになったときにカウントが止まる
    • 通知音の音量が調整できない
    • テーマ切替が欲しい
  • Next.js+Cloudflare Workersの素振りがしたい

作ったもの

「Iteract」という名前のポモドーロタイマーをリリースしました。結構真面目に作ったので、普通に使っていただけるものになっていると思います!良かったら使ってみてください。

ちなみに「Iteract」という名前は iterate(反復する) + act(行う) の自作かばん語です。「○○ポモドーロタイマー」という名前にしても良かったですが、個性が欲しかったのでとがった名前にしました。

Url
https://iteract.trhr-core.dev/ja

Github
https://github.com/snd-primary/iteract-web

特徴

  • PWA対応
  • ユーザ設定の保存機能
  • 選べるアラート音
  • 多言語対応(日・英・韓)
  • ライト/ダークモード切替&ミニマルなUI

開発環境

  • Win11 home
  • WSL2 (ubuntu)
  • VSCode, Cursor
    • 自分の中でなんとなく使い分けてる
  • Claude, Gemini(対話型UI、壁打ち用)

もともとこのアプリを開発しようと思った時、VibeCodingですべてを終わらせる予定でした。
しかし気が付いたらついつい自分でコードを書いており、結局コーディングの担当割合は自分50:AI50になっていたと思います。VibeCodingの感想については、で述べています🧤

要件定義

細かいことは決められていないですが、一応人に使ってもらう想定のアプリとしてリリースしており、最低限の機能と品質は担保できているつもりです。(アラは山ほどあるが)

機能要件

  • タブが非アクティブ時もタイマーがカウントダウンを続ける
  • 作業時間・休憩時間を設定できる
  • N回に1回、長休憩を設定できる
  • ユーザー設定を保存できる
  • オフラインで実行できる
  • テーマを切り替えられる(ライト・ダーク)
  • UIの言語を選択できる
  • 通知音を有する

非機能要件

  • 見た目がかっこいいこと
  • 軽量であること
  • アクセシブルであること

SEO

  • GoogleSearchConsole
  • GoogleAnalytics(GA4)
  • サイトマップ、JSON-LD

技術仕様

App

  • Next.js 15.3.1
    • Routing -> app router
    • UI -> shadcn/ui
    • 多言語対応 -> next-intl
    • 状態管理 -> jotai
    • アニメ -> motion.dev
    • テーマ切替 -> next-themes
    • PWA -> Next.js標準の機能で対応

クライアントサイドで完結するアプリなので、別にNext.jsじゃなくてもよかったなとは思っています。

Deploy

  • Cloudflare Workers
    • opennextでWorkersにdeploy

opennextで簡単にデプロイできました[1]
Freeプランで運用していますが、リクエスト数やビルド時間には十分に余裕があります。

https://developers.cloudflare.com/pages/functions/pricing/#free-plan

Domain

  • Cloudflare Register

デザイン周り

デザインカンプを作ったりしておらず、マークアップしていたらこのレイアウトになりました。
気に入っているのはフォントで、Departure MonoというオープンソースのWebフォントを使用しています。カウントダウンしている数字と、英字表示はすべてこのフォントを使っています。

ピクセルフォントなのだけど、インダストリアルな雰囲気もあってめちゃくちゃかっこいいです。

https://github.com/rektdeckard/departure-mono

学んだこと

プログレスUIの描画コストは結構高い

ここでいうプログレスUIというのは、タイマー系のアプリに必ずと言っていいほど実装されている、経過時間をグラフィカルに表示するUIのことです。

この手のUIは、1秒ごとにUIを更新する必要があり再描画のコストがそれなりにかかってきます。
(CPU使用率が上がる)
無視できないほど重くなるわけじゃないけど、あってもなくてもそんなに変わらないにぎやかし要素のた
めに、ユーザーのCPUリソースを蝕みたくないという気持ちがありました。

devtoolsのperformance monitorのcpu usageを見てました。再描画が走るごとに値が跳ね上がるのを確認できます。

解決案

とはいえ数字だけのUIも味気ないため、以下のような代替案で対応しました
与えられた時間を10分割して、満了に向かってそれが埋まっていくタイプのプログレスUIを実装してみました。ライブ感は損なわれますが、これはこれで気に入っています。

描画更新の回数も相当少なるなるので、これならパフォーマンスの問題は無視できるレベルに収まります。
<table>要素を使って簡単に実装できたところも気に入っています。

jotai便利すぎ

状態管理ライブラリのjotaiを初めて使用してみましたが、とても便利でした。
stores配下にアプリで使用する状態を定義しておけば、それをあらゆるコンポーネントから呼び出すことができます。

中でもatomWithStorage , createJSONStorage が良かった。
この2つはjotaiで状態を永続化(今回の場合localStorageに)するためのユーティリティで、データ永続化がかなりスマートになりました。

import { atom } from "jotai";
import { atomWithStorage, createJSONStorage } from "jotai/utils";

/** ~~諸々省略~~ */

//ストレージの作成
//SSR時のエラー対策として、windowオブジェクトにアクセスできない場合は、null, {} を返す
const storage = createJSONStorage<PomodoroSettings>(() => {
  if (typeof window !== "undefined") {
    return localStorage;
  }
  return {
    getItem: () => null,
    setItem: () => {},
    removeItem: () => {},
  };
});

// pomodoro-settingsというキーで、↑ のstorageにJSONとして保存・読み込みを行うatomを作成
export const settingsAtom = atomWithStorage<PomodoroSettings>(
  "pomodoro-settings", // 1. localStorage のキー名
  initialSettingsData, // 2. 初期値 (上記のオブジェクトを指定)
  storage, // 3. ストレージ設定
	  { getOnInit: true }, // 4. 初期値を取得するかどうか (これ大事)
);

export const settingsOpenAtom = atom(false); // Settings Modal open state atom

こだわりポイント

WebWorkerの使用
タイマーのカウントダウン処理は、WebWorkerで実行しています。
これにより、タブが非アクティブになってもタイマーのカウントダウンは止まらず実行することができます。

多言語対応
そういえばi18n対応したことなかったな・・・と思い、ノリで多言語対応(日・英・韓)してみました。next-intlというライブラリを使いました。

PWA対応
とりあえずPWAにはなっています。特にライブラリは使用せず、Next.js公式Docsの通りに実装を進めたら簡単にPWA化できました。

通知音のバリエーション
通知音をカスタマイズ可能にしています。現在4種類用意しているのですが、もっと増やしたいです(いろんな音が鳴ると楽しいので)

課題

パフォーマンス
Lighthouseまわしてみた感じ、以下のようなスコアになりました。
モバイル85は微妙だと思う。これを改善するのもそれはそれで面白そうなので、後日パフォーマンス改善に取り組んでみたいと思います。

プッシュ通知
PWAしているものの、タイマー完了などを知らせるプッシュ通知は未実装です。
私自身は通知の類が基本的に全部嫌いなので実装しませんでしたが、もしもそういう要望があれば実装したいと思っています。

マネタイズ
この程度のアプリなので大したことはできませんが、マネタイズの機会は逃したくありません。せめてドメイン代が賄えるぐらいには稼げるようにしたい。

VibeCoding

がっつりVibeCodingするぞ!という意気込みでアプリ開発を始めることにしました。エディタはCursorを使用しています。

開発当初の要件はRDD↓にわりとしっかり書き込めていたので、mockレベルのものは30分足らずでつくれました。
ただし、その後の細かい修正やリファクタリング、要件の変更はほぼ自分で実装したという感じです。
Vibecodingのコツがまだつかめてないところがあるので、もっと使い込んでいく必要があると思いました。

RDD
# 要件定義書:ポモドーロタイマー Web アプリ

**バージョン:** 1.0
**作成日:** 2025 年 4 月 19 日
**作成者:** (Gemini および ユーザー)

## 1. 概要 (Overview)

本ドキュメントは、Web ブラウザで動作するカスタマイズ可能なポモドーロタイマーアプリケーションの要件を定義するものです。ユーザーが作業時間と休憩時間を管理し、ポモドーロテクニックを用いて集中力を維持・向上させることを支援します。

### 1.1. アプリケーション名(仮)

シンプルポモドーロタイマー (Simple Pomodoro Timer)
※名称は開発中に変更される可能性があります。

### 1.2. 目的

- ポモドーロテクニックの実践を容易にする。
- ユーザーが集中して作業や学習に取り組める環境を提供する。
- シンプルで直感的、かつカスタマイズ可能なタイマー機能を提供する。

## 2. ターゲットユーザー (Target Users)

- 集中して作業や勉強に取り組みたい学生、社会人、フリーランスなど。
- ポモドーロテクニックを手軽に試したい、または日常的に利用したいユーザー。
- 自身の作業スタイルに合わせてタイマー設定をカスタマイズしたいユーザー。

## 3. 機能要件 (Functional Requirements)

### 3.1. タイマー機能 (MUST)

- **時間設定:** 以下の時間をユーザーが分単位で設定可能であること。
  - 作業時間 (デフォルト: 25 分)
  - 短い休憩時間 (デフォルト: 5 分)
  - 長い休憩時間 (デフォルト: 15 分)
- **繰り返し設定:** 短い休憩を何回繰り返した後に長い休憩に入るか、その回数をユーザーが設定可能であること (デフォルト: 4 回)。
- **自動/手動開始設定:** 以下の両方のタイミングについて、タイマーの自動開始か手動開始かをユーザーが個別に選択可能であること。
  - 作業時間終了後 → 休憩タイマー開始
  - 休憩時間終了後 → 作業タイマー開始
- **タイマー表示:**
  - 現在のモード(例: 作業中 / 短い休憩中 / 長い休憩中 / 一時停止中)を明確に表示すること。
  - 残り時間を `MM:SS` 形式でリアルタイムに表示すること。
- **タイマー操作:**
  - タイマーを `開始` する機能。
  - 動作中のタイマーを `一時停止` する機能。
  - 現在のタイマーサイクルを `リセット` し、次の作業セッション(または初期状態)に戻せる機能。
- **サイクル動作:** 設定された回数だけ「作業 → 短い休憩」を繰り返し、その後「長い休憩」に入ること。長い休憩後は次の「作業」セッションを開始すること。

### 3.2. 通知機能 (MUST)

- 作業時間、休憩時間の各タイマー終了時に、**サウンド**(1 種類)で通知すること。
- デスクトップ通知機能は実装しない。

### 3.3. 記録機能 (MUST)

- ポモドーロ(作業セッション)が**完了**した際、またはユーザーによって**中断**(リセット等)された際に、以下の情報を記録すること。
  - 記録情報: 開始時刻、終了時刻、完了/中断の別、その日の何回目のポモドーロ(作業セッション)か。
- 記録データはクライアント(ユーザーのブラウザ)の **`localStorage`** に保存すること。
- アプリケーションの UI 上に「**今日の完了したポモドーロ数**」を分かりやすく表示すること。

### 3.4. 設定保存機能 (MUST)

- ユーザーが設定した以下の項目をクライアントの **`localStorage`** に保存し、次回アプリケーションアクセス時にも設定が維持されること。
  - 各タイマー時間(作業、短休憩、長休憩)
  - 長い休憩までの繰り返し回数
  - 自動/手動開始の設定(2 箇所分)
  - 選択中の UI モード(ライト/ダーク)

### 3.5. UI モード (MUST)

- **ライトモード****ダークモード**の 2 つのテーマに対応すること。
- ユーザーが UI 上で容易にモードを切り替えられること。
- 選択されたモード設定は `localStorage` に保存されること (上記 3.4 参照)。

## 4. 非機能要件 (Non-Functional Requirements)

- **パフォーマンス:** タイマーは正確に動作し、UI の応答性は良好であること。
- **ユーザビリティ:** 初めてのユーザーでも直感的に操作できるシンプルで分かりやすいインターフェースであること。設定変更などが容易に行えること。
- **互換性:** 主要なモダン Web ブラウザ(Google Chrome, Mozilla Firefox, Apple Safari, Microsoft Edge の最新安定版)で正常に動作すること。
- **保守性:** コードはリーダブルであり、将来的な軽微な修正や機能追加が比較的容易に行えるように、コンポーネント分割などを適切に行うこと。

## 5. 技術構成 (Technical Stack)

- **フレームワーク:** Next.js (v14 以降を推奨)
- **言語:** TypeScript
- **UI ライブラリ:** Shadcn/ui (内部で Tailwind CSS を使用)
- **状態管理:** jotai(npm パッケージ)を使用する、 必要に応じてReact Hooks (`useState`, `useEffect`, `useReducer`, `useContext` 等、必要に応じて)
- **データ永続化:** `localStorage` (Web Storage API)
- **開発環境:** Node.js (LTS バージョン),  pnpm を使用
- **デプロイ先:** 未定(Vercel, Netlify 等の静的ホスティング/Next.js ホスティングサービスを想定)

## 6. 画面構成案 (Screen Layout Proposal)

- **メイン画面:** アプリケーションの主要なインターフェース。以下の要素を含む。
  - 現在のタイマーモード表示(例:「作業中」「休憩中」)
  - 大きなフォントでの残り時間表示 (`MM:SS`)
  - タイマー操作ボタン (`開始` / `一時停止` / `リセット`)
  - 設定画面を開くためのボタンまたはアイコン
  - 今日の完了ポモドーロ数を表示するエリア
  - UI モード(ライト/ダーク)を切り替えるためのボタンまたはスイッチ
- **設定画面:** メイン画面から遷移可能な画面(モーダルダイアログまたは別ビュー)。以下の設定項目を含む。
  - 作業時間、短い休憩、長い休憩の時間設定用の入力フィールド(数値入力など)
  - 長い休憩に入るまでの繰り返し回数を設定する入力フィールド
  - 自動/手動開始を設定するためのスイッチまたはラジオボタン(「作業 → 休憩」「休憩 → 作業」の 2 箇所)
  - 設定を保存または適用するためのボタン

## 7. 今後の拡張可能性 (Future Enhancements - Scope out)

以下の機能は今回のバージョンでは実装範囲外とするが、将来的な拡張候補として考えられる。

- 記録データの詳細な履歴表示(カレンダー表示、リスト表示、日付フィルタリングなど)
- 簡単なタスク名を入力・関連付ける機能
- 通知サウンドの選択・カスタマイズ機能
- 音量調整機能
- Web Push 通知による、ブラウザを閉じていてもタイマー終了を知らせる機能(Service Worker 利用)
- アカウント登録・ログインによる複数デバイスでの設定・記録の同期
- 多言語対応(国際化)
- デスクトップアプリ化
	- Tauriを用いて、デスクトップアプリ化する構想がある。

最後に

仕事探さないといけないなーと思いながら取り組む何のしがらみもない個人開発、楽しかった。
同時に、AIすごすぎて普通におれ復職できない気がしています。

Iteractのフィードバックなど、お待ちしております~!!

脚注
  1. 2025年5月現在、opennextはWindows環境を正式にサポートしてないです。自分もWin11環境でopennextうまく動かなかったので、開発環境をWSL2に移行しました。 ↩︎

Discussion