mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
refactor: clean up pomodoro view (#103)
- Remove quick action buttons and complex task selector for cleaner UI - Add functional +/- 30-second time adjustment buttons - Implement skip break functionality for better session flow - Fix daily note data appending vs overwriting issue - Update progress circle to use actual active time accounting for pauses - Redesign with larger timer (300px), minimalist buttons, and simplified layout - Fix session completion flow to automatically prepare next session timer - Improve button styling with borderless design and subtle hover effects
This commit is contained in:
parent
83dfc36639
commit
bfef3d09cc
3 changed files with 454 additions and 469 deletions
|
|
@ -475,10 +475,7 @@ export class PomodoroService {
|
|||
}
|
||||
}
|
||||
|
||||
// Update daily note with pomodoro count for work sessions
|
||||
if (session.type === 'work') {
|
||||
await this.updateDailyNotePomodoroCount();
|
||||
}
|
||||
// Daily note update is now handled by addSessionToHistory method
|
||||
|
||||
// Determine next session based on session history
|
||||
let shouldTakeLongBreak = false;
|
||||
|
|
@ -518,79 +515,44 @@ export class PomodoroService {
|
|||
this.playCompletionSound();
|
||||
}
|
||||
|
||||
// Clear current session
|
||||
// Clear current session and set up for next session
|
||||
this.state.currentSession = undefined;
|
||||
this.state.isRunning = false;
|
||||
this.state.timeRemaining = 0;
|
||||
|
||||
// Set up appropriate timer for next session
|
||||
if (session.type === 'work') {
|
||||
// After work session, prepare break timer
|
||||
const breakDuration = shouldTakeLongBreak
|
||||
? this.plugin.settings.pomodoroLongBreakDuration
|
||||
: this.plugin.settings.pomodoroShortBreakDuration;
|
||||
this.state.timeRemaining = breakDuration * 60;
|
||||
|
||||
// Auto-start break if configured, otherwise just prepare the timer
|
||||
if (this.plugin.settings.pomodoroAutoStartBreaks) {
|
||||
const timeout = setTimeout(() => this.startBreak(shouldTakeLongBreak), 1000) as unknown as number;
|
||||
this.cleanupTimeouts.add(timeout);
|
||||
}
|
||||
} else {
|
||||
// After break session, prepare work timer
|
||||
this.state.timeRemaining = this.plugin.settings.pomodoroWorkDuration * 60;
|
||||
|
||||
// Auto-start work if configured, otherwise just prepare the timer
|
||||
if (this.plugin.settings.pomodoroAutoStartWork) {
|
||||
const timeout = setTimeout(() => this.startPomodoro(), 1000) as unknown as number;
|
||||
this.cleanupTimeouts.add(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
await this.saveState();
|
||||
|
||||
// Auto-start next session if configured
|
||||
if (session.type === 'work' && this.plugin.settings.pomodoroAutoStartBreaks) {
|
||||
const timeout = setTimeout(() => this.startBreak(shouldTakeLongBreak), 1000) as unknown as number;
|
||||
this.cleanupTimeouts.add(timeout);
|
||||
} else if (session.type !== 'work' && this.plugin.settings.pomodoroAutoStartWork) {
|
||||
const timeout = setTimeout(() => this.startPomodoro(), 1000) as unknown as number;
|
||||
this.cleanupTimeouts.add(timeout);
|
||||
}
|
||||
|
||||
// Emit tick event to update UI with new timer duration
|
||||
this.plugin.emitter.trigger(EVENT_POMODORO_TICK, {
|
||||
timeRemaining: this.state.timeRemaining,
|
||||
session: this.state.currentSession
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async updateDailyNotePomodoroCount() {
|
||||
try {
|
||||
// Check if Daily Notes plugin is enabled
|
||||
if (!appHasDailyNotesPluginLoaded()) {
|
||||
console.warn('Daily Notes core plugin is not enabled, skipping pomodoro update');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert date to moment for the API
|
||||
const moment = (window as any).moment(new Date());
|
||||
|
||||
// Get all daily notes to check if one exists for today
|
||||
const allDailyNotes = getAllDailyNotes();
|
||||
let file = getDailyNote(moment, allDailyNotes);
|
||||
|
||||
if (!file) {
|
||||
// Daily note does not exist, create it using the core plugin
|
||||
try {
|
||||
file = await createDailyNote(moment);
|
||||
// Created daily note with 1 pomodoro
|
||||
} catch (createError: any) {
|
||||
if (createError.message.includes('File already exists')) {
|
||||
// File was created between our check and create attempt
|
||||
// Daily note was created by another process, try to get it again
|
||||
file = getDailyNote(moment, getAllDailyNotes());
|
||||
if (file instanceof TFile) {
|
||||
await this.plugin.app.fileManager.processFrontMatter(file, (frontmatter) => {
|
||||
const pomodoroField = this.plugin.fieldMapper.toUserField('pomodoros');
|
||||
if (!frontmatter[pomodoroField] || typeof frontmatter[pomodoroField] !== 'number') {
|
||||
frontmatter[pomodoroField] = 0;
|
||||
}
|
||||
frontmatter[pomodoroField]++;
|
||||
// Updated pomodoro count
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw createError;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Daily note exists, updating pomodoro count
|
||||
// Update existing daily note
|
||||
await this.plugin.app.fileManager.processFrontMatter(file, (frontmatter) => {
|
||||
const pomodoroField = this.plugin.fieldMapper.toUserField('pomodoros');
|
||||
if (!frontmatter[pomodoroField]) {
|
||||
frontmatter[pomodoroField] = 0;
|
||||
}
|
||||
frontmatter[pomodoroField]++;
|
||||
// Updated pomodoro count
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update daily note pomodoros:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private playCompletionSound() {
|
||||
try {
|
||||
|
|
@ -645,6 +607,33 @@ export class PomodoroService {
|
|||
return { ...this.state };
|
||||
}
|
||||
|
||||
adjustSessionTime(newTimeInSeconds: number): void {
|
||||
if (this.state.currentSession) {
|
||||
this.state.timeRemaining = newTimeInSeconds;
|
||||
this.state.currentSession.plannedDuration = Math.ceil(newTimeInSeconds / 60);
|
||||
this.saveState();
|
||||
|
||||
// Emit tick event to update UI
|
||||
this.plugin.emitter.trigger(EVENT_POMODORO_TICK, {
|
||||
timeRemaining: this.state.timeRemaining,
|
||||
session: this.state.currentSession
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
adjustPreparedTimer(newTimeInSeconds: number): void {
|
||||
if (!this.state.currentSession) {
|
||||
this.state.timeRemaining = newTimeInSeconds;
|
||||
this.saveState();
|
||||
|
||||
// Emit tick event to update UI
|
||||
this.plugin.emitter.trigger(EVENT_POMODORO_TICK, {
|
||||
timeRemaining: this.state.timeRemaining,
|
||||
session: this.state.currentSession
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isRunning(): boolean {
|
||||
return this.state.isRunning;
|
||||
}
|
||||
|
|
@ -768,9 +757,15 @@ export class PomodoroService {
|
|||
};
|
||||
|
||||
try {
|
||||
const history = await this.getSessionHistory();
|
||||
history.push(historyEntry);
|
||||
await this.saveSessionHistory(history);
|
||||
if (this.plugin.settings.pomodoroStorageLocation === 'daily-notes') {
|
||||
// For daily notes, add only this session to the appropriate daily note
|
||||
await this.addSingleSessionToDailyNote(historyEntry);
|
||||
} else {
|
||||
// For plugin storage, add to the full history
|
||||
const history = await this.getSessionHistory();
|
||||
history.push(historyEntry);
|
||||
await this.saveSessionHistory(history);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to add session to history:', error);
|
||||
}
|
||||
|
|
@ -933,6 +928,40 @@ export class PomodoroService {
|
|||
return grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single session to the appropriate daily note
|
||||
*/
|
||||
private async addSingleSessionToDailyNote(session: PomodoroSessionHistory): Promise<void> {
|
||||
try {
|
||||
const sessionDate = new Date(session.startTime);
|
||||
const moment = (window as any).moment(sessionDate);
|
||||
|
||||
// Get or create daily note
|
||||
const allDailyNotes = getAllDailyNotes();
|
||||
let dailyNote = getDailyNote(moment, allDailyNotes);
|
||||
|
||||
if (!dailyNote) {
|
||||
dailyNote = await createDailyNote(moment);
|
||||
}
|
||||
|
||||
// Update frontmatter
|
||||
const pomodoroField = this.plugin.fieldMapper.toUserField('pomodoros');
|
||||
|
||||
await this.plugin.app.fileManager.processFrontMatter(dailyNote, (frontmatter) => {
|
||||
// Get existing sessions
|
||||
const existingSessions = frontmatter[pomodoroField] || [];
|
||||
const existingIds = new Set(existingSessions.map((s: any) => s.id));
|
||||
|
||||
// Only add session if it doesn't already exist
|
||||
if (!existingIds.has(session.id)) {
|
||||
frontmatter[pomodoroField] = [...existingSessions, session];
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to add session to daily note:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific daily note with pomodoro sessions
|
||||
*/
|
||||
|
|
@ -953,7 +982,16 @@ export class PomodoroService {
|
|||
const pomodoroField = this.plugin.fieldMapper.toUserField('pomodoros');
|
||||
|
||||
await this.plugin.app.fileManager.processFrontMatter(dailyNote, (frontmatter) => {
|
||||
frontmatter[pomodoroField] = sessions;
|
||||
// Get existing sessions and append new ones
|
||||
const existingSessions = frontmatter[pomodoroField] || [];
|
||||
const existingIds = new Set(existingSessions.map((s: any) => s.id));
|
||||
|
||||
// Only add sessions that don't already exist
|
||||
const newSessions = sessions.filter(session => !existingIds.has(session.id));
|
||||
|
||||
if (newSessions.length > 0) {
|
||||
frontmatter[pomodoroField] = [...existingSessions, ...newSessions];
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to update daily note for ${dateStr}:`, error);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ItemView, WorkspaceLeaf, Notice, EventRef, Setting } from 'obsidian';
|
||||
import { ItemView, WorkspaceLeaf, Notice, EventRef } from 'obsidian';
|
||||
import TaskNotesPlugin from '../main';
|
||||
import {
|
||||
POMODORO_VIEW_TYPE,
|
||||
|
|
@ -27,6 +27,9 @@ export class PomodoroView extends ItemView {
|
|||
private statsDisplay: HTMLElement | null = null;
|
||||
private taskSelectButton: HTMLButtonElement | null = null;
|
||||
private currentSelectedTask: TaskInfo | null = null;
|
||||
private addTimeButton: HTMLButtonElement | null = null;
|
||||
private subtractTimeButton: HTMLButtonElement | null = null;
|
||||
private skipBreakButton: HTMLButtonElement | null = null;
|
||||
|
||||
// Cache stat elements to avoid innerHTML
|
||||
private statElements: {
|
||||
|
|
@ -116,6 +119,8 @@ export class PomodoroView extends ItemView {
|
|||
async render() {
|
||||
const container = this.contentEl.createDiv({ cls: 'tasknotes-plugin pomodoro-view' });
|
||||
|
||||
// Status display at the top
|
||||
this.statusDisplay = container.createDiv({ cls: 'pomodoro-view__status', text: 'Focus' });
|
||||
|
||||
// Timer display with progress circle
|
||||
const timerSection = container.createDiv({ cls: 'pomodoro-view__timer-section' });
|
||||
|
|
@ -126,34 +131,34 @@ export class PomodoroView extends ItemView {
|
|||
// Create SVG progress circle
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('class', 'pomodoro-view__progress-svg');
|
||||
svg.setAttribute('width', '240');
|
||||
svg.setAttribute('height', '240');
|
||||
svg.setAttribute('viewBox', '0 0 240 240');
|
||||
svg.setAttribute('width', '300');
|
||||
svg.setAttribute('height', '300');
|
||||
svg.setAttribute('viewBox', '0 0 300 300');
|
||||
this.progressContainer.appendChild(svg);
|
||||
|
||||
// Background circle
|
||||
const bgCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
||||
bgCircle.setAttributeNS(null, 'cx', '120');
|
||||
bgCircle.setAttributeNS(null, 'cy', '120');
|
||||
bgCircle.setAttributeNS(null, 'r', '110');
|
||||
bgCircle.setAttributeNS(null, 'cx', '150');
|
||||
bgCircle.setAttributeNS(null, 'cy', '150');
|
||||
bgCircle.setAttributeNS(null, 'r', '140');
|
||||
bgCircle.setAttributeNS(null, 'fill', 'none');
|
||||
bgCircle.setAttributeNS(null, 'stroke', 'var(--tn-border-color)');
|
||||
bgCircle.setAttributeNS(null, 'stroke-width', '4');
|
||||
bgCircle.setAttributeNS(null, 'stroke-width', '2');
|
||||
svg.appendChild(bgCircle);
|
||||
|
||||
// Progress circle
|
||||
this.progressCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle') as SVGCircleElement;
|
||||
this.progressCircle.setAttributeNS(null, 'cx', '120');
|
||||
this.progressCircle.setAttributeNS(null, 'cy', '120');
|
||||
this.progressCircle.setAttributeNS(null, 'r', '110');
|
||||
this.progressCircle.setAttributeNS(null, 'cx', '150');
|
||||
this.progressCircle.setAttributeNS(null, 'cy', '150');
|
||||
this.progressCircle.setAttributeNS(null, 'r', '140');
|
||||
this.progressCircle.setAttributeNS(null, 'fill', 'none');
|
||||
this.progressCircle.setAttributeNS(null, 'stroke', 'var(--tn-interactive-accent)');
|
||||
this.progressCircle.setAttributeNS(null, 'stroke-width', '6');
|
||||
this.progressCircle.setAttributeNS(null, 'stroke-width', '4');
|
||||
this.progressCircle.setAttributeNS(null, 'stroke-linecap', 'round');
|
||||
|
||||
// Calculate circumference: 2 * π * radius
|
||||
const radius = 110;
|
||||
const circumference = 2 * Math.PI * radius; // ≈ 691.15
|
||||
const radius = 140;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
|
||||
this.progressCircle.setAttributeNS(null, 'stroke-dasharray', circumference.toString());
|
||||
this.progressCircle.setAttributeNS(null, 'stroke-dashoffset', circumference.toString());
|
||||
|
|
@ -164,44 +169,37 @@ export class PomodoroView extends ItemView {
|
|||
const timerOverlay = this.progressContainer.createDiv({ cls: 'pomodoro-view__timer-overlay' });
|
||||
|
||||
// Timer display
|
||||
this.timerDisplay = timerOverlay.createDiv({ cls: 'pomodoro-view__timer-display', text: '25:00' });
|
||||
const defaultDuration = this.plugin.settings.pomodoroWorkDuration;
|
||||
const defaultTime = `${defaultDuration.toString().padStart(2, '0')}:00`;
|
||||
this.timerDisplay = timerOverlay.createDiv({ cls: 'pomodoro-view__timer-display', text: defaultTime });
|
||||
|
||||
// Status display
|
||||
this.statusDisplay = timerSection.createDiv({ cls: 'pomodoro-view__status', text: 'Ready to start' });
|
||||
// Time adjustment controls
|
||||
const timeControls = timerOverlay.createDiv({ cls: 'pomodoro-view__time-controls' });
|
||||
|
||||
// Task display
|
||||
this.subtractTimeButton = timeControls.createEl('button', {
|
||||
cls: 'pomodoro-view__time-adjust-button pomodoro-view__subtract-time',
|
||||
text: '-'
|
||||
});
|
||||
// Don't hide initially since we want them always visible
|
||||
|
||||
this.addTimeButton = timeControls.createEl('button', {
|
||||
cls: 'pomodoro-view__time-adjust-button pomodoro-view__add-time',
|
||||
text: '+'
|
||||
});
|
||||
// Don't hide initially since we want them always visible
|
||||
|
||||
// Task display (minimal)
|
||||
this.taskDisplay = container.createDiv({ cls: 'pomodoro-view__task-display' });
|
||||
|
||||
// Task selector
|
||||
// Simplified task selector
|
||||
const taskSelectorSection = container.createDiv({ cls: 'pomodoro-view__task-selector' });
|
||||
taskSelectorSection.createEl('label', { cls: 'pomodoro-view__task-selector-label', text: 'Select task (optional):' });
|
||||
|
||||
const taskSelectorContainer = taskSelectorSection.createDiv({ cls: 'pomodoro-view__task-selector-container' });
|
||||
|
||||
this.taskSelectButton = taskSelectorContainer.createEl('button', {
|
||||
this.taskSelectButton = taskSelectorSection.createEl('button', {
|
||||
cls: 'pomodoro-view__task-select-button',
|
||||
text: 'Choose task...'
|
||||
});
|
||||
|
||||
// Add click handler for task selector button
|
||||
this.registerDomEvent(this.taskSelectButton, 'click', async () => {
|
||||
await this.openTaskSelector();
|
||||
});
|
||||
|
||||
// Add clear button
|
||||
const clearButton = taskSelectorContainer.createEl('button', {
|
||||
cls: 'pomodoro-view__task-clear-button',
|
||||
text: 'Clear'
|
||||
});
|
||||
|
||||
this.registerDomEvent(clearButton, 'click', async () => {
|
||||
await this.selectTask(null);
|
||||
});
|
||||
|
||||
// Load and restore last selected task
|
||||
this.restoreLastSelectedTask();
|
||||
|
||||
// Main control section
|
||||
// Main control section - simplified
|
||||
const controlSection = container.createDiv({ cls: 'pomodoro-view__control-section' });
|
||||
|
||||
// Primary controls (main timer controls)
|
||||
|
|
@ -224,61 +222,25 @@ export class PomodoroView extends ItemView {
|
|||
});
|
||||
this.stopButton.addClass('pomodoro-view__stop-button--hidden');
|
||||
|
||||
// Quick start actions (grouped together)
|
||||
const quickStartSection = controlSection.createDiv({ cls: 'pomodoro-view__quick-start-section' });
|
||||
quickStartSection.createDiv({ cls: 'pomodoro-view__section-label', text: 'Quick start' });
|
||||
|
||||
const quickActions = quickStartSection.createDiv({ cls: 'pomodoro-view__quick-actions' });
|
||||
|
||||
const workButton = quickActions.createEl('button', {
|
||||
text: 'Work session',
|
||||
cls: 'pomodoro-view__quick-button pomodoro-view__work-button'
|
||||
// Skip break button (only shown after sessions)
|
||||
this.skipBreakButton = controlSection.createEl('button', {
|
||||
cls: 'pomodoro-view__skip-break-button',
|
||||
text: 'Skip break'
|
||||
});
|
||||
this.skipBreakButton.addClass('pomodoro-view__skip-break-button--hidden');
|
||||
|
||||
const shortBreakButton = quickActions.createEl('button', {
|
||||
text: 'Short break',
|
||||
cls: 'pomodoro-view__quick-button pomodoro-view__short-break-button'
|
||||
});
|
||||
|
||||
const longBreakButton = quickActions.createEl('button', {
|
||||
text: 'Long break',
|
||||
cls: 'pomodoro-view__quick-button pomodoro-view__long-break-button'
|
||||
});
|
||||
|
||||
// Statistics
|
||||
// Minimal stats at the bottom
|
||||
const statsSection = container.createDiv({ cls: 'pomodoro-view__stats-section' });
|
||||
const statsHeader = statsSection.createDiv({ cls: 'pomodoro-view__stats-header' });
|
||||
new Setting(statsHeader)
|
||||
.setName('Today\'s progress')
|
||||
.setHeading();
|
||||
|
||||
const viewStatsButton = statsHeader.createEl('button', {
|
||||
cls: 'pomodoro-view__view-stats-button',
|
||||
text: 'View all stats'
|
||||
});
|
||||
|
||||
this.registerDomEvent(viewStatsButton, 'click', async () => {
|
||||
await this.plugin.activatePomodoroStatsView();
|
||||
});
|
||||
|
||||
this.statsDisplay = statsSection.createDiv({ cls: 'pomodoro-view__stats' });
|
||||
|
||||
// Create stat elements and cache references
|
||||
// Create minimal stat elements
|
||||
const pomodoroStat = this.statsDisplay.createDiv({ cls: 'pomodoro-view__stat' });
|
||||
this.statElements.pomodoros = pomodoroStat.createSpan({ cls: 'pomodoro-view__stat-value', text: '0' });
|
||||
pomodoroStat.createSpan({ cls: 'pomodoro-view__stat-label', text: 'Pomodoros completed' });
|
||||
|
||||
const streakStat = this.statsDisplay.createDiv({ cls: 'pomodoro-view__stat' });
|
||||
this.statElements.streak = streakStat.createSpan({ cls: 'pomodoro-view__stat-value', text: '0' });
|
||||
streakStat.createSpan({ cls: 'pomodoro-view__stat-label', text: 'Current streak' });
|
||||
|
||||
const minutesStat = this.statsDisplay.createDiv({ cls: 'pomodoro-view__stat' });
|
||||
this.statElements.minutes = minutesStat.createSpan({ cls: 'pomodoro-view__stat-value', text: '0' });
|
||||
minutesStat.createSpan({ cls: 'pomodoro-view__stat-label', text: 'Minutes focused' });
|
||||
pomodoroStat.createSpan({ cls: 'pomodoro-view__stat-label', text: 'completed today' });
|
||||
|
||||
// Add event listeners
|
||||
this.registerDomEvent(this.startButton, 'click', async () => {
|
||||
// Prevent double clicks
|
||||
if (this.startButton?.hasClass('is-loading')) return;
|
||||
this.startButton?.addClass('pomodoro-view__start-button--loading');
|
||||
|
||||
|
|
@ -287,7 +249,17 @@ export class PomodoroView extends ItemView {
|
|||
if (state.currentSession && !state.isRunning) {
|
||||
await this.plugin.pomodoroService.resumePomodoro();
|
||||
} else {
|
||||
await this.plugin.pomodoroService.startPomodoro(this.currentSelectedTask || undefined);
|
||||
// Determine what to start based on current timer value
|
||||
const shortBreakDuration = this.plugin.settings.pomodoroShortBreakDuration * 60;
|
||||
const longBreakDuration = this.plugin.settings.pomodoroLongBreakDuration * 60;
|
||||
|
||||
if (state.timeRemaining === shortBreakDuration) {
|
||||
this.plugin.pomodoroService.startBreak(false); // Short break
|
||||
} else if (state.timeRemaining === longBreakDuration) {
|
||||
this.plugin.pomodoroService.startBreak(true); // Long break
|
||||
} else {
|
||||
await this.plugin.pomodoroService.startPomodoro(this.currentSelectedTask || undefined);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.startButton?.removeClass('pomodoro-view__start-button--loading');
|
||||
|
|
@ -302,18 +274,32 @@ export class PomodoroView extends ItemView {
|
|||
this.plugin.pomodoroService.stopPomodoro();
|
||||
});
|
||||
|
||||
this.registerDomEvent(workButton, 'click', async () => {
|
||||
await this.plugin.pomodoroService.startPomodoro(this.currentSelectedTask || undefined);
|
||||
this.registerDomEvent(this.skipBreakButton, 'click', () => {
|
||||
const state = this.plugin.pomodoroService.getState();
|
||||
if (state.currentSession) {
|
||||
// Currently in a break session, stop it
|
||||
this.plugin.pomodoroService.stopPomodoro();
|
||||
} else {
|
||||
// Ready to start break but user wants to skip, go directly to work
|
||||
this.plugin.pomodoroService.startPomodoro(this.currentSelectedTask || undefined);
|
||||
}
|
||||
});
|
||||
|
||||
this.registerDomEvent(shortBreakButton, 'click', () => {
|
||||
this.plugin.pomodoroService.startBreak(false);
|
||||
this.registerDomEvent(this.addTimeButton, 'click', () => {
|
||||
this.adjustSessionTime(30);
|
||||
});
|
||||
|
||||
this.registerDomEvent(longBreakButton, 'click', () => {
|
||||
this.plugin.pomodoroService.startBreak(true);
|
||||
this.registerDomEvent(this.subtractTimeButton, 'click', () => {
|
||||
this.adjustSessionTime(-30);
|
||||
});
|
||||
|
||||
this.registerDomEvent(this.taskSelectButton, 'click', async () => {
|
||||
await this.openTaskSelector();
|
||||
});
|
||||
|
||||
// Load and restore last selected task
|
||||
this.restoreLastSelectedTask();
|
||||
|
||||
// Initial display update
|
||||
this.updateDisplay();
|
||||
this.updateStats().catch(error => {
|
||||
|
|
@ -354,8 +340,8 @@ export class PomodoroView extends ItemView {
|
|||
// Update button text
|
||||
if (this.taskSelectButton) {
|
||||
if (task) {
|
||||
const displayText = task.title.length > 30
|
||||
? task.title.substring(0, 27) + '...'
|
||||
const displayText = task.title.length > 40
|
||||
? task.title.substring(0, 37) + '...'
|
||||
: task.title;
|
||||
this.taskSelectButton.textContent = displayText;
|
||||
this.taskSelectButton.title = task.title; // Full title in tooltip
|
||||
|
|
@ -492,14 +478,53 @@ export class PomodoroView extends ItemView {
|
|||
this.pauseButton.addClass('pomodoro-view__pause-button--hidden');
|
||||
this.stopButton.removeClass('pomodoro-view__stop-button--hidden');
|
||||
} else {
|
||||
// Idle
|
||||
// Idle - check if we're in post-completion state
|
||||
this.startButton.removeClass('pomodoro-view__start-button--hidden');
|
||||
this.startButton.textContent = 'Start';
|
||||
|
||||
// Determine what type of session to start next based on timer value
|
||||
const workDuration = this.plugin.settings.pomodoroWorkDuration * 60;
|
||||
const shortBreakDuration = this.plugin.settings.pomodoroShortBreakDuration * 60;
|
||||
const longBreakDuration = this.plugin.settings.pomodoroLongBreakDuration * 60;
|
||||
|
||||
if (state.timeRemaining === shortBreakDuration || state.timeRemaining === longBreakDuration) {
|
||||
// Timer is set to break duration, show start break
|
||||
this.startButton.textContent = state.timeRemaining === longBreakDuration ? 'Start Long Break' : 'Start Short Break';
|
||||
} else {
|
||||
// Timer is set to work duration or other, show start work
|
||||
this.startButton.textContent = state.timeRemaining === workDuration ? 'Start' : 'Start Work';
|
||||
}
|
||||
|
||||
this.pauseButton.addClass('pomodoro-view__pause-button--hidden');
|
||||
this.stopButton.addClass('pomodoro-view__stop-button--hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Update skip break button visibility
|
||||
if (this.skipBreakButton) {
|
||||
// Show skip break when:
|
||||
// 1. Currently in a break session
|
||||
// 2. Timer is ready to start a break (post work completion)
|
||||
const workDuration = this.plugin.settings.pomodoroWorkDuration * 60;
|
||||
const shortBreakDuration = this.plugin.settings.pomodoroShortBreakDuration * 60;
|
||||
const longBreakDuration = this.plugin.settings.pomodoroLongBreakDuration * 60;
|
||||
const isBreakTimerReady = !state.currentSession && (state.timeRemaining === shortBreakDuration || state.timeRemaining === longBreakDuration);
|
||||
|
||||
if ((state.currentSession && (state.currentSession.type === 'short-break' || state.currentSession.type === 'long-break')) || isBreakTimerReady) {
|
||||
this.skipBreakButton.removeClass('pomodoro-view__skip-break-button--hidden');
|
||||
if (isBreakTimerReady) {
|
||||
this.skipBreakButton.textContent = 'Skip break';
|
||||
}
|
||||
} else {
|
||||
this.skipBreakButton.addClass('pomodoro-view__skip-break-button--hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Update time adjustment button visibility - always show them
|
||||
if (this.addTimeButton && this.subtractTimeButton) {
|
||||
this.addTimeButton.removeClass('pomodoro-view__time-adjust-button--hidden');
|
||||
this.subtractTimeButton.removeClass('pomodoro-view__time-adjust-button--hidden');
|
||||
}
|
||||
|
||||
this.updateStats().catch(error => {
|
||||
console.error('Failed to update stats:', error);
|
||||
});
|
||||
|
|
@ -523,28 +548,57 @@ export class PomodoroView extends ItemView {
|
|||
}
|
||||
|
||||
private updateProgress(state: PomodoroState) {
|
||||
if (!this.progressCircle || !state.currentSession) {
|
||||
// No session active, reset progress
|
||||
if (this.progressCircle) {
|
||||
const radius = 110;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
this.progressCircle.setAttributeNS(null, 'stroke-dashoffset', circumference.toString());
|
||||
// Legacy classes already removed during BEM cleanup
|
||||
this.progressCircle.removeClass('pomodoro-view__progress-circle--work');
|
||||
this.progressCircle.removeClass('pomodoro-view__progress-circle--short-break');
|
||||
this.progressCircle.removeClass('pomodoro-view__progress-circle--long-break');
|
||||
}
|
||||
if (!this.progressCircle) return;
|
||||
|
||||
const radius = 140;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
|
||||
if (!state.currentSession) {
|
||||
// No session active - show full circle (ready to start)
|
||||
this.progressCircle.setAttributeNS(null, 'stroke-dashoffset', circumference.toString());
|
||||
this.progressCircle.removeClass('pomodoro-view__progress-circle--work');
|
||||
this.progressCircle.removeClass('pomodoro-view__progress-circle--short-break');
|
||||
this.progressCircle.removeClass('pomodoro-view__progress-circle--long-break');
|
||||
this.progressCircle.removeClass('pomodoro-view__progress-circle--warning');
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate progress
|
||||
const totalDuration = state.currentSession.plannedDuration * 60; // Convert to seconds
|
||||
const elapsed = totalDuration - state.timeRemaining;
|
||||
const progress = Math.max(0, Math.min(1, elapsed / totalDuration));
|
||||
// Calculate progress based on actual active time (accounting for pauses)
|
||||
const activePeriods = state.currentSession.activePeriods || [];
|
||||
let totalActiveSeconds = 0;
|
||||
|
||||
// Calculate stroke-dashoffset
|
||||
const radius = 110;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
// Sum up all completed active periods
|
||||
for (const period of activePeriods) {
|
||||
if (period.endTime) {
|
||||
// Completed period
|
||||
const start = new Date(period.startTime).getTime();
|
||||
const end = new Date(period.endTime).getTime();
|
||||
totalActiveSeconds += Math.floor((end - start) / 1000);
|
||||
} else if (state.isRunning) {
|
||||
// Current running period
|
||||
const start = new Date(period.startTime).getTime();
|
||||
const now = Date.now();
|
||||
totalActiveSeconds += Math.floor((now - start) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Use current planned duration (which gets updated when user adjusts time)
|
||||
const totalDuration = state.currentSession.plannedDuration * 60;
|
||||
|
||||
// Progress based on actual active time vs current planned duration
|
||||
const progress = Math.max(0, Math.min(1, totalActiveSeconds / totalDuration));
|
||||
|
||||
// Debug logging
|
||||
console.log('Circle progress:', {
|
||||
totalDuration: totalDuration/60,
|
||||
timeRemaining: state.timeRemaining/60,
|
||||
actualActiveTime: totalActiveSeconds/60,
|
||||
activePeriods: activePeriods.length,
|
||||
isRunning: state.isRunning,
|
||||
progress: Math.round(progress * 100) + '%'
|
||||
});
|
||||
|
||||
// Calculate stroke-dashoffset (progress goes clockwise)
|
||||
const offset = circumference - (progress * circumference);
|
||||
|
||||
// Update progress circle
|
||||
|
|
@ -574,26 +628,37 @@ export class PomodoroView extends ItemView {
|
|||
this.statElements.pomodoros.textContent = stats.pomodorosCompleted.toString();
|
||||
}
|
||||
|
||||
if (this.statElements.streak && this.statElements.streak.textContent !== stats.currentStreak.toString()) {
|
||||
this.statElements.streak.textContent = stats.currentStreak.toString();
|
||||
}
|
||||
|
||||
if (this.statElements.minutes && this.statElements.minutes.textContent !== stats.totalMinutes.toString()) {
|
||||
this.statElements.minutes.textContent = stats.totalMinutes.toString();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update stats:', error);
|
||||
// Fallback to show zeros if stats loading fails
|
||||
if (this.statElements.pomodoros) this.statElements.pomodoros.textContent = '0';
|
||||
if (this.statElements.streak) this.statElements.streak.textContent = '0';
|
||||
if (this.statElements.minutes) this.statElements.minutes.textContent = '0';
|
||||
}
|
||||
}
|
||||
|
||||
private adjustSessionTime(seconds: number) {
|
||||
const state = this.plugin.pomodoroService.getState();
|
||||
|
||||
// Allow adjustment at any time - calculate new time
|
||||
const newTime = Math.max(60, state.timeRemaining + seconds); // Minimum 1 minute
|
||||
|
||||
if (state.currentSession) {
|
||||
// Session exists (running or paused), adjust the current session
|
||||
this.plugin.pomodoroService.adjustSessionTime(newTime);
|
||||
} else {
|
||||
// No session (ready to start), adjust the prepared timer
|
||||
this.plugin.pomodoroService.adjustPreparedTimer(newTime);
|
||||
}
|
||||
|
||||
// Force an immediate update to ensure UI reflects changes
|
||||
const updatedState = this.plugin.pomodoroService.getState();
|
||||
this.updateTimer(updatedState.timeRemaining);
|
||||
this.updateProgress(updatedState);
|
||||
}
|
||||
|
||||
private onPomodoroComplete(session: PomodoroSession, nextType: string) {
|
||||
this.updateDisplay();
|
||||
|
||||
// Show completion message
|
||||
// Show completion message and skip break option
|
||||
if (this.statusDisplay) {
|
||||
if (session.type === 'work') {
|
||||
const isLongBreak = nextType === 'long-break';
|
||||
|
|
|
|||
|
|
@ -4,15 +4,17 @@
|
|||
|
||||
/* Pomodoro View Container - Root Block */
|
||||
.tasknotes-plugin .pomodoro-view {
|
||||
padding: var(--tn-spacing-md);
|
||||
padding: var(--tn-spacing-xl);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
gap: var(--tn-spacing-lg);
|
||||
gap: var(--tn-spacing-xl);
|
||||
max-width: 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
justify-content: center;
|
||||
background: var(--tn-bg-primary);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
|
|
@ -38,9 +40,9 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
margin: 0 auto var(--tn-spacing-sm) auto;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Progress SVG */
|
||||
|
|
@ -96,13 +98,14 @@
|
|||
|
||||
/* Timer Display */
|
||||
.tasknotes-plugin .pomodoro-view__timer-display {
|
||||
font-size: 2.5rem;
|
||||
font-size: 3.5rem;
|
||||
color: var(--tn-text-normal);
|
||||
text-align: center;
|
||||
font-family: var(--tn-font-mono);
|
||||
letter-spacing: 0.05em;
|
||||
transition: color var(--tn-transition-fast);
|
||||
font-weight: var(--tn-font-weight-bold);
|
||||
margin-bottom: var(--tn-spacing-md);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__timer-display--warning {
|
||||
|
|
@ -117,12 +120,13 @@
|
|||
|
||||
/* Status Display */
|
||||
.tasknotes-plugin .pomodoro-view__status {
|
||||
font-size: var(--tn-font-size-lg);
|
||||
font-size: var(--tn-font-size-xl);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
text-align: center;
|
||||
color: var(--tn-text-muted);
|
||||
text-transform: capitalize;
|
||||
margin-top: var(--tn-spacing-sm);
|
||||
margin-bottom: var(--tn-spacing-lg);
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__status--work {
|
||||
|
|
@ -147,20 +151,20 @@
|
|||
|
||||
/* Task Display Container */
|
||||
.tasknotes-plugin .pomodoro-view__task-display {
|
||||
min-height: 60px;
|
||||
min-width: 300px;
|
||||
min-height: 40px;
|
||||
min-width: 200px;
|
||||
width: auto;
|
||||
max-width: 500px;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--tn-spacing-md);
|
||||
background: var(--tn-bg-secondary);
|
||||
border-radius: var(--tn-radius-md);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
padding: var(--tn-spacing-sm);
|
||||
background: transparent;
|
||||
border-radius: var(--tn-radius-sm);
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
max-height: 200px;
|
||||
max-height: 100px;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
transition: all var(--tn-transition-medium);
|
||||
|
|
@ -194,18 +198,13 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-label {
|
||||
.tasknotes-plugin .pomodoro-view__task-title {
|
||||
font-size: var(--tn-font-size-md);
|
||||
color: var(--tn-text-muted);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-title {
|
||||
font-size: var(--tn-font-size-lg);
|
||||
color: var(--tn-text-normal);
|
||||
font-weight: var(--tn-font-weight-semibold);
|
||||
max-width: 300px;
|
||||
max-width: 400px;
|
||||
word-wrap: break-word;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
|
|
@ -217,41 +216,31 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--tn-spacing-md);
|
||||
max-width: 500px;
|
||||
gap: var(--tn-spacing-sm);
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: var(--tn-spacing-md) 0;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-selector-label {
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
color: var(--tn-text-normal);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-selector-container {
|
||||
display: flex;
|
||||
gap: var(--tn-spacing-sm);
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Task Select Button */
|
||||
.tasknotes-plugin .pomodoro-view__task-select-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
padding: var(--tn-spacing-sm) var(--tn-spacing-md);
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: var(--tn-spacing-sm) var(--tn-spacing-lg);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
border-radius: var(--tn-radius-sm);
|
||||
background: var(--tn-bg-primary);
|
||||
border-radius: var(--tn-radius-lg);
|
||||
background: var(--tn-bg-secondary);
|
||||
color: var(--tn-text-normal);
|
||||
cursor: pointer;
|
||||
font-size: var(--tn-font-size-lg);
|
||||
font-size: var(--tn-font-size-sm);
|
||||
font-weight: var(--tn-font-weight-normal);
|
||||
transition: all var(--tn-transition-fast);
|
||||
min-height: var(--tn-button-height-md);
|
||||
min-height: var(--tn-button-height-sm);
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-select-button:hover {
|
||||
|
|
@ -264,24 +253,6 @@
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Task Clear Button */
|
||||
.tasknotes-plugin .pomodoro-view__task-clear-button {
|
||||
padding: var(--tn-spacing-sm) var(--tn-spacing-md);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
border-radius: var(--tn-radius-sm);
|
||||
background: var(--tn-bg-secondary);
|
||||
color: var(--tn-text-normal);
|
||||
cursor: pointer;
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
transition: all var(--tn-transition-fast);
|
||||
min-height: var(--tn-button-height-md);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-clear-button:hover {
|
||||
background: var(--tn-interactive-hover);
|
||||
border-color: var(--tn-border-color-hover);
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
CONTROL SECTION - Main Controls Container
|
||||
|
|
@ -293,13 +264,12 @@
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--tn-spacing-lg);
|
||||
padding: var(--tn-spacing-lg) var(--tn-spacing-md);
|
||||
border-top: 1px solid var(--tn-border-color);
|
||||
border-bottom: 1px solid var(--tn-border-color);
|
||||
margin: var(--tn-spacing-md) auto;
|
||||
gap: var(--tn-spacing-md);
|
||||
padding: var(--tn-spacing-md);
|
||||
border: none;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
/* Primary Controls Container */
|
||||
|
|
@ -316,12 +286,12 @@
|
|||
|
||||
/* Primary Control Buttons */
|
||||
.tasknotes-plugin .pomodoro-view__start-button {
|
||||
min-width: 120px;
|
||||
padding: var(--tn-spacing-md) var(--tn-spacing-lg);
|
||||
min-width: 200px;
|
||||
padding: var(--tn-spacing-lg) var(--tn-spacing-xl);
|
||||
background: var(--tn-interactive-accent);
|
||||
color: var(--tn-bg-primary);
|
||||
border: 1px solid var(--tn-interactive-accent);
|
||||
border-radius: var(--tn-radius-md);
|
||||
border: 2px solid var(--tn-interactive-accent);
|
||||
border-radius: var(--tn-radius-lg);
|
||||
font-size: var(--tn-font-size-lg);
|
||||
font-weight: var(--tn-font-weight-semibold);
|
||||
cursor: pointer;
|
||||
|
|
@ -336,12 +306,12 @@
|
|||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__pause-button {
|
||||
min-width: 120px;
|
||||
padding: var(--tn-spacing-md) var(--tn-spacing-lg);
|
||||
background: var(--tn-color-warning);
|
||||
color: var(--tn-bg-primary);
|
||||
border: 1px solid var(--tn-color-warning);
|
||||
border-radius: var(--tn-radius-md);
|
||||
min-width: 200px;
|
||||
padding: var(--tn-spacing-lg) var(--tn-spacing-xl);
|
||||
background: transparent;
|
||||
color: var(--tn-interactive-accent);
|
||||
border: 2px solid var(--tn-interactive-accent);
|
||||
border-radius: var(--tn-radius-lg);
|
||||
font-size: var(--tn-font-size-lg);
|
||||
font-weight: var(--tn-font-weight-semibold);
|
||||
cursor: pointer;
|
||||
|
|
@ -350,33 +320,96 @@
|
|||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__pause-button:hover {
|
||||
background: var(--tn-color-warning);
|
||||
opacity: 0.9;
|
||||
background: var(--tn-interactive-accent);
|
||||
color: var(--tn-bg-primary);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tn-shadow-medium);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__stop-button {
|
||||
min-width: 120px;
|
||||
padding: var(--tn-spacing-md) var(--tn-spacing-lg);
|
||||
background: var(--tn-color-error);
|
||||
color: var(--tn-bg-primary);
|
||||
border: 1px solid var(--tn-color-error);
|
||||
padding: var(--tn-spacing-sm) var(--tn-spacing-md);
|
||||
background: transparent;
|
||||
color: var(--tn-text-muted);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
border-radius: var(--tn-radius-md);
|
||||
font-size: var(--tn-font-size-lg);
|
||||
font-weight: var(--tn-font-weight-semibold);
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
cursor: pointer;
|
||||
transition: all var(--tn-transition-fast);
|
||||
min-height: var(--tn-button-height-lg);
|
||||
min-height: var(--tn-button-height-md);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__stop-button:hover {
|
||||
background: var(--tn-color-error);
|
||||
opacity: 0.9;
|
||||
color: var(--tn-bg-primary);
|
||||
border-color: var(--tn-color-error);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tn-shadow-medium);
|
||||
}
|
||||
|
||||
/* Skip Break Button */
|
||||
.tasknotes-plugin .pomodoro-view__skip-break-button {
|
||||
min-width: 120px;
|
||||
padding: var(--tn-spacing-sm) var(--tn-spacing-md);
|
||||
background: transparent;
|
||||
color: var(--tn-text-muted);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
border-radius: var(--tn-radius-md);
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
cursor: pointer;
|
||||
transition: all var(--tn-transition-fast);
|
||||
min-height: var(--tn-button-height-md);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__skip-break-button:hover {
|
||||
background: var(--tn-interactive-hover);
|
||||
border-color: var(--tn-interactive-accent);
|
||||
color: var(--tn-interactive-accent);
|
||||
}
|
||||
|
||||
/* Time Adjustment Controls */
|
||||
.tasknotes-plugin .pomodoro-view__time-controls {
|
||||
display: flex;
|
||||
gap: var(--tn-spacing-md);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: var(--tn-spacing-sm);
|
||||
pointer-events: auto; /* Re-enable pointer events for the buttons */
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__time-adjust-button {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--tn-text-muted);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-weight: var(--tn-font-weight-normal);
|
||||
transition: all var(--tn-transition-fast);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__time-adjust-button:hover {
|
||||
background: var(--tn-bg-secondary);
|
||||
color: var(--tn-interactive-accent);
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__time-adjust-button:focus {
|
||||
outline: none;
|
||||
background: var(--tn-bg-secondary);
|
||||
color: var(--tn-interactive-accent);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Button States */
|
||||
.tasknotes-plugin .pomodoro-view__start-button--loading {
|
||||
opacity: 0.7;
|
||||
|
|
@ -385,109 +418,12 @@
|
|||
|
||||
.tasknotes-plugin .pomodoro-view__start-button--hidden,
|
||||
.tasknotes-plugin .pomodoro-view__pause-button--hidden,
|
||||
.tasknotes-plugin .pomodoro-view__stop-button--hidden {
|
||||
.tasknotes-plugin .pomodoro-view__stop-button--hidden,
|
||||
.tasknotes-plugin .pomodoro-view__skip-break-button--hidden,
|
||||
.tasknotes-plugin .pomodoro-view__time-adjust-button--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
QUICK START SECTION
|
||||
================================================ */
|
||||
|
||||
/* Quick Start Container */
|
||||
.tasknotes-plugin .pomodoro-view__quick-start-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--tn-spacing-sm);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__section-label {
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-weight: var(--tn-font-weight-semibold);
|
||||
color: var(--tn-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
text-align: center;
|
||||
margin-bottom: var(--tn-spacing-xs);
|
||||
}
|
||||
|
||||
/* Quick Actions Container */
|
||||
.tasknotes-plugin .pomodoro-view__quick-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--tn-spacing-sm);
|
||||
flex-wrap: wrap;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Quick Action Buttons */
|
||||
.tasknotes-plugin .pomodoro-view__quick-button {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
padding: var(--tn-spacing-sm) var(--tn-spacing-md);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
border-radius: var(--tn-radius-md);
|
||||
background: var(--tn-bg-secondary);
|
||||
color: var(--tn-text-normal);
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
cursor: pointer;
|
||||
transition: all var(--tn-transition-fast);
|
||||
min-height: var(--tn-button-height-md);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__quick-button:hover {
|
||||
background: var(--tn-interactive-hover);
|
||||
border-color: var(--tn-interactive-accent);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__work-button {
|
||||
background: var(--tn-status-in-progress-color, var(--tn-interactive-accent));
|
||||
color: var(--tn-bg-primary);
|
||||
border-color: var(--tn-status-in-progress-color, var(--tn-interactive-accent));
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__work-button:hover {
|
||||
background: var(--tn-status-in-progress-color, var(--tn-interactive-accent));
|
||||
color: var(--tn-bg-primary);
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tn-shadow-medium);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__short-break-button {
|
||||
background: var(--tn-priority-medium-color, #10B981);
|
||||
color: var(--tn-bg-primary);
|
||||
border-color: var(--tn-priority-medium-color, #10B981);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__short-break-button:hover {
|
||||
background: var(--tn-priority-medium-color, #10B981);
|
||||
color: var(--tn-bg-primary);
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tn-shadow-medium);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__long-break-button {
|
||||
background: var(--tn-priority-high-color, #3B82F6);
|
||||
color: var(--tn-bg-primary);
|
||||
border-color: var(--tn-priority-high-color, #3B82F6);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__long-break-button:hover {
|
||||
background: var(--tn-priority-high-color, #3B82F6);
|
||||
color: var(--tn-bg-primary);
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tn-shadow-medium);
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
STATISTICS SECTION
|
||||
|
|
@ -495,77 +431,45 @@
|
|||
|
||||
/* Statistics Section Container */
|
||||
.tasknotes-plugin .pomodoro-view__stats-section {
|
||||
border-top: 1px solid var(--tn-border-color);
|
||||
padding: var(--tn-spacing-xl) var(--tn-spacing-md) 0;
|
||||
border: none;
|
||||
padding: var(--tn-spacing-md) 0;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
max-width: 300px;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Statistics Header */
|
||||
.tasknotes-plugin .pomodoro-view__stats-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--tn-spacing-md);
|
||||
}
|
||||
|
||||
/* Statistics Title */
|
||||
.tasknotes-plugin .pomodoro-view__stats-title {
|
||||
font-size: var(--tn-font-size-lg);
|
||||
font-weight: var(--tn-font-weight-semibold);
|
||||
color: var(--tn-text-normal);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__view-stats-button {
|
||||
padding: var(--tn-spacing-xs) var(--tn-spacing-sm);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
border-radius: var(--tn-radius-sm);
|
||||
background: var(--tn-bg-primary);
|
||||
color: var(--tn-text-normal);
|
||||
cursor: pointer;
|
||||
font-size: var(--tn-font-size-sm);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
transition: all var(--tn-transition-fast);
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__view-stats-button:hover {
|
||||
background: var(--tn-interactive-hover);
|
||||
border-color: var(--tn-interactive-accent);
|
||||
}
|
||||
|
||||
/* Statistics Grid */
|
||||
.tasknotes-plugin .pomodoro-view__stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: var(--tn-spacing-md);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--tn-spacing-sm);
|
||||
}
|
||||
|
||||
/* Individual Stat */
|
||||
.tasknotes-plugin .pomodoro-view__stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: var(--tn-spacing-md);
|
||||
background: var(--tn-bg-secondary);
|
||||
border-radius: var(--tn-radius-md);
|
||||
border: 1px solid var(--tn-border-color);
|
||||
gap: var(--tn-spacing-xs);
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__stat-value {
|
||||
font-size: var(--tn-font-size-2xl);
|
||||
font-size: var(--tn-font-size-lg);
|
||||
font-weight: var(--tn-font-weight-bold);
|
||||
color: var(--tn-interactive-accent);
|
||||
margin-bottom: var(--tn-spacing-xs);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__stat-label {
|
||||
font-size: var(--tn-font-size-md);
|
||||
font-size: var(--tn-font-size-sm);
|
||||
color: var(--tn-text-muted);
|
||||
font-weight: var(--tn-font-weight-medium);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ================================================
|
||||
|
|
@ -579,12 +483,12 @@
|
|||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__progress-container {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__timer-display {
|
||||
font-size: 2rem;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__control-section {
|
||||
|
|
@ -606,41 +510,20 @@
|
|||
max-width: 250px;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__quick-actions {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--tn-spacing-sm);
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__quick-button {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-selector-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__task-clear-button {
|
||||
align-self: center;
|
||||
max-width: 100px;
|
||||
gap: var(--tn-spacing-xs);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.tasknotes-plugin .pomodoro-view__progress-container {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.tasknotes-plugin .pomodoro-view__timer-display {
|
||||
font-size: 1.8rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -665,10 +548,9 @@
|
|||
.tasknotes-plugin .pomodoro-view__start-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__pause-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__stop-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__quick-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__task-select-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__task-clear-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__view-stats-button:focus {
|
||||
.tasknotes-plugin .pomodoro-view__skip-break-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__time-adjust-button:focus,
|
||||
.tasknotes-plugin .pomodoro-view__task-select-button:focus {
|
||||
outline: 2px solid var(--tn-interactive-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue