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).