From 361df69243efb6691058abe32fc68662e140d59d Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:16:26 -0600 Subject: [PATCH 1/9] Add Valentines theme with falling hearts animation --- src/ThemeManager.ts | 11 +++ src/themes/Valentines.js | 151 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 src/themes/Valentines.js diff --git a/src/ThemeManager.ts b/src/ThemeManager.ts index 6ab95ba..2f7b232 100644 --- a/src/ThemeManager.ts +++ b/src/ThemeManager.ts @@ -8,11 +8,13 @@ import * as Halloween from './themes/Halloween' import * as Jonathan from './themes/Jonathan' import { dark as PastelDark, light as PastelLight } from './themes/Pastel' import { dark as RainbowDark, light as RainbowLight } from './themes/Rainbow' +import * as Valentines from './themes/Valentines' import * as Winter from './themes/Winter' const themes: ITheme[] = [ Winter, Halloween, + Valentines, DefaultLight, DefaultDark, GradientLight, @@ -44,6 +46,14 @@ export default class ThemeManager { } } + private isDebugMode (): boolean { + // add a debug parameter so that developers can test seasonal themes (amah853) + if (typeof window === 'undefined') return false + const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' + const urlParams = new URLSearchParams(window.location.search) + return isLocalhost && urlParams.get('mode') === 'debug' + } + get defaultTheme (): ITheme { // The first available theme in the array is the default for (const theme of themes) { @@ -72,6 +82,7 @@ export default class ThemeManager { } public isAvailable (themeName: string): boolean { + if (this.isDebugMode()) return true // show all themes if in debug return !this.themes[themeName].enabled || this.themes[themeName].enabled!(this.secrets) } diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js new file mode 100644 index 0000000..dc49a09 --- /dev/null +++ b/src/themes/Valentines.js @@ -0,0 +1,151 @@ +const BasicColorTheme = require('./templates/BasicColorTheme') +const ColorSchemeTransformations = require('./ColorSchemeTransformations') +const BasicTiming = require('./timings/BasicTiming') + +const hearts = new Set() + +function drawHeart(ctx, x, y, size, rotation) { + ctx.save() + ctx.translate(x, y) + ctx.rotate(rotation) + ctx.beginPath() + const topCurveHeight = size * 0.3 + ctx.moveTo(0, topCurveHeight) + // Left curve + ctx.bezierCurveTo( + 0, 0, + -size / 2, 0, + -size / 2, topCurveHeight + ) + ctx.bezierCurveTo( + -size / 2, (topCurveHeight + size) / 2, + 0, (topCurveHeight + size) / 2, + 0, size + ) + // Right curve + ctx.bezierCurveTo( + 0, (topCurveHeight + size) / 2, + size / 2, (topCurveHeight + size) / 2, + size / 2, topCurveHeight + ) + ctx.bezierCurveTo( + size / 2, 0, + 0, 0, + 0, topCurveHeight + ) + ctx.closePath() + ctx.restore() +} + +module.exports = { + name: 'Valentines', + enabled: (secrets) => { + const now = new Date() + return (now.getMonth() + 1) === 2 // February + }, + specialEffects: (ctx, canvas) => { + if (hearts.size < 30 && Math.random() < 0.08) { + hearts.add({ + x: Math.random() * window.innerWidth, + y: -20, + size: 10 + Math.random() * 15, + rotation: Math.random() * Math.PI * 2, + rotationSpeed: (Math.random() - 0.5) * 0.05, + xSpeed: (Math.random() - 0.5) * 1.5, + ySpeed: 1 + Math.random() * 2, + color: Math.random() > 0.5 ? '#ff006e' : '#ff4d6d', + opacity: 0.6 + Math.random() * 0.4, + clicked: false, + pulsePhase: Math.random() * Math.PI * 2 + }) + } + + for (const heart of hearts) { + ctx.globalAlpha = heart.opacity + ctx.fillStyle = heart.clicked ? '#ffffff' : heart.color + + // Calculate pulse scale (subtle pulse between 0.9 and 1.1) + const pulseScale = 1 + Math.sin(heart.pulsePhase) * 0.3 + const currentSize = heart.clicked ? heart.size * 1.5 : heart.size + + drawHeart(ctx, heart.x, heart.y, currentSize * pulseScale, heart.rotation) + ctx.fill() + + if (heart.y > window.innerHeight + 50) { + hearts.delete(heart) + } + + heart.y += heart.ySpeed + heart.x += heart.xSpeed + heart.rotation += heart.rotationSpeed + heart.pulsePhase += 0.05 + } + ctx.globalAlpha = 1.0 + + canvas.addEventListener('mousedown', (e) => { + for (const heart of hearts) { + const distance = Math.sqrt(Math.pow(heart.x - e.x, 2) + Math.pow(heart.y - e.y, 2)) + if (distance < heart.size) { + heart.clicked = !heart.clicked + break + } + } + }) + }, + theme: BasicColorTheme( + ColorSchemeTransformations.fromObjectStrings, + BasicTiming, + [{ + background: { + 'background-image': 'linear-gradient(135deg, #ff6b9d 0%, #ffc3e0 100%)', + 'background-color': '#ff85b3' + }, + text: '#8b0028', + subtext: '#c41e5c', + contrast: 'white' + }, { + background: { + 'background-image': 'linear-gradient(135deg, #ff1f5a 0%, #ff6b9d 100%)', + 'background-color': '#ff4578' + }, + text: '#8b0028', + subtext: '#c41e5c', + contrast: 'white' + }, { + background: { + 'background-image': 'linear-gradient(135deg, #c9184a 0%, #ff4d6d 100%)', + 'background-color': '#e6335b' + }, + text: '#ffffff', + subtext: '#ffc3e0', + contrast: 'white' + }, { + background: { + 'background-image': 'linear-gradient(135deg, #ff4d6d 0%, #ff758f 100%)', + 'background-color': '#ff617e' + }, + text: '#ffffff', + subtext: '#ffc3e0', + contrast: 'white' + }], { + holiday: { + background: { + 'background-image': 'linear-gradient(135deg, #ff006e 0%, #ff85b3 100%)', + 'background-color': '#ff4390' + }, + text: '#ffffff', + subtext: '#ffc3e0', + contrast: 'white' + }, + weekend: { + background: { + 'background-image': 'linear-gradient(135deg, #ff006e 0%, #ff85b3 100%)', + 'background-color': '#ff4390' + }, + text: '#ffffff', + subtext: '#ffc3e0', + contrast: 'white' + } + } + ) +} From 43df4c65f2ca1329ebb4dd803a20dedc57b73a21 Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:55:19 -0600 Subject: [PATCH 2/9] Replace background timer with a heart on the valentines theme and make floating hearts slightly bigger --- src/themes/Valentines.js | 60 ++++++++++++++++++++++++++++++++++++++-- src/ui/Page1.js | 18 ++++++++---- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js index dc49a09..3b3568e 100644 --- a/src/themes/Valentines.js +++ b/src/themes/Valentines.js @@ -37,18 +37,74 @@ function drawHeart(ctx, x, y, size, rotation) { ctx.restore() } +// Helper function to draw a heart path (reusable for both background and foreground) +function drawHeartPath(ctx, w, h, yOffset) { + ctx.beginPath() + ctx.moveTo(0, h * 0.45 + yOffset) + ctx.bezierCurveTo( + -w * 0.4, h * 0.15 + yOffset, + -w, h * 0.05 + yOffset, + -w, -h * 0.15 + yOffset + ) + ctx.bezierCurveTo( + -w, -h * 0.42 + yOffset, + -w * 0.5, -h * 0.5 + yOffset, + 0, -h * 0.3 + yOffset + ) + ctx.bezierCurveTo( + w * 0.5, -h * 0.5 + yOffset, + w, -h * 0.42 + yOffset, + w, -h * 0.15 + yOffset + ) + ctx.bezierCurveTo( + w, h * 0.05 + yOffset, + w * 0.4, h * 0.15 + yOffset, + 0, h * 0.45 + yOffset + ) + ctx.closePath() +} + +function drawHeartCountdown(ctx, x, y, radius, proportion) { + const heartWidth = radius * 2 + const heartHeight = radius * 2 + const w = heartWidth * 0.5 + const h = heartHeight * 0.95 + const yOffset = h * 0.02 + // ===== DRAW BACKGROUND HEART (so they can see that its supposed to be a heart) ===== + ctx.save() + ctx.translate(x, y) + ctx.globalAlpha = 0.5 + drawHeartPath(ctx, w, h, yOffset) + ctx.fill() + ctx.restore() + // ===== DRAW FOREGROUND HEART (countdown, clipped) ===== + ctx.save() + ctx.beginPath() + ctx.arc(x, y, radius * 1.2, (Math.PI / -2), (Math.PI / -2) + (-2 * Math.PI) * (1 - proportion), true) + ctx.lineTo(x, y) + ctx.closePath() + ctx.clip() + ctx.globalAlpha = 0.98 + ctx.fillStyle = 'rgba(255,255,255,0.98)' + ctx.translate(x, y) + drawHeartPath(ctx, w, h, yOffset) + ctx.fill() + ctx.restore() +} + module.exports = { name: 'Valentines', + drawHeartCountdown: drawHeartCountdown, enabled: (secrets) => { const now = new Date() - return (now.getMonth() + 1) === 2 // February + return (now.getMonth() + 1) === 1 // February }, specialEffects: (ctx, canvas) => { if (hearts.size < 30 && Math.random() < 0.08) { hearts.add({ x: Math.random() * window.innerWidth, y: -20, - size: 10 + Math.random() * 15, + size: 30 + Math.random() * 30, rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.05, xSpeed: (Math.random() - 0.5) * 1.5, diff --git a/src/ui/Page1.js b/src/ui/Page1.js index 44eb147..b8614a4 100644 --- a/src/ui/Page1.js +++ b/src/ui/Page1.js @@ -1,4 +1,5 @@ const m = require('mithril') +const Valentines = require('../themes/Valentines') // To replace the circle with a heart on Valentines theme var getIconImage = function (min) { var faviconColors = { @@ -79,11 +80,18 @@ var updateGraphics = function (vnode) { var posX = width / 2 var posY = height / 2 - ctx.beginPath() - ctx.arc(posX, posY, radius, (Math.PI / -2), (Math.PI / -2) + (-2 * Math.PI) * (1 - proportion), true) - ctx.lineTo(posX, posY) - ctx.closePath() - ctx.fill() + // Check if current theme is Valentines + if (themeManager.currentTheme.name === 'Valentines') { + // Draw heart countdown instead of circle - same size and behavior + Valentines.drawHeartCountdown(ctx, posX, posY, radius, proportion) + } else { + // Draw the normal circle countdown + ctx.beginPath() + ctx.arc(posX, posY, radius, (Math.PI / -2), (Math.PI / -2) + (-2 * Math.PI) * (1 - proportion), true) + ctx.lineTo(posX, posY) + ctx.closePath() + ctx.fill() + } } const updateSnowflakes = function (vnode) { const c = vnode.dom From 91c79f312f0e94ae46232a1ff18d45e3e550d5a7 Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:59:38 -0600 Subject: [PATCH 3/9] change back to feb --- src/themes/Valentines.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js index 3b3568e..77e012c 100644 --- a/src/themes/Valentines.js +++ b/src/themes/Valentines.js @@ -97,7 +97,7 @@ module.exports = { drawHeartCountdown: drawHeartCountdown, enabled: (secrets) => { const now = new Date() - return (now.getMonth() + 1) === 1 // February + return (now.getMonth() + 1) === 2 // February }, specialEffects: (ctx, canvas) => { if (hearts.size < 30 && Math.random() < 0.08) { From b8d2dbd88d3364963337ee68a211e449de9841d2 Mon Sep 17 00:00:00 2001 From: Ahmad Mahrous <101132087+amah853@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:55:34 -0600 Subject: [PATCH 4/9] Clean up whitespace in Valentines.js Removed unnecessary blank lines for cleaner code. --- src/themes/Valentines.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js index 77e012c..b9fdb67 100644 --- a/src/themes/Valentines.js +++ b/src/themes/Valentines.js @@ -123,21 +123,17 @@ module.exports = { // Calculate pulse scale (subtle pulse between 0.9 and 1.1) const pulseScale = 1 + Math.sin(heart.pulsePhase) * 0.3 const currentSize = heart.clicked ? heart.size * 1.5 : heart.size - drawHeart(ctx, heart.x, heart.y, currentSize * pulseScale, heart.rotation) ctx.fill() - if (heart.y > window.innerHeight + 50) { hearts.delete(heart) } - heart.y += heart.ySpeed heart.x += heart.xSpeed heart.rotation += heart.rotationSpeed heart.pulsePhase += 0.05 } ctx.globalAlpha = 1.0 - canvas.addEventListener('mousedown', (e) => { for (const heart of hearts) { const distance = Math.sqrt(Math.pow(heart.x - e.x, 2) + Math.pow(heart.y - e.y, 2)) From 726da74a11d7aaec748a3a0d4a0f365ccf7fd4f6 Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:45:31 -0600 Subject: [PATCH 5/9] Fix formatting issues (https://github.com/nicolaschan/bell/actions/runs/21381506019/job/61551523008?pr=126) --- src/themes/Valentines.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js index b9fdb67..f542d00 100644 --- a/src/themes/Valentines.js +++ b/src/themes/Valentines.js @@ -4,7 +4,7 @@ const BasicTiming = require('./timings/BasicTiming') const hearts = new Set() -function drawHeart(ctx, x, y, size, rotation) { +function drawHeart (ctx, x, y, size, rotation) { ctx.save() ctx.translate(x, y) ctx.rotate(rotation) @@ -38,7 +38,7 @@ function drawHeart(ctx, x, y, size, rotation) { } // Helper function to draw a heart path (reusable for both background and foreground) -function drawHeartPath(ctx, w, h, yOffset) { +function drawHeartPath (ctx, w, h, yOffset) { ctx.beginPath() ctx.moveTo(0, h * 0.45 + yOffset) ctx.bezierCurveTo( @@ -64,7 +64,7 @@ function drawHeartPath(ctx, w, h, yOffset) { ctx.closePath() } -function drawHeartCountdown(ctx, x, y, radius, proportion) { +function drawHeartCountdown (ctx, x, y, radius, proportion) { const heartWidth = radius * 2 const heartHeight = radius * 2 const w = heartWidth * 0.5 @@ -101,8 +101,8 @@ module.exports = { }, specialEffects: (ctx, canvas) => { if (hearts.size < 30 && Math.random() < 0.08) { - hearts.add({ - x: Math.random() * window.innerWidth, + hearts.add({ + x: Math.random() * window.innerWidth, y: -20, size: 30 + Math.random() * 30, rotation: Math.random() * Math.PI * 2, @@ -115,11 +115,11 @@ module.exports = { pulsePhase: Math.random() * Math.PI * 2 }) } - + for (const heart of hearts) { ctx.globalAlpha = heart.opacity ctx.fillStyle = heart.clicked ? '#ffffff' : heart.color - + // Calculate pulse scale (subtle pulse between 0.9 and 1.1) const pulseScale = 1 + Math.sin(heart.pulsePhase) * 0.3 const currentSize = heart.clicked ? heart.size * 1.5 : heart.size @@ -134,7 +134,7 @@ module.exports = { heart.pulsePhase += 0.05 } ctx.globalAlpha = 1.0 - canvas.addEventListener('mousedown', (e) => { + canvas.addEventListener('mousedown', function (e) { for (const heart of hearts) { const distance = Math.sqrt(Math.pow(heart.x - e.x, 2) + Math.pow(heart.y - e.y, 2)) if (distance < heart.size) { From c663390b96c7f0146b873712f3528656f9cf481e Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Fri, 30 Jan 2026 07:41:44 -0600 Subject: [PATCH 6/9] Refactor Valentines theme logic and remove debug code --- chrome_extension/src/AlarmManager.js | 3 +-- css/style-mithril.css | 3 ++- src/BellTimer.js | 9 +-------- src/ThemeManager.ts | 8 -------- src/themes/Valentines.js | 7 +++++-- src/ui/Page1.js | 29 ++++++++++++---------------- 6 files changed, 21 insertions(+), 38 deletions(-) diff --git a/chrome_extension/src/AlarmManager.js b/chrome_extension/src/AlarmManager.js index 1fb5d81..2616d54 100755 --- a/chrome_extension/src/AlarmManager.js +++ b/chrome_extension/src/AlarmManager.js @@ -40,8 +40,7 @@ class AlarmManager { this.bellTimer = bellTimer this.cookman = cookman this.started = false - // DEBUG - this.timeScale = bellTimer.devMode ? bellTimer.timeScale : 1 + this.timeScale = 1 } start () { diff --git a/css/style-mithril.css b/css/style-mithril.css index f2b123c..0a9bd9a 100755 --- a/css/style-mithril.css +++ b/css/style-mithril.css @@ -77,6 +77,7 @@ body { top: 50%; left: 50%; transform: translate(-50%, -50%); + z-index: 2; } .container { @@ -698,5 +699,5 @@ checkbox css is modified from https://kyusuf.com/post/completely-css-custom-chec left: 0; overflow: hidden; opacity: 1; - z-index: 2; + z-index: 0; } \ No newline at end of file diff --git a/src/BellTimer.js b/src/BellTimer.js index 1035512..5f7ff46 100644 --- a/src/BellTimer.js +++ b/src/BellTimer.js @@ -8,15 +8,9 @@ class BellTimer { this.cookieManager = cookieManager this.requestManager = requestManager - this.debug = function () {} - this.devMode = false this.startTime = 0 this.timeScale = 1 - var devModeCookie = this.cookieManager.get('dev_mode') - if (devModeCookie) { - this.enableDevMode(devModeCookie.startDate, devModeCookie.scale) - } } loadCustomCourses () { @@ -104,8 +98,7 @@ class BellTimer { interval: 4 * 60 * 1000 }) - ts.on('change', offset => - this.debug('Timesync offset: ' + offset)) + // ts.on('change', offset => {}) // Debug removed this.ts = ts diff --git a/src/ThemeManager.ts b/src/ThemeManager.ts index 2f7b232..b5fd390 100644 --- a/src/ThemeManager.ts +++ b/src/ThemeManager.ts @@ -46,13 +46,6 @@ export default class ThemeManager { } } - private isDebugMode (): boolean { - // add a debug parameter so that developers can test seasonal themes (amah853) - if (typeof window === 'undefined') return false - const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' - const urlParams = new URLSearchParams(window.location.search) - return isLocalhost && urlParams.get('mode') === 'debug' - } get defaultTheme (): ITheme { // The first available theme in the array is the default @@ -82,7 +75,6 @@ export default class ThemeManager { } public isAvailable (themeName: string): boolean { - if (this.isDebugMode()) return true // show all themes if in debug return !this.themes[themeName].enabled || this.themes[themeName].enabled!(this.secrets) } diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js index f542d00..ac13af2 100644 --- a/src/themes/Valentines.js +++ b/src/themes/Valentines.js @@ -96,8 +96,11 @@ module.exports = { name: 'Valentines', drawHeartCountdown: drawHeartCountdown, enabled: (secrets) => { - const now = new Date() - return (now.getMonth() + 1) === 2 // February + const now = new Date(); + // Only enabled for 10 days ending on Feb 14th + const month = now.getMonth() + 1; + const day = now.getDate(); + return month === 1 && day >= 5 && day <= 34; }, specialEffects: (ctx, canvas) => { if (hearts.size < 30 && Math.random() < 0.08) { diff --git a/src/ui/Page1.js b/src/ui/Page1.js index b8614a4..307e790 100644 --- a/src/ui/Page1.js +++ b/src/ui/Page1.js @@ -80,18 +80,12 @@ var updateGraphics = function (vnode) { var posX = width / 2 var posY = height / 2 - // Check if current theme is Valentines - if (themeManager.currentTheme.name === 'Valentines') { - // Draw heart countdown instead of circle - same size and behavior - Valentines.drawHeartCountdown(ctx, posX, posY, radius, proportion) - } else { - // Draw the normal circle countdown - ctx.beginPath() - ctx.arc(posX, posY, radius, (Math.PI / -2), (Math.PI / -2) + (-2 * Math.PI) * (1 - proportion), true) - ctx.lineTo(posX, posY) - ctx.closePath() - ctx.fill() - } + // Draw the background circle countdown + ctx.beginPath() + ctx.arc(posX, posY, radius, (Math.PI / -2), (Math.PI / -2) + (-2 * Math.PI) * (1 - proportion), true) + ctx.lineTo(posX, posY) + ctx.closePath() + ctx.fill() } const updateSnowflakes = function (vnode) { const c = vnode.dom @@ -177,6 +171,12 @@ module.exports = { style: vnode.state.bottomStyle }, [ m(Background, vnode.attrs), + // Move snowflakes canvas to the back + m('canvas.snowflakes', { + onupdate: updateSnowflakes, + bellTimer: vnode.attrs.bellTimer, + themeManager: vnode.attrs.themeManager + }), m('.centered.time-text', [ m(SoundInteraction, vnode.attrs), m(Timer, vnode.attrs), @@ -187,11 +187,6 @@ module.exports = { onupdate: updateGraphics, bellTimer: vnode.attrs.bellTimer, themeManager: vnode.attrs.themeManager - }), - m('canvas.snowflakes', { - onupdate: updateSnowflakes, - bellTimer: vnode.attrs.bellTimer, - themeManager: vnode.attrs.themeManager }) ]) } From ebd072f98b3ef3b3019bc45078ef07bbf13c77ad Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:20:09 -0600 Subject: [PATCH 7/9] adjust heart clicking to match the current rendered size, update date range, and ensure colors have enough contrast --- src/themes/Valentines.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js index ac13af2..ee2ddf8 100644 --- a/src/themes/Valentines.js +++ b/src/themes/Valentines.js @@ -3,6 +3,7 @@ const ColorSchemeTransformations = require('./ColorSchemeTransformations') const BasicTiming = require('./timings/BasicTiming') const hearts = new Set() +const OFFSCREEN_BUFFER = 50 // Extra pixels below viewport before removing heart function drawHeart (ctx, x, y, size, rotation) { ctx.save() @@ -34,6 +35,7 @@ function drawHeart (ctx, x, y, size, rotation) { 0, topCurveHeight ) ctx.closePath() + ctx.fill() ctx.restore() } @@ -93,14 +95,14 @@ function drawHeartCountdown (ctx, x, y, radius, proportion) { } module.exports = { - name: 'Valentines', + name: "Valentine's", drawHeartCountdown: drawHeartCountdown, enabled: (secrets) => { const now = new Date(); // Only enabled for 10 days ending on Feb 14th const month = now.getMonth() + 1; const day = now.getDate(); - return month === 1 && day >= 5 && day <= 34; + return month === 2 && day >= 5 && day <= 14; }, specialEffects: (ctx, canvas) => { if (hearts.size < 30 && Math.random() < 0.08) { @@ -123,12 +125,12 @@ module.exports = { ctx.globalAlpha = heart.opacity ctx.fillStyle = heart.clicked ? '#ffffff' : heart.color - // Calculate pulse scale (subtle pulse between 0.9 and 1.1) const pulseScale = 1 + Math.sin(heart.pulsePhase) * 0.3 const currentSize = heart.clicked ? heart.size * 1.5 : heart.size - drawHeart(ctx, heart.x, heart.y, currentSize * pulseScale, heart.rotation) - ctx.fill() - if (heart.y > window.innerHeight + 50) { + const renderedSize = currentSize * pulseScale + drawHeart(ctx, heart.x, heart.y, renderedSize, heart.rotation) + + if (heart.y > window.innerHeight + OFFSCREEN_BUFFER) { hearts.delete(heart) } heart.y += heart.ySpeed @@ -139,8 +141,11 @@ module.exports = { ctx.globalAlpha = 1.0 canvas.addEventListener('mousedown', function (e) { for (const heart of hearts) { + const pulseScale = 1 + Math.sin(heart.pulsePhase) * 0.3 + const currentSize = heart.clicked ? heart.size * 1.5 : heart.size + const renderedSize = currentSize * pulseScale const distance = Math.sqrt(Math.pow(heart.x - e.x, 2) + Math.pow(heart.y - e.y, 2)) - if (distance < heart.size) { + if (distance < renderedSize) { heart.clicked = !heart.clicked break } @@ -155,16 +160,16 @@ module.exports = { 'background-image': 'linear-gradient(135deg, #ff6b9d 0%, #ffc3e0 100%)', 'background-color': '#ff85b3' }, - text: '#8b0028', - subtext: '#c41e5c', + text: '#4a0016', + subtext: '#7a1038', contrast: 'white' }, { background: { 'background-image': 'linear-gradient(135deg, #ff1f5a 0%, #ff6b9d 100%)', 'background-color': '#ff4578' }, - text: '#8b0028', - subtext: '#c41e5c', + text: '#4a0016', + subtext: '#600020', contrast: 'white' }, { background: { From 3aefa9a09a685096eb5bd4970a894d892a4b217e Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:31:06 -0600 Subject: [PATCH 8/9] Updated fix for lint tests --- src/themes/Valentines.js | 8 ++++---- src/ui/Page1.js | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/themes/Valentines.js b/src/themes/Valentines.js index ee2ddf8..2e7a569 100644 --- a/src/themes/Valentines.js +++ b/src/themes/Valentines.js @@ -98,11 +98,11 @@ module.exports = { name: "Valentine's", drawHeartCountdown: drawHeartCountdown, enabled: (secrets) => { - const now = new Date(); + const now = new Date() // Only enabled for 10 days ending on Feb 14th - const month = now.getMonth() + 1; - const day = now.getDate(); - return month === 2 && day >= 5 && day <= 14; + const month = now.getMonth() + 1 + const day = now.getDate() + return month === 2 && day >= 5 && day <= 14 }, specialEffects: (ctx, canvas) => { if (hearts.size < 30 && Math.random() < 0.08) { diff --git a/src/ui/Page1.js b/src/ui/Page1.js index 307e790..9c39172 100644 --- a/src/ui/Page1.js +++ b/src/ui/Page1.js @@ -1,5 +1,4 @@ const m = require('mithril') -const Valentines = require('../themes/Valentines') // To replace the circle with a heart on Valentines theme var getIconImage = function (min) { var faviconColors = { From 0a48f6f6b20459f25481ecb3a6dcd423abcff9dc Mon Sep 17 00:00:00 2001 From: amah853 <101132087+amah853@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:50:50 -0600 Subject: [PATCH 9/9] all tests now pass --- src/BellTimer.js | 5 +++++ src/ThemeManager.ts | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/BellTimer.js b/src/BellTimer.js index 5f7ff46..96a9d0b 100644 --- a/src/BellTimer.js +++ b/src/BellTimer.js @@ -11,6 +11,11 @@ class BellTimer { this.startTime = 0 this.timeScale = 1 + const devModeCookie = this.cookieManager.get('dev_mode') + if (devModeCookie) { + this.devMode = true + this.timeScale = devModeCookie.scale + } } loadCustomCourses () { diff --git a/src/ThemeManager.ts b/src/ThemeManager.ts index b5fd390..cf78a0e 100644 --- a/src/ThemeManager.ts +++ b/src/ThemeManager.ts @@ -46,7 +46,6 @@ export default class ThemeManager { } } - get defaultTheme (): ITheme { // The first available theme in the array is the default for (const theme of themes) {