From bc1b75beebf76bc4b35f8d51e7773ab655424748 Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Thu, 9 Apr 2026 22:16:35 +0200 Subject: [PATCH] code cleanup Signed-off-by: Peter Siegmund --- plugin/ActionBase.js | 61 ++++++++++++++++++++++ plugin/actions/CopilotAction.js | 92 ++++++++------------------------- plugin/actions/GiteaAction.js | 61 +++------------------- plugin/actions/PetrolAction.js | 74 ++++---------------------- plugin/app.html | 1 + 5 files changed, 102 insertions(+), 187 deletions(-) create mode 100644 plugin/ActionBase.js diff --git a/plugin/ActionBase.js b/plugin/ActionBase.js new file mode 100644 index 0000000..933083c --- /dev/null +++ b/plugin/ActionBase.js @@ -0,0 +1,61 @@ +class ActionBase { + constructor($UD, context) { + this.$UD = $UD; + this.context = context; + this.refreshTimer = null; + this.debounceTimer = 0; + } + + onClear() { + if (this.refreshTimer) { + clearInterval(this.refreshTimer); + this.refreshTimer = null; + } + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + this.debounceTimer = 0; + } + } + + startTimer(fetchFn, refreshRate) { + this.onClear(); + fetchFn(); + const duration = this.getRefreshDuration(refreshRate); + if (duration > 0) { + this.refreshTimer = setInterval(() => fetchFn(), duration * 60 * 1000); + } + } + + debounce(fn, delay = 300) { + if (this.debounceTimer) clearTimeout(this.debounceTimer); + this.debounceTimer = setTimeout(() => fn(), delay); + } + + getRefreshDuration(refreshRate) { + switch (refreshRate) { + case '1': return 1; + case '2': return 2; + case '3': return 5; + case '4': return 10; + case '5': return 30; + case '6': return 60; + default: return 0; + } + } + + createCanvas() { + const canvas = document.createElement('canvas'); + canvas.width = 196; + canvas.height = 196; + const ctx = canvas.getContext('2d'); + ctx.fillStyle = '#000000'; + ctx.fillRect(0, 0, 196, 196); + ctx.textBaseline = 'middle'; + ctx.textAlign = 'center'; + return { canvas, ctx }; + } + + setIcon(canvas) { + this.$UD.setBaseDataIcon(this.context, canvas.toDataURL('image/png')); + } +} diff --git a/plugin/actions/CopilotAction.js b/plugin/actions/CopilotAction.js index 2196fd0..c5ebdcc 100644 --- a/plugin/actions/CopilotAction.js +++ b/plugin/actions/CopilotAction.js @@ -1,62 +1,42 @@ -class CopilotAction { +class CopilotAction extends ActionBase { constructor($UD, context) { - this.$UD = $UD; - this.context = context; + super($UD, context); this.config = { url: '', refreshRate: '4', }; - this.refreshTimer = null; - this.debounceTimer = 0; } setActive() {} - onClear() { - if (this.refreshTimer) { - clearInterval(this.refreshTimer); - this.refreshTimer = null; - } - if (this.debounceTimer) { - clearTimeout(this.debounceTimer); - this.debounceTimer = 0; - } - } - setParams(jsn) { this.config = Object.assign(this.config, (jsn && jsn.param) || {}); - this.start(); + this.startTimer(() => this.fetchData(), this.config.refreshRate); } onRun() { this.fetchData(); } - start() { - this.onClear(); - this.fetchData(); - const duration = this.getRefreshDuration(); - if (duration > 0) { - this.refreshTimer = setInterval(() => this.fetchData(), duration * 60 * 1000); - } - } - - async fetchData() { - if (this.debounceTimer) clearTimeout(this.debounceTimer); - this.debounceTimer = setTimeout(async () => { + fetchData() { + this.debounce(async () => { if (!this.config.url) { - this.renderGauge(null); + this.renderGauge(null, false); return; } try { const response = await fetch(this.config.url); + if (!response.ok) { + this.renderGauge(null, true); + return; + } const text = await response.text(); const value = this.parsePrometheus(text); - this.renderGauge(value !== null ? parseFloat(value) : null); + this.renderGauge(value !== null ? parseFloat(value) : null, false); } catch (e) { - this.renderGauge(null); + this.renderGauge(null, true); } - }, 300); + }); } parsePrometheus(text) { @@ -66,7 +46,6 @@ class CopilotAction { if (trimmed.startsWith('github_copilot_usage_percentage')) { const closingBrace = trimmed.indexOf('}'); if (closingBrace !== -1) return trimmed.slice(closingBrace + 1).trim(); - // no labels const parts = trimmed.split(/\s+/); if (parts.length >= 2) return parts[1]; } @@ -74,25 +53,18 @@ class CopilotAction { return null; } - renderGauge(value) { - const canvas = document.createElement('canvas'); - canvas.width = 196; - canvas.height = 196; - const ctx = canvas.getContext('2d'); - - ctx.fillStyle = '#000000'; - ctx.fillRect(0, 0, 196, 196); + renderGauge(value, isError) { + const { canvas, ctx } = this.createCanvas(); const cx = 98, cy = 100; const r = 68; const lineWidth = 14; - // Gauge runs from 225° to 315° clockwise (270° sweep) const startAngle = 135 * Math.PI / 180; const totalSweep = 270 * Math.PI / 180; - const greenEnd = startAngle + 0.50 * totalSweep; // 50% - const yellowEnd = startAngle + 0.80 * totalSweep; // 80% - const endAngle = startAngle + totalSweep; // 100% + const greenEnd = startAngle + 0.50 * totalSweep; + const yellowEnd = startAngle + 0.80 * totalSweep; + const endAngle = startAngle + totalSweep; // Background track ctx.beginPath(); @@ -106,7 +78,6 @@ class CopilotAction { const pct = Math.max(0, Math.min(100, value)) / 100; const valueAngle = startAngle + pct * totalSweep; - // Green segment (0–50%) if (pct > 0) { ctx.beginPath(); ctx.arc(cx, cy, r, startAngle, Math.min(valueAngle, greenEnd), false); @@ -115,8 +86,6 @@ class CopilotAction { ctx.lineCap = 'round'; ctx.stroke(); } - - // Yellow segment (50–80%) if (pct > 0.5) { ctx.beginPath(); ctx.arc(cx, cy, r, greenEnd, Math.min(valueAngle, yellowEnd), false); @@ -125,8 +94,6 @@ class CopilotAction { ctx.lineCap = 'butt'; ctx.stroke(); } - - // Red segment (80–100%) if (pct > 0.8) { ctx.beginPath(); ctx.arc(cx, cy, r, yellowEnd, valueAngle, false); @@ -136,21 +103,16 @@ class CopilotAction { ctx.stroke(); } - // Value text — centered, colored const color = pct <= 0.5 ? '#4caf50' : pct <= 0.8 ? '#ffeb3b' : '#f44336'; const displayText = value.toFixed(1) + '%'; const fSize = displayText.length > 6 ? 28 : 34; ctx.fillStyle = color; ctx.font = `bold ${fSize}px "Source Han Sans SC"`; - ctx.textBaseline = 'middle'; - ctx.textAlign = 'center'; ctx.fillText(displayText, cx, cy + 8); } else { - ctx.fillStyle = '#666666'; + ctx.fillStyle = isError ? '#ff6b6b' : '#666666'; ctx.font = 'bold 28px "Source Han Sans SC"'; - ctx.textBaseline = 'middle'; - ctx.textAlign = 'center'; - ctx.fillText('N/A', cx, cy + 8); + ctx.fillText(isError ? 'ERR' : 'N/A', cx, cy + 8); } // Label — bottom @@ -158,18 +120,6 @@ class CopilotAction { ctx.font = 'bold 20px "Source Han Sans SC"'; ctx.fillText('Copilot', cx, 165); - this.$UD.setBaseDataIcon(this.context, canvas.toDataURL('image/png')); - } - - getRefreshDuration() { - switch (this.config.refreshRate) { - case '1': return 1; - case '2': return 2; - case '3': return 5; - case '4': return 10; - case '5': return 30; - case '6': return 60; - default: return 0; - } + this.setIcon(canvas); } } diff --git a/plugin/actions/GiteaAction.js b/plugin/actions/GiteaAction.js index 5ccf6f9..377f8a4 100644 --- a/plugin/actions/GiteaAction.js +++ b/plugin/actions/GiteaAction.js @@ -1,7 +1,6 @@ -class GiteaAction { +class GiteaAction extends ActionBase { constructor($UD, context) { - this.$UD = $UD; - this.context = context; + super($UD, context); this.config = { url: '', owner: '', @@ -9,26 +8,13 @@ class GiteaAction { token: '', refreshRate: '4', }; - this.refreshTimer = null; - this.debounceTimer = 0; } setActive() {} - onClear() { - if (this.refreshTimer) { - clearInterval(this.refreshTimer); - this.refreshTimer = null; - } - if (this.debounceTimer) { - clearTimeout(this.debounceTimer); - this.debounceTimer = 0; - } - } - setParams(jsn) { this.config = Object.assign(this.config, (jsn && jsn.param) || {}); - this.start(); + this.startTimer(() => this.fetchRun(), this.config.refreshRate); } onRun() { @@ -37,18 +23,8 @@ class GiteaAction { } } - start() { - this.onClear(); - this.fetchRun(); - const duration = this.getRefreshDuration(); - if (duration > 0) { - this.refreshTimer = setInterval(() => this.fetchRun(), duration * 60 * 1000); - } - } - - async fetchRun() { - if (this.debounceTimer) clearTimeout(this.debounceTimer); - this.debounceTimer = setTimeout(async () => { + fetchRun() { + this.debounce(async () => { if (!this.config.url || !this.config.owner || !this.config.repo) { this.renderButton(null); return; @@ -69,20 +45,11 @@ class GiteaAction { } catch (e) { this.renderButton({ error: 'ERR' }); } - }, 300); + }); } renderButton(run) { - const canvas = document.createElement('canvas'); - canvas.width = 196; - canvas.height = 196; - const ctx = canvas.getContext('2d'); - - ctx.fillStyle = '#000000'; - ctx.fillRect(0, 0, 196, 196); - - ctx.textBaseline = 'middle'; - ctx.textAlign = 'center'; + const { canvas, ctx } = this.createCanvas(); // Owner — top const ownerLabel = this.config.owner || '?'; @@ -135,18 +102,6 @@ class GiteaAction { ctx.fillText(info.length > 16 ? info.slice(0, 15) + '…' : info, 98, 172); } - this.$UD.setBaseDataIcon(this.context, canvas.toDataURL('image/png')); - } - - getRefreshDuration() { - switch (this.config.refreshRate) { - case '1': return 1; - case '2': return 2; - case '3': return 5; - case '4': return 10; - case '5': return 30; - case '6': return 60; - default: return 0; - } + this.setIcon(canvas); } } diff --git a/plugin/actions/PetrolAction.js b/plugin/actions/PetrolAction.js index 84b9f9c..bf8aaab 100644 --- a/plugin/actions/PetrolAction.js +++ b/plugin/actions/PetrolAction.js @@ -1,15 +1,12 @@ -class PetrolAction { +class PetrolAction extends ActionBase { constructor($UD, context) { - this.$UD = $UD; - this.context = context; + super($UD, context); this.config = { url: '', stationUuid: '', fuelType: '', refreshRate: '1', }; - this.refreshTimer = null; - this.debounceTimer = 0; this.previousPrice = null; this.lastDelta = null; @@ -24,40 +21,18 @@ class PetrolAction { setActive() {} - onClear() { - if (this.refreshTimer) { - clearInterval(this.refreshTimer); - this.refreshTimer = null; - } - if (this.debounceTimer) { - clearTimeout(this.debounceTimer); - this.debounceTimer = 0; - } - } - setParams(jsn) { - const params = (jsn && jsn.param) || {}; - this.config = Object.assign(this.config, params); + this.config = Object.assign(this.config, (jsn && jsn.param) || {}); this.$UD.getSettings(this.context); - this.start(); + this.startTimer(() => this.fetchPrice(), this.config.refreshRate); } onRun() { this.fetchPrice(); } - start() { - this.onClear(); - this.fetchPrice(); - const duration = this.getRefreshDuration(); - if (duration > 0) { - this.refreshTimer = setInterval(() => this.fetchPrice(), duration * 60 * 1000); - } - } - - async fetchPrice() { - if (this.debounceTimer) clearTimeout(this.debounceTimer); - this.debounceTimer = setTimeout(async () => { + fetchPrice() { + this.debounce(async () => { if (!this.config.url || !this.config.stationUuid || !this.config.fuelType) { this.renderButton('?', null); return; @@ -73,7 +48,7 @@ class PetrolAction { if (delta !== 0) this.lastDelta = delta; } this.previousPrice = current; - this.renderButton(`${raw}€`, this.lastDelta); + this.renderButton(`${parseFloat(raw).toFixed(3)}€`, this.lastDelta); this.$UD.setSettings({ ...this.config, previousPrice: this.previousPrice, lastDelta: this.lastDelta }, this.context); } else { this.renderButton('N/A', null); @@ -81,7 +56,7 @@ class PetrolAction { } catch (e) { this.renderButton('ERR', null); } - }, 300); + }); } parsePrometheus(text) { @@ -97,55 +72,28 @@ class PetrolAction { } renderButton(priceText, delta) { - const canvas = document.createElement('canvas'); - canvas.width = 196; - canvas.height = 196; - const ctx = canvas.getContext('2d'); - - ctx.fillStyle = '#000000'; - ctx.fillRect(0, 0, 196, 196); - - ctx.strokeStyle = '#000'; - ctx.lineWidth = 3; - ctx.textBaseline = 'middle'; - ctx.textAlign = 'center'; + const { canvas, ctx } = this.createCanvas(); // Fuel type — top, light blue ctx.fillStyle = '#7ec8e3'; ctx.font = 'bold 28px "Source Han Sans SC"'; - ctx.strokeText(this.config.fuelType, 98, 45); ctx.fillText(this.config.fuelType, 98, 45); // Price — middle, yellow const fSize = priceText.length > 6 ? 34 - priceText.length : 40; ctx.fillStyle = '#f0c040'; ctx.font = `bold ${fSize}px "Source Han Sans SC"`; - ctx.strokeText(priceText, 98, 105); ctx.fillText(priceText, 98, 105); // Delta — bottom if (delta !== null) { const arrow = delta > 0 ? '▲' : '▼'; const color = delta > 0 ? '#ff6b6b' : '#6bff6b'; - const deltaText = `${arrow} ${Math.abs(delta).toFixed(3)}`; ctx.fillStyle = color; ctx.font = '18px "Source Han Sans SC"'; - ctx.strokeText(deltaText, 98, 142); - ctx.fillText(deltaText, 98, 142); + ctx.fillText(`${arrow} ${Math.abs(delta).toFixed(3)}`, 98, 142); } - this.$UD.setBaseDataIcon(this.context, canvas.toDataURL('image/png')); - } - - getRefreshDuration() { - switch (this.config.refreshRate) { - case '1': return 1; - case '2': return 2; - case '3': return 5; - case '4': return 10; - case '5': return 30; - case '6': return 60; - default: return 0; - } + this.setIcon(canvas); } } diff --git a/plugin/app.html b/plugin/app.html index 485affb..9121a62 100644 --- a/plugin/app.html +++ b/plugin/app.html @@ -11,6 +11,7 @@ +