From e7234991a1d6d9bed6b319c0be48a75c0e2c0678 Mon Sep 17 00:00:00 2001 From: Chris Monahan Date: Tue, 7 Apr 2026 07:37:43 -0400 Subject: [PATCH 1/4] prepare for next version --- metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata.json b/metadata.json index d929d76..60996a7 100644 --- a/metadata.json +++ b/metadata.json @@ -8,7 +8,7 @@ ], "url": "https://github.com/corecoding/Vitals", "uuid": "Vitals@CoreCoding.com", - "version": 73, + "version": 74, "donations": { "paypal": "corecoding" } From 2d6d2c540588a72a2e4c8475b541cdb8169ba524 Mon Sep 17 00:00:00 2001 From: Chris Monahan Date: Tue, 7 Apr 2026 07:46:21 -0400 Subject: [PATCH 2/4] don't record when disabled --- extension.js | 14 ++++++++++++-- values.js | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/extension.js b/extension.js index 470b28d..63be359 100644 --- a/extension.js +++ b/extension.js @@ -67,7 +67,8 @@ var VitalsMenuButton = GObject.registerClass({ this._sensors = new Sensors.Sensors(this._settings, this._sensorIcons); this._values = new Values.Values(this._settings, this._sensorIcons); this._historyCachePath = GLib.get_user_cache_dir() + '/vitals/history.json'; - this._values.loadTimeSeries(this._historyCachePath); + if (this._settings.get_boolean('show-sensor-history-graph')) + this._values.loadTimeSeries(this._historyCachePath); this._menuLayout = new St.BoxLayout({ vertical: false, clip_to_allocation: true, @@ -87,6 +88,14 @@ var VitalsMenuButton = GObject.registerClass({ this._addSettingChangedSignal('position-in-panel', this._positionInPanelChanged.bind(this)); this._addSettingChangedSignal('menu-centered', this._positionInPanelChanged.bind(this)); this._addSettingChangedSignal('icon-style', this._iconStyleChanged.bind(this)); + this._addSettingChangedSignal('show-sensor-history-graph', () => { + if (!this._settings.get_boolean('show-sensor-history-graph')) { + this._hideHistoryPopout(); + this._values.clearTimeSeries(this._historyCachePath); + } else { + this._values.loadTimeSeries(this._historyCachePath); + } + }); let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info', 'include-static-gpu-info', 'show-sensor-history-graph' ]; for (let setting of Object.values(settings)) @@ -875,7 +884,8 @@ var VitalsMenuButton = GObject.registerClass({ this._historyPopout = null; } this._destroyTimer(); - this._values.saveTimeSeries(this._historyCachePath); + if (this._settings.get_boolean('show-sensor-history-graph')) + this._values.saveTimeSeries(this._historyCachePath); this._sensors.destroy(); for (let signal of Object.values(this._settingChangedSignals)) diff --git a/values.js b/values.js index 97ad2ab..48339c7 100644 --- a/values.js +++ b/values.js @@ -62,6 +62,7 @@ export const Values = GObject.registerClass({ } _pushTimePoint(key, value, format) { + if (!this._settings.get_boolean('show-sensor-history-graph')) return; if (!this._graphableFormats.includes(format)) return; const num = typeof value === 'number' ? value : parseFloat(value); if (num !== num) return; // NaN check From ac7af1573abf6a513db4018647cacb882208e658 Mon Sep 17 00:00:00 2001 From: Chris Monahan Date: Tue, 7 Apr 2026 07:50:31 -0400 Subject: [PATCH 3/4] default popout to false --- schemas/org.gnome.shell.extensions.vitals.gschema.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/org.gnome.shell.extensions.vitals.gschema.xml b/schemas/org.gnome.shell.extensions.vitals.gschema.xml index 72f8f40..a3121b0 100644 --- a/schemas/org.gnome.shell.extensions.vitals.gschema.xml +++ b/schemas/org.gnome.shell.extensions.vitals.gschema.xml @@ -152,7 +152,7 @@ Set the style for the displayed sensor icons ('original', 'updated') - true + false Show sensor history graph on hover When hovering a sensor row in the menu, show a pop-out graph of its history to the left From e172f1fbf32efde19bd6544701e92b79dd43087f Mon Sep 17 00:00:00 2001 From: Chris Monahan Date: Tue, 7 Apr 2026 23:26:59 -0400 Subject: [PATCH 4/4] remove history for now --- extension.js | 257 +----------------- history.js | 157 ----------- prefs.js | 3 +- prefs.ui | 29 -- schemas/gschemas.compiled | Bin 2084 -> 1972 bytes ....gnome.shell.extensions.vitals.gschema.xml | 10 - stylesheet.css | 61 ----- values.js | 138 ---------- 8 files changed, 3 insertions(+), 652 deletions(-) delete mode 100644 history.js diff --git a/extension.js b/extension.js index 63be359..642c4d7 100644 --- a/extension.js +++ b/extension.js @@ -17,7 +17,6 @@ import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js'; import * as Values from './values.js'; import * as Config from 'resource:///org/gnome/shell/misc/config.js'; import * as MenuItem from './menuItem.js'; -import * as HistoryGraph from './history.js'; let vitalsMenu; @@ -59,16 +58,9 @@ var VitalsMenuButton = GObject.registerClass({ this._newGpuDetected = false; this._newGpuDetectedCount = 0; this._last_query = new Date().getTime(); - this._historyPopout = null; - this._historyHideTimeoutId = null; - this._historyPopoutSensorKey = null; - this._historyPopoutLabel = null; this._sensors = new Sensors.Sensors(this._settings, this._sensorIcons); this._values = new Values.Values(this._settings, this._sensorIcons); - this._historyCachePath = GLib.get_user_cache_dir() + '/vitals/history.json'; - if (this._settings.get_boolean('show-sensor-history-graph')) - this._values.loadTimeSeries(this._historyCachePath); this._menuLayout = new St.BoxLayout({ vertical: false, clip_to_allocation: true, @@ -88,16 +80,8 @@ var VitalsMenuButton = GObject.registerClass({ this._addSettingChangedSignal('position-in-panel', this._positionInPanelChanged.bind(this)); this._addSettingChangedSignal('menu-centered', this._positionInPanelChanged.bind(this)); this._addSettingChangedSignal('icon-style', this._iconStyleChanged.bind(this)); - this._addSettingChangedSignal('show-sensor-history-graph', () => { - if (!this._settings.get_boolean('show-sensor-history-graph')) { - this._hideHistoryPopout(); - this._values.clearTimeSeries(this._historyCachePath); - } else { - this._values.loadTimeSeries(this._historyCachePath); - } - }); - let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info', 'include-static-gpu-info', 'show-sensor-history-graph' ]; + let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info', 'include-static-gpu-info' ]; for (let setting of Object.values(settings)) this._addSettingChangedSignal(setting, this._redrawMenu.bind(this)); @@ -106,7 +90,6 @@ var VitalsMenuButton = GObject.registerClass({ this._addSettingChangedSignal('show-' + sensor, this._showHideSensorsChanged.bind(this)); this._initializeMenu(); - this._createHistoryPopout(); // start off with fresh sensors this._querySensors(); @@ -193,214 +176,8 @@ var VitalsMenuButton = GObject.registerClass({ // refresh sensors now this._querySensors(); - } else { - this._hideHistoryPopout(); - } - }); - } - - _createHistoryPopout() { - const popoutWidth = 280; - const popoutHeight = 145; - this._historyPopout = new St.BoxLayout({ - vertical: true, - style_class: 'vitals-history-popout', - width: popoutWidth, - height: popoutHeight, - reactive: true, - visible: false - }); - this._historyPopout.clip_to_allocation = true; - this._historyGraph = new HistoryGraph.HistoryGraph(); - this._historyTitleLabel = new St.Label({ - text: '', - style_class: 'vitals-history-popout-label', - x_align: Clutter.ActorAlign.END - }); - this._historyPopout.add_child(this._historyTitleLabel); - this._historyGraphRow = new St.BoxLayout({ - vertical: false, - x_expand: true, - style_class: 'vitals-history-graph-row' - }); - this._historyYAxis = new St.BoxLayout({ - vertical: true, - width: 56, - style_class: 'vitals-history-y-axis' - }); - this._historyYMax = new St.Label({ - text: '', - style_class: 'vitals-history-popout-axis', - x_align: Clutter.ActorAlign.END - }); - this._historyYMin = new St.Label({ - text: '', - style_class: 'vitals-history-popout-axis', - x_align: Clutter.ActorAlign.END - }); - this._historyYSpacer = new St.BoxLayout({ vertical: true, y_expand: true }); - this._historyYAxis.add_child(this._historyYMax); - this._historyYAxis.add_child(this._historyYSpacer); - this._historyYAxis.add_child(this._historyYMin); - this._historyGraphRow.add_child(this._historyYAxis); - this._historyGraphRow.add_child(this._historyGraph); - this._historyGraphRightSpacer = new St.BoxLayout({ - vertical: true, - width: 18, - style_class: 'vitals-history-graph-right-spacer' - }); - this._historyGraphRow.add_child(this._historyGraphRightSpacer); - this._historyPopout.add_child(this._historyGraphRow); - this._historyXWrap = new St.BoxLayout({ - vertical: false, - x_expand: true, - style_class: 'vitals-history-x-wrap' - }); - this._historyXSpacer = new St.BoxLayout({ - vertical: true, - width: 62, - style_class: 'vitals-history-x-spacer' - }); - this._historyXRow = new St.BoxLayout({ - vertical: false, - x_expand: true, - style_class: 'vitals-history-x-row' - }); - this._historyXLeft = new St.Label({ - text: '', - style_class: 'vitals-history-popout-axis', - x_align: Clutter.ActorAlign.START, - x_expand: true - }); - this._historyXRight = new St.Label({ - text: _('now'), - style_class: 'vitals-history-popout-axis', - x_align: Clutter.ActorAlign.END - }); - this._historyXRow.add_child(this._historyXLeft); - this._historyXRow.add_child(this._historyXRight); - this._historyXWrap.add_child(this._historyXSpacer); - this._historyXWrap.add_child(this._historyXRow); - this._historyPopout.add_child(this._historyXWrap); - this._historyPopout.connect('enter-event', () => { - if (this._historyHideTimeoutId) { - GLib.Source.remove(this._historyHideTimeoutId); - this._historyHideTimeoutId = null; } }); - this._historyPopout.connect('leave-event', () => { - this._scheduleHistoryPopoutHide(); - }); - } - - _updateHistoryGraph(key, label, samples) { - const historyDuration = Math.max(60, this._settings.get_int('sensor-history-duration')); - const nowSec = Date.now() / 1000; - const cutoff = nowSec - historyDuration; - const windowed = samples.filter(s => s.t >= cutoff); - while (windowed.length > 0 && windowed[0].v === null) windowed.shift(); - const base = Math.max(1, Math.ceil(windowed.length / 200)); - this._historyGraph.setData(windowed, label, '', base); - const actualSpan = this._historyGraph.getTimeSpan(); - const displayDuration = actualSpan > 0 ? Math.min(historyDuration, Math.round(actualSpan)) : historyDuration; - this._historyXLeft.text = this._values.formatDuration(displayDuration) + ' ' + _('ago'); - const rawRange = this._historyGraph.getRawRange(); - if (rawRange) { - this._historyYMax.text = this._values.formatValue(key, rawRange.max); - this._historyYMin.text = this._values.formatValue(key, rawRange.min); - this._historyYAxis.show(); - } else { - this._historyYMax.text = ''; - this._historyYMin.text = ''; - this._historyYAxis.hide(); - } - } - - _showHistoryPopout(key, label, itemActor) { - if (!this._settings.get_boolean('show-sensor-history-graph')) return; - const samples = this._values.getTimeSeries(key); - if (samples.length === 0) return; - this._historyPopoutSensorKey = key; - this._historyPopoutLabel = label; - try { - this._historyTitleLabel.text = label + ' ' + _('history'); - this._historyTitleLabel.show(); - this._updateHistoryGraph(key, label, samples); - } catch (e) { - this._historyYMax.text = ''; - this._historyYMin.text = ''; - } - const parent = this.menu.actor.get_parent(); - if (!parent) return; - if (this._historyPopout.get_parent() !== parent) { - if (this._historyPopout.get_parent()) - this._historyPopout.get_parent().remove_child(this._historyPopout); - parent.add_child(this._historyPopout); - } - const menuX = this.menu.actor.get_x(); - const menuY = this.menu.actor.get_y(); - let popoutY = menuY; - if (itemActor) { - let relY = 0; - let node = itemActor; - while (node && node !== this.menu.actor) { - relY += node.get_y(); - node = node.get_parent(); - } - const rowH = itemActor.get_height(); - const popoutH = this._historyPopout.get_height(); - popoutY = menuY + relY + Math.round((rowH - popoutH) / 2); - popoutY = Math.max(0, popoutY); - } - const popoutW = this._historyPopout.get_width(); - const menuW = this.menu.actor.get_width(); - let popoutX = menuX - popoutW - 8; - if (popoutX < 0) - popoutX = menuX + menuW + 8; - this._historyPopout.set_position(popoutX, popoutY); - this._historyPopout.show(); - if (this._historyHideTimeoutId) { - GLib.Source.remove(this._historyHideTimeoutId); - this._historyHideTimeoutId = null; - } - } - - _scheduleHistoryPopoutHide() { - if (this._historyHideTimeoutId) return; - this._historyHideTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { - this._hideHistoryPopout(); - this._historyHideTimeoutId = null; - return GLib.SOURCE_REMOVE; - }); - } - - _hideHistoryPopout() { - if (this._historyHideTimeoutId) { - GLib.Source.remove(this._historyHideTimeoutId); - this._historyHideTimeoutId = null; - } - if (this._historyPopout && this._historyPopout.get_parent()) { - this._historyPopout.hide(); - this._historyPopout.get_parent().remove_child(this._historyPopout); - } - this._historyPopoutSensorKey = null; - this._historyPopoutLabel = null; - } - - _refreshHistoryPopout() { - const key = this._historyPopoutSensorKey; - const label = this._historyPopoutLabel; - if (!key || !label) return; - if (!this._historyPopout || !this._historyPopout.visible) return; - - const samples = this._values.getTimeSeries(key); - if (samples.length === 0) return; - - try { - this._updateHistoryGraph(key, label, samples); - } catch (e) { - // ignore - } } _initializeMenuGroup(groupName, optionName, menuSuffix = '', position = -1) { @@ -468,7 +245,7 @@ var VitalsMenuButton = GObject.registerClass({ GLib.PRIORITY_DEFAULT, update_time, (self) => { - // always query sensors (for panel display when hot, and for history graph data) + // always query sensors (for panel display when hot) this._querySensors(); return GLib.SOURCE_CONTINUE; } @@ -570,7 +347,6 @@ var VitalsMenuButton = GObject.registerClass({ } _redrawMenu() { - this._hideHistoryPopout(); this._removeHotItems(); for (let key in this._sensorMenuItems) { @@ -607,7 +383,6 @@ var VitalsMenuButton = GObject.registerClass({ _updateTimeSettingChanged() { this._destroyTimer(); - this._values.clearTimeSeries(this._historyCachePath); this._initializeTimer(); } @@ -705,25 +480,6 @@ var VitalsMenuButton = GObject.registerClass({ } this._groups[type].menu.addMenuItem(item, i); - - if (this._settings.get_boolean('show-sensor-history-graph')) { - const key = item.key; - const label = item.label; - item.actor.connect('enter-event', () => { - const samples = this._values.getTimeSeries(key); - if (this._historyHideTimeoutId) { - GLib.Source.remove(this._historyHideTimeoutId); - this._historyHideTimeoutId = null; - } - if (samples.length > 0) - this._showHistoryPopout(key, label, item.actor); - else - this._hideHistoryPopout(); - }); - item.actor.connect('leave-event', () => { - this._scheduleHistoryPopoutHide(); - }); - } } _defaultLabel() { @@ -865,8 +621,6 @@ var VitalsMenuButton = GObject.registerClass({ this._notify('Vitals', this._warnings.join("\n"), 'folder-symbolic'); this._warnings = []; } - - this._refreshHistoryPopout(); } _notify(msg, details, icon) { @@ -878,14 +632,7 @@ var VitalsMenuButton = GObject.registerClass({ } destroy() { - this._hideHistoryPopout(); - if (this._historyPopout) { - this._historyPopout.destroy(); - this._historyPopout = null; - } this._destroyTimer(); - if (this._settings.get_boolean('show-sensor-history-graph')) - this._values.saveTimeSeries(this._historyCachePath); this._sensors.destroy(); for (let signal of Object.values(this._settingChangedSignals)) diff --git a/history.js b/history.js deleted file mode 100644 index 2c065b5..0000000 --- a/history.js +++ /dev/null @@ -1,157 +0,0 @@ -/* - Copyright (c) 2018, Chris Monahan - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the GNOME nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -import GObject from 'gi://GObject'; -import St from 'gi://St'; - -const GRAPH_WIDTH = 208; -const GRAPH_HEIGHT = 90; -const PADDING = 4; -const MIN_BAR_WIDTH = 1; - -export const HistoryGraph = GObject.registerClass({ - GTypeName: 'HistoryGraph', -}, class HistoryGraph extends St.Widget { - - _init(params = {}) { - super._init({ - width: GRAPH_WIDTH, - height: GRAPH_HEIGHT, - style_class: 'vitals-history-graph', - ...params - }); - this._samples = []; - this._label = ''; - this._unit = ''; - this._vMin = 0; - this._vMax = 0; - this._base = 1; - this._dataOffset = 0; - this.clip_to_allocation = true; - this._barContainer = new St.Widget({ - x_expand: true, - y_expand: true - }); - this._barContainer.clip_to_allocation = true; - this.add_child(this._barContainer); - } - - setData(samples, label, unit, base) { - this._samples = Array.isArray(samples) ? samples : []; - this._label = label || ''; - this._unit = unit || ''; - this._base = Math.max(1, base); - this._rebuildBars(); - } - - _rebuildBars() { - try { - const children = this._barContainer.get_children(); - if (children && children.length > 0) { - for (let i = children.length - 1; i >= 0; i--) - children[i].destroy(); - } - } catch (e) { - // ignore - } - const data = this._samples; - if (data.length === 0) return; - - const graphW = GRAPH_WIDTH - 2 * PADDING; - const graphH = GRAPH_HEIGHT - PADDING; - if (graphW <= 0 || graphH <= 0) return; - - const base = this._base; - const maxBars = Math.floor(graphW / MIN_BAR_WIDTH); - const totalBars = Math.ceil(data.length / base); - const numBars = Math.min(totalBars, maxBars); - const dataOffset = (totalBars - numBars) * base; - this._dataOffset = dataOffset; - const barWidth = graphW / numBars; - - let vMin = Infinity, vMax = -Infinity; - for (let i = dataOffset; i < data.length; i++) { - if (data[i].v === null) continue; - if (data[i].v < vMin) vMin = data[i].v; - if (data[i].v > vMax) vMax = data[i].v; - } - if (vMin === Infinity) { - vMin = 0; - vMax = 1; - } else if (vMax <= vMin) { - const v = vMin; - if (v >= 0 && v <= 1) { - const margin = 0.05; - vMin = Math.max(0, v - margin); - vMax = Math.min(1, v + margin); - if (vMax <= vMin) vMax = vMin + margin; - } else { - vMin -= 1; - vMax += 1; - } - } - this._vMin = vMin; - this._vMax = vMax; - const vRange = vMax - vMin; - - for (let b = 0; b < numBars; b++) { - const iStart = dataOffset + b * base; - const iEnd = Math.min(iStart + base, data.length); - let sum = 0; - let count = 0; - for (let i = iStart; i < iEnd; i++) { - if (data[i].v !== null) { - sum += data[i].v; - count++; - } - } - if (count === 0) continue; - const avg = sum / count; - const norm = (avg - vMin) / vRange; - const barH = Math.max(1, Math.round(norm * graphH)); - const x = Math.round(b * barWidth); - const w = Math.round((b + 1) * barWidth) - x; - const bar = new St.Bin({ - width: w, - height: barH, - style_class: 'vitals-history-graph-bar' - }); - bar.set_position(x, graphH - barH); - this._barContainer.add_child(bar); - } - } - - getRawRange() { - if (this._samples.length === 0) return null; - return { min: this._vMin, max: this._vMax }; - } - - getTimeSpan() { - const start = this._dataOffset; - if (this._samples.length - start < 2) return 0; - return this._samples[this._samples.length - 1].t - this._samples[start].t; - } -}); diff --git a/prefs.js b/prefs.js index ce7885c..07916bd 100644 --- a/prefs.js +++ b/prefs.js @@ -49,8 +49,7 @@ const Settings = new GObject.Class({ 'alphabetize', 'hide-zeros', 'include-public-ip', 'show-battery', 'fixed-widths', 'hide-icons', 'menu-centered', 'include-static-info', - 'show-gpu', 'include-static-gpu-info', - 'show-sensor-history-graph' ]; + 'show-gpu', 'include-static-gpu-info' ]; for (let key in sensors) { let sensor = sensors[key]; diff --git a/prefs.ui b/prefs.ui index aa63bde..950c1b0 100644 --- a/prefs.ui +++ b/prefs.ui @@ -415,35 +415,6 @@ - - - 100 - 0 - - - 0 - 6 - 6 - - - 1 - 0 - start - 5 - 5 - Show sensor history graph on hover - - - - - end - 5 - - - - - - diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index e60cb0f1a650ca893c3a45db007e3a7cdee12b84..936fdb3a506ffb8d2405e995628fbde747e729c0 100644 GIT binary patch literal 1972 zcmZWqUuYaf93C~bHEEh_`p>A<7EvzB+z~1|PRe;k)nKZ)azIzu)Zq z?zxxzEc6pA%IkjcbfK$vm)>3A8?_(D$$WNHoD%22eSJdgI3UCwTq`?ai08#B_;WyK z?OHq0v8tIsMNv(4tV&F%6H}WFtsfZ<7ovK^kr14l>k;<=m|Aek9{Qn)ff9q@J-}XI zAFv-dSmF?vd5n~J0Q}%K9EK(yF7e&@HwKu4%SX(L3Kky+j$U~B$$K$|x&r?c_yypn zM6J-L-VZ+jr$B40(WOs40RK(!Md0el(mDFneef@V7l6t9+8llA-SC&eE5K$=pX$(ti5XtbYf59$0-V_=`UEA@~d6 zMc|M5W3CR=gYZ|uUjVNi-xvltJq&*hH`jp=ZvQexpL!4cAHY8Y-!CoFa+>jQvA==* z!iO$jYWfv?v!x%5&!Y5MrNw_r> z1ojB_6rTSIa38>P&pyH)z@D)SV2@xAVm$0E>>)gU>_x*s5Vn*Io9JJ-aToG+K^m*G zGD!F?PUp|~N$)<;RvNTFof$7yvCg_)Zt#vLJkvF(qZetv<&DKKyj~Hrju-nK&l5vO zj)|&oiq(qO%XJu!G`1TRwPbzEXk@H>gN=z*+1i;KpT`=7)|qU^Ejq$&wJf5Lk<_;D Zqul5J`KW^Pu%7$q=2fN{9PZ1p{{VT+hkyV8 literal 2084 zcmZ8iL1-LR7=G2p)}(2+X>AiVn%FA4%Iq4d)uM=?#T+~oY^C%veY^XzGj?X)IBzD| zja3BOgCbb)Qcy_`ih`)|AcjiCi`a`0p)Fpdh?gKXr3Yzp(D;3CW}C)^kMEmr{`~K~ z|NZmlg_mXG%P1AaZwP!O*X7#^Zv*&p_4j>bz8e!0;sp5VjY9O?CB!Ozqnlxf7X^6_ zpp#a$73ok_ji-X3s@kcFjISe8o$^x|7={Z`ejsPT++6SW|DgxZn*gT2vp^qs3$V37 z2|NgF2X+A1(oO7y-c^8S8wTzM?g8!t?k_M3UaGxvnCtLdePT*XfF*DxJMbNS>R$LU zI0N3*Zy%*k&Gy?2E^HRDf%&j963?h<|K&G65HKLK9bx3Zr;^)~qP;Pb$> z2jgD))H~q+3jPgv_`;`O)2HUTi{RZTRQE2Zc^7-Y9|H%npQag4y#?`e;BSEUZ~S?X zKJ`}k7r;LQKfL}%AMcd9hwFm>1+tSL*!oa2Pw5WS85o&;Y=wEK>6gI{@KocWGJR^E z>sjywaBX({GJR^s9|0c&zI<-AMxUDJioqH1x~!KZ*^_vr{0D5v*6E& zuS)0WQ?vf_;Pb$ZcfQ|ApL#p|1@I-{;_OEY^r^X@Meq`EeP*w{FY12yY{VX5?Bo-V zFrK;ue+WDRES~%JL;BRr^C88Sjj$=~x9*sZNmb|+FVOL(;`vSG%&0^c zU%V*SE5_5)PN+hibT;tWZ|KM|X(v!v1;4VNhfK;Se;+l13~A!59{3H%j|Juh{w_1h z@%)yj5+_bn!{>a-GcZZL;_1{e7&|&KZoZtG8QD^>T0T838zN_!|Q_pE#E|WAKTJQGhe9 zALs=*i#U_E0i02sZ}$S6Uz|^zQ)oS5%QdBg6kE6Fwnm)jhB7!CB8qxcW0`vPQPePi zYxYuVb^zDb25>DOz%?YE^xLZGO#98$E3RiPe^HM-U0&~48$U&j9Y1nn8L1#Y5AUlk z(^MrL#{~N3{}TE#h&@?XsXvq7?OOi)2J&ZnN4QFZcIr6IERkG>OP5$g@Tn9j>~nXEp^^qGVq^Jh_7>ywcEv}s{x$OF s@BQ^hZ^c=0eAdPGM{y&Jx4*8B(C%}$Icon styles Set the style for the displayed sensor icons ('original', 'updated') - - false - Show sensor history graph on hover - When hovering a sensor row in the menu, show a pop-out graph of its history to the left - - - 3600 - Sensor history duration (seconds) - How many seconds of history to keep and display in the hover graph (e.g. 3600 for 1 hour) - diff --git a/stylesheet.css b/stylesheet.css index e4e3d1b..4fcd985 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -17,64 +17,3 @@ .vitals-button-action:hover, .vitals-button-action:focus { border-color: #777; } .vitals-button-action > StIcon { icon-size: 16px; } .vitals-button-box { padding: 0px; spacing: 22px; } - -.vitals-history-popout { - padding: 6px 14px 6px 6px; - border-radius: 8px; - border: 1px solid rgba(128, 128, 128, 0.4); - background-color: rgba(30, 30, 30, 0.95); -} - -.vitals-history-popout-label { - font-size: 10px; - color: rgba(200, 200, 200, 0.9); -} - -.vitals-history-popout-axis { - font-size: 9px; - color: rgba(180, 180, 180, 0.85); -} - -.vitals-history-graph-row { - margin-top: 2px; - margin-bottom: 2px; -} - -.vitals-history-y-axis { - margin-right: 6px; - padding-top: 2px; - padding-bottom: 2px; -} - -.vitals-history-x-wrap { - margin-top: 2px; -} - -.vitals-history-x-spacer { - width: 62px; - min-width: 62px; -} - -.vitals-history-x-row { - min-width: 0; -} - -.vitals-history-x-row .vitals-history-popout-axis { - margin-left: 0; - margin-right: 4px; -} - -.vitals-history-graph { - background-color: rgba(0, 0, 0, 0.25); - border-radius: 4px; - padding: 4px 4px 0 4px; - margin: 2px 0 2px 0; -} - -.vitals-history-graph-bars { - spacing: 0px; -} - -.vitals-history-graph-bar { - background-color: rgba(51, 128, 230, 0.87); -} diff --git a/values.js b/values.js index 48339c7..f16778c 100644 --- a/values.js +++ b/values.js @@ -25,7 +25,6 @@ */ import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; import GObject from 'gi://GObject'; const cbFun = (d, c) => { @@ -49,139 +48,9 @@ export const Values = GObject.registerClass({ this._history = {}; //this._history2 = {}; - this._timeSeries = {}; - this._timeSeriesFormat = {}; - this._graphableFormats = ['temp', 'in', 'fan', 'percent', 'hertz', 'memory', 'speed', 'storage', 'watt', 'watt-gpu', 'milliamp', 'milliamp-hour', 'load']; this.resetHistory(); } - _getHistoryDurationSeconds() { - if (this._settings && this._settings.get_int) - return Math.max(60, this._settings.get_int('sensor-history-duration')); - return 3600; - } - - _pushTimePoint(key, value, format) { - if (!this._settings.get_boolean('show-sensor-history-graph')) return; - if (!this._graphableFormats.includes(format)) return; - const num = typeof value === 'number' ? value : parseFloat(value); - if (num !== num) return; // NaN check - this._timeSeriesFormat[key] = format; - const now = Date.now() / 1000; - if (!(key in this._timeSeries)) this._timeSeries[key] = []; - const buf = this._timeSeries[key]; - const interval = Math.max(1, this._settings.get_int('update-time')); - const minInterval = interval - 0.5; - if (buf.length > 0 && buf[buf.length - 1].v !== null && (now - buf[buf.length - 1].t) < minInterval) { - buf[buf.length - 1].v = num; - return; - } - if (buf.length > 0) { - const lastT = buf[buf.length - 1].t; - const gap = now - lastT; - if (gap > interval * 3) { - let fillT = lastT + interval; - while (fillT < now - interval * 0.5) { - buf.push({ t: fillT, v: null }); - fillT += interval; - } - } - } - buf.push({ t: now, v: num }); - const maxAge = this._getHistoryDurationSeconds(); - while (buf.length > 0 && buf[0].t < now - maxAge) buf.shift(); - const maxPoints = 3600; - while (buf.length > maxPoints) buf.shift(); - } - - clearTimeSeries(cachePath) { - this._timeSeries = {}; - this._timeSeriesFormat = {}; - if (cachePath) { - try { - const file = Gio.File.new_for_path(cachePath); - if (file.query_exists(null)) - file.delete(null); - } catch (e) { - // ignore - } - } - } - - saveTimeSeries(path) { - try { - const obj = { - version: 1, - timeSeries: this._timeSeries, - timeSeriesFormat: this._timeSeriesFormat - }; - const json = JSON.stringify(obj); - const dir = GLib.path_get_dirname(path); - GLib.mkdir_with_parents(dir, 0o755); - GLib.file_set_contents(path, json); - } catch (e) { - // ignore write failures - } - } - - loadTimeSeries(path) { - try { - const file = Gio.File.new_for_path(path); - if (!file.query_exists(null)) return; - const [ok, contents] = GLib.file_get_contents(path); - if (!ok) return; - const decoder = new TextDecoder('utf-8'); - const json = decoder.decode(contents); - const obj = JSON.parse(json); - if (!obj || obj.version !== 1) return; - if (obj.timeSeries && typeof obj.timeSeries === 'object') - this._timeSeries = obj.timeSeries; - if (obj.timeSeriesFormat && typeof obj.timeSeriesFormat === 'object') - this._timeSeriesFormat = obj.timeSeriesFormat; - const now = Date.now() / 1000; - const maxAge = this._getHistoryDurationSeconds(); - const cutoff = now - maxAge; - for (const key in this._timeSeries) { - const buf = this._timeSeries[key]; - if (!Array.isArray(buf)) { - delete this._timeSeries[key]; - continue; - } - while (buf.length > 0 && buf[0].t < cutoff) - buf.shift(); - while (buf.length > 0 && buf[0].v === null) - buf.shift(); - if (buf.length === 0) { - delete this._timeSeries[key]; - delete this._timeSeriesFormat[key]; - } - } - } catch (e) { - // ignore corrupt or missing file - } - } - - getTimeSeries(key) { - if (!(key in this._timeSeries)) return []; - return this._timeSeries[key].slice(); - } - - formatValue(key, rawValue) { - const format = key in this._timeSeriesFormat ? this._timeSeriesFormat[key] : 'percent'; - return this._legible(rawValue, format); - } - - formatDuration(seconds) { - seconds = Math.round(Math.abs(seconds)); - if (seconds < 60) return seconds + 's'; - const m = Math.floor(seconds / 60); - if (m < 60) return m + 'm'; - const h = Math.floor(m / 60); - const rm = m % 60; - if (rm === 0) return h + 'h'; - return h + 'h ' + rm + 'm'; - } - _legible(value, sensorClass) { let unit = 1000; if (value === null) return 'N/A'; @@ -376,8 +245,6 @@ export const Values = GObject.registerClass({ // save previous values to update screen on changes only let previousValue = this._history[type][key]; this._history[type][key] = [legible, value]; - if (type !== 'network-rx' && type !== 'network-tx') - this._pushTimePoint(key, value, format); // process average, min and max values if (type == 'temperature' || type == 'voltage' || type == 'fan') { @@ -409,7 +276,6 @@ export const Values = GObject.registerClass({ let vals = Object.values(this._history[type]).map(x => parseFloat(x[1])); let sum = vals.reduce((partialSum, a) => partialSum + a, 0); const memUnit = this._settings.get_int('memory-measurement') ? 1000 : 1024; - this._pushTimePoint('__' + type + '_boot__', sum / memUnit, 'memory'); output.push(['Boot ' + direction, this._legible(sum, format), type, '__' + type + '_boot__']); // keeps track of session start point @@ -417,14 +283,11 @@ export const Values = GObject.registerClass({ this._networkSpeedOffset[key] = sum; // outputs session upload and download for all interfaces for #234 - const sessionVal = sum - this._networkSpeedOffset[key]; - this._pushTimePoint('__' + type + '_ses__', sessionVal / memUnit, 'memory'); output.push(['Session ' + direction, this._legible(sum - this._networkSpeedOffset[key], format), type, '__' + type + '_ses__']); // calculate speed for this interface let speed = (value - previousValue[1]) / dwell; output.push([label, this._legible(speed, 'speed'), type, key]); - this._pushTimePoint(key, speed, 'speed'); // store speed for Device report if (!(direction in this._networkSpeeds)) this._networkSpeeds[direction] = {}; @@ -440,7 +303,6 @@ export const Values = GObject.registerClass({ for (let iface in this._networkSpeeds[direction]) sumNum += parseFloat(this._networkSpeeds[direction][iface]); - this._pushTimePoint('__network-' + direction + '_max__', sumNum, 'speed'); let sum = this._legible(sumNum, 'speed'); output.push(['Device ' + direction, sum, 'network-' + direction, '__network-' + direction + '_max__']); // append download speed to group itself