Release v1.1.4: Pomodoro & Stopwatch Mode Streamlining

Streamlined timer modes with bug fixes and code cleanup:

Bug Fixes:
- Fixed "POMODORO COMPLETE" showing in stopwatch mode
- Fixed break timer showing pomodoro notices in stopwatch mode
- Fixed pause/resume resetting timer to 0 in stopwatch mode
- Fixed "Continue Working" button appearing in stopwatch mode
- Fixed "Skip Break" button changing to "Continue" incorrectly

Improvements:
- Stopwatch mode now completely independent from pomodoro workflow
- Removed break functionality from stopwatch mode (only Pause, Complete, Stop buttons)
- Stopwatch mode maintains "FOCUSING ON" label throughout session
- Cleaned up dead code (wasStopwatchBeforeBreak flag and related logic)
- Simplified break resume logic (always returns to pomodoro mode)

Technical:
- Added isStopwatchMode flag for proper mode tracking
- Fixed toggleTimer to handle stopwatch count-up vs pomodoro countdown
- Removed unnecessary break integration code from stopwatch workflow
This commit is contained in:
2025-11-25 21:41:12 +01:00
parent 271780f48a
commit 8d6a20ff05
7 changed files with 45 additions and 15 deletions

View File

@@ -4,7 +4,7 @@ A powerful task management and focus timer plugin for [Obsidian](https://obsidia
![Immerse Banner](https://img.shields.io/badge/Obsidian-Plugin-7c3aed?style=for-the-badge&logo=obsidian&logoColor=white) ![Immerse Banner](https://img.shields.io/badge/Obsidian-Plugin-7c3aed?style=for-the-badge&logo=obsidian&logoColor=white)
![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge) ![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)
![Version](https://img.shields.io/badge/Version-1.1.3-blue?style=for-the-badge) ![Version](https://img.shields.io/badge/Version-1.1.4-blue?style=for-the-badge)
## 🎯 Overview ## 🎯 Overview

21
main.js
View File

@@ -749,7 +749,12 @@ var ImmerseView = class extends import_obsidian2.ItemView {
const breakLabel = this.plugin.currentTimerSeconds > 0 ? "\u2615 BREAK TIME" : "\u2728 BREAK COMPLETE"; const breakLabel = this.plugin.currentTimerSeconds > 0 ? "\u2615 BREAK TIME" : "\u2728 BREAK COMPLETE";
activeCard.createEl("div", { cls: "immerse-active-label", text: breakLabel }); activeCard.createEl("div", { cls: "immerse-active-label", text: breakLabel });
} else { } else {
const workLabel = this.plugin.currentTimerSeconds > 0 ? "\u{1F3AF} FOCUSING ON" : "\u{1F345} POMODORO COMPLETE"; let workLabel;
if (this.plugin.currentTimerSeconds > 0 || this.plugin.isStopwatchMode) {
workLabel = "\u{1F3AF} FOCUSING ON";
} else {
workLabel = "\u{1F345} POMODORO COMPLETE";
}
activeCard.createEl("div", { cls: "immerse-active-label", text: workLabel }); activeCard.createEl("div", { cls: "immerse-active-label", text: workLabel });
} }
activeCard.createEl("div", { cls: "immerse-active-task-name", text: task.text }); activeCard.createEl("div", { cls: "immerse-active-task-name", text: task.text });
@@ -809,7 +814,7 @@ var ImmerseView = class extends import_obsidian2.ItemView {
}); });
} }
} else { } else {
if (this.plugin.currentTimerSeconds > 0) { if (this.plugin.currentTimerSeconds > 0 || this.plugin.isStopwatchMode) {
this.pauseBtnEl = controls.createEl("button", { cls: "immerse-btn immerse-btn-secondary" }); this.pauseBtnEl = controls.createEl("button", { cls: "immerse-btn immerse-btn-secondary" });
this.pauseBtnEl.innerHTML = this.plugin.isTimerRunning ? "\u23F8 Pause" : "\u25B6 Resume"; this.pauseBtnEl.innerHTML = this.plugin.isTimerRunning ? "\u23F8 Pause" : "\u25B6 Resume";
this.pauseBtnEl.addEventListener("click", () => this.plugin.toggleTimer()); this.pauseBtnEl.addEventListener("click", () => this.plugin.toggleTimer());
@@ -1250,6 +1255,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
this.currentTimerSeconds = 0; this.currentTimerSeconds = 0;
this.isTimerRunning = false; this.isTimerRunning = false;
this.isBreakMode = false; this.isBreakMode = false;
this.isStopwatchMode = false;
this.activeTaskId = null; this.activeTaskId = null;
this.pomodoroCount = 0; this.pomodoroCount = 0;
// Timestamp-based tracking for reliable background timing // Timestamp-based tracking for reliable background timing
@@ -1652,6 +1658,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
this.activeTaskId = taskId; this.activeTaskId = taskId;
task.isActive = true; task.isActive = true;
this.isBreakMode = false; this.isBreakMode = false;
this.isStopwatchMode = true;
this.currentTimerSeconds = 0; this.currentTimerSeconds = 0;
this.isTimerRunning = true; this.isTimerRunning = true;
this.secondsWorkedOnCurrentTask = task.actualMinutes * 60; this.secondsWorkedOnCurrentTask = task.actualMinutes * 60;
@@ -1690,6 +1697,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
this.activeTaskId = taskId; this.activeTaskId = taskId;
task.isActive = true; task.isActive = true;
this.isBreakMode = false; this.isBreakMode = false;
this.isStopwatchMode = false;
this.currentTimerSeconds = this.settings.pomodoroWorkMinutes * 60; this.currentTimerSeconds = this.settings.pomodoroWorkMinutes * 60;
this.isTimerRunning = true; this.isTimerRunning = true;
this.secondsWorkedOnCurrentTask = Math.floor(task.actualMinutes * 60); this.secondsWorkedOnCurrentTask = Math.floor(task.actualMinutes * 60);
@@ -1788,7 +1796,11 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
const now = Date.now(); const now = Date.now();
const elapsedMs = now - this.timerStartTimestamp; const elapsedMs = now - this.timerStartTimestamp;
const elapsedSeconds = Math.floor(elapsedMs / 1e3); const elapsedSeconds = Math.floor(elapsedMs / 1e3);
this.currentTimerSeconds = Math.max(0, this.pausedTimeRemaining - elapsedSeconds); if (this.pausedTimeRemaining === 0 || this.isStopwatchMode) {
this.currentTimerSeconds = this.pausedTimeRemaining + elapsedSeconds;
} else {
this.currentTimerSeconds = Math.max(0, this.pausedTimeRemaining - elapsedSeconds);
}
if (task && !this.isBreakMode) { if (task && !this.isBreakMode) {
this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds; this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds;
task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60); task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60);
@@ -1797,7 +1809,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
} }
this.updateStatusBar(); this.updateStatusBar();
this.updateTimerDisplay(); this.updateTimerDisplay();
if (this.currentTimerSeconds <= 0) { if (this.currentTimerSeconds <= 0 && !this.isStopwatchMode) {
this.handlePomodoroEnd(); this.handlePomodoroEnd();
} }
}, 1e3); }, 1e3);
@@ -1822,6 +1834,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
} }
} }
this.isTimerRunning = false; this.isTimerRunning = false;
this.isStopwatchMode = false;
this.activeTaskId = null; this.activeTaskId = null;
this.secondsWorkedOnCurrentTask = 0; this.secondsWorkedOnCurrentTask = 0;
this.timerStartTimestamp = 0; this.timerStartTimestamp = 0;

View File

@@ -1,7 +1,7 @@
{ {
"id": "immerse", "id": "immerse",
"name": "Immerse", "name": "Immerse",
"version": "1.1.3", "version": "1.1.4",
"minAppVersion": "0.15.0", "minAppVersion": "0.15.0",
"description": "A Blitzit-inspired task management and focus timer plugin. Plan your day, track time with Pomodoro technique, and crush your tasks with satisfying checkoffs.", "description": "A Blitzit-inspired task management and focus timer plugin. Plan your day, track time with Pomodoro technique, and crush your tasks with satisfying checkoffs.",
"author": "Crib", "author": "Crib",

View File

@@ -1,6 +1,6 @@
{ {
"name": "immerse", "name": "immerse",
"version": "1.1.3", "version": "1.1.4",
"description": "A Blitzit-inspired task management and focus timer plugin for Obsidian", "description": "A Blitzit-inspired task management and focus timer plugin for Obsidian",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@@ -35,6 +35,7 @@ export default class ImmersePlugin extends Plugin {
currentTimerSeconds: number = 0; currentTimerSeconds: number = 0;
isTimerRunning: boolean = false; isTimerRunning: boolean = false;
isBreakMode: boolean = false; isBreakMode: boolean = false;
isStopwatchMode: boolean = false;
activeTaskId: string | null = null; activeTaskId: string | null = null;
pomodoroCount: number = 0; pomodoroCount: number = 0;
@@ -551,6 +552,7 @@ export default class ImmersePlugin extends Plugin {
this.activeTaskId = taskId; this.activeTaskId = taskId;
task.isActive = true; task.isActive = true;
this.isBreakMode = false; this.isBreakMode = false;
this.isStopwatchMode = true;
this.currentTimerSeconds = 0; this.currentTimerSeconds = 0;
this.isTimerRunning = true; this.isTimerRunning = true;
this.secondsWorkedOnCurrentTask = task.actualMinutes * 60; this.secondsWorkedOnCurrentTask = task.actualMinutes * 60;
@@ -610,6 +612,7 @@ export default class ImmersePlugin extends Plugin {
this.activeTaskId = taskId; this.activeTaskId = taskId;
task.isActive = true; task.isActive = true;
this.isBreakMode = false; this.isBreakMode = false;
this.isStopwatchMode = false;
this.currentTimerSeconds = this.settings.pomodoroWorkMinutes * 60; this.currentTimerSeconds = this.settings.pomodoroWorkMinutes * 60;
this.isTimerRunning = true; this.isTimerRunning = true;
@@ -766,8 +769,14 @@ export default class ImmersePlugin extends Plugin {
const elapsedMs = now - this.timerStartTimestamp; const elapsedMs = now - this.timerStartTimestamp;
const elapsedSeconds = Math.floor(elapsedMs / 1000); const elapsedSeconds = Math.floor(elapsedMs / 1000);
// Update timer (countdown from paused position) // Update timer based on mode
this.currentTimerSeconds = Math.max(0, this.pausedTimeRemaining - elapsedSeconds); if (this.pausedTimeRemaining === 0 || this.isStopwatchMode) {
// Stopwatch mode - count up from paused position
this.currentTimerSeconds = this.pausedTimeRemaining + elapsedSeconds;
} else {
// Countdown mode (pomodoro/break) - count down from paused position
this.currentTimerSeconds = Math.max(0, this.pausedTimeRemaining - elapsedSeconds);
}
if (task && !this.isBreakMode) { if (task && !this.isBreakMode) {
// Update actual time worked // Update actual time worked
@@ -783,7 +792,7 @@ export default class ImmersePlugin extends Plugin {
this.updateStatusBar(); this.updateStatusBar();
this.updateTimerDisplay(); this.updateTimerDisplay();
if (this.currentTimerSeconds <= 0) { if (this.currentTimerSeconds <= 0 && !this.isStopwatchMode) {
this.handlePomodoroEnd(); this.handlePomodoroEnd();
} }
}, 1000); }, 1000);
@@ -814,6 +823,7 @@ export default class ImmersePlugin extends Plugin {
} }
this.isTimerRunning = false; this.isTimerRunning = false;
this.isStopwatchMode = false;
this.activeTaskId = null; this.activeTaskId = null;
this.secondsWorkedOnCurrentTask = 0; this.secondsWorkedOnCurrentTask = 0;
this.timerStartTimestamp = 0; this.timerStartTimestamp = 0;

View File

@@ -154,7 +154,13 @@ export class ImmerseView extends ItemView {
const breakLabel = this.plugin.currentTimerSeconds > 0 ? '☕ BREAK TIME' : '✨ BREAK COMPLETE'; const breakLabel = this.plugin.currentTimerSeconds > 0 ? '☕ BREAK TIME' : '✨ BREAK COMPLETE';
activeCard.createEl('div', { cls: 'immerse-active-label', text: breakLabel }); activeCard.createEl('div', { cls: 'immerse-active-label', text: breakLabel });
} else { } else {
const workLabel = this.plugin.currentTimerSeconds > 0 ? '🎯 FOCUSING ON' : '🍅 POMODORO COMPLETE'; // Determine label based on whether timer is active and mode (stopwatch vs pomodoro)
let workLabel: string;
if (this.plugin.currentTimerSeconds > 0 || this.plugin.isStopwatchMode) {
workLabel = '🎯 FOCUSING ON';
} else {
workLabel = '🍅 POMODORO COMPLETE';
}
activeCard.createEl('div', { cls: 'immerse-active-label', text: workLabel }); activeCard.createEl('div', { cls: 'immerse-active-label', text: workLabel });
} }
@@ -234,8 +240,8 @@ export class ImmerseView extends ItemView {
} }
} else { } else {
// Work mode controls // Work mode controls
if (this.plugin.currentTimerSeconds > 0) { if (this.plugin.currentTimerSeconds > 0 || this.plugin.isStopwatchMode) {
// Work session still running // Work session still running (or stopwatch mode active)
this.pauseBtnEl = controls.createEl('button', { cls: 'immerse-btn immerse-btn-secondary' }); this.pauseBtnEl = controls.createEl('button', { cls: 'immerse-btn immerse-btn-secondary' });
this.pauseBtnEl.innerHTML = this.plugin.isTimerRunning ? '⏸ Pause' : '▶ Resume'; this.pauseBtnEl.innerHTML = this.plugin.isTimerRunning ? '⏸ Pause' : '▶ Resume';
this.pauseBtnEl.addEventListener('click', () => this.plugin.toggleTimer()); this.pauseBtnEl.addEventListener('click', () => this.plugin.toggleTimer());
@@ -248,7 +254,7 @@ export class ImmerseView extends ItemView {
stopBtn.innerHTML = '✕ Stop'; stopBtn.innerHTML = '✕ Stop';
stopBtn.addEventListener('click', () => this.plugin.stopTimer()); stopBtn.addEventListener('click', () => this.plugin.stopTimer());
} else { } else {
// Work session finished - show break and completion options // Pomodoro session finished - show break and completion options
const startBreakBtn = controls.createEl('button', { cls: 'immerse-btn immerse-btn-secondary' }); const startBreakBtn = controls.createEl('button', { cls: 'immerse-btn immerse-btn-secondary' });
startBreakBtn.innerHTML = '☕ Start Break'; startBreakBtn.innerHTML = '☕ Start Break';
startBreakBtn.addEventListener('click', () => this.plugin.startBreak()); startBreakBtn.addEventListener('click', () => this.plugin.startBreak());

View File

@@ -5,5 +5,6 @@
"1.0.7": "0.15.0", "1.0.7": "0.15.0",
"1.0.8": "0.15.0", "1.0.8": "0.15.0",
"1.0.9": "0.15.0", "1.0.9": "0.15.0",
"1.1.3": "0.15.0" "1.1.3": "0.15.0",
"1.1.4": "0.15.0"
} }