From 2ea1e644dc121403127090657cc6d3704ecb3316 Mon Sep 17 00:00:00 2001 From: Dhruv-Rankoti Date: Sat, 22 Nov 2025 17:07:35 +0000 Subject: [PATCH 01/10] feat: add keyboard shortcuts for timers (#9) --- src/pages/home.js | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/pages/home.js b/src/pages/home.js index ad1c0ca..0db8d9a 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -168,6 +168,7 @@ export class HomePage extends LitElement { connectedCallback() { super.connectedCallback(); + window.addEventListener('keydown', this.#handleShortcut); this._settings = { ...settingsStore.settings }; this.#reset(); settingsStore.addEventListener( @@ -177,6 +178,7 @@ export class HomePage extends LitElement { } disconnectedCallback() { + window.removeEventListener('keydown', this.#handleShortcut); super.disconnectedCallback(); settingsStore.removeEventListener( SETTINGS_EVENT.SETTINGS_FORM_SUBMIT, @@ -380,6 +382,62 @@ export class HomePage extends LitElement { this.#updatePomodoroTimer(); } + /** @param {KeyboardEvent} event */ + #handleShortcut = (event) => { + const key = event.key.toLowerCase(); + + /** @type {"start"|"pause"|"reset"|null} */ + let action = null; + + switch (key) { + case 's': + action = 'start'; + break; + case 'p': + action = 'pause'; + break; + case 'r': + action = 'reset'; + break; + case ' ': + action = this.#isRunning ? 'pause' : 'start'; + } + + if (action) { + event.preventDefault(); + this.#triggerTimerAction(action); + } + }; + + /** @param {'start' | 'pause' | 'reset'} action*/ + #triggerTimerAction(action) { + switch (action) { + case POMODORO_TIMER_ACTION.START: { + const timerComplete = this._minutes === 0 && this._seconds === 0; + + if (!this.#isRunning && !timerComplete) { + this.#start(); + this.#dismissExercises(); + } + break; + } + + case POMODORO_TIMER_ACTION.PAUSE: { + this.#pause(); + break; + } + + case POMODORO_TIMER_ACTION.RESET: { + this.#reset(); + this.#dismissExercises(); + break; + } + + default: + console.warn('Unknown timer action:', action); + } + } + #complete() { const { enableNotifications, exercisesCount, showMotivationalQuote } = this._settings; From e80431100d974ed48dd8ce6e0fc19a9e8a937f78 Mon Sep 17 00:00:00 2001 From: Dhruv-Rankoti Date: Sat, 22 Nov 2025 19:01:51 +0000 Subject: [PATCH 02/10] feat: updating keyboard bindings for modes and timer --- src/pages/home.js | 64 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/pages/home.js b/src/pages/home.js index 0db8d9a..bf248d1 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -186,7 +186,7 @@ export class HomePage extends LitElement { ); } - /** @returns {Element[]} */ + /** @returns {HTMLButtonElement[]} */ get #pomodoroModeButtonEls() { return Array.from( this.renderRoot?.querySelectorAll('button[data-mode]') ?? [] @@ -384,28 +384,56 @@ export class HomePage extends LitElement { /** @param {KeyboardEvent} event */ #handleShortcut = (event) => { - const key = event.key.toLowerCase(); + // ALT + key shortcuts for modes + if (event.altKey) { + let targetButton; + switch (event.key.toLowerCase()) { + case 'p': + // Find the Pomodoro mode button + targetButton = this.#pomodoroModeButtonEls.find( + (b) => b.dataset.mode === POMODORO_MODE.POMODORO + ); + break; + case 's': + // Short Break mode button + targetButton = this.#pomodoroModeButtonEls.find( + (b) => b.dataset.mode === POMODORO_MODE.SHORT_BREAK + ); + break; + case 'l': + // Long Break mode button + targetButton = this.#pomodoroModeButtonEls.find( + (b) => b.dataset.mode === POMODORO_MODE.LONG_BREAK + ); + break; + case 'r': + // Trigger reset action + this.#triggerTimerAction(POMODORO_TIMER_ACTION.RESET); + event.preventDefault(); + return; + default: + return; + } - /** @type {"start"|"pause"|"reset"|null} */ - let action = null; + // If a button was found, simulate the click and change the mode + if (targetButton) { + const simulatedEvent = new MouseEvent('click', { + bubbles: true, + cancelable: true, + }); - switch (key) { - case 's': - action = 'start'; - break; - case 'p': - action = 'pause'; - break; - case 'r': - action = 'reset'; - break; - case ' ': - action = this.#isRunning ? 'pause' : 'start'; + targetButton.dispatchEvent(simulatedEvent); + } } - if (action) { + // SPACE key to toggle start/pause + if (event.code === 'Space') { + this.#triggerTimerAction( + this.#isRunning + ? POMODORO_TIMER_ACTION.PAUSE + : POMODORO_TIMER_ACTION.START + ); event.preventDefault(); - this.#triggerTimerAction(action); } }; From f17b0122cb64571e0366d26ea32984c00d0b73d1 Mon Sep 17 00:00:00 2001 From: Dhruv-Rankoti Date: Thu, 27 Nov 2025 16:41:08 +0000 Subject: [PATCH 03/10] resolving feat(Keyboard) --- src/pages/home.js | 129 +++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 75 deletions(-) diff --git a/src/pages/home.js b/src/pages/home.js index bf248d1..f4f821d 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -168,7 +168,7 @@ export class HomePage extends LitElement { connectedCallback() { super.connectedCallback(); - window.addEventListener('keydown', this.#handleShortcut); + window.addEventListener('keydown', this.#onKeydown); this._settings = { ...settingsStore.settings }; this.#reset(); settingsStore.addEventListener( @@ -178,7 +178,7 @@ export class HomePage extends LitElement { } disconnectedCallback() { - window.removeEventListener('keydown', this.#handleShortcut); + window.removeEventListener('keydown', this.#onKeydown); super.disconnectedCallback(); settingsStore.removeEventListener( SETTINGS_EVENT.SETTINGS_FORM_SUBMIT, @@ -290,7 +290,7 @@ export class HomePage extends LitElement { `; } - /** @param {Event} event */ + /** @param {{ target: EventTarget | null }} event */ #selectMode({ target }) { if (target instanceof HTMLButtonElement) { const mode = /** @type {import("../index.d.js").PomodoroModeKind} */ ( @@ -383,87 +383,66 @@ export class HomePage extends LitElement { } /** @param {KeyboardEvent} event */ - #handleShortcut = (event) => { - // ALT + key shortcuts for modes + #onKeydown = (event) => { + if (event.code === 'Space') { + event.preventDefault(); + if (this.#isRunning) { + this.#pause(); + } else if (!this.#timerComplete) { + this.#start(); + this.#dismissExercises(); + } + } + if (event.altKey) { - let targetButton; - switch (event.key.toLowerCase()) { - case 'p': - // Find the Pomodoro mode button - targetButton = this.#pomodoroModeButtonEls.find( - (b) => b.dataset.mode === POMODORO_MODE.POMODORO - ); + switch (event.code) { + case 'KeyP': + { + event.preventDefault(); + this.#selectMode({ + target: + this.#pomodoroModeButtonEls.find( + (buttonEl) => + buttonEl.dataset?.mode === POMODORO_MODE.POMODORO + ) ?? null, + }); + } break; - case 's': - // Short Break mode button - targetButton = this.#pomodoroModeButtonEls.find( - (b) => b.dataset.mode === POMODORO_MODE.SHORT_BREAK - ); + case 'KeyS': + { + event.preventDefault(); + this.#selectMode({ + target: + this.#pomodoroModeButtonEls.find( + (buttonEl) => + buttonEl.dataset?.mode === POMODORO_MODE.SHORT_BREAK + ) ?? null, + }); + } break; - case 'l': - // Long Break mode button - targetButton = this.#pomodoroModeButtonEls.find( - (b) => b.dataset.mode === POMODORO_MODE.LONG_BREAK - ); + case 'KeyL': + { + event.preventDefault(); + this.#selectMode({ + target: + this.#pomodoroModeButtonEls.find( + (buttonEl) => + buttonEl.dataset?.mode === POMODORO_MODE.LONG_BREAK + ) ?? null, + }); + } break; - case 'r': - // Trigger reset action - this.#triggerTimerAction(POMODORO_TIMER_ACTION.RESET); + case 'KeyR': event.preventDefault(); - return; - default: - return; - } - - // If a button was found, simulate the click and change the mode - if (targetButton) { - const simulatedEvent = new MouseEvent('click', { - bubbles: true, - cancelable: true, - }); - - targetButton.dispatchEvent(simulatedEvent); + this.#reset(); + break; } } - - // SPACE key to toggle start/pause - if (event.code === 'Space') { - this.#triggerTimerAction( - this.#isRunning - ? POMODORO_TIMER_ACTION.PAUSE - : POMODORO_TIMER_ACTION.START - ); - event.preventDefault(); - } }; - /** @param {'start' | 'pause' | 'reset'} action*/ - #triggerTimerAction(action) { - switch (action) { - case POMODORO_TIMER_ACTION.START: { - const timerComplete = this._minutes === 0 && this._seconds === 0; - - if (!this.#isRunning && !timerComplete) { - this.#start(); - this.#dismissExercises(); - } - break; - } - - case POMODORO_TIMER_ACTION.PAUSE: { - this.#pause(); - break; - } - - case POMODORO_TIMER_ACTION.RESET: { - this.#reset(); - this.#dismissExercises(); - break; - } - - default: - console.warn('Unknown timer action:', action); - } + /** @returns {boolean} */ + get #timerComplete() { + return this._minutes === 0 && this._seconds === 0; } #complete() { From 0b9efdf0e9422a44cb7ab0eacba6b86c9aca3787 Mon Sep 17 00:00:00 2001 From: Dhruv-Rankoti Date: Thu, 27 Nov 2025 18:52:01 +0000 Subject: [PATCH 04/10] feat(keyboard): moved getter #timerComplete --- src/pages/home.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/home.js b/src/pages/home.js index f4f821d..89bed51 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -204,6 +204,11 @@ export class HomePage extends LitElement { }; } + /** @returns {boolean} */ + get #timerComplete() { + return this._minutes === 0 && this._seconds === 0; + } + render() { const { enableExerciseDisplay, @@ -440,11 +445,6 @@ export class HomePage extends LitElement { } }; - /** @returns {boolean} */ - get #timerComplete() { - return this._minutes === 0 && this._seconds === 0; - } - #complete() { const { enableNotifications, exercisesCount, showMotivationalQuote } = this._settings; From 34f08a4d4f99d05de4b5b56b9bf9375319e791f5 Mon Sep 17 00:00:00 2001 From: Dhruv Rankoti <97913909+Dhruv-Rankoti@users.noreply.github.com> Date: Fri, 28 Nov 2025 01:15:22 +0530 Subject: [PATCH 05/10] feat(keyboard): using getter #timerComplete --- src/pages/home.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/home.js b/src/pages/home.js index 89bed51..08b13f3 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -332,11 +332,8 @@ export class HomePage extends LitElement { switch (action) { case POMODORO_TIMER_ACTION.START: { - // Check if timer is complete - const timerComplete = this._minutes === 0 && this._seconds === 0; - // Only start if not running and timer is not complete - if (!this.#isRunning && !timerComplete) { + if (!this.#isRunning && !this.#timerComplete) { this.#start(); this.#dismissExercises(); } From ab1ee4ba4b1a364dd49c015af59a215718daa484 Mon Sep 17 00:00:00 2001 From: Dhruv-Rankoti Date: Fri, 28 Nov 2025 14:03:30 +0000 Subject: [PATCH 06/10] feat(keyboard)#10: move event listener --- .github/pull_request_template.md | 118 +++++++++++++++---------------- src/pages/home.js | 2 +- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 183f73c..9d647df 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,59 +1,59 @@ - - - -## Change(s) - - - - -## What is the purpose of the change(s)? - - - - - -## Screenshots and/or recordings - - - - -### Before - - - -### After - - - -## Additional details - - - - -## Related issues and/or pull requests - - - + + + +## Change(s) + + + + +## What is the purpose of the change(s)? + + + + + +## Screenshots and/or recordings + + + + +### Before + + + +### After + + + +## Additional details + + + + +## Related issues and/or pull requests + + + diff --git a/src/pages/home.js b/src/pages/home.js index 08b13f3..fffba67 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -178,8 +178,8 @@ export class HomePage extends LitElement { } disconnectedCallback() { - window.removeEventListener('keydown', this.#onKeydown); super.disconnectedCallback(); + window.removeEventListener('keydown', this.#onKeydown); settingsStore.removeEventListener( SETTINGS_EVENT.SETTINGS_FORM_SUBMIT, this.#onSettingsFormSubmit From 3237c466b73936fd454cd35efdaf6102d7e12dd2 Mon Sep 17 00:00:00 2001 From: Dhruv-Rankoti Date: Fri, 28 Nov 2025 17:02:28 +0000 Subject: [PATCH 07/10] feat(keyboard): resolving untrack file pull_request_template.md --- src/app.js | 10 +++--- src/components/header.js | 8 ----- src/pages/home.js | 74 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/app.js b/src/app.js index 2a726e1..43c9247 100644 --- a/src/app.js +++ b/src/app.js @@ -20,8 +20,8 @@ import './components/app-top-bar.js'; import './components/header.js'; const POMODORO_RESOURCE_LINKS = Object.freeze({ - wiki: 'https://en.wikipedia.org/wiki/Pomodoro_Technique', - video: 'https://youtu.be/dC4ZYCiRF_w?si=ekRqmmWpnqrAZM-c&t=8', + WIKI: 'https://en.wikipedia.org/wiki/Pomodoro_Technique', + VIDEO: 'https://youtu.be/dC4ZYCiRF_w?si=ekRqmmWpnqrAZM-c&t=8', }); const SYSTEM_NOTIFICATIONS_RESOURCE_LINKS = Object.freeze({ @@ -207,11 +207,11 @@ export class App extends LitElement {

Also, view short - pomodoro video explantion or visit - pomodoro technique wikipedia page for more information. @@ -347,7 +347,7 @@ export class App extends LitElement { -

Set Times (Minutes)
+
Set Times (In Minutes)
`; } - /** @param {Event} event */ + /** @param {{ target: EventTarget | null }} event */ #selectMode({ target }) { if (target instanceof HTMLButtonElement) { const mode = /** @type {import("../index.d.js").PomodoroModeKind} */ ( @@ -325,11 +332,8 @@ export class HomePage extends LitElement { switch (action) { case POMODORO_TIMER_ACTION.START: { - // Check if timer is complete - const timerComplete = this._minutes === 0 && this._seconds === 0; - // Only start if not running and timer is not complete - if (!this.#isRunning && !timerComplete) { + if (!this.#isRunning && !this.#timerComplete) { this.#start(); this.#dismissExercises(); } @@ -380,6 +384,64 @@ export class HomePage extends LitElement { this.#updatePomodoroTimer(); } + /** @param {KeyboardEvent} event */ + #onKeydown = (event) => { + if (event.code === 'Space') { + event.preventDefault(); + if (this.#isRunning) { + this.#pause(); + } else if (!this.#timerComplete) { + this.#start(); + this.#dismissExercises(); + } + } + + if (event.altKey) { + switch (event.code) { + case 'KeyP': + { + event.preventDefault(); + this.#selectMode({ + target: + this.#pomodoroModeButtonEls.find( + (buttonEl) => + buttonEl.dataset?.mode === POMODORO_MODE.POMODORO + ) ?? null, + }); + } + break; + case 'KeyS': + { + event.preventDefault(); + this.#selectMode({ + target: + this.#pomodoroModeButtonEls.find( + (buttonEl) => + buttonEl.dataset?.mode === POMODORO_MODE.SHORT_BREAK + ) ?? null, + }); + } + break; + case 'KeyL': + { + event.preventDefault(); + this.#selectMode({ + target: + this.#pomodoroModeButtonEls.find( + (buttonEl) => + buttonEl.dataset?.mode === POMODORO_MODE.LONG_BREAK + ) ?? null, + }); + } + break; + case 'KeyR': + event.preventDefault(); + this.#reset(); + break; + } + } + }; + #complete() { const { enableNotifications, exercisesCount, showMotivationalQuote } = this._settings; From 191819c178cbb3e55262af0a14ab3ca7c36f2bd9 Mon Sep 17 00:00:00 2001 From: Dhruv Rankoti <97913909+Dhruv-Rankoti@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:42:26 +0530 Subject: [PATCH 08/10] Revert "chore(repo): add pull request template (#14)" This reverts commit a603024c040f14942f33dc1b002c0d9c855b2fae. --- .github/pull_request_template.md | 59 -------------------------------- 1 file changed, 59 deletions(-) delete mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 9d647df..0000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,59 +0,0 @@ - - - -## Change(s) - - - - -## What is the purpose of the change(s)? - - - - - -## Screenshots and/or recordings - - - - -### Before - - - -### After - - - -## Additional details - - - - -## Related issues and/or pull requests - - - From 4ed1a55b458cf0fb2facd3f4c0a4a690a770093a Mon Sep 17 00:00:00 2001 From: Dhruv Rankoti <97913909+Dhruv-Rankoti@users.noreply.github.com> Date: Fri, 28 Nov 2025 23:00:17 +0530 Subject: [PATCH 09/10] feat(Keyboard): Revert unwanted changes in this file (#10) --- .github/pull_request_template.md | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..183f73c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,59 @@ + + + +## Change(s) + + + + +## What is the purpose of the change(s)? + + + + + +## Screenshots and/or recordings + + + + +### Before + + + +### After + + + +## Additional details + + + + +## Related issues and/or pull requests + + + From 849b7e404a79c536d00a39e821e963b05a3b9a66 Mon Sep 17 00:00:00 2001 From: Dhruv-Rankoti Date: Fri, 28 Nov 2025 19:17:46 +0000 Subject: [PATCH 10/10] feat(Keyboard): adding .changeset for the issue (#10) --- .changeset/chilly-dancers-stare.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chilly-dancers-stare.md diff --git a/.changeset/chilly-dancers-stare.md b/.changeset/chilly-dancers-stare.md new file mode 100644 index 0000000..824a905 --- /dev/null +++ b/.changeset/chilly-dancers-stare.md @@ -0,0 +1,5 @@ +--- +'studytimer.io': minor +--- + +Added new keyboard shortcuts: Space (start/stop), Alt+P (Pomodoro), Alt+S (Short Break), Alt+L (Long Break), Alt+R (Reset Timer).