watch plugin: analog clock overlay with SVG watchface
Simple watchface with second-hand wobble animation, 5-min idle auto-show, W key toggle, light/dark theme colors, backdrop blur from theme settings. Grace period prevents instant dismiss. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
00d14b3015
commit
b9345a410f
11 files changed
+374
-2
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
define('APP_NAME', 'hyprcrd');
|
||||
define('APP_VERSION', '1.3.2');
|
||||
define('APP_VERSION', '1.3.3');
|
||||
|
||||
// Database path — writable directory, outside webroot
|
||||
// DB_PATH / UPLOAD_DIR can be pre-defined by the Electron router for app isolation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hyprcrd",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"description": "hyprcrd — self-hosted card manager",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -271,6 +271,7 @@ registerShortcut({ key: 'j', guard: gBoard, section: 'navigation', keys: '<kbd>J
|
||||
registerShortcut({ key: 't', guard: gBoard, section: 'navigation', keys: '<kbd>T</kbd>', label: 'tag quick-pick', handler: () => openTagQuickPick() });
|
||||
registerShortcut({ key: 'c', guard: gBoard, section: 'navigation', keys: '<kbd>C</kbd>', label: 'calendar', handler: () => { const cal = document.getElementById('calendar'); cal?.classList.contains('open') ? closeCalendar() : openCalendar(); } });
|
||||
registerShortcut({ key: 'm', guard: () => false, section: 'navigation', keys: '<kbd>M</kbd>', label: 'mail (plugin)' });
|
||||
registerShortcut({ key: 'w', guard: gBoard, section: 'navigation', keys: '<kbd>W</kbd>', label: 'watch', handler: () => window._watchToggle?.() });
|
||||
registerShortcut({ key: '/', guard: gBoard, section: 'navigation', keys: '<kbd>/</kbd>', label: 'search', handler: () => openSearch() });
|
||||
registerShortcut({ match: (e) => e.key === 'Tab' && !e.ctrlKey && !e.metaKey && !e.altKey && gBoard(), section: 'navigation', keys: '<kbd>⇥</kbd> / <kbd>⇧</kbd> <kbd>⇥</kbd>', label: 'cycle buckets', handler: (e) => {
|
||||
const tiles = [...document.querySelectorAll('#bucket-bar .bucket-tab[data-bucket], #bucket-bar .bucket-tab[data-bucket-super]')];
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<svg
|
||||
width="176.00125mm"
|
||||
height="176.00124mm"
|
||||
viewBox="0 0 176.00125 176.00124"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-16.173035,-17.825715)"><path
|
||||
id="rect233"
|
||||
style="display:inline;fill:var(--watch-svg-markers-fill, var(--watch-num-color, #fff));fill-opacity:1;stroke:none;stroke-width:0.19504;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 411.61352,-261.01455 c 1.3819,0 2.49444,1.11255 2.49444,2.49445 v 8.93128 c 0,1.38191 -1.11254,2.49445 -2.49444,2.49445 -1.38191,0 -2.49445,-1.11254 -2.49445,-2.49445 v -8.93128 c 0,-1.3819 1.11254,-2.49445 2.49445,-2.49445 z m 53.93185,14.43191 c 0.40192,0.0109 0.80726,0.11988 1.18125,0.33581 1.19676,0.69095 1.60412,2.21084 0.91316,3.40761 l -4.46598,7.73467 c -0.69096,1.19677 -2.21015,1.60342 -3.40692,0.91246 -1.19677,-0.69095 -1.60411,-2.21015 -0.91316,-3.40691 l 4.46529,-7.73467 c 0.47503,-0.82278 1.34214,-1.27285 2.22636,-1.24897 z m -107.86371,0 c 0.88422,-0.0239 1.75063,0.42619 2.22566,1.24897 l 4.46599,7.73467 c 0.69095,1.19676 0.28361,2.71596 -0.91316,3.40691 -1.19676,0.69095 -2.71666,0.2843 -3.40761,-0.91246 l -4.46529,-7.73467 c -0.69096,-1.19677 -0.2843,-2.71666 0.91246,-3.40761 0.374,-0.21593 0.78004,-0.32495 1.18195,-0.33581 z m 147.16469,39.43293 c 0.88422,-0.0239 1.75063,0.42549 2.22566,1.24827 0.69095,1.19676 0.2843,2.71666 -0.91246,3.40761 l -7.73467,4.46599 c -1.19677,0.69095 -2.71667,0.28359 -3.40762,-0.91317 -0.69095,-1.19677 -0.28359,-2.71666 0.91317,-3.40761 l 7.73467,-4.46529 c 0.37399,-0.21592 0.77934,-0.32495 1.18125,-0.3358 z m -186.46637,0 c 0.40192,0.0109 0.80797,0.11988 1.18195,0.3358 l 7.73467,4.46529 c 1.19676,0.69095 1.60342,2.21084 0.91247,3.40761 -0.69095,1.19676 -2.21015,1.60412 -3.40691,0.91317 l -7.73467,-4.46599 c -1.19677,-0.69096 -1.60412,-2.21085 -0.91317,-3.40761 0.47504,-0.82279 1.34145,-1.27216 2.22566,-1.24827 z m 200.9653,53.86762 c 1.3819,0 2.49444,1.11255 2.49444,2.49445 0,1.38191 -1.11254,2.49445 -2.49444,2.49445 H 510.414 c -1.38191,0 -2.49445,-1.11254 -2.49445,-2.49445 0,-1.3819 1.11254,-2.49445 2.49445,-2.49445 z m -206.53295,0 c 1.38191,0 2.49445,1.11255 2.49445,2.49445 0,1.38191 -1.11254,2.49445 -2.49445,2.49445 h -8.93127 c -1.38191,0 -2.49445,-1.11254 -2.49445,-2.49445 0,-1.3819 1.11254,-2.49445 2.49445,-2.49445 z m 184.4306,49.39885 c 0.40192,0.0109 0.80796,0.11988 1.18195,0.3358 l 7.73467,4.465294 c 1.19676,0.690953 1.60341,2.210851 0.91246,3.407611 -0.69095,1.19677 -2.21015,1.604118 -3.40691,0.913165 l -7.73467,-4.465989 c -1.19677,-0.690952 -1.60412,-2.210151 -0.91317,-3.406911 0.47503,-0.82278 1.34145,-1.27286 2.22567,-1.24897 z m -171.25952,0 c 0.88421,-0.0239 1.75063,0.42619 2.22566,1.24897 0.69095,1.19676 0.2843,2.715956 -0.91247,3.406911 l -7.73467,4.465989 c -1.19676,0.69095 -2.71665,0.283605 -3.40761,-0.913165 -0.69095,-1.19676 -0.2836,-2.716656 0.91317,-3.407611 l 7.73467,-4.465294 c 0.37399,-0.21592 0.77933,-0.32495 1.18125,-0.3358 z m 134.96472,36.163545 c 0.88422,-0.02388 1.75064,0.426193 2.22567,1.248969 l 4.46598,7.73467 c 0.69096,1.196766 0.2836,2.716662 -0.91316,3.407612 -1.19677,0.690955 -2.71666,0.2836 -3.40761,-0.913165 l -4.46529,-7.73467 c -0.69095,-1.196766 -0.2836,-2.716661 0.91316,-3.407611 0.374,-0.215925 0.77933,-0.324948 1.18125,-0.335805 z m -98.66923,0 c 0.40192,0.01085 0.80726,0.11988 1.18125,0.335805 1.19676,0.69095 1.60411,2.21085 0.91316,3.407611 l -4.46599,7.73467 c -0.69095,1.196769 -2.21085,1.604115 -3.40761,0.913165 -1.19677,-0.690955 -1.60342,-2.210842 -0.91246,-3.407612 l 4.46529,-7.73467 c 0.47503,-0.822776 1.34215,-1.272853 2.22636,-1.248969 z m 49.33462,13.238092 c 1.3819,0 2.49444,1.112541 2.49444,2.494447 v 8.931279 c 0,1.381905 -1.11254,2.494447 -2.49444,2.494447 -1.38191,0 -2.49445,-1.112542 -2.49445,-2.494447 v -8.931279 c 0,-1.381906 1.11254,-2.494447 2.49445,-2.494447 z"
|
||||
transform="matrix(-0.74020373,0,0,0.74020373,408.85134,217.42417)" /><circle
|
||||
style="fill:none;fill-opacity:0.38222;stroke-width:0.0278984;stroke-linejoin:round;paint-order:fill markers stroke"
|
||||
id="circle256"
|
||||
cx="104.17366"
|
||||
cy="105.82634"
|
||||
r="88.000626" /></g></svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg
|
||||
width="176.00125mm"
|
||||
height="176.00124mm"
|
||||
viewBox="0 0 176.00125 176.00124"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-16.173035,-17.825715)"><path
|
||||
id="rect256"
|
||||
style="display:inline;fill:var(--watch-svg-hour-fill, var(--watch-hand-color, #fff));fill-opacity:1;stroke-width:0.809242;stroke-linejoin:round;paint-order:fill markers stroke"
|
||||
d="m 104.17365,63.017641 c 4.52102,0 8.16069,3.639666 8.16069,8.160687 v 34.700732 c 0,4.52102 -3.63967,8.16069 -8.16069,8.16069 -4.521021,0 -8.160688,-3.63967 -8.160688,-8.16069 V 71.178328 c 0,-4.521021 3.639667,-8.160687 8.160688,-8.160687 z" /><circle
|
||||
style="display:none;fill:none;fill-opacity:0.38222;stroke-width:0.0278984;stroke-linejoin:round;paint-order:fill markers stroke"
|
||||
id="circle256"
|
||||
cx="104.17366"
|
||||
cy="105.82634"
|
||||
r="88.000626" /></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg
|
||||
width="176.00125mm"
|
||||
height="176.00124mm"
|
||||
viewBox="0 0 176.00125 176.00124"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-16.173035,-17.825715)"><path
|
||||
id="rect231"
|
||||
style="display:inline;fill:var(--watch-svg-minute-fill, var(--watch-hand-color, #fff));stroke-width:0.809242;stroke-linejoin:round;paint-order:fill markers stroke"
|
||||
d="m 104.17365,40.792641 c 4.52102,0 8.16069,3.639666 8.16069,8.160687 v 56.925732 c 0,4.52102 -3.63967,8.16069 -8.16069,8.16069 -4.521021,0 -8.160688,-3.63967 -8.160688,-8.16069 V 48.953328 c 0,-4.521021 3.639667,-8.160687 8.160688,-8.160687 z" /><circle
|
||||
style="display:none;fill:none;fill-opacity:0.38222;stroke-width:0.0278984;stroke-linejoin:round;paint-order:fill markers stroke"
|
||||
id="circle256"
|
||||
cx="104.17366"
|
||||
cy="105.82634"
|
||||
r="88.000626" /></g></svg>
|
||||
|
After Width: | Height: | Size: 1013 B |
@@ -0,0 +1,25 @@
|
||||
<svg
|
||||
width="176.00125mm"
|
||||
height="176.00124mm"
|
||||
viewBox="0 0 176.00125 176.00124"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-16.173035,-17.825715)"><rect
|
||||
style="display:inline;fill:var(--watch-svg-second-fill, var(--watch-second-color, #fff));fill-opacity:1;stroke:none;stroke-width:0.144369;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="rect237"
|
||||
width="3.69275"
|
||||
height="10.303658"
|
||||
x="-106.02003"
|
||||
y="24.220301"
|
||||
ry="1.846375"
|
||||
transform="scale(-1,1)" /><circle
|
||||
style="display:none;fill:none;fill-opacity:0.38222;stroke-width:0.0278984;stroke-linejoin:round;paint-order:fill markers stroke"
|
||||
id="circle256"
|
||||
cx="104.17366"
|
||||
cy="105.82634"
|
||||
r="88.000626" /></g></svg>
|
||||
|
After Width: | Height: | Size: 972 B |
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "watch",
|
||||
"version": "1.0.0",
|
||||
"description": "Analog clock overlay. Shows after 5 min idle or press W.",
|
||||
"frontend": {
|
||||
"js": ["watch.js"],
|
||||
"css": ["watch.css"],
|
||||
"html": ["watch.html"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/* ── Watch overlay ── */
|
||||
|
||||
#watch-overlay {
|
||||
position: fixed; inset: 0; z-index: 9000;
|
||||
display: grid; place-items: center;
|
||||
cursor: pointer; overflow: hidden;
|
||||
opacity: 0; transform: scale(0.985);
|
||||
transition: opacity 220ms ease, transform 220ms ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
#watch-overlay.visible {
|
||||
opacity: 1; transform: scale(1);
|
||||
pointer-events: auto;
|
||||
}
|
||||
#watch-overlay.hidden { display: none; }
|
||||
|
||||
#watch-darkening {
|
||||
position: absolute; inset: 0;
|
||||
background: #000; opacity: 0.8;
|
||||
pointer-events: none;
|
||||
backdrop-filter: blur(var(--ui-blur-overlay, 0px));
|
||||
-webkit-backdrop-filter: blur(var(--ui-blur-overlay, 0px));
|
||||
}
|
||||
|
||||
#watch-analog {
|
||||
position: relative; z-index: 1;
|
||||
width: min(78vmin, 720px);
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
#watch-dial {
|
||||
position: relative; width: 100%; height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.watch-layer {
|
||||
position: absolute; inset: 0;
|
||||
transform-origin: 50% 50%;
|
||||
will-change: transform;
|
||||
}
|
||||
.watch-layer svg { display: block; width: 100%; height: 100%; }
|
||||
|
||||
#watch-layer-digits { z-index: 1; filter: drop-shadow(0 0 12px rgba(0,0,0,0.8)); }
|
||||
#watch-layer-hour { z-index: 3; }
|
||||
#watch-layer-minute { z-index: 2; }
|
||||
#watch-layer-second { z-index: 4; }
|
||||
|
||||
#watch-second-wobble {
|
||||
width: 100%; height: 100%;
|
||||
transform-origin: 50% 50%;
|
||||
transition: transform 92ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Dark theme (default): white hands/markers */
|
||||
#watch-overlay {
|
||||
--watch-svg-markers-fill: #fff;
|
||||
--watch-svg-hour-fill: var(--t-accent, #68c6ed);
|
||||
--watch-svg-minute-fill: #fff;
|
||||
--watch-svg-second-fill: var(--t-accent, #68c6ed);
|
||||
}
|
||||
|
||||
/* Light theme: black hands/markers */
|
||||
@media (prefers-color-scheme: light) {
|
||||
#watch-overlay {
|
||||
--watch-svg-markers-fill: #111;
|
||||
--watch-svg-hour-fill: var(--t-accent, #2563eb);
|
||||
--watch-svg-minute-fill: #111;
|
||||
--watch-svg-second-fill: var(--t-accent, #2563eb);
|
||||
}
|
||||
#watch-darkening { background: #fff; opacity: 0.85; }
|
||||
#watch-layer-digits { filter: drop-shadow(0 0 12px rgba(255,255,255,0.6)); }
|
||||
}
|
||||
|
||||
/* Also detect hyprcrd's own theme mode via body class / data attribute */
|
||||
body.light-theme #watch-overlay,
|
||||
[data-theme-mode="light"] #watch-overlay {
|
||||
--watch-svg-markers-fill: #111;
|
||||
--watch-svg-hour-fill: var(--t-accent, #2563eb);
|
||||
--watch-svg-minute-fill: #111;
|
||||
--watch-svg-second-fill: var(--t-accent, #2563eb);
|
||||
}
|
||||
body.light-theme #watch-darkening,
|
||||
[data-theme-mode="light"] #watch-darkening {
|
||||
background: #fff; opacity: 0.85;
|
||||
}
|
||||
body.light-theme #watch-layer-digits,
|
||||
[data-theme-mode="light"] #watch-layer-digits {
|
||||
filter: drop-shadow(0 0 12px rgba(255,255,255,0.6));
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
#watch-overlay { transition: none !important; }
|
||||
#watch-second-wobble { transition: none !important; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<div id="watch-overlay" class="hidden" aria-hidden="true">
|
||||
<div id="watch-darkening"></div>
|
||||
<div id="watch-analog">
|
||||
<div id="watch-dial">
|
||||
<div id="watch-layer-digits" class="watch-layer"></div>
|
||||
<div id="watch-layer-hour" class="watch-layer"></div>
|
||||
<div id="watch-layer-minute" class="watch-layer"></div>
|
||||
<div id="watch-layer-second" class="watch-layer">
|
||||
<div id="watch-second-wobble"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,169 @@
|
||||
// ── Watch plugin — analog clock overlay ──
|
||||
|
||||
const $ = id => document.getElementById(id);
|
||||
|
||||
const LAYER_MAP = {
|
||||
digits: 'watch-layer-digits',
|
||||
hour: 'watch-layer-hour',
|
||||
minute: 'watch-layer-minute',
|
||||
second: 'watch-second-wobble',
|
||||
};
|
||||
|
||||
let visible = false;
|
||||
let tickTimer = null;
|
||||
let idleTimer = null;
|
||||
let lastActivity = Date.now();
|
||||
let showedAt = 0;
|
||||
let wobbleKick = null, wobbleRebound = null;
|
||||
let svgCache = {};
|
||||
let loaded = false;
|
||||
|
||||
const IDLE_MINUTES = 5;
|
||||
|
||||
// ── SVG loading ──
|
||||
|
||||
async function loadSvg(layer) {
|
||||
if (svgCache[layer]) return svgCache[layer];
|
||||
try {
|
||||
const res = await fetch(`plugins/watch/faces/simple/${layer}.svg`);
|
||||
if (!res.ok) return '';
|
||||
let svg = await res.text();
|
||||
svg = svg.replace(/<\?xml[\s\S]*?\?>/gi, '').replace(/<!DOCTYPE[\s\S]*?>/gi, '').trim();
|
||||
svgCache[layer] = svg;
|
||||
return svg;
|
||||
} catch { return ''; }
|
||||
}
|
||||
|
||||
async function loadFace() {
|
||||
if (loaded) return;
|
||||
for (const [layer, elId] of Object.entries(LAYER_MAP)) {
|
||||
const el = $(elId);
|
||||
if (el) el.innerHTML = await loadSvg(layer);
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
// ── Clock logic ──
|
||||
|
||||
function tick() {
|
||||
const now = new Date();
|
||||
const h12 = now.getHours() % 12;
|
||||
const m = now.getMinutes();
|
||||
const s = now.getSeconds();
|
||||
|
||||
const hourEl = $('watch-layer-hour');
|
||||
const minEl = $('watch-layer-minute');
|
||||
const secEl = $('watch-layer-second');
|
||||
|
||||
if (hourEl) hourEl.style.transform = `rotate(${(((h12 + m / 60) / 12) * 360).toFixed(3)}deg)`;
|
||||
if (minEl) minEl.style.transform = `rotate(${(((m + s / 60) / 60) * 360).toFixed(3)}deg)`;
|
||||
if (secEl) secEl.style.transform = `rotate(${((s / 60) * 360).toFixed(3)}deg)`;
|
||||
|
||||
triggerWobble();
|
||||
}
|
||||
|
||||
function triggerWobble() {
|
||||
const wobEl = $('watch-second-wobble');
|
||||
if (!wobEl) return;
|
||||
clearTimeout(wobbleKick);
|
||||
clearTimeout(wobbleRebound);
|
||||
wobEl.style.transform = 'rotate(0deg)';
|
||||
requestAnimationFrame(() => {
|
||||
wobEl.style.transform = 'rotate(0.78deg)';
|
||||
wobbleKick = setTimeout(() => { wobEl.style.transform = 'rotate(-0.25deg)'; }, 95);
|
||||
wobbleRebound = setTimeout(() => { wobEl.style.transform = 'rotate(0deg)'; }, 205);
|
||||
});
|
||||
}
|
||||
|
||||
function startTick() {
|
||||
if (tickTimer) return;
|
||||
tick();
|
||||
tickTimer = setInterval(tick, 1000);
|
||||
}
|
||||
|
||||
function stopTick() {
|
||||
if (tickTimer) { clearInterval(tickTimer); tickTimer = null; }
|
||||
clearTimeout(wobbleKick);
|
||||
clearTimeout(wobbleRebound);
|
||||
}
|
||||
|
||||
// ── Show / hide ──
|
||||
|
||||
async function show() {
|
||||
if (visible) return;
|
||||
visible = true;
|
||||
const overlay = $('watch-overlay');
|
||||
if (!overlay) return;
|
||||
showedAt = Date.now();
|
||||
await loadFace();
|
||||
overlay.classList.remove('hidden');
|
||||
startTick();
|
||||
requestAnimationFrame(() => overlay.classList.add('visible'));
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (!visible) return;
|
||||
visible = false;
|
||||
const overlay = $('watch-overlay');
|
||||
if (!overlay) return;
|
||||
overlay.classList.remove('visible');
|
||||
stopTick();
|
||||
setTimeout(() => { if (!visible) overlay.classList.add('hidden'); }, 250);
|
||||
}
|
||||
|
||||
function toggle() { visible ? hide() : show(); }
|
||||
|
||||
// ── Idle auto-show ──
|
||||
|
||||
function resetIdleTimer() {
|
||||
clearTimeout(idleTimer);
|
||||
if (!IDLE_MINUTES) return;
|
||||
idleTimer = setTimeout(() => {
|
||||
if (visible) return;
|
||||
if (document.visibilityState !== 'visible') return;
|
||||
if (document.querySelector('.overlay.open')) return;
|
||||
show();
|
||||
}, IDLE_MINUTES * 60000);
|
||||
}
|
||||
|
||||
function onActivity() {
|
||||
const now = Date.now();
|
||||
if (now - lastActivity < 300) return;
|
||||
lastActivity = now;
|
||||
if (visible && now - showedAt > 500) hide();
|
||||
resetIdleTimer();
|
||||
}
|
||||
|
||||
// ── Init ──
|
||||
|
||||
function init() {
|
||||
const overlay = $('watch-overlay');
|
||||
if (!overlay) return;
|
||||
|
||||
overlay.addEventListener('click', () => { hide(); onActivity(); });
|
||||
|
||||
window.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape' && visible) {
|
||||
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
|
||||
hide(); onActivity();
|
||||
}
|
||||
}, { capture: true });
|
||||
|
||||
for (const ev of ['pointermove', 'pointerdown', 'keydown', 'wheel', 'touchstart']) {
|
||||
window.addEventListener(ev, onActivity, { passive: true });
|
||||
}
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') onActivity();
|
||||
});
|
||||
|
||||
resetIdleTimer();
|
||||
window.hyprcrd?.registerPlugin({ name: 'watch', version: '1.0.0' });
|
||||
}
|
||||
|
||||
window._watchToggle = toggle;
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
Reference in new issue
Block a user