diff --git a/README.md b/README.md
index b364524..6ecf68d 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ Focus Task brings the power of time-boxed task management directly into your Obs
- **Pomodoro Count**: Track total pomodoros completed
### 🎨 User Experience
-- **Floating Timer Widget**: Draggable timer that stays visible while you work
+- **Status Bar Timer**: timer that stays visible while you work
- **Celebration Messages**: Fun, randomized messages when you complete tasks
- **Sound Notifications**: Audio alerts for timer completion and task completion
- **Keyboard Shortcuts**: Quick access to common actions
diff --git a/manifest.json b/manifest.json
index 7fe3bdf..bef1e97 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "focus-task",
"name": "Focus Task",
- "version": "1.0.0",
+ "version": "1.0.1",
"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.",
"author": "Crib",
diff --git a/package.json b/package.json
index 0681790..e772400 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "focus-task",
- "version": "1.0.0",
+ "version": "1.0.1",
"description": "A Blitzit-inspired task management and focus timer plugin for Obsidian",
"main": "main.js",
"scripts": {
diff --git a/src/main.ts b/src/main.ts
index dd7d2b1..c04d9d9 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -36,8 +36,8 @@ export default class FocusTaskPlugin extends Plugin {
activeTaskId: string | null = null;
pomodoroCount: number = 0;
- // Floating timer element
- floatingTimerEl: HTMLElement | null = null;
+ // Status Bar element
+ statusBarEl: HTMLElement | null = null;
async onload() {
await this.loadAllData();
@@ -91,15 +91,13 @@ export default class FocusTaskPlugin extends Plugin {
this.addSettingTab(new FocusTaskSettingTab(this.app, this));
// Create floating timer if enabled
- if (this.settings.showFloatingTimer) {
- this.createFloatingTimer();
- }
+ this.createStatusBar();
}
onunload() {
- this.stopTimer();
- this.removeFloatingTimer();
- }
+ this.stopTimer();
+ }
+
async loadAllData() {
const loaded = await this.loadData();
@@ -272,7 +270,7 @@ export default class FocusTaskPlugin extends Plugin {
}, 1000);
this.saveAllData();
- this.refreshView();
+ this.updateTimerDisplay();
this.updateFloatingTimer();
}
@@ -304,7 +302,7 @@ export default class FocusTaskPlugin extends Plugin {
}, 1000);
this.updateFloatingTimer();
- this.refreshView();
+ this.updateTimerDisplay();
}
handlePomodoroEnd() {
@@ -357,7 +355,7 @@ export default class FocusTaskPlugin extends Plugin {
}
this.updateFloatingTimer();
- this.refreshView();
+ this.updateTimerDisplay();
}
toggleTimer() {
@@ -388,7 +386,7 @@ export default class FocusTaskPlugin extends Plugin {
}
this.updateFloatingTimer();
- this.refreshView();
+ this.updateTimerDisplay();
}
stopTimer() {
@@ -419,106 +417,38 @@ export default class FocusTaskPlugin extends Plugin {
}
}
- // ============ Floating Timer ============
+ // ============ Status Bar Timer ============
- createFloatingTimer() {
- if (this.floatingTimerEl) return;
-
- this.floatingTimerEl = document.body.createEl('div', {
- cls: 'focus-task-floating-timer',
- });
-
- this.floatingTimerEl.innerHTML = `
-
-
No active task
-
00:00
-
-
-
-
-
- `;
-
- // Make draggable
- this.makeDraggable(this.floatingTimerEl);
-
- // Add event listeners
- const playPauseBtn = this.floatingTimerEl.querySelector('.play-pause');
- const completeBtn = this.floatingTimerEl.querySelector('.complete');
-
- playPauseBtn?.addEventListener('click', () => this.toggleTimer());
- completeBtn?.addEventListener('click', () => this.completeActiveTask());
+ createStatusBar() {
+ this.statusBarEl = this.addStatusBarItem();
+ this.statusBarEl.addClass('focus-task-status-bar');
+ this.updateStatusBar();
+
+ // Click to open panel
+ this.statusBarEl.addEventListener('click', () => {
+ this.activateView();
+ });
}
- removeFloatingTimer() {
- if (this.floatingTimerEl) {
- this.floatingTimerEl.remove();
- this.floatingTimerEl = null;
- }
- }
-
- updateFloatingTimer() {
- if (!this.floatingTimerEl) return;
-
- const taskEl = this.floatingTimerEl.querySelector('.focus-task-floating-task');
- const timeEl = this.floatingTimerEl.querySelector('.focus-task-floating-time');
- const playPauseBtn = this.floatingTimerEl.querySelector('.play-pause');
+ updateStatusBar() {
+ if (!this.statusBarEl) return;
if (this.activeTaskId) {
const task = this.data.tasks.find(t => t.id === this.activeTaskId);
- if (task && taskEl) {
- taskEl.textContent = this.isBreakMode ? '☕ Break Time' : task.text;
- }
- } else if (taskEl) {
- taskEl.textContent = 'No active task';
- }
-
- if (timeEl) {
- timeEl.textContent = this.formatTime(this.currentTimerSeconds);
- timeEl.classList.toggle('focus-task-overtime', this.currentTimerSeconds < 0);
- }
-
- if (playPauseBtn) {
- playPauseBtn.textContent = this.isTimerRunning ? '⏸' : '▶';
- }
-
- // Update color based on state
- this.floatingTimerEl.classList.toggle('focus-task-break-mode', this.isBreakMode);
- this.floatingTimerEl.classList.toggle('focus-task-active', this.isTimerRunning);
- }
-
- makeDraggable(el: HTMLElement) {
- let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
-
- el.onmousedown = dragMouseDown;
-
- function dragMouseDown(e: MouseEvent) {
- if ((e.target as HTMLElement).tagName === 'BUTTON') return;
- e.preventDefault();
- pos3 = e.clientX;
- pos4 = e.clientY;
- document.onmouseup = closeDragElement;
- document.onmousemove = elementDrag;
- }
-
- function elementDrag(e: MouseEvent) {
- e.preventDefault();
- pos1 = pos3 - e.clientX;
- pos2 = pos4 - e.clientY;
- pos3 = e.clientX;
- pos4 = e.clientY;
- el.style.top = (el.offsetTop - pos2) + "px";
- el.style.left = (el.offsetLeft - pos1) + "px";
- el.style.right = 'auto';
- el.style.bottom = 'auto';
- }
-
- function closeDragElement() {
- document.onmouseup = null;
- document.onmousemove = null;
+ const taskName = this.isBreakMode ? '☕ Break' : (task?.text.substring(0, 20) || 'Task');
+ const timeStr = this.formatTime(this.currentTimerSeconds);
+ const icon = this.isTimerRunning ? '▶' : '⏸';
+
+ this.statusBarEl.setText(`⚡ ${icon} ${timeStr} - ${taskName}${task && task.text.length > 20 ? '...' : ''}`);
+ this.statusBarEl.addClass('focus-task-status-active');
+ } else {
+ this.statusBarEl.setText('⚡ Focus Task');
+ this.statusBarEl.removeClass('focus-task-status-active');
}
}
+
+
// ============ Sounds & Celebrations ============
showCelebration(task: FocusTask) {
@@ -612,6 +542,16 @@ export default class FocusTaskPlugin extends Plugin {
});
}
+ // Light refresh - only updates timer display without rebuilding DOM
+ updateTimerDisplay() {
+ const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_FOCUS_TASK);
+ leaves.forEach(leaf => {
+ if (leaf.view instanceof FocusTaskView) {
+ leaf.view.updateTimerDisplay();
+ }
+ });
+ }
+
getTasksByList(listId: string): FocusTask[] {
return this.data.tasks.filter(t => t.list === listId);
}
@@ -761,21 +701,7 @@ class FocusTaskSettingTab extends PluginSettingTab {
await this.plugin.saveAllData();
}));
- new Setting(containerEl)
- .setName('Show Floating Timer')
- .setDesc('Display a draggable floating timer widget')
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.showFloatingTimer)
- .onChange(async value => {
- this.plugin.settings.showFloatingTimer = value;
- if (value) {
- this.plugin.createFloatingTimer();
- } else {
- this.plugin.removeFloatingTimer();
- }
- await this.plugin.saveAllData();
- }));
-
+
// Lists Management
containerEl.createEl('h2', { text: '📋 Lists' });
diff --git a/src/types.ts b/src/types.ts
index 6a92e8d..9b6b5ea 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -30,7 +30,7 @@ export interface FocusTaskSettings {
enableCelebrations: boolean;
defaultEstimateMinutes: number;
lists: TaskList[];
- showFloatingTimer: boolean;
+ showFloatingTimer: true;
autoStartBreak: boolean;
tickSoundEnabled: boolean;
}
diff --git a/src/view.ts b/src/view.ts
index 00278f5..29ef077 100644
--- a/src/view.ts
+++ b/src/view.ts
@@ -12,6 +12,12 @@ import FocusTaskPlugin from './main';
export class FocusTaskView extends ItemView {
plugin: FocusTaskPlugin;
currentFilter: string = 'all';
+
+ // References to elements that need frequent updates
+ private timerTimeEl: HTMLElement | null = null;
+ private progressBarEl: HTMLElement | null = null;
+ private actualTimeEl: HTMLElement | null = null;
+ private pauseBtnEl: HTMLElement | null = null;
constructor(leaf: WorkspaceLeaf, plugin: FocusTaskPlugin) {
super(leaf);
@@ -34,10 +40,47 @@ export class FocusTaskView extends ItemView {
this.refresh();
}
+ // Light update - only updates timer display without rebuilding DOM
+ updateTimerDisplay() {
+ if (!this.timerTimeEl) return;
+
+ // Update timer text
+ this.timerTimeEl.textContent = this.plugin.formatTime(this.plugin.currentTimerSeconds);
+
+ // Update progress bar
+ if (this.progressBarEl) {
+ let progressPercent = 0;
+ if (this.plugin.isBreakMode) {
+ const breakDuration = this.plugin.pomodoroCount % this.plugin.settings.longBreakInterval === 0
+ ? this.plugin.settings.longBreakMinutes * 60
+ : this.plugin.settings.pomodoroBreakMinutes * 60;
+ progressPercent = ((breakDuration - this.plugin.currentTimerSeconds) / breakDuration) * 100;
+ } else {
+ const workDuration = this.plugin.settings.pomodoroWorkMinutes * 60;
+ progressPercent = ((workDuration - this.plugin.currentTimerSeconds) / workDuration) * 100;
+ }
+ this.progressBarEl.style.width = `${Math.min(Math.max(progressPercent, 0), 100)}%`;
+ }
+
+ // Update actual time display
+ if (this.actualTimeEl && this.plugin.activeTaskId) {
+ const task = this.plugin.data.tasks.find(t => t.id === this.plugin.activeTaskId);
+ if (task) {
+ this.actualTimeEl.textContent = `Actual: ${this.plugin.formatTimeHuman(task.actualMinutes)}`;
+ }
+ }
+ }
+
refresh() {
const container = this.containerEl.children[1];
container.empty();
container.addClass('focus-task-container');
+
+ // Reset element references
+ this.timerTimeEl = null;
+ this.progressBarEl = null;
+ this.actualTimeEl = null;
+ this.pauseBtnEl = null;
// Header
this.renderHeader(container);
@@ -109,16 +152,16 @@ export class FocusTaskView extends ItemView {
activeCard.createEl('div', { cls: 'focus-task-active-task-name', text: task.text });
- // Timer display
+ // Timer display - store reference for updates
const timerDisplay = activeCard.createEl('div', { cls: 'focus-task-timer-display' });
- timerDisplay.createEl('span', {
+ this.timerTimeEl = timerDisplay.createEl('span', {
cls: 'focus-task-timer-time',
text: this.plugin.formatTime(this.plugin.currentTimerSeconds)
});
- // Progress bar
+ // Progress bar - store reference for updates
const progressWrap = activeCard.createEl('div', { cls: 'focus-task-progress-wrap' });
- const progress = progressWrap.createEl('div', { cls: 'focus-task-progress-bar' });
+ this.progressBarEl = progressWrap.createEl('div', { cls: 'focus-task-progress-bar' });
let progressPercent = 0;
if (this.plugin.isBreakMode) {
@@ -131,22 +174,22 @@ export class FocusTaskView extends ItemView {
progressPercent = ((workDuration - this.plugin.currentTimerSeconds) / workDuration) * 100;
}
- progress.style.width = `${Math.min(Math.max(progressPercent, 0), 100)}%`;
- if (progressPercent >= 100) progress.addClass('focus-task-overtime');
+ this.progressBarEl.style.width = `${Math.min(Math.max(progressPercent, 0), 100)}%`;
+ if (progressPercent >= 100) this.progressBarEl.addClass('focus-task-overtime');
- // Time info
+ // Time info - store reference for actual time updates
if (!this.plugin.isBreakMode) {
const timeInfo = activeCard.createEl('div', { cls: 'focus-task-time-info' });
timeInfo.createEl('span', { text: `Est: ${this.plugin.formatTimeHuman(task.estimatedMinutes)}` });
- timeInfo.createEl('span', { text: `Actual: ${this.plugin.formatTimeHuman(task.actualMinutes)}` });
+ this.actualTimeEl = timeInfo.createEl('span', { text: `Actual: ${this.plugin.formatTimeHuman(task.actualMinutes)}` });
}
// Controls
const controls = activeCard.createEl('div', { cls: 'focus-task-active-controls' });
- const pauseBtn = controls.createEl('button', { cls: 'focus-task-btn focus-task-btn-secondary' });
- pauseBtn.innerHTML = this.plugin.isTimerRunning ? '⏸ Pause' : '▶ Resume';
- pauseBtn.addEventListener('click', () => this.plugin.toggleTimer());
+ this.pauseBtnEl = controls.createEl('button', { cls: 'focus-task-btn focus-task-btn-secondary' });
+ this.pauseBtnEl.innerHTML = this.plugin.isTimerRunning ? '⏸ Pause' : '▶ Resume';
+ this.pauseBtnEl.addEventListener('click', () => this.plugin.toggleTimer());
if (!this.plugin.isBreakMode) {
const completeBtn = controls.createEl('button', { cls: 'focus-task-btn focus-task-btn-success' });
@@ -316,4 +359,4 @@ export class FocusTaskView extends ItemView {
this.plugin.deleteTask(task.id);
});
}
-}
+}
\ No newline at end of file
diff --git a/styles.css b/styles.css
index 3785fe6..3c661db 100644
--- a/styles.css
+++ b/styles.css
@@ -512,86 +512,24 @@
background: var(--ft-danger);
}
-/* ============ Floating Timer ============ */
-.focus-task-floating-timer {
- position: fixed;
- bottom: 20px;
- right: 20px;
- z-index: 9999;
- background: var(--background-primary);
- border: 1px solid var(--background-modifier-border);
- border-radius: 16px;
- padding: 12px 16px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
- cursor: move;
- min-width: 180px;
- transition: all 0.3s ease;
+/* ============ Status Bar Timer ============ */
+.focus-task-status-bar {
+ cursor: pointer;
+ padding: 0 8px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 12px;
+ transition: all 0.2s ease;
}
-.focus-task-floating-timer:hover {
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
+.focus-task-status-bar:hover {
+ color: var(--text-accent);
}
-.focus-task-floating-timer.focus-task-active {
- border-color: var(--ft-primary);
- box-shadow: 0 8px 32px rgba(99, 102, 241, 0.3);
-}
-
-.focus-task-floating-timer.focus-task-break-mode {
- border-color: var(--ft-break);
- box-shadow: 0 8px 32px rgba(6, 182, 212, 0.3);
-}
-
-.focus-task-floating-inner {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 8px;
-}
-
-.focus-task-floating-task {
- font-size: 12px;
- color: var(--text-muted);
- max-width: 150px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.focus-task-floating-time {
- font-size: 28px;
- font-weight: 700;
- font-variant-numeric: tabular-nums;
- color: var(--ft-primary);
-}
-
-.focus-task-floating-time.focus-task-overtime {
- color: var(--ft-warning);
-}
-
-.focus-task-floating-controls {
- display: flex;
- gap: 8px;
-}
-
-.focus-task-float-btn {
- width: 36px;
- height: 36px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- background: var(--ft-bg-secondary);
- border: 1px solid var(--ft-border);
- cursor: pointer;
- font-size: 14px;
- transition: all 0.2s ease;
-}
-
-.focus-task-float-btn:hover {
- background: var(--ft-primary);
- color: white;
- border-color: var(--ft-primary);
+.focus-task-status-bar.focus-task-status-active {
+ color: var(--text-accent);
+ font-weight: 500;
}
/* ============ Modal Styles ============ */
diff --git a/versions.json b/versions.json
index af9a39e..1b5630e 100644
--- a/versions.json
+++ b/versions.json
@@ -1,3 +1,3 @@
{
- "1.0.0": "0.15.0"
+ "1.0.1": "0.15.0"
}