Skip to content

Commit b14eecd

Browse files
tylerdotrarblaiyz
andauthored
feat: shutdown button, temperature unit toggle, and clock customizations (#44)
* Implemented shutdown button, temperature unit toggle, military time & seconds support. * Changed vite build to utilize npx to reduce global dependencies. --------- Co-authored-by: blaiyz <139375534+blaiyz@users.noreply.github.com>
1 parent 92980d8 commit b14eecd

File tree

8 files changed

+130
-69
lines changed

8 files changed

+130
-69
lines changed

scripts/build.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async function release() {
1515

1616
// Run build using static directory
1717
console.log("🏗️ Running release build...");
18-
execSync("vite build", { stdio: "inherit" });
18+
execSync("npx vite build", { stdio: "inherit" });
1919

2020
console.log("✅ Release build completed successfully!");
2121
} catch (error) {

src/components/LeftGroup.svelte

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import * as zebar from "zebar";
2222
2323
import IconHeartFilled from "@tabler/icons-svelte/icons/heart-filled";
24-
// import Test from "./Test.svelte";
24+
import IconPowerButton from "@tabler/icons-svelte/icons/power";
2525
import { config } from "$lib/config.svelte";
2626
import { providers } from "$lib/providers.svelte";
2727
@@ -39,12 +39,39 @@
3939
return expanded.slice(0, 5);
4040
}
4141
}
42+
43+
// Shutdown handler
44+
let shuttingDown = $state(false);
45+
async function handlePowerOff() {
46+
try {
47+
if (shuttingDown) {
48+
await zebar.shellExec("shutdown", ["/a"]);
49+
shuttingDown = false;
50+
} else {
51+
await zebar.shellExec("shutdown", ["/s"]);
52+
shuttingDown = true;
53+
}
54+
} catch (err) {
55+
console.error("Shutdown command failed", err);
56+
shuttingDown = false;
57+
}
58+
}
4259
</script>
4360

4461
<div class="flex flex-row gap-3 items-center">
62+
{#if config.showShutdownButton}
63+
<Button class="text-zb-icon">
64+
<IconPowerButton
65+
class="text-zb-icon"
66+
onclick={() => {
67+
handlePowerOff();
68+
}}
69+
/>
70+
</Button>
71+
{/if}
4572
{#if config.showHeartButton}
46-
<Button class="text-zb-icon"
47-
><IconHeartFilled
73+
<Button class="text-zb-icon">
74+
<IconHeartFilled
4875
class="text-zb-icon"
4976
onclick={() => {
5077
zebar.shellExec("cmd", [
@@ -53,8 +80,13 @@
5380
"https://github.com/blaiyz/neosoft-zebar"
5481
]);
5582
}}
56-
/></Button
57-
>
83+
/>
84+
</Button>
85+
{/if}
86+
{#if config.showCpuSection}
87+
<Meter class="stroke-zb-cpu h-8" percent={Math.round(cpu?.usage ?? 0)}>
88+
<Cpu />
89+
</Meter>
5890
{/if}
5991
{#if config.showMemorySection}
6092
<Meter
@@ -64,11 +96,6 @@
6496
<MemoryStick />
6597
</Meter>
6698
{/if}
67-
{#if config.showCpuSection}
68-
<Meter class="stroke-zb-cpu h-8" percent={Math.round(cpu?.usage ?? 0)}>
69-
<Cpu />
70-
</Meter>
71-
{/if}
7299
{#if config.showBatterySection && battery?.state}
73100
<Meter
74101
class="stroke-zb-battery-good h-8"
@@ -139,10 +166,4 @@
139166
</div>
140167
{/if}
141168
{/if}
142-
<!-- <Test /> -->
143-
<!-- <div class="max-w-72 text-sm max-h-4">
144-
<div class="overflow-scroll">
145-
config: {JSON.stringify(config)}
146-
</div>
147-
</div> -->
148169
</div>

src/components/RightGroup.svelte

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
1010
let date = $derived(providers.date);
1111
let weather = $derived(providers.weather);
12+
let useCelsius = $derived(config.useCelsiusByDefault);
1213
1314
let fullDate = $derived(config.showFullDateByDefault);
1415
const getDate = (date: Date) => {
@@ -17,10 +18,16 @@
1718
return shortDay + " " + date.toLocaleDateString();
1819
};
1920
20-
const timeOptions: Intl.DateTimeFormatOptions = {
21+
let timeOptions: Intl.DateTimeFormatOptions = $derived({
2122
hour: "numeric",
22-
minute: "2-digit"
23-
};
23+
minute: "2-digit",
24+
...(config.use24hClock === "auto"
25+
? {}
26+
: config.use24hClock
27+
? { hour12: false }
28+
: { hour12: true }),
29+
...(config.showSeconds ? { second: "numeric" } : {})
30+
});
2431
</script>
2532

2633
<div class="flex flex-row items-center h-full">
@@ -29,40 +36,49 @@
2936
<VolumeControl />
3037
{/if}
3138
{#if config.showWeatherSection && weather}
32-
<div
33-
class="truncate flex items-center pr-2 {!isOnPrimaryMonitor()
34-
? 'pl-1'
35-
: ''}"
39+
<button
40+
class="hover:text-zb-accent"
41+
onclick={() => (useCelsius = !useCelsius)}
3642
>
37-
<span class="text-2xl">
38-
{#if weather.status === "clear_day"}
39-
<i class="nf nf-weather-day_sunny"></i>
40-
{:else if weather.status === "clear_night"}
41-
<i class="nf nf-weather-night_clear"></i>
42-
{:else if weather.status === "cloudy_day"}
43-
<i class="nf nf-weather-day_cloudy"></i>
44-
{:else if weather.status === "cloudy_night"}
45-
<i class="nf nf-weather-night_alt_cloudy"></i>
46-
{:else if weather.status === "light_rain_day"}
47-
<i class="nf nf-weather-day_sprinkle"></i>
48-
{:else if weather.status === "light_rain_night"}
49-
<i class="nf nf-weather-night_alt_sprinkle"></i>
50-
{:else if weather.status === "heavy_rain_day"}
51-
<i class="nf nf-weather-day_rain"></i>
52-
{:else if weather.status === "heavy_rain_night"}
53-
<i class="nf nf-weather-night_alt_rain"></i>
54-
{:else if weather.status === "snow_day"}
55-
<i class="nf nf-weather-day_snow"></i>
56-
{:else if weather.status === "snow_night"}
57-
<i class="nf nf-weather-night_alt_snow"></i>
58-
{:else if weather.status === "thunder_day"}
59-
<i class="nf nf-weather-day_lightning"></i>
60-
{:else if weather.status === "thunder_night"}
61-
<i class="nf nf-weather-night_alt_lightning"></i>
43+
<div
44+
class="truncate flex items-center pr-2 {!isOnPrimaryMonitor()
45+
? 'pl-1'
46+
: ''}"
47+
>
48+
<span class="text-2xl">
49+
{#if weather.status === "clear_day"}
50+
<i class="nf nf-weather-day_sunny"></i>
51+
{:else if weather.status === "clear_night"}
52+
<i class="nf nf-weather-night_clear"></i>
53+
{:else if weather.status === "cloudy_day"}
54+
<i class="nf nf-weather-day_cloudy"></i>
55+
{:else if weather.status === "cloudy_night"}
56+
<i class="nf nf-weather-night_alt_cloudy"></i>
57+
{:else if weather.status === "light_rain_day"}
58+
<i class="nf nf-weather-day_sprinkle"></i>
59+
{:else if weather.status === "light_rain_night"}
60+
<i class="nf nf-weather-night_alt_sprinkle"></i>
61+
{:else if weather.status === "heavy_rain_day"}
62+
<i class="nf nf-weather-day_rain"></i>
63+
{:else if weather.status === "heavy_rain_night"}
64+
<i class="nf nf-weather-night_alt_rain"></i>
65+
{:else if weather.status === "snow_day"}
66+
<i class="nf nf-weather-day_snow"></i>
67+
{:else if weather.status === "snow_night"}
68+
<i class="nf nf-weather-night_alt_snow"></i>
69+
{:else if weather.status === "thunder_day"}
70+
<i class="nf nf-weather-day_lightning"></i>
71+
{:else if weather.status === "thunder_night"}
72+
<i class="nf nf-weather-night_alt_lightning"></i>
73+
{/if}
74+
</span>
75+
{#if useCelsius}
76+
{Math.round(weather.celsiusTemp)}°C
77+
{:else}
78+
{Math.round(weather.fahrenheitTemp)}°F
6279
{/if}
63-
</span>
64-
{Math.round(weather.celsiusTemp)}°
65-
</div>
80+
</div>
81+
</button>
6682
{/if}
6783
{#if !isOnPrimaryMonitor() || !config.taskbarIntegration.enabled}
6884
<PointFilled class="mr-2" />
@@ -71,8 +87,8 @@
7187
class="hover:text-zb-accent"
7288
onclick={() => (fullDate = !fullDate)}
7389
>
74-
<p class="whitespace-nowrap">
75-
{date?.new.toLocaleTimeString(undefined, timeOptions)}
90+
<p class="whitespace-nowrap tabular-nums">
91+
{date?.new.toLocaleTimeString(undefined, timeOptions).toUpperCase()}
7692
{#if fullDate}
7793
-
7894
{date && getDate(date?.new)}

src/components/VolumeControl/VolumeControl.svelte

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
<script lang="ts">
2+
import { toggleModes } from "$lib/binding_modes.svelte";
3+
import { config, configLoaded } from "$lib/config.svelte";
24
import { providers } from "$lib/providers.svelte";
3-
import VolumeX from "@lucide/svelte/icons/volume-x";
5+
import ChevronLeft from "@lucide/svelte/icons/chevron-left";
46
import Volume from "@lucide/svelte/icons/volume";
57
import Volume1 from "@lucide/svelte/icons/volume-1";
68
import Volume2 from "@lucide/svelte/icons/volume-2";
79
import VolumeOff from "@lucide/svelte/icons/volume-off";
8-
import ChevronLeft from "@lucide/svelte/icons/chevron-left";
10+
import VolumeX from "@lucide/svelte/icons/volume-x";
11+
import { onMount } from "svelte";
912
import RangeSlider from "svelte-range-slider-pips";
13+
import { fly } from "svelte/transition";
1014
import SmoothDiv from "../SmoothDiv.svelte";
1115
import "./VolumeControl.css";
12-
import { toggleModes } from "$lib/binding_modes.svelte";
13-
import { fly } from "svelte/transition";
14-
import { config, configLoaded } from "$lib/config.svelte";
15-
import { onMount } from "svelte";
1616
1717
let audio = $derived(providers.audio);
1818
let device = $derived(audio?.defaultPlaybackDevice);
@@ -32,7 +32,7 @@
3232
3333
onMount(async () => {
3434
await configLoaded;
35-
if (config.enableVolumeSlider) {
35+
if (config.extendVolumeSliderByDefault) {
3636
sliderOpen = true;
3737
}
3838
});

src/lib/config.svelte.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export interface Config {
8484
/**
8585
* Enable volume slider by default.
8686
*/
87-
enableVolumeSlider: boolean;
87+
extendVolumeSliderByDefault: boolean;
8888

8989
// Individual section toggles
9090

@@ -94,6 +94,10 @@ export interface Config {
9494
showBatterySection: boolean;
9595
showNetworkSection: boolean;
9696
showWeatherSection: boolean;
97+
showShutdownButton: boolean;
98+
useCelsiusByDefault: boolean;
99+
use24hClock: true | false | "auto";
100+
showSeconds: boolean;
97101
}
98102

99103
export type AttachmentDirection = "top" | "bottom" | "floating";

static/config.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222
},
2323
"backgroundEffect": "inherit",
2424
"enableAutoTiling": true,
25-
"showFullDateByDefault": false,
2625
"clickThroughByDefault": false,
27-
"enableVolumeSlider": false,
26+
"showShutdownButton": true,
2827
"showHeartButton": true,
2928
"showCpuSection": true,
3029
"showMemorySection": true,
3130
"showBatterySection": true,
3231
"showNetworkSection": true,
33-
"showWeatherSection": true
32+
"extendVolumeSliderByDefault": false,
33+
"showWeatherSection": true,
34+
"useCelsiusByDefault": true,
35+
"showFullDateByDefault": false,
36+
"use24hClock": "auto",
37+
"showSeconds": false
3438
}

static/schema.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
"description": "Whether to enable built-in automatic tiling of windows.",
9090
"type": "boolean"
9191
},
92-
"enableVolumeSlider": {
92+
"extendVolumeSliderByDefault": {
9393
"description": "Enable volume slider by default.",
9494
"type": "boolean"
9595
},
@@ -120,6 +120,12 @@
120120
"showNetworkSection": {
121121
"type": "boolean"
122122
},
123+
"showSeconds": {
124+
"type": "boolean"
125+
},
126+
"showShutdownButton": {
127+
"type": "boolean"
128+
},
123129
"showWeatherSection": {
124130
"type": "boolean"
125131
},
@@ -130,6 +136,12 @@
130136
"transitionDuration": {
131137
"description": "The duration of transitions in milliseconds.",
132138
"type": "number"
139+
},
140+
"use24hClock": {
141+
"enum": ["auto", false, true]
142+
},
143+
"useCelsiusByDefault": {
144+
"type": "boolean"
133145
}
134146
},
135147
"type": "object"

static/zpack.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://github.com/glzr-io/zebar/raw/v3.1.1/resources/zpack-schema.json",
33
"name": "neosoft-zebar",
4-
"version": "1.2.5",
4+
"version": "1.2.6",
55
"description": "A highly customizable (and smooooooth) Zebar build",
66
"tags": [
77
"glazewm",
@@ -18,7 +18,7 @@
1818
"repositoryUrl": "https://github.com/blaiyz/neosoft-zebar",
1919
"widgets": [
2020
{
21-
"name": "default",
21+
"name": "Default",
2222
"htmlPath": "./index.html",
2323
"zOrder": "normal",
2424
"shownInTaskbar": false,
@@ -35,12 +35,16 @@
3535
{
3636
"program": "cmd",
3737
"argsRegex": "\\/c start https:\\/\\/github\\.com\\/blaiyz\\/neosoft-zebar"
38+
},
39+
{
40+
"program": "shutdown",
41+
"argsRegex": ".*"
3842
}
3943
]
4044
},
4145
"presets": [
4246
{
43-
"name": "default",
47+
"name": "Default",
4448
"anchor": "top_left",
4549
"offsetX": "0px",
4650
"offsetY": "0px",

0 commit comments

Comments
 (0)