neiam
/gitgud-plasmoid
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
Live wall: tint all text one hue, green unless set
e49521d · gmorell · 2026-11-06 04:20
Message
{commit_body(@commit)}
Files changed
modified
README.md
+3
−1
@@ -74,7 +74,7 @@ Every job running right now, a card each, tiled to fill the space, the same as G
| 74 | 74 | |
| 75 | 75 | There are two ways to show it: |
| 76 | 76 | |
| 77 | −- **A window**: the grid button in the popup's header, or right-click the widget → **Open Live CI Wall**. **F11** (or the button) toggles full screen and **Esc** leaves it. **T** (or the opacity button) makes the background see-through and the cards partly so, keeping the text solid. KWin's own *More Actions → Opacity* fades the text too. Put it on a spare monitor. | |
| 77 | +- **A window**: the grid button in the popup's header, or right-click the widget → **Open Live CI Wall**. **F11** (or the button) toggles full screen and **Esc** leaves it. **T** (or the opacity button) makes the background see-through and the cards partly so, keeping the text solid. KWin's own *More Actions → Opacity* fades the text too. **H** (or the tint button) draws all the text, the clock, the card borders and the icons in one hue, green by default, which suits the see-through look. Failed steps and errors keep their colours. Pick the hue under *Behavior → Live CI wall*. Put it on a spare monitor. | |
| 78 | 78 | - **Wall mode on the desktop**: *Behavior → Live CI wall → Wall mode*. A widget on the desktop (not in a panel) then *is* the wall. Resize it as large as you like. In a panel the widget stays the circles. |
| 79 | 79 | |
| 80 | 80 | It needs a GitGud server with the `/api/mobile/v1/ci/live` endpoint and a token with `ci:read`. |
@@ -123,6 +123,8 @@ Ring the **bell** on a run that is still going (on hover in the popup, in the ru
| 123 | 123 | | Hide repositories that have never run CI | on | | |
| 124 | 124 | | Notify when any run I triggered fails | off | See [Notifications](#notifications) | |
| 125 | 125 | | Wall mode: on the desktop, show the wall | off | See [Live CI wall](#live-ci-wall) | |
| 126 | +| Tint all text one hue | off | Also **H** in the wall window | | |
| 127 | +| Tint hue | 120° (green) | | | |
| 126 | 128 | |
| 127 | 129 | Each poll makes one `/repos` call, which carries every repo's latest CI status. It then calls `/repos/:owner/:name/runs` only for repos that need it, so the fast poll stays cheap even when you watch a large organization. |
| 128 | 130 |
modified
contents/config/main.xml
+13
−0
@@ -47,6 +47,19 @@
| 47 | 47 | <default>false</default> |
| 48 | 48 | </entry> |
| 49 | 49 | |
| 50 | + <entry name="wallTint" type="Bool"> | |
| 51 | + <label>Live wall: tint all text one hue</label> | |
| 52 | + <default>false</default> | |
| 53 | + </entry> | |
| 54 | + | |
| 55 | + <!-- degrees on the colour wheel; 120 is green --> | |
| 56 | + <entry name="wallTintHue" type="Int"> | |
| 57 | + <label>Live wall: the hue text is tinted</label> | |
| 58 | + <default>120</default> | |
| 59 | + <min>0</min> | |
| 60 | + <max>359</max> | |
| 61 | + </entry> | |
| 62 | + | |
| 50 | 63 | <entry name="desktopWall" type="Bool"> |
| 51 | 64 | <label>On the desktop, the widget is the live CI wall</label> |
| 52 | 65 | <default>false</default> |
modified
contents/ui/WallCard.qml
+4
−1
@@ -14,6 +14,9 @@ Rectangle {
| 14 | 14 | required property var card |
| 15 | 15 | property double now: Date.now() |
| 16 | 16 | property bool translucent: false |
| 17 | + // The step bar's colour for a step in progress. A status colour, not | |
| 18 | + // a text one, so the wall's tint leaves it alone. | |
| 19 | + property color runningColor: Kirigami.Theme.neutralTextColor | |
| 17 | 20 | |
| 18 | 21 | readonly property var job: card.job || {} |
| 19 | 22 | readonly property var run: card.run || {} |
@@ -123,7 +126,7 @@ Rectangle {
| 123 | 126 | Layout.fillHeight: true |
| 124 | 127 | radius: 2 |
| 125 | 128 | color: status === "running" || status === "in_progress" |
| 126 | − ? Kirigami.Theme.neutralTextColor | |
| 129 | + ? cardItem.runningColor | |
| 127 | 130 | : Status.isFinished(status) ? Status.color(status, Kirigami.Theme) |
| 128 | 131 | : Qt.alpha(Kirigami.Theme.disabledTextColor, 0.3) |
| 129 | 132 | } |
modified
contents/ui/WallView.qml
+40
−0
@@ -40,10 +40,33 @@ Item {
| 40 | 40 | // A small desktop widget: the header keeps only what it must. |
| 41 | 41 | readonly property bool narrow: width < Kirigami.Units.gridUnit * 34 |
| 42 | 42 | |
| 43 | + // Tint: every bit of text — and the clock, borders and shimmer that | |
| 44 | + // share its colours — in one hue (store.wallTintHue, green unless | |
| 45 | + // set), a terminal look that suits the translucent window. Done by | |
| 46 | + // overriding the theme's text colours on the layout below, which | |
| 47 | + // every Label, icon and card under it inherits. Status colours — | |
| 48 | + // a failed step, "offline" — keep their meaning and aren't touched. | |
| 49 | + // | |
| 50 | + // Lightness comes from the theme's background, not its text colour: | |
| 51 | + // reading the text colour the layout itself overrides would be a | |
| 52 | + // loop. A dark theme gets a bright tint, a light one a deep one. | |
| 53 | + readonly property bool tinted: store.wallTint | |
| 54 | + readonly property real tintHue: store.wallTintHue / 360 | |
| 55 | + readonly property bool darkTheme: Kirigami.Theme.backgroundColor.hslLightness < 0.5 | |
| 56 | + // The accent (clock, live dot, borders) is the most saturated, so it | |
| 57 | + // still stands out from the body text the way the neutral colour does. | |
| 58 | + readonly property color tintText: Qt.hsla(tintHue, 0.70, darkTheme ? 0.62 : 0.30, 1) | |
| 59 | + readonly property color tintDim: Qt.hsla(tintHue, 0.40, darkTheme ? 0.45 : 0.50, 1) | |
| 60 | + readonly property color tintAccent: Qt.hsla(tintHue, 1.0, darkTheme ? 0.55 : 0.28, 1) | |
| 61 | + | |
| 43 | 62 | Component.onCompleted: store.wallViewers += 1 |
| 44 | 63 | Component.onDestruction: store.wallViewers -= 1 |
| 45 | 64 | |
| 46 | 65 | ColumnLayout { |
| 66 | + Kirigami.Theme.textColor: wall.tinted ? wall.tintText : wall.Kirigami.Theme.textColor | |
| 67 | + Kirigami.Theme.disabledTextColor: wall.tinted ? wall.tintDim : wall.Kirigami.Theme.disabledTextColor | |
| 68 | + Kirigami.Theme.neutralTextColor: wall.tinted ? wall.tintAccent : wall.Kirigami.Theme.neutralTextColor | |
| 69 | + | |
| 47 | 70 | anchors.fill: parent |
| 48 | 71 | anchors.margins: Kirigami.Units.smallSpacing |
| 49 | 72 | spacing: Kirigami.Units.smallSpacing |
@@ -157,6 +180,19 @@ Item {
| 157 | 180 | PlasmaComponents3.ToolTip { text: i18n("See-through background (T)") } |
| 158 | 181 | } |
| 159 | 182 | |
| 183 | + PlasmaComponents3.ToolButton { | |
| 184 | + checkable: true | |
| 185 | + checked: wall.tinted | |
| 186 | + icon.name: "fill-color" | |
| 187 | + text: i18n("Tint") | |
| 188 | + display: QQC2.AbstractButton.IconOnly | |
| 189 | + onClicked: wall.store.setWallTint(!wall.tinted) | |
| 190 | + PlasmaComponents3.ToolTip { | |
| 191 | + text: wall.inWindow ? i18n("All text in one hue (H) — pick the hue in the widget's settings") | |
| 192 | + : i18n("All text in one hue — pick the hue in the widget's settings") | |
| 193 | + } | |
| 194 | + } | |
| 195 | + | |
| 160 | 196 | PlasmaComponents3.ToolButton { |
| 161 | 197 | visible: wall.inWindow |
| 162 | 198 | icon.name: wall.fullScreen ? "view-restore" : "view-fullscreen" |
@@ -220,6 +256,10 @@ Item {
| 220 | 256 | Layout.preferredHeight: 1 // equal shares |
| 221 | 257 | card: modelData |
| 222 | 258 | translucent: wall.translucent |
| 259 | + // The theme's own, from above the tint: | |
| 260 | + // a running step stays told apart from | |
| 261 | + // a passed one whatever the hue. | |
| 262 | + runningColor: wall.Kirigami.Theme.neutralTextColor | |
| 223 | 263 | now: wall.store.now |
| 224 | 264 | } |
| 225 | 265 | } |
modified
contents/ui/WallWindow.qml
+1
−0
@@ -48,6 +48,7 @@ Window {
| 48 | 48 | Shortcut { sequences: [StandardKey.FullScreen]; onActivated: wallWindow.toggleFullScreen() } |
| 49 | 49 | Shortcut { sequence: "Escape"; enabled: wallWindow.fullScreen; onActivated: wallWindow.showNormal() } |
| 50 | 50 | Shortcut { sequence: "T"; onActivated: wallWindow.store.setWallTranslucent(!wallWindow.store.wallTranslucent) } |
| 51 | + Shortcut { sequence: "H"; onActivated: wallWindow.store.setWallTint(!wallWindow.store.wallTint) } | |
| 51 | 52 | Shortcut { sequence: "R"; onActivated: wallWindow.store.setWallNewestFirst(!wallWindow.store.wallNewestFirst) } |
| 52 | 53 | |
| 53 | 54 | WallView { |
modified
contents/ui/configBehavior.qml
+54
−0
@@ -13,6 +13,8 @@ KCM.SimpleKCM {
| 13 | 13 | property alias cfg_hideIdle: hideIdleCheckBox.checked |
| 14 | 14 | property alias cfg_notifyMyFailures: notifyMyFailuresCheckBox.checked |
| 15 | 15 | property alias cfg_desktopWall: desktopWallCheckBox.checked |
| 16 | + property alias cfg_wallTint: wallTintCheckBox.checked | |
| 17 | + property alias cfg_wallTintHue: wallTintHueSpinBox.value | |
| 16 | 18 | |
| 17 | 19 | Kirigami.FormLayout { |
| 18 | 20 | QQC2.SpinBox { |
@@ -67,6 +69,58 @@ KCM.SimpleKCM {
| 67 | 69 | text: i18n("Every job running right now, a card each, tiled to fill the widget. Resize it, or give it a screen of its own. In a panel the widget stays the circles; open the wall from the popup or the right-click menu instead.") |
| 68 | 70 | } |
| 69 | 71 | |
| 72 | + QQC2.CheckBox { | |
| 73 | + id: wallTintCheckBox | |
| 74 | + text: i18n("Tint all text one hue") | |
| 75 | + } | |
| 76 | + | |
| 77 | + RowLayout { | |
| 78 | + Kirigami.FormData.label: i18n("Tint hue:") | |
| 79 | + enabled: wallTintCheckBox.checked | |
| 80 | + spacing: Kirigami.Units.smallSpacing | |
| 81 | + | |
| 82 | + QQC2.SpinBox { | |
| 83 | + id: wallTintHueSpinBox | |
| 84 | + from: 0 | |
| 85 | + to: 359 | |
| 86 | + value: 120 | |
| 87 | + // The wheel goes round: past 359 is 0 again. | |
| 88 | + wrap: true | |
| 89 | + textFromValue: function(value) { return value + "°" } | |
| 90 | + valueFromText: function(text) { return parseInt(text, 10) || 0 } | |
| 91 | + } | |
| 92 | + | |
| 93 | + // What the wall's text will look like, on a dark theme. | |
| 94 | + Rectangle { | |
| 95 | + Layout.preferredWidth: Kirigami.Units.gridUnit * 3 | |
| 96 | + Layout.preferredHeight: wallTintHueSpinBox.implicitHeight | |
| 97 | + radius: Kirigami.Units.cornerRadius | |
| 98 | + color: "#1b1e20" | |
| 99 | + opacity: parent.enabled ? 1 : 0.5 | |
| 100 | + QQC2.Label { | |
| 101 | + anchors.centerIn: parent | |
| 102 | + font.family: "monospace" | |
| 103 | + font.bold: true | |
| 104 | + color: Qt.hsla(wallTintHueSpinBox.value / 360, 0.85, 0.62, 1) | |
| 105 | + text: "0:42" | |
| 106 | + } | |
| 107 | + } | |
| 108 | + | |
| 109 | + QQC2.Button { | |
| 110 | + text: i18n("Green") | |
| 111 | + visible: wallTintHueSpinBox.value !== 120 | |
| 112 | + onClicked: wallTintHueSpinBox.value = 120 | |
| 113 | + } | |
| 114 | + } | |
| 115 | + | |
| 116 | + QQC2.Label { | |
| 117 | + Layout.fillWidth: true | |
| 118 | + wrapMode: Text.WordWrap | |
| 119 | + font: Kirigami.Theme.smallFont | |
| 120 | + color: Kirigami.Theme.disabledTextColor | |
| 121 | + text: i18n("Labels, the clock, card borders and icons, for a terminal look that pairs with the window's translucent background. Failed steps and errors keep their colours. H toggles it in the window, as does the header's tint button.") | |
| 122 | + } | |
| 123 | + | |
| 70 | 124 | QQC2.CheckBox { |
| 71 | 125 | id: notifyMyFailuresCheckBox |
| 72 | 126 | Kirigami.FormData.label: i18n("Notifications:") |
modified
contents/ui/main.qml
+6
−0
@@ -86,6 +86,8 @@ PlasmoidItem {
| 86 | 86 | readonly property string wallScope: Plasmoid.configuration.wallScope |
| 87 | 87 | readonly property bool wallNewestFirst: Plasmoid.configuration.wallNewestFirst |
| 88 | 88 | readonly property bool wallTranslucent: Plasmoid.configuration.wallTranslucent |
| 89 | + readonly property bool wallTint: Plasmoid.configuration.wallTint | |
| 90 | + readonly property int wallTintHue: Plasmoid.configuration.wallTintHue | |
| 89 | 91 | readonly property bool wallConfigured: api.configured |
| 90 | 92 | readonly property bool onDesktop: Plasmoid.formFactor !== PlasmaCore.Types.Horizontal |
| 91 | 93 | && Plasmoid.formFactor !== PlasmaCore.Types.Vertical |
@@ -707,6 +709,10 @@ PlasmoidItem {
| 707 | 709 | Plasmoid.configuration.wallTranslucent = on |
| 708 | 710 | } |
| 709 | 711 | |
| 712 | + function setWallTint(on) { | |
| 713 | + Plasmoid.configuration.wallTint = on | |
| 714 | + } | |
| 715 | + | |
| 710 | 716 | function wallWebUrl() { |
| 711 | 717 | return api.wallUrl(wallScope) |
| 712 | 718 | } |
Parents: ac30301