Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe42d42500 | |||
| 087d22f1fd | |||
| a74fd244b0 | |||
| 7fbb789fdd |
10
README.md
10
README.md
@@ -4,7 +4,7 @@ A powerful task management and focus timer plugin for [Obsidian](https://obsidia
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
@@ -82,10 +82,6 @@ All settings available in Settings → Immerse:
|
||||
- Daily note integration (auto-log completed tasks)
|
||||
- Custom lists with names, emojis, and colors
|
||||
|
||||
## 🙏 Credits
|
||||
|
||||
Heavily inspired by [Blitzit](https://www.blitzit.app/) - a fantastic productivity app. Immerse brings that experience into Obsidian.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions welcome! Fork, create a feature branch, and open a PR.
|
||||
@@ -107,5 +103,7 @@ MIT License - see [LICENSE](LICENSE) file.
|
||||
**Links:** [Repository](https://git.cribdev.com/crib/immerse) • [Issues](https://git.cribdev.com/crib/immerse/issues) • [Blitzit](https://www.blitzit.app/)
|
||||
|
||||
<p align="center">
|
||||
<em>Made with ❤️ for the Obsidian community • Inspired by Blitzit ⚡</em>
|
||||
Made with ❤️ for the Obsidian community
|
||||
<br><br>
|
||||
<em>✨ Coded with assistance from <a href="https://claude.ai">Claude.ai</a></em>
|
||||
</p>
|
||||
|
||||
87
main.js
87
main.js
@@ -763,19 +763,21 @@ var ImmerseView = class extends import_obsidian2.ItemView {
|
||||
cls: "immerse-timer-time",
|
||||
text: this.plugin.formatTime(this.plugin.currentTimerSeconds)
|
||||
});
|
||||
const progressWrap = activeCard.createEl("div", { cls: "immerse-progress-wrap" });
|
||||
this.progressBarEl = progressWrap.createEl("div", { cls: "immerse-progress-bar" });
|
||||
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;
|
||||
if (!this.plugin.isStopwatchMode) {
|
||||
const progressWrap = activeCard.createEl("div", { cls: "immerse-progress-wrap" });
|
||||
this.progressBarEl = progressWrap.createEl("div", { cls: "immerse-progress-bar" });
|
||||
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)}%`;
|
||||
if (progressPercent >= 100)
|
||||
this.progressBarEl.addClass("immerse-overtime");
|
||||
}
|
||||
this.progressBarEl.style.width = `${Math.min(Math.max(progressPercent, 0), 100)}%`;
|
||||
if (progressPercent >= 100)
|
||||
this.progressBarEl.addClass("immerse-overtime");
|
||||
if (!this.plugin.isBreakMode) {
|
||||
const timeInfo = activeCard.createEl("div", { cls: "immerse-time-info" });
|
||||
timeInfo.createEl("span", { text: `Est: ${this.plugin.formatTimeHuman(task.estimatedMinutes)}` });
|
||||
@@ -980,6 +982,10 @@ var ReportView = class extends import_obsidian3.ItemView {
|
||||
constructor(leaf, plugin) {
|
||||
super(leaf);
|
||||
this.selectedListIds = [];
|
||||
this.activeQuickFilter = "Last 7 days";
|
||||
// Track active filter
|
||||
this.startDateInput = null;
|
||||
this.endDateInput = null;
|
||||
this.plugin = plugin;
|
||||
const today = new Date();
|
||||
this.endDate = today.toISOString().split("T")[0];
|
||||
@@ -1021,12 +1027,20 @@ var ReportView = class extends import_obsidian3.ItemView {
|
||||
const filtersSection = container.createEl("div", { cls: "immerse-report-filters" });
|
||||
const dateRow = filtersSection.createEl("div", { cls: "immerse-report-filter-row" });
|
||||
new import_obsidian3.Setting(dateRow).setName("Start Date").addText((text) => {
|
||||
text.setValue(this.startDate).onChange((value) => this.startDate = value);
|
||||
text.setValue(this.startDate).onChange((value) => {
|
||||
this.startDate = value;
|
||||
this.activeQuickFilter = null;
|
||||
});
|
||||
text.inputEl.type = "date";
|
||||
this.startDateInput = text.inputEl;
|
||||
});
|
||||
new import_obsidian3.Setting(dateRow).setName("End Date").addText((text) => {
|
||||
text.setValue(this.endDate).onChange((value) => this.endDate = value);
|
||||
text.setValue(this.endDate).onChange((value) => {
|
||||
this.endDate = value;
|
||||
this.activeQuickFilter = null;
|
||||
});
|
||||
text.inputEl.type = "date";
|
||||
this.endDateInput = text.inputEl;
|
||||
});
|
||||
const quickFilters = filtersSection.createEl("div", { cls: "immerse-report-quick-filters" });
|
||||
quickFilters.createEl("span", { text: "Quick select: ", cls: "immerse-filter-label" });
|
||||
@@ -1041,12 +1055,24 @@ var ReportView = class extends import_obsidian3.ItemView {
|
||||
text: filter.label,
|
||||
cls: "immerse-quick-filter-btn"
|
||||
});
|
||||
if (filter.label === this.activeQuickFilter) {
|
||||
btn.addClass("active");
|
||||
}
|
||||
btn.addEventListener("click", () => {
|
||||
const today = new Date();
|
||||
this.endDate = today.toISOString().split("T")[0];
|
||||
const startDate = new Date(today);
|
||||
startDate.setDate(startDate.getDate() - filter.days);
|
||||
this.startDate = startDate.toISOString().split("T")[0];
|
||||
if (this.startDateInput)
|
||||
this.startDateInput.value = this.startDate;
|
||||
if (this.endDateInput)
|
||||
this.endDateInput.value = this.endDate;
|
||||
this.activeQuickFilter = filter.label;
|
||||
quickFilters.querySelectorAll(".immerse-quick-filter-btn").forEach((b) => {
|
||||
b.removeClass("active");
|
||||
});
|
||||
btn.addClass("active");
|
||||
this.renderReport(container);
|
||||
});
|
||||
});
|
||||
@@ -1264,19 +1290,29 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
// Focus time tracking (in seconds for accuracy)
|
||||
this.focusSecondsToday = 0;
|
||||
this.secondsWorkedOnCurrentTask = 0;
|
||||
this.sessionStartSeconds = 0;
|
||||
// Track seconds at start of current session
|
||||
// Status bar element
|
||||
this.statusBarEl = null;
|
||||
// Reminder system
|
||||
this.reminderCheckInterval = null;
|
||||
this.notifiedReminders = /* @__PURE__ */ new Set();
|
||||
// Track which reminders have been shown
|
||||
// Daily reset check interval
|
||||
this.dailyResetCheckInterval = null;
|
||||
}
|
||||
// Track which reminders have been shown
|
||||
async onload() {
|
||||
await this.loadAllData();
|
||||
this.checkDailyReset();
|
||||
this.dailyResetCheckInterval = window.setInterval(() => {
|
||||
this.checkDailyReset();
|
||||
}, 6e4);
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (!document.hidden && this.isTimerRunning) {
|
||||
this.syncTimerFromTimestamp();
|
||||
if (!document.hidden) {
|
||||
this.checkDailyReset();
|
||||
if (this.isTimerRunning) {
|
||||
this.syncTimerFromTimestamp();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.registerView(
|
||||
@@ -1329,6 +1365,10 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
onunload() {
|
||||
this.stopTimer();
|
||||
this.stopReminderSystem();
|
||||
if (this.dailyResetCheckInterval) {
|
||||
window.clearInterval(this.dailyResetCheckInterval);
|
||||
this.dailyResetCheckInterval = null;
|
||||
}
|
||||
}
|
||||
async loadAllData() {
|
||||
const loaded = await this.loadData();
|
||||
@@ -1440,6 +1480,10 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
task.isActive = false;
|
||||
this.data.completedToday++;
|
||||
this.data.lastActiveDate = new Date().toDateString();
|
||||
if (this.activeTaskId === taskId && this.sessionStartSeconds !== void 0) {
|
||||
const sessionTime = this.secondsWorkedOnCurrentTask - this.sessionStartSeconds;
|
||||
this.focusSecondsToday += sessionTime;
|
||||
}
|
||||
this.archiveCompletedTask(task);
|
||||
this.updateDailyStats(task);
|
||||
if (this.settings.enableCelebrations) {
|
||||
@@ -1665,6 +1709,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
this.timerStartTimestamp = Date.now();
|
||||
this.pausedTimeRemaining = 0;
|
||||
const initialSecondsWorked = this.secondsWorkedOnCurrentTask;
|
||||
this.sessionStartSeconds = this.secondsWorkedOnCurrentTask;
|
||||
let alertShown = false;
|
||||
this.refreshView();
|
||||
this.updateStatusBar();
|
||||
@@ -1675,8 +1720,6 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
this.currentTimerSeconds = elapsedSeconds;
|
||||
this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds;
|
||||
task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60);
|
||||
const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds;
|
||||
this.focusSecondsToday = newFocusSeconds;
|
||||
this.updateStatusBar();
|
||||
this.updateTimerDisplay();
|
||||
if (!alertShown && this.currentTimerSeconds >= task.estimatedMinutes * 60) {
|
||||
@@ -1704,6 +1747,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
this.timerStartTimestamp = Date.now();
|
||||
this.pausedTimeRemaining = this.currentTimerSeconds;
|
||||
const initialSecondsWorked = this.secondsWorkedOnCurrentTask;
|
||||
this.sessionStartSeconds = this.secondsWorkedOnCurrentTask;
|
||||
this.refreshView();
|
||||
this.updateStatusBar();
|
||||
this.timerInterval = window.setInterval(() => {
|
||||
@@ -1717,8 +1761,6 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
if (task.actualMinutes !== actualMinutes) {
|
||||
task.actualMinutes = actualMinutes;
|
||||
}
|
||||
const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds;
|
||||
this.focusSecondsToday = newFocusSeconds;
|
||||
}
|
||||
this.updateStatusBar();
|
||||
this.updateTimerDisplay();
|
||||
@@ -1804,8 +1846,6 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
if (task && !this.isBreakMode) {
|
||||
this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds;
|
||||
task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60);
|
||||
const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds;
|
||||
this.focusSecondsToday = newFocusSeconds;
|
||||
}
|
||||
this.updateStatusBar();
|
||||
this.updateTimerDisplay();
|
||||
@@ -1837,6 +1877,7 @@ var ImmersePlugin = class extends import_obsidian4.Plugin {
|
||||
this.isStopwatchMode = false;
|
||||
this.activeTaskId = null;
|
||||
this.secondsWorkedOnCurrentTask = 0;
|
||||
this.sessionStartSeconds = 0;
|
||||
this.timerStartTimestamp = 0;
|
||||
this.pausedTimeRemaining = 0;
|
||||
this.updateStatusBar();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "immerse",
|
||||
"name": "Immerse",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immerse",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "A Blitzit-inspired task management and focus timer plugin for Obsidian",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
44
src/main.ts
44
src/main.ts
@@ -46,6 +46,7 @@ export default class ImmersePlugin extends Plugin {
|
||||
// Focus time tracking (in seconds for accuracy)
|
||||
private focusSecondsToday: number = 0;
|
||||
private secondsWorkedOnCurrentTask: number = 0;
|
||||
private sessionStartSeconds: number = 0; // Track seconds at start of current session
|
||||
|
||||
// Status bar element
|
||||
statusBarEl: HTMLElement | null = null;
|
||||
@@ -54,16 +55,28 @@ export default class ImmersePlugin extends Plugin {
|
||||
private reminderCheckInterval: number | null = null;
|
||||
private notifiedReminders: Set<string> = new Set(); // Track which reminders have been shown
|
||||
|
||||
// Daily reset check interval
|
||||
private dailyResetCheckInterval: number | null = null;
|
||||
|
||||
async onload() {
|
||||
await this.loadAllData();
|
||||
|
||||
// Check and reset daily stats
|
||||
this.checkDailyReset();
|
||||
|
||||
// Start daily reset check interval (check every 60 seconds)
|
||||
this.dailyResetCheckInterval = window.setInterval(() => {
|
||||
this.checkDailyReset();
|
||||
}, 60000);
|
||||
|
||||
// Handle visibility changes to sync timer when app comes back to foreground
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden && this.isTimerRunning) {
|
||||
this.syncTimerFromTimestamp();
|
||||
if (!document.hidden) {
|
||||
// Check for day change when app becomes visible
|
||||
this.checkDailyReset();
|
||||
if (this.isTimerRunning) {
|
||||
this.syncTimerFromTimestamp();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -135,6 +148,12 @@ export default class ImmersePlugin extends Plugin {
|
||||
onunload() {
|
||||
this.stopTimer();
|
||||
this.stopReminderSystem();
|
||||
|
||||
// Stop daily reset check interval
|
||||
if (this.dailyResetCheckInterval) {
|
||||
window.clearInterval(this.dailyResetCheckInterval);
|
||||
this.dailyResetCheckInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
async loadAllData() {
|
||||
@@ -279,6 +298,12 @@ export default class ImmersePlugin extends Plugin {
|
||||
this.data.completedToday++;
|
||||
this.data.lastActiveDate = new Date().toDateString();
|
||||
|
||||
// Add focus time from this session (only on completion)
|
||||
if (this.activeTaskId === taskId && this.sessionStartSeconds !== undefined) {
|
||||
const sessionTime = this.secondsWorkedOnCurrentTask - this.sessionStartSeconds;
|
||||
this.focusSecondsToday += sessionTime;
|
||||
}
|
||||
|
||||
// Archive task for historical reporting
|
||||
this.archiveCompletedTask(task);
|
||||
|
||||
@@ -563,6 +588,7 @@ export default class ImmersePlugin extends Plugin {
|
||||
|
||||
// Store initial values
|
||||
const initialSecondsWorked = this.secondsWorkedOnCurrentTask;
|
||||
this.sessionStartSeconds = this.secondsWorkedOnCurrentTask; // Track session start for focus time
|
||||
let alertShown = false;
|
||||
|
||||
// Full refresh to show the active task card
|
||||
@@ -583,10 +609,6 @@ export default class ImmersePlugin extends Plugin {
|
||||
this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds;
|
||||
task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60);
|
||||
|
||||
// Update focus time
|
||||
const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds;
|
||||
this.focusSecondsToday = newFocusSeconds;
|
||||
|
||||
// Light update - only timer display, no full refresh
|
||||
this.updateStatusBar();
|
||||
this.updateTimerDisplay();
|
||||
@@ -626,6 +648,7 @@ export default class ImmersePlugin extends Plugin {
|
||||
|
||||
// Store the initial seconds worked to calculate delta
|
||||
const initialSecondsWorked = this.secondsWorkedOnCurrentTask;
|
||||
this.sessionStartSeconds = this.secondsWorkedOnCurrentTask; // Track session start for focus time
|
||||
|
||||
// Full refresh to show the active task card
|
||||
this.refreshView();
|
||||
@@ -648,10 +671,6 @@ export default class ImmersePlugin extends Plugin {
|
||||
if (task.actualMinutes !== actualMinutes) {
|
||||
task.actualMinutes = actualMinutes;
|
||||
}
|
||||
|
||||
// Update focus time based on elapsed seconds
|
||||
const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds;
|
||||
this.focusSecondsToday = newFocusSeconds;
|
||||
}
|
||||
|
||||
// Light update - only timer display, no full refresh
|
||||
@@ -782,10 +801,6 @@ export default class ImmersePlugin extends Plugin {
|
||||
// Update actual time worked
|
||||
this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds;
|
||||
task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60);
|
||||
|
||||
// Update focus time
|
||||
const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds;
|
||||
this.focusSecondsToday = newFocusSeconds;
|
||||
}
|
||||
|
||||
// Light update - only timer display
|
||||
@@ -826,6 +841,7 @@ export default class ImmersePlugin extends Plugin {
|
||||
this.isStopwatchMode = false;
|
||||
this.activeTaskId = null;
|
||||
this.secondsWorkedOnCurrentTask = 0;
|
||||
this.sessionStartSeconds = 0;
|
||||
this.timerStartTimestamp = 0;
|
||||
this.pausedTimeRemaining = 0;
|
||||
this.updateStatusBar();
|
||||
|
||||
@@ -14,6 +14,9 @@ export class ReportView extends ItemView {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
selectedListIds: string[] = [];
|
||||
activeQuickFilter: string | null = 'Last 7 days'; // Track active filter
|
||||
startDateInput: HTMLInputElement | null = null;
|
||||
endDateInput: HTMLInputElement | null = null;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf, plugin: ImmersePlugin) {
|
||||
super(leaf);
|
||||
@@ -83,16 +86,24 @@ export class ReportView extends ItemView {
|
||||
.setName('Start Date')
|
||||
.addText(text => {
|
||||
text.setValue(this.startDate)
|
||||
.onChange(value => this.startDate = value);
|
||||
.onChange(value => {
|
||||
this.startDate = value;
|
||||
this.activeQuickFilter = null; // Clear active filter when manually changing dates
|
||||
});
|
||||
text.inputEl.type = 'date';
|
||||
this.startDateInput = text.inputEl; // Store reference
|
||||
});
|
||||
|
||||
new Setting(dateRow)
|
||||
.setName('End Date')
|
||||
.addText(text => {
|
||||
text.setValue(this.endDate)
|
||||
.onChange(value => this.endDate = value);
|
||||
.onChange(value => {
|
||||
this.endDate = value;
|
||||
this.activeQuickFilter = null; // Clear active filter when manually changing dates
|
||||
});
|
||||
text.inputEl.type = 'date';
|
||||
this.endDateInput = text.inputEl; // Store reference
|
||||
});
|
||||
|
||||
// Quick filters
|
||||
@@ -111,12 +122,33 @@ export class ReportView extends ItemView {
|
||||
text: filter.label,
|
||||
cls: 'immerse-quick-filter-btn'
|
||||
});
|
||||
|
||||
// Set active class if this is the default active filter
|
||||
if (filter.label === this.activeQuickFilter) {
|
||||
btn.addClass('active');
|
||||
}
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
// Update dates
|
||||
const today = new Date();
|
||||
this.endDate = today.toISOString().split('T')[0];
|
||||
const startDate = new Date(today);
|
||||
startDate.setDate(startDate.getDate() - filter.days);
|
||||
this.startDate = startDate.toISOString().split('T')[0];
|
||||
|
||||
// Update date inputs
|
||||
if (this.startDateInput) this.startDateInput.value = this.startDate;
|
||||
if (this.endDateInput) this.endDateInput.value = this.endDate;
|
||||
|
||||
// Update active filter
|
||||
this.activeQuickFilter = filter.label;
|
||||
|
||||
// Update button states
|
||||
quickFilters.querySelectorAll('.immerse-quick-filter-btn').forEach(b => {
|
||||
b.removeClass('active');
|
||||
});
|
||||
btn.addClass('active');
|
||||
|
||||
this.renderReport(container);
|
||||
});
|
||||
});
|
||||
|
||||
34
src/view.ts
34
src/view.ts
@@ -173,24 +173,26 @@ export class ImmerseView extends ItemView {
|
||||
text: this.plugin.formatTime(this.plugin.currentTimerSeconds)
|
||||
});
|
||||
|
||||
// Progress bar - store reference for updates
|
||||
const progressWrap = activeCard.createEl('div', { cls: 'immerse-progress-wrap' });
|
||||
this.progressBarEl = progressWrap.createEl('div', { cls: 'immerse-progress-bar' });
|
||||
|
||||
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;
|
||||
// Progress bar - only show in pomodoro/break mode, not stopwatch
|
||||
if (!this.plugin.isStopwatchMode) {
|
||||
const progressWrap = activeCard.createEl('div', { cls: 'immerse-progress-wrap' });
|
||||
this.progressBarEl = progressWrap.createEl('div', { cls: 'immerse-progress-bar' });
|
||||
|
||||
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)}%`;
|
||||
if (progressPercent >= 100) this.progressBarEl.addClass('immerse-overtime');
|
||||
}
|
||||
|
||||
this.progressBarEl.style.width = `${Math.min(Math.max(progressPercent, 0), 100)}%`;
|
||||
if (progressPercent >= 100) this.progressBarEl.addClass('immerse-overtime');
|
||||
|
||||
// Time info - store reference for actual time updates
|
||||
if (!this.plugin.isBreakMode) {
|
||||
const timeInfo = activeCard.createEl('div', { cls: 'immerse-time-info' });
|
||||
|
||||
25
styles.css
25
styles.css
@@ -757,15 +757,38 @@
|
||||
border: 1px solid var(--ft-border);
|
||||
color: var(--ft-text);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.immerse-quick-filter-btn:hover {
|
||||
background: var(--ft-primary);
|
||||
color: white;
|
||||
border-color: var(--ft-primary);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
|
||||
}
|
||||
|
||||
.immerse-quick-filter-btn.active {
|
||||
background: var(--ft-primary);
|
||||
color: white;
|
||||
border-color: var(--ft-primary);
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2),
|
||||
0 0 20px rgba(99, 102, 241, 0.4);
|
||||
animation: pulse-glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2),
|
||||
0 0 20px rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.3),
|
||||
0 0 25px rgba(99, 102, 241, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.immerse-report-generate-btn {
|
||||
|
||||
Reference in New Issue
Block a user