1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

obsidianと連動したポモドーロタイマーアプリの作成

1
Posted at

はじめに

ポモドーロタイマー+記録の自動化をするためのデスクトップアプリを作りました。
ポモドーロテクニックは知っていたけれど、どうにも使う気になれませんでした。

記録を別のノートアプリに手動でコピーするのが億劫というのが大きかったと思います。

そこで、ポモドーロテクニックを続けるために、マウスポインタの方向を示す機能と
obsidianのデイリーノートへの連携を追加しました。
ポモドーロ完了のたびにObsidianのデイリーノートへ自動記録する機能も実装しています。

完成品

PomoTimerDesk — ミニマルな半透明ポモドーロタイマー
image.png

  • 半透明・常に最前面表示で作業の邪魔にならない
  • マウスポインタをアプリ内のリングに反映(アプリ外でも追従)
  • 作業完了時にObsidianデイリーノートへ自動記録
  • 通知音のカスタム設定対応

技術スタック

レイヤー 技術 バージョン
UIフレームワーク React 19
言語 TypeScript 5
バンドラー Vite 7
デスクトップランタイム Tauri v2
バックエンド Rust latest stable
テスト Vitest 4

Tauri v2はRustをバックエンドに使うデスクトップフレームワークです。Electronと比べてバイナリサイズが小さく、メモリ使用量も抑えられます。

主な機能

1. マウスポインタの追従

円形プログレスリングの内側に、現在のマウス位置をドットで表示します。アプリウィンドウの外にカーソルがあっても追従します。

通常のWebブラウザではウィンドウ外のカーソル座標を取れませんが、Tauriを使うとRust側からシステムのカーソル座標を取得してフロントエンドに渡せます。

Rust側(src-tauri/src/lib.rs

#[tauri::command]
fn get_cursor_pos(window: tauri::WebviewWindow) -> Result<(f64, f64), String> {
    let pos = window.cursor_position().map_err(|e| e.to_string())?;
    Ok((pos.x, pos.y))
}

TypeScript側

const pos = await invoke<[number, number]>("get_cursor_pos");
// pos[0] = x, pos[1] = y (物理スクリーン座標)

invoke はTauri IPCのフロントエンドAPIで、Rustのコマンドを非同期で呼び出せます。


2. 背景透明度の動的制御

設定画面のスライダーでリアルタイムに透明度を変更できます。CSS変数をReactから動的に書き換えることで実現しています。

image.png

3. 通知音のカスタム設定

デフォルトのベル音に加えて、任意のフォルダにある音声ファイル(mp3 / wav / ogg / m4a / flac)を選択できます。

const selected = await open({
  directory: true,
  multiple: false,
  title: "通知音フォルダを選択",
});

4. Obsidianデイリーノートへの自動記録【メイン機能】

ポモドーロ完了時に、指定したObsidian VaultのMarkdownファイルへ作業時刻を自動追記します。
image.png

image.png

記録される内容のイメージ(デイリーノート 2026-03-28.md

- work: 10:25
- work: 11:00
- work: 14:30

Rust側の実装

#[tauri::command]
fn append_to_daily_note(
    vault_path: String,
    daily_notes_folder: Option<String>,
    date_format: String,
    folder_structure: String,
    work_label: String,
) -> Result<(), String> {
    let now = Local::now();
    let date_str = now.format(&date_format).to_string();
    let time_str = now.format("%H:%M").to_string();

    let mut path = PathBuf::from(&vault_path);
    if let Some(folder) = daily_notes_folder {
        if !folder.trim().is_empty() {
            path.push(&folder);
        }
    }
    if folder_structure == "year-month" {
        path.push(now.format("%Y").to_string());
        path.push(now.format("%m").to_string());
    }
    std::fs::create_dir_all(&path).map_err(|e| e.to_string())?;
    path.push(format!("{}.md", date_str));

    let mut file = OpenOptions::new().create(true).append(true).open(&path)?;
    writeln!(file, "- {}: {}", work_label, time_str)?;
    Ok(())
}

設定できる項目:

設定項目 説明 デフォルト
Vaultのパス ObsidianのVaultフォルダ
Daily Notesフォルダ Vault内のサブフォルダ (ルート直下)
日付フォーマット ファイル名の日付形式 %Y-%m-%d
フォルダ構造 フラット or 年/月サブフォルダ flat
作業ラベル 記録する文字列 work

ファイルが存在しない場合は自動作成され、存在する場合は末尾に追記されます。create_dir_all によってフォルダも自動生成されるため、新しい月になっても設定変更不要です。

設定の永続化

設定はlocalStorageに保存しています。Tauriのデスクトップアプリでもブラウザと同じlocalStorage APIが使えます。

// 変更のたびに自動保存
useEffect(() => {
  localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings));
}, [settings]);

// 起動時に読み込み
const [settings, setSettings] = useState<AppSettings>(() => {
  const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
  return stored ? { ...DEFAULT_SETTINGS, ...JSON.parse(stored) } : DEFAULT_SETTINGS;
});

`Partial<AppSettings>` を使った部分更新パターンにより設定の一部だけを変更しても他の値が失われません

```typescript
const updateSettings = (patch: Partial<AppSettings>) => {
  setSettings(prev => ({ ...prev, ...patch }));
};

Partial を使った部分更新パターンにより、設定の一部だけを変更しても他の値が失われません。

おわりに

Tauri v2 + React + TypeScriptの組み合わせで、Webの開発体験を保ちながらネイティブに近い機能(ファイルI/O、グローバルカーソル取得)を実現できました。
特にObsidian連携は、記録するコストがゼロになったことでタイマーを回すこと自体が習慣化しやすくなったと感じています。

ご自身でビルドして使っていただくことも可能です。前提条件は Node.js (v18+) と Rust (latest stable) のみです。

git clone https://github.com/MameMame777/PomoTimerDesk.git
cd PomoTimerDesk
npm install
npm run tauri build

ビルドが完了すると src-tauri/target/release/bundle/nsis/ にインストーラーが生成されます。
ぜひ使ってみてください。
GitHub: https://github.com/MameMame777/PomoTimerDesk

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?