From 12fcb92a8a060be6f504d819ad987cffc41001fd Mon Sep 17 00:00:00 2001 From: Wyatt Breitner Date: Thu, 29 Jan 2026 03:55:01 -0500 Subject: [PATCH] plugin: clock widget (#4161) --- src/i18n/resources/en.json | 11 ++++ src/plugins/clock/index.tsx | 109 ++++++++++++++++++++++++++++++++++++ src/plugins/clock/style.css | 6 ++ src/plugins/clock/types.ts | 5 ++ 4 files changed, 131 insertions(+) create mode 100644 src/plugins/clock/index.tsx create mode 100644 src/plugins/clock/style.css create mode 100644 src/plugins/clock/types.ts diff --git a/src/i18n/resources/en.json b/src/i18n/resources/en.json index 4792625c0..728b99d7b 100644 --- a/src/i18n/resources/en.json +++ b/src/i18n/resources/en.json @@ -413,6 +413,17 @@ "no-captions": "No captions available for this song" } }, + "clock": { + "description": "Add a clock to the navigation bar", + "name": "Clock", + "menu": { + "format": { + "label": "Format", + "display-seconds": "Display Seconds", + "24-hour-format": "24-Hour Format" + } + } + }, "compact-sidebar": { "description": "Always set the sidebar in compact mode", "name": "Compact Sidebar" diff --git a/src/plugins/clock/index.tsx b/src/plugins/clock/index.tsx new file mode 100644 index 000000000..50f9ecbef --- /dev/null +++ b/src/plugins/clock/index.tsx @@ -0,0 +1,109 @@ +import { render } from 'solid-js/web'; +import { createSignal, onMount } from 'solid-js'; + +import style from './style.css?inline'; + +import { createPlugin } from '@/utils'; +import { type MenuTemplate } from '@/menu'; +import { t } from '@/i18n'; +import { type ClockPluginConfig } from './types'; + +const defaultConfig: ClockPluginConfig = { + enabled: false, + displaySeconds: false, + hour12: false, +}; + +export default createPlugin({ + name: () => t('plugins.clock.name'), + description: () => t('plugins.clock.description'), + restartNeeded: false, + config: defaultConfig, + stylesheets: [style], + menu: async ({ getConfig, setConfig }): Promise => { + const config = await getConfig(); + + return [ + { + label: t('plugins.clock.menu.format.label'), + submenu: [ + { + label: t('plugins.clock.menu.format.display-seconds'), + type: 'checkbox', + checked: config.displaySeconds, + click(item) { + setConfig({ displaySeconds: item.checked }); + }, + }, + { + label: t('plugins.clock.menu.format.24-hour-format'), + type: 'checkbox', + checked: !config.hour12, + click(item) { + setConfig({ hour12: !item.checked }); + }, + }, + ], + }, + ]; + }, + renderer: { + displaySeconds: defaultConfig.displaySeconds, + hour12: defaultConfig.hour12, + + interval: null as NodeJS.Timeout | null, + clockContainer: document.createElement('div'), + updateTime: null as unknown as () => void, + + async start({ getConfig }) { + const config = await getConfig(); + this.displaySeconds = config.displaySeconds; + this.hour12 = config.hour12; + + if (!this.clockContainer) { + this.clockContainer = document.createElement('div'); + } + + const [time, setTime] = createSignal(); + + const updateTime = () => { + const timeFormat: Intl.DateTimeFormatOptions = { + hour12: this.hour12, + hour: 'numeric', + minute: 'numeric', + second: this.displaySeconds ? 'numeric' : undefined, + }; + const now = new Date(); + setTime(now.toLocaleTimeString('en', timeFormat)); + }; + this.updateTime = updateTime; + + onMount(() => { + this.interval = setInterval(updateTime, 1000); + }); + + render( + () => ( + <> +

{time()}

+ + ), + this.clockContainer, + ); + const menu = document.querySelector('.center-content'); + menu?.append(this.clockContainer); + }, + onConfigChange(newConfig) { + this.displaySeconds = newConfig.displaySeconds; + this.hour12 = newConfig.hour12; + this.updateTime(); + }, + stop() { + this.clockContainer.remove(); + this.clockContainer.replaceChildren(); + if (this.interval) { + clearInterval(this.interval); + } + }, + }, +}); diff --git a/src/plugins/clock/style.css b/src/plugins/clock/style.css new file mode 100644 index 000000000..dc7856111 --- /dev/null +++ b/src/plugins/clock/style.css @@ -0,0 +1,6 @@ +.clock { + position: absolute; + left: 50%; + transform: translateX(-50%); + align-self: center; +} diff --git a/src/plugins/clock/types.ts b/src/plugins/clock/types.ts new file mode 100644 index 000000000..823c29cdc --- /dev/null +++ b/src/plugins/clock/types.ts @@ -0,0 +1,5 @@ +export type ClockPluginConfig = { + enabled: boolean; + displaySeconds: boolean; + hour12: boolean; +};