feat: Implement Pomodoro plugin core logic, UI, settings, and daily note logging
This commit is contained in:
parent
6d09ce3e39
commit
ccb2ec8439
|
@ -15,7 +15,7 @@ const context = await esbuild.context({
|
|||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
entryPoints: ["main.ts"],
|
||||
entryPoints: ["src/main.ts"],
|
||||
bundle: true,
|
||||
external: [
|
||||
"obsidian",
|
||||
|
|
134
main.ts
134
main.ts
|
@ -1,134 +0,0 @@
|
|||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
|
||||
|
||||
// Remember to rename these classes and interfaces!
|
||||
|
||||
interface MyPluginSettings {
|
||||
mySetting: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: MyPluginSettings = {
|
||||
mySetting: 'default'
|
||||
}
|
||||
|
||||
export default class MyPlugin extends Plugin {
|
||||
settings: MyPluginSettings;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// This creates an icon in the left ribbon.
|
||||
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
|
||||
// Called when the user clicks the icon.
|
||||
new Notice('This is a notice!');
|
||||
});
|
||||
// Perform additional things with the ribbon
|
||||
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
||||
|
||||
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
||||
const statusBarItemEl = this.addStatusBarItem();
|
||||
statusBarItemEl.setText('Status Bar Text');
|
||||
|
||||
// This adds a simple command that can be triggered anywhere
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-simple',
|
||||
name: 'Open sample modal (simple)',
|
||||
callback: () => {
|
||||
new SampleModal(this.app).open();
|
||||
}
|
||||
});
|
||||
// This adds an editor command that can perform some operation on the current editor instance
|
||||
this.addCommand({
|
||||
id: 'sample-editor-command',
|
||||
name: 'Sample editor command',
|
||||
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||
console.log(editor.getSelection());
|
||||
editor.replaceSelection('Sample Editor Command');
|
||||
}
|
||||
});
|
||||
// This adds a complex command that can check whether the current state of the app allows execution of the command
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-complex',
|
||||
name: 'Open sample modal (complex)',
|
||||
checkCallback: (checking: boolean) => {
|
||||
// Conditions to check
|
||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (markdownView) {
|
||||
// If checking is true, we're simply "checking" if the command can be run.
|
||||
// If checking is false, then we want to actually perform the operation.
|
||||
if (!checking) {
|
||||
new SampleModal(this.app).open();
|
||||
}
|
||||
|
||||
// This command will only show up in Command Palette when the check function returns true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||
this.addSettingTab(new SampleSettingTab(this.app, this));
|
||||
|
||||
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
|
||||
// Using this function will automatically remove the event listener when this plugin is disabled.
|
||||
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
|
||||
console.log('click', evt);
|
||||
});
|
||||
|
||||
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
|
||||
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
|
||||
}
|
||||
|
||||
onunload() {
|
||||
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
||||
class SampleModal extends Modal {
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
contentEl.setText('Woah!');
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class SampleSettingTab extends PluginSettingTab {
|
||||
plugin: MyPlugin;
|
||||
|
||||
constructor(app: App, plugin: MyPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Setting #1')
|
||||
.setDesc('It\'s a secret')
|
||||
.addText(text => text
|
||||
.setPlaceholder('Enter your secret')
|
||||
.setValue(this.plugin.settings.mySetting)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.mySetting = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -20,5 +20,9 @@
|
|||
"obsidian": "latest",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"moment": "^2.30.1",
|
||||
"obsidian-daily-notes-interface": "^0.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
|
||||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, WorkspaceLeaf } from 'obsidian';
|
||||
import { PomodoroView, POMODORO_VIEW_TYPE } from './pomodoroView';
|
||||
import { PomodoroTimer } from './timer';
|
||||
import { createDailyNote, getDailyNote, getAllDailyNotes } from 'obsidian-daily-notes-interface';
|
||||
import moment from 'moment';
|
||||
|
||||
|
||||
// Remember to rename these classes and interfaces!
|
||||
|
||||
interface PomodoroPluginSettings {
|
||||
pomodoroDuration: number;
|
||||
shortBreakDuration: number;
|
||||
longBreakDuration: number;
|
||||
cyclesBeforeLongBreak: number;
|
||||
autoStartNextSession: boolean;
|
||||
enableNotifications: boolean;
|
||||
enableSound: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: PomodoroPluginSettings = {
|
||||
pomodoroDuration: 25, // minutes
|
||||
shortBreakDuration: 5, // minutes
|
||||
longBreakDuration: 15, // minutes
|
||||
cyclesBeforeLongBreak: 4,
|
||||
autoStartNextSession: false,
|
||||
enableNotifications: true,
|
||||
enableSound: false,
|
||||
}
|
||||
|
||||
export default class PomodoroPlugin extends Plugin {
|
||||
settings: PomodoroPluginSettings;
|
||||
timer: PomodoroTimer;
|
||||
statusBarItemEl: HTMLElement;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
this.timer = new PomodoroTimer(
|
||||
this.settings.pomodoroDuration * 60,
|
||||
this.settings.shortBreakDuration * 60,
|
||||
this.settings.longBreakDuration * 60,
|
||||
this.settings.cyclesBeforeLongBreak
|
||||
);
|
||||
|
||||
this.statusBarItemEl = this.addStatusBarItem();
|
||||
this.updateStatusBar();
|
||||
|
||||
this.addRibbonIcon('timer', 'Open Pomodoro Timer', () => {
|
||||
this.activateView();
|
||||
});
|
||||
|
||||
this.registerView(
|
||||
POMODORO_VIEW_TYPE,
|
||||
(leaf) => new PomodoroView(leaf, this)
|
||||
);
|
||||
|
||||
this.addCommand({
|
||||
id: 'pomodoro-start',
|
||||
name: 'Start Pomodoro',
|
||||
callback: () => this.startTimer(),
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: 'pomodoro-pause',
|
||||
name: 'Pause Pomodoro',
|
||||
callback: () => this.pauseTimer(),
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: 'pomodoro-reset',
|
||||
name: 'Reset Pomodoro',
|
||||
callback: () => this.resetTimer(),
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: 'pomodoro-skip',
|
||||
name: 'Skip Session',
|
||||
callback: () => this.skipTimer(),
|
||||
});
|
||||
|
||||
this.addSettingTab(new PomodoroSettingTab(this.app, this));
|
||||
}
|
||||
|
||||
onunload() {
|
||||
this.timer.pause();
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
|
||||
async activateView() {
|
||||
this.app.workspace.detachLeavesOfType(POMODORO_VIEW_TYPE);
|
||||
|
||||
await this.app.workspace.getRightLeaf(false).setViewState({
|
||||
type: POMODORO_VIEW_TYPE,
|
||||
active: true,
|
||||
});
|
||||
|
||||
this.app.workspace.revealLeaf(
|
||||
this.app.workspace.getLeavesOfType(POMODORO_VIEW_TYPE)[0]
|
||||
);
|
||||
}
|
||||
|
||||
updateStatusBar() {
|
||||
const remainingTime = this.timer.getRemainingTime();
|
||||
const minutes = Math.floor(remainingTime / 60);
|
||||
const seconds = remainingTime % 60;
|
||||
const timeString = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
const statusText = `🍅 ${timeString}`;
|
||||
this.statusBarItemEl.setText(statusText);
|
||||
}
|
||||
|
||||
startTimer() {
|
||||
this.timer.start(
|
||||
(remainingTime, isPomodoro) => {
|
||||
this.updateStatusBar();
|
||||
// The PomodoroView will update itself via its registered interval
|
||||
},
|
||||
() => {
|
||||
new Notice(this.timer.getIsPomodoro() ? 'Pomodoro Complete!' : 'Break Complete!');
|
||||
this.logSession();
|
||||
if (this.settings.autoStartNextSession) {
|
||||
this.startTimer();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
pauseTimer() {
|
||||
this.timer.pause();
|
||||
this.updateStatusBar();
|
||||
}
|
||||
|
||||
resetTimer() {
|
||||
this.timer.reset();
|
||||
this.updateStatusBar();
|
||||
}
|
||||
|
||||
skipTimer() {
|
||||
this.timer.skip();
|
||||
this.updateStatusBar();
|
||||
if (this.settings.autoStartNextSession) {
|
||||
this.startTimer();
|
||||
}
|
||||
}
|
||||
|
||||
async logSession() {
|
||||
const now = moment();
|
||||
const dailyNote = getDailyNote(now, getAllDailyNotes());
|
||||
|
||||
let fileContent = '';
|
||||
if (dailyNote) {
|
||||
fileContent = await this.app.vault.read(dailyNote);
|
||||
} else {
|
||||
const newDailyNote = await createDailyNote(now);
|
||||
fileContent = await this.app.vault.read(newDailyNote);
|
||||
}
|
||||
|
||||
const sessionType = this.timer.getIsPomodoro() ? 'Break' : 'Pomodoro'; // Log the *completed* session type
|
||||
const duration = this.timer.getIsPomodoro() ? this.settings.pomodoroDuration : (this.timer.currentCycle % this.settings.cyclesBeforeLongBreak === 0 ? this.settings.longBreakDuration : this.settings.shortBreakDuration);
|
||||
const activeNote = this.app.workspace.getActiveViewOfType(MarkdownView)?.file?.basename || 'No active note';
|
||||
|
||||
const logEntry = `- [x] ${now.format('HH:mm')} - ${sessionType} (${duration}分) - [[${activeNote}]]`;
|
||||
|
||||
if (dailyNote) {
|
||||
await this.app.vault.append(dailyNote, `\n${logEntry}`);
|
||||
} else {
|
||||
const newDailyNote = await createDailyNote(now);
|
||||
await this.app.vault.append(newDailyNote, `\n${logEntry}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PomodoroSettingTab extends PluginSettingTab {
|
||||
plugin: PomodoroPlugin;
|
||||
|
||||
constructor(app: App, plugin: PomodoroPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Pomodoro Duration')
|
||||
.setDesc('Duration of a pomodoro session in minutes.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('25')
|
||||
.setValue(this.plugin.settings.pomodoroDuration.toString())
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.pomodoroDuration = parseInt(value);
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.timer = new PomodoroTimer(
|
||||
this.plugin.settings.pomodoroDuration * 60,
|
||||
this.plugin.settings.shortBreakDuration * 60,
|
||||
this.plugin.settings.longBreakDuration * 60,
|
||||
this.plugin.settings.cyclesBeforeLongBreak
|
||||
);
|
||||
this.plugin.resetTimer();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Short Break Duration')
|
||||
.setDesc('Duration of a short break in minutes.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('5')
|
||||
.setValue(this.plugin.settings.shortBreakDuration.toString())
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.shortBreakDuration = parseInt(value);
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.timer = new PomodoroTimer(
|
||||
this.plugin.settings.pomodoroDuration * 60,
|
||||
this.plugin.settings.shortBreakDuration * 60,
|
||||
this.plugin.settings.longBreakDuration * 60,
|
||||
this.plugin.settings.cyclesBeforeLongBreak
|
||||
);
|
||||
this.plugin.resetTimer();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Long Break Duration')
|
||||
.setDesc('Duration of a long break in minutes.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('15')
|
||||
.setValue(this.plugin.settings.longBreakDuration.toString())
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.longBreakDuration = parseInt(value);
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.timer = new PomodoroTimer(
|
||||
this.plugin.settings.pomodoroDuration * 60,
|
||||
this.plugin.settings.shortBreakDuration * 60,
|
||||
this.plugin.settings.longBreakDuration * 60,
|
||||
this.plugin.settings.cyclesBeforeLongBreak
|
||||
);
|
||||
this.plugin.resetTimer();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Cycles Before Long Break')
|
||||
.setDesc('Number of pomodoro sessions before a long break.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('4')
|
||||
.setValue(this.plugin.settings.cyclesBeforeLongBreak.toString())
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.cyclesBeforeLongBreak = parseInt(value);
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.timer = new PomodoroTimer(
|
||||
this.plugin.settings.pomodoroDuration * 60,
|
||||
this.plugin.settings.shortBreakDuration * 60,
|
||||
this.plugin.settings.longBreakDuration * 60,
|
||||
this.plugin.settings.cyclesBeforeLongBreak
|
||||
);
|
||||
this.plugin.resetTimer();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Auto Start Next Session')
|
||||
.setDesc('Automatically start the next pomodoro or break session after the current one ends.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.autoStartNextSession)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.autoStartNextSession = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable Notifications')
|
||||
.setDesc('Display OS native notifications when a session ends.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableNotifications)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableNotifications = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable Sound')
|
||||
.setDesc('Play a sound when a session ends.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableSound)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableSound = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
|
||||
import { ItemView, WorkspaceLeaf, setIcon } from 'obsidian';
|
||||
import { PomodoroTimer } from './timer';
|
||||
import PomodoroPlugin from './main';
|
||||
|
||||
export const POMODORO_VIEW_TYPE = 'pomodoro-view';
|
||||
|
||||
export class PomodoroView extends ItemView {
|
||||
plugin: PomodoroPlugin;
|
||||
timer: PomodoroTimer;
|
||||
private timerDisplay: HTMLElement;
|
||||
private startPauseButton: HTMLElement;
|
||||
private resetButton: HTMLElement;
|
||||
private skipButton: HTMLElement;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf, plugin: PomodoroPlugin) {
|
||||
super(leaf);
|
||||
this.plugin = plugin;
|
||||
this.timer = plugin.timer;
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
return POMODORO_VIEW_TYPE;
|
||||
}
|
||||
|
||||
getDisplayText(): string {
|
||||
return 'Pomodoro Timer';
|
||||
}
|
||||
|
||||
getIcon(): string {
|
||||
return 'timer';
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
const container = this.containerEl.children[1];
|
||||
container.empty();
|
||||
container.addClass('pomodoro-view-container');
|
||||
|
||||
// Timer Display
|
||||
this.timerDisplay = container.createEl('div', { cls: 'pomodoro-timer-display' });
|
||||
this.timerDisplay.setText('25:00'); // Initial display
|
||||
|
||||
// Control Buttons
|
||||
const controls = container.createEl('div', { cls: 'pomodoro-controls' });
|
||||
|
||||
this.startPauseButton = controls.createEl('button', { cls: 'pomodoro-button' });
|
||||
setIcon(this.startPauseButton, 'play');
|
||||
this.startPauseButton.onclick = () => this.toggleTimer();
|
||||
|
||||
this.resetButton = controls.createEl('button', { cls: 'pomodoro-button' });
|
||||
setIcon(this.resetButton, 'rotate-ccw');
|
||||
this.resetButton.onclick = () => this.plugin.resetTimer();
|
||||
|
||||
this.skipButton = controls.createEl('button', { cls: 'pomodoro-button' });
|
||||
setIcon(this.skipButton, 'skip-forward');
|
||||
this.skipButton.onclick = () => this.plugin.skipTimer();
|
||||
|
||||
this.updateUI();
|
||||
this.plugin.registerInterval(window.setInterval(() => this.updateUI(), 1000));
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
// Nothing to clean up.
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
const remainingTime = this.timer.getRemainingTime();
|
||||
const minutes = Math.floor(remainingTime / 60);
|
||||
const seconds = remainingTime % 60;
|
||||
this.timerDisplay.setText(`${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`);
|
||||
|
||||
if (this.timer.getIsRunning()) {
|
||||
setIcon(this.startPauseButton, 'pause');
|
||||
} else {
|
||||
setIcon(this.startPauseButton, 'play');
|
||||
}
|
||||
}
|
||||
|
||||
toggleTimer() {
|
||||
if (this.timer.getIsRunning()) {
|
||||
this.plugin.pauseTimer();
|
||||
} else {
|
||||
this.plugin.startTimer();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
|
||||
export class PomodoroTimer {
|
||||
private pomodoroDuration: number; // in seconds
|
||||
private shortBreakDuration: number; // in seconds
|
||||
private longBreakDuration: number; // in seconds
|
||||
private cyclesBeforeLongBreak: number;
|
||||
|
||||
private timerId: NodeJS.Timeout | null = null;
|
||||
private remainingTime: number; // in seconds
|
||||
private isRunning: boolean = false;
|
||||
private currentCycle: number = 0;
|
||||
private isPomodoro: boolean = true; // true for pomodoro, false for break
|
||||
|
||||
constructor(
|
||||
pomodoroDuration: number = 25 * 60,
|
||||
shortBreakDuration: number = 5 * 60,
|
||||
longBreakDuration: number = 15 * 60,
|
||||
cyclesBeforeLongBreak: number = 4
|
||||
) {
|
||||
this.pomodoroDuration = pomodoroDuration;
|
||||
this.shortBreakDuration = shortBreakDuration;
|
||||
this.longBreakDuration = longBreakDuration;
|
||||
this.cyclesBeforeLongBreak = cyclesBeforeLongBreak;
|
||||
this.remainingTime = this.pomodoroDuration;
|
||||
}
|
||||
|
||||
public start(onTick: (remainingTime: number, isPomodoro: boolean) => void, onComplete: () => void) {
|
||||
if (this.isRunning) {
|
||||
return;
|
||||
}
|
||||
this.isRunning = true;
|
||||
this.timerId = setInterval(() => {
|
||||
this.remainingTime--;
|
||||
onTick(this.remainingTime, this.isPomodoro);
|
||||
if (this.remainingTime <= 0) {
|
||||
this.stop();
|
||||
onComplete();
|
||||
this.nextCycle();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
public pause() {
|
||||
if (this.timerId) {
|
||||
clearInterval(this.timerId);
|
||||
this.timerId = null;
|
||||
}
|
||||
this.isRunning = false;
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.pause();
|
||||
this.currentCycle = 0;
|
||||
this.isPomodoro = true;
|
||||
this.remainingTime = this.pomodoroDuration;
|
||||
}
|
||||
|
||||
public skip() {
|
||||
this.pause();
|
||||
this.nextCycle();
|
||||
}
|
||||
|
||||
private stop() {
|
||||
if (this.timerId) {
|
||||
clearInterval(this.timerId);
|
||||
this.timerId = null;
|
||||
}
|
||||
this.isRunning = false;
|
||||
}
|
||||
|
||||
private nextCycle() {
|
||||
if (this.isPomodoro) {
|
||||
this.currentCycle++;
|
||||
if (this.currentCycle % this.cyclesBeforeLongBreak === 0) {
|
||||
this.isPomodoro = false;
|
||||
this.remainingTime = this.longBreakDuration;
|
||||
} else {
|
||||
this.isPomodoro = false;
|
||||
this.remainingTime = this.shortBreakDuration;
|
||||
}
|
||||
} else {
|
||||
this.isPomodoro = true;
|
||||
this.remainingTime = this.pomodoroDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public getRemainingTime(): number {
|
||||
return this.remainingTime;
|
||||
}
|
||||
|
||||
public getIsRunning(): boolean {
|
||||
return this.isRunning;
|
||||
}
|
||||
|
||||
public getIsPomodoro(): boolean {
|
||||
return this.isPomodoro;
|
||||
}
|
||||
}
|
58
styles.css
58
styles.css
|
@ -1,8 +1,56 @@
|
|||
/*
|
||||
|
||||
This CSS file will be included with your plugin, and
|
||||
available in the app when your plugin is enabled.
|
||||
/* Pomodoro View Container */
|
||||
.pomodoro-view-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--background-primary);
|
||||
}
|
||||
|
||||
If your plugin does not need CSS, delete this file.
|
||||
/* Timer Display */
|
||||
.pomodoro-timer-display {
|
||||
font-size: 4em;
|
||||
font-weight: bold;
|
||||
color: var(--text-normal);
|
||||
margin-bottom: 30px;
|
||||
font-variant-numeric: tabular-nums; /* Ensures monospaced numbers */
|
||||
}
|
||||
|
||||
*/
|
||||
/* Control Buttons */
|
||||
.pomodoro-controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.pomodoro-button {
|
||||
background-color: var(--background-modifier-form-field);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
color: var(--text-normal);
|
||||
border-radius: var(--button-radius);
|
||||
padding: 10px 15px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2em;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.pomodoro-button:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.pomodoro-button svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
/* Settings Page Styling (if needed, though Obsidian handles most of it) */
|
||||
.setting-item-control input[type="number"] {
|
||||
width: 80px; /* Adjust as needed for number inputs */
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue