Skip to content

Commit 7a62b24

Browse files
committed
refactor(timer): redesign timer UI with text-based interface
Replace complex button-based UI with simple text links for better integration and cleaner appearance. Improve subtask detection logic to only show timers on actual parent tasks. - Replace buttons with underlined text links for all actions - Remove complex CSS styling in favor of minimal text styling - Fix subtask detection to stop at same/lower indentation levels - Update real-time display to refresh every second - Add click event propagation prevention - Simplify UI to show "⏱ time | action | action" format - Remove 280+ lines of unnecessary CSS styling
1 parent 0d82b84 commit 7a62b24

2 files changed

Lines changed: 79 additions & 406 deletions

File tree

src/editor-ext/taskTimer.ts

Lines changed: 76 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -65,94 +65,69 @@ class TaskTimerWidget extends WidgetType {
6565
return this.dom;
6666
}
6767

68-
this.dom = createDiv({ cls: `task-timer-widget ${this.timerState?.status || 'idle'}` });
69-
this.createTimeDisplay(this.dom);
70-
this.createButtons(this.dom);
71-
return this.dom;
72-
}
73-
74-
/**
75-
* Create time display area
76-
*/
77-
private createTimeDisplay(container: HTMLElement): void {
78-
const timeSpan = container.createSpan({ cls: "task-timer-display" });
68+
// Create a simple text-based widget
69+
this.dom = createDiv({ cls: 'task-timer-widget' });
70+
this.dom.style.cssText = 'margin: 2px 0; font-size: 0.9em; color: var(--text-muted);';
7971
this.updateTimerState();
80-
this.refreshTimeDisplay();
72+
this.createContent();
73+
return this.dom;
8174
}
8275

8376
/**
84-
* Create action buttons
77+
* Create content based on timer state
8578
*/
86-
private createButtons(container: HTMLElement): void {
87-
const buttonContainer = container.createDiv({ cls: "task-timer-buttons" });
79+
private createContent(): void {
80+
if (!this.dom) return;
8881

89-
this.updateTimerState();
82+
this.dom.empty();
9083

9184
if (!this.timerState || this.timerState.status === 'idle') {
92-
const startButton = buttonContainer.createEl("button", {
93-
cls: "task-timer-button start",
94-
text: "Start Task"
95-
});
96-
startButton.addEventListener("click", (e) => {
85+
// Create text-style start button
86+
const startSpan = this.dom.createSpan();
87+
startSpan.style.cssText = 'cursor: pointer; text-decoration: underline; color: var(--text-accent);';
88+
startSpan.setText('Start Task');
89+
startSpan.addEventListener('click', (e) => {
9790
e.preventDefault();
91+
e.stopPropagation();
9892
this.startTimer();
9993
});
100-
} else if (this.timerState.status === 'running') {
101-
const pauseButton = buttonContainer.createEl("button", {
102-
cls: "task-timer-button pause",
103-
text: "Pause"
104-
});
105-
pauseButton.addEventListener("click", (e) => {
106-
e.preventDefault();
107-
this.pauseTimer();
108-
});
109-
110-
const resetButton = buttonContainer.createEl("button", {
111-
cls: "task-timer-button reset",
112-
text: "Reset"
113-
});
114-
resetButton.addEventListener("click", (e) => {
115-
e.preventDefault();
116-
this.resetTimer();
117-
});
118-
119-
const completeButton = buttonContainer.createEl("button", {
120-
cls: "task-timer-button complete",
121-
text: "Complete"
122-
});
123-
completeButton.addEventListener("click", (e) => {
124-
e.preventDefault();
125-
this.completeTimer();
126-
});
127-
} else if (this.timerState.status === 'paused') {
128-
const resumeButton = buttonContainer.createEl("button", {
129-
cls: "task-timer-button resume",
130-
text: "Resume"
131-
});
132-
resumeButton.addEventListener("click", (e) => {
133-
e.preventDefault();
134-
this.resumeTimer();
135-
});
136-
137-
const resetButton = buttonContainer.createEl("button", {
138-
cls: "task-timer-button reset",
139-
text: "Reset"
140-
});
141-
resetButton.addEventListener("click", (e) => {
142-
e.preventDefault();
143-
this.resetTimer();
144-
});
145-
146-
const completeButton = buttonContainer.createEl("button", {
147-
cls: "task-timer-button complete",
148-
text: "Complete"
149-
});
150-
completeButton.addEventListener("click", (e) => {
151-
e.preventDefault();
152-
this.completeTimer();
153-
});
94+
} else {
95+
// Show elapsed time
96+
const elapsedMs = Date.now() - this.timerState.startTime + (this.timerState.elapsed || 0);
97+
const formattedTime = TaskTimerFormatter.formatDuration(elapsedMs, this.settings.timeFormat);
98+
const timeSpan = this.dom.createSpan();
99+
timeSpan.setText(`⏱ ${formattedTime} `);
100+
101+
// Add action links
102+
if (this.timerState.status === 'running') {
103+
this.addActionLink('Pause', () => this.pauseTimer());
104+
this.dom.appendText(' | ');
105+
this.addActionLink('Complete', () => this.completeTimer());
106+
} else if (this.timerState.status === 'paused') {
107+
this.addActionLink('Resume', () => this.resumeTimer());
108+
this.dom.appendText(' | ');
109+
this.addActionLink('Complete', () => this.completeTimer());
110+
}
111+
this.dom.appendText(' | ');
112+
this.addActionLink('Reset', () => this.resetTimer());
154113
}
155114
}
115+
116+
/**
117+
* Add clickable action link
118+
*/
119+
private addActionLink(text: string, action: () => void): void {
120+
if (!this.dom) return;
121+
122+
const link = this.dom.createSpan();
123+
link.style.cssText = 'cursor: pointer; text-decoration: underline; color: var(--text-accent);';
124+
link.setText(text);
125+
link.addEventListener('click', (e) => {
126+
e.preventDefault();
127+
e.stopPropagation();
128+
action();
129+
});
130+
}
156131

157132
/**
158133
* Start timer
@@ -340,8 +315,8 @@ class TaskTimerWidget extends WidgetType {
340315
}
341316

342317
this.updateInterval = window.setInterval(() => {
343-
this.refreshTimeDisplay();
344-
}, 5000); // Update every 5 seconds for performance
318+
this.createContent(); // Update the entire content
319+
}, 1000); // Update every second
345320
}
346321

347322
/**
@@ -354,51 +329,14 @@ class TaskTimerWidget extends WidgetType {
354329
}
355330
}
356331

357-
/**
358-
* Refresh only the time display (performance optimization)
359-
*/
360-
private refreshTimeDisplay(): void {
361-
if (!this.dom) return;
362-
363-
const timeDisplay = this.dom.querySelector('.task-timer-display') as HTMLElement;
364-
if (!timeDisplay) return;
365-
366-
this.updateTimerState();
367-
368-
if (this.timerState) {
369-
const elapsedMs = Date.now() - this.timerState.startTime;
370-
const formattedTime = TaskTimerFormatter.formatDuration(elapsedMs, this.settings.timeFormat);
371-
372-
// Only update if the text has changed
373-
if (timeDisplay.textContent !== formattedTime) {
374-
timeDisplay.textContent = formattedTime;
375-
}
376-
377-
// Update animation class
378-
if (this.timerState.status === 'running') {
379-
timeDisplay.addClass('running');
380-
} else {
381-
timeDisplay.removeClass('running');
382-
}
383-
} else {
384-
timeDisplay.textContent = "00:00";
385-
timeDisplay.removeClass('running');
386-
}
387-
}
388-
389332
/**
390333
* Refresh the entire UI (used when state changes significantly)
391334
*/
392335
private refreshUI(): void {
393336
if (!this.dom) return;
394337

395-
// Update widget class
396-
this.dom.className = `task-timer-widget ${this.timerState?.status || 'idle'}`;
397-
398-
// Clear and recreate content
399-
this.dom.empty();
400-
this.createTimeDisplay(this.dom);
401-
this.createButtons(this.dom);
338+
this.updateTimerState();
339+
this.createContent();
402340
}
403341

404342
destroy() {
@@ -536,18 +474,33 @@ function hasSubTasks(rangeText: string, parentLineText: string): boolean {
536474
const parentMatch = parentLineText.match(/^(\s*)/);
537475
const parentIndent = parentMatch ? parentMatch[1].length : 0;
538476

477+
console.log(`[TaskTimer] Checking subtasks for parent with indent ${parentIndent}`);
478+
539479
// Check subsequent lines for subtasks
480+
let foundSubtask = false;
540481
for (let i = 1; i < lines.length; i++) {
541482
const line = lines[i];
542-
if (isTaskLine(line)) {
543-
const lineMatch = line.match(/^(\s*)/);
544-
const lineIndent = lineMatch ? lineMatch[1].length : 0;
545-
if (lineIndent > parentIndent) {
546-
return true; // Found a subtask with greater indentation
547-
}
483+
484+
// Skip empty lines
485+
if (!line.trim()) continue;
486+
487+
const lineMatch = line.match(/^(\s*)/);
488+
const lineIndent = lineMatch ? lineMatch[1].length : 0;
489+
490+
// If we find a line with same or less indentation that's not empty, stop checking
491+
if (lineIndent <= parentIndent) {
492+
break;
493+
}
494+
495+
// Check if this indented line is a task
496+
if (isTaskLine(line) && lineIndent > parentIndent) {
497+
console.log(`[TaskTimer] Found subtask: ${line.trim()}`);
498+
foundSubtask = true;
499+
break;
548500
}
549501
}
550-
return false;
502+
503+
return foundSubtask;
551504
}
552505

553506
function extractBlockRef(lineText: string): string | undefined {

0 commit comments

Comments
 (0)