Skip to content

Commit b43d0dd

Browse files
authored
Add custom theming (#2081)
Adds support for user-created custom themes. Custom theme interface is tucked into the global settings in a non-invasive manner to avoid major design changes. Builds on the theme structure established by the dark theme update. <img width="1486" height="953" alt="image" src="https://github.com/user-attachments/assets/716bcfc7-af74-41dc-b14a-cfc2f2d2caa9" /> <img width="1486" height="956" alt="image" src="https://github.com/user-attachments/assets/a00f9620-0b1d-4f67-b010-e94dda5dc212" /> Here's a few examples of what teams could do, using a few color schemes from local teams. Imagine the possibilities! <img width="1485" height="951" alt="image" src="https://github.com/user-attachments/assets/c3da37b8-f6be-4152-81e0-533297f517fc" /> <img width="1483" height="951" alt="image" src="https://github.com/user-attachments/assets/0d453f7a-cf6f-4c27-97db-603b54c1f73e" /> <img width="1485" height="952" alt="image" src="https://github.com/user-attachments/assets/bf8c7770-e60d-4875-9580-ed7e54e089f4" /> <img width="1484" height="952" alt="image" src="https://github.com/user-attachments/assets/326d89e6-dd6e-4e05-a9fa-c9fc6f880847" /> <img width="1482" height="951" alt="image" src="https://github.com/user-attachments/assets/eb5a2a5d-c103-482c-a62a-5ccd5ba21cc5" /> <img width="1482" height="950" alt="image" src="https://github.com/user-attachments/assets/4831ca56-f322-4345-97af-8963ae8539b1" /> Looking for high contrast? Just moments away: <img width="1484" height="949" alt="image" src="https://github.com/user-attachments/assets/7ffc65c6-7000-4566-b4f0-c8247f75fb3d" />
1 parent 3300b90 commit b43d0dd

File tree

9 files changed

+397
-38
lines changed

9 files changed

+397
-38
lines changed

photon-client/src/App.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { useStateStore } from "@/stores/StateStore";
33
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
44
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
55
import { AutoReconnectingWebsocket } from "@/lib/AutoReconnectingWebsocket";
6-
import { inject } from "vue";
6+
import { inject, onBeforeMount } from "vue";
77
import PhotonSidebar from "@/components/app/photon-sidebar.vue";
88
import PhotonLogView from "@/components/app/photon-log-view.vue";
99
import PhotonErrorSnackbar from "@/components/app/photon-error-snackbar.vue";
10+
import { useTheme } from "vuetify";
11+
import { restoreThemeConfig } from "@/lib/ThemeManager";
1012
1113
const is_demo = import.meta.env.MODE === "demo";
1214
if (!is_demo) {
@@ -50,6 +52,11 @@ if (!is_demo) {
5052
);
5153
useStateStore().$patch({ websocket: websocket });
5254
}
55+
56+
const theme = useTheme();
57+
onBeforeMount(() => {
58+
restoreThemeConfig(theme);
59+
});
5360
</script>
5461

5562
<template>

photon-client/src/components/app/photon-camera-stream.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import { computed, inject, ref, onBeforeUnmount } from "vue";
33
import { useStateStore } from "@/stores/StateStore";
44
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
5-
import loadingImage from "@/assets/images/loading-transparent.svg";
65
import type { StyleValue } from "vue";
76
import PvIcon from "@/components/common/pv-icon.vue";
87
import type { UiCameraConfiguration } from "@/types/SettingTypes";
8+
import PvLoading from "@/components/common/pv-loading.vue";
99
1010
const props = defineProps<{
1111
streamType: "Raw" | "Processed";
@@ -92,7 +92,7 @@ onBeforeUnmount(() => {
9292

9393
<template>
9494
<div class="stream-container" :style="containerStyle">
95-
<img :src="loadingImage" class="stream-loading" />
95+
<pv-loading class="stream-loading" />
9696
<img
9797
:id="id"
9898
ref="mjpgStream"

photon-client/src/components/app/photon-sidebar.vue

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useStateStore } from "@/stores/StateStore";
55
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
66
import { useRoute } from "vue-router";
77
import { useDisplay, useTheme } from "vuetify";
8-
import { onBeforeMount } from "vue";
8+
import { toggleTheme } from "@/lib/ThemeManager";
99
1010
const compact = computed<boolean>({
1111
get: () => {
@@ -19,19 +19,6 @@ const { mdAndUp } = useDisplay();
1919
2020
const theme = useTheme();
2121
22-
const changeTheme = () => {
23-
const newTheme = theme.global.name.value === "LightTheme" ? "DarkTheme" : "LightTheme";
24-
theme.global.name.value = newTheme;
25-
localStorage.setItem("theme", newTheme);
26-
};
27-
28-
onBeforeMount(() => {
29-
const storedTheme = localStorage.getItem("theme");
30-
if (storedTheme) {
31-
theme.global.name.value = storedTheme;
32-
}
33-
});
34-
3522
const renderCompact = computed<boolean>(() => compact.value || !mdAndUp.value);
3623
</script>
3724

@@ -88,7 +75,7 @@ const renderCompact = computed<boolean>(() => compact.value || !mdAndUp.value);
8875
<v-list-item
8976
link
9077
:prepend-icon="theme.global.name.value === 'LightTheme' ? 'mdi-white-balance-sunny' : 'mdi-weather-night'"
91-
@click="changeTheme"
78+
@click="() => toggleTheme(theme)"
9279
>
9380
<v-list-item-title>Theme</v-list-item-title>
9481
</v-list-item>

photon-client/src/components/cameras/CamerasView.vue

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,7 @@ const fpsTooLow = computed<boolean>(() => {
9090
</div>
9191
</v-card-text>
9292
<v-card-text class="pt-0">
93-
<v-btn-toggle
94-
v-model="value"
95-
:multiple="true"
96-
mandatory
97-
class="fill"
98-
style="width: 100%"
99-
base-color="surface-variant"
100-
>
93+
<v-btn-toggle v-model="value" :multiple="true" mandatory class="fill" style="width: 100%">
10194
<v-btn
10295
color="buttonPassive"
10396
class="fill"
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<template>
2+
<svg
3+
xmlns="http://www.w3.org/2000/svg"
4+
viewBox="0 0 100 100"
5+
preserveAspectRatio="xMidYMid"
6+
width="200"
7+
height="200"
8+
style="shape-rendering: auto; display: block; background: rgba(0, 100, 146, 0)"
9+
xmlns:xlink="http://www.w3.org/1999/xlink"
10+
>
11+
<g>
12+
<g transform="translate(80,50)">
13+
<g transform="rotate(0)">
14+
<circle class="loader-circle" fill-opacity="1" fill="#ffd943" r="6" cy="0" cx="0">
15+
<animateTransform
16+
repeatCount="indefinite"
17+
dur="0.9345794392523364s"
18+
keyTimes="0;1"
19+
values="1.5 1.5;1 1"
20+
begin="-0.8177570093457943s"
21+
type="scale"
22+
attributeName="transform"
23+
></animateTransform>
24+
<animate
25+
begin="-0.8177570093457943s"
26+
values="1;0"
27+
repeatCount="indefinite"
28+
dur="0.9345794392523364s"
29+
keyTimes="0;1"
30+
attributeName="fill-opacity"
31+
></animate>
32+
</circle>
33+
</g>
34+
</g>
35+
<g transform="translate(71.21320343559643,71.21320343559643)">
36+
<g transform="rotate(45)">
37+
<circle class="loader-circle" fill-opacity="0.875" fill="#ffd943" r="6" cy="0" cx="0">
38+
<animateTransform
39+
repeatCount="indefinite"
40+
dur="0.9345794392523364s"
41+
keyTimes="0;1"
42+
values="1.5 1.5;1 1"
43+
begin="-0.7009345794392523s"
44+
type="scale"
45+
attributeName="transform"
46+
></animateTransform>
47+
<animate
48+
begin="-0.7009345794392523s"
49+
values="1;0"
50+
repeatCount="indefinite"
51+
dur="0.9345794392523364s"
52+
keyTimes="0;1"
53+
attributeName="fill-opacity"
54+
></animate>
55+
</circle>
56+
</g>
57+
</g>
58+
<g transform="translate(50,80)">
59+
<g transform="rotate(90)">
60+
<circle class="loader-circle" fill-opacity="0.75" fill="#ffd943" r="6" cy="0" cx="0">
61+
<animateTransform
62+
repeatCount="indefinite"
63+
dur="0.9345794392523364s"
64+
keyTimes="0;1"
65+
values="1.5 1.5;1 1"
66+
begin="-0.5841121495327103s"
67+
type="scale"
68+
attributeName="transform"
69+
></animateTransform>
70+
<animate
71+
begin="-0.5841121495327103s"
72+
values="1;0"
73+
repeatCount="indefinite"
74+
dur="0.9345794392523364s"
75+
keyTimes="0;1"
76+
attributeName="fill-opacity"
77+
></animate>
78+
</circle>
79+
</g>
80+
</g>
81+
<g transform="translate(28.786796564403577,71.21320343559643)">
82+
<g transform="rotate(135)">
83+
<circle class="loader-circle" fill-opacity="0.625" fill="#ffd943" r="6" cy="0" cx="0">
84+
<animateTransform
85+
repeatCount="indefinite"
86+
dur="0.9345794392523364s"
87+
keyTimes="0;1"
88+
values="1.5 1.5;1 1"
89+
begin="-0.4672897196261682s"
90+
type="scale"
91+
attributeName="transform"
92+
></animateTransform>
93+
<animate
94+
begin="-0.4672897196261682s"
95+
values="1;0"
96+
repeatCount="indefinite"
97+
dur="0.9345794392523364s"
98+
keyTimes="0;1"
99+
attributeName="fill-opacity"
100+
></animate>
101+
</circle>
102+
</g>
103+
</g>
104+
<g transform="translate(20,50.00000000000001)">
105+
<g transform="rotate(180)">
106+
<circle class="loader-circle" fill-opacity="0.5" fill="#ffd943" r="6" cy="0" cx="0">
107+
<animateTransform
108+
repeatCount="indefinite"
109+
dur="0.9345794392523364s"
110+
keyTimes="0;1"
111+
values="1.5 1.5;1 1"
112+
begin="-0.35046728971962615s"
113+
type="scale"
114+
attributeName="transform"
115+
></animateTransform>
116+
<animate
117+
begin="-0.35046728971962615s"
118+
values="1;0"
119+
repeatCount="indefinite"
120+
dur="0.9345794392523364s"
121+
keyTimes="0;1"
122+
attributeName="fill-opacity"
123+
></animate>
124+
</circle>
125+
</g>
126+
</g>
127+
<g transform="translate(28.78679656440357,28.786796564403577)">
128+
<g transform="rotate(225)">
129+
<circle class="loader-circle" fill-opacity="0.375" fill="#ffd943" r="6" cy="0" cx="0">
130+
<animateTransform
131+
repeatCount="indefinite"
132+
dur="0.9345794392523364s"
133+
keyTimes="0;1"
134+
values="1.5 1.5;1 1"
135+
begin="-0.2336448598130841s"
136+
type="scale"
137+
attributeName="transform"
138+
></animateTransform>
139+
<animate
140+
begin="-0.2336448598130841s"
141+
values="1;0"
142+
repeatCount="indefinite"
143+
dur="0.9345794392523364s"
144+
keyTimes="0;1"
145+
attributeName="fill-opacity"
146+
></animate>
147+
</circle>
148+
</g>
149+
</g>
150+
<g transform="translate(49.99999999999999,20)">
151+
<g transform="rotate(270)">
152+
<circle class="loader-circle" fill-opacity="0.25" fill="#ffd943" r="6" cy="0" cx="0">
153+
<animateTransform
154+
repeatCount="indefinite"
155+
dur="0.9345794392523364s"
156+
keyTimes="0;1"
157+
values="1.5 1.5;1 1"
158+
begin="-0.11682242990654206s"
159+
type="scale"
160+
attributeName="transform"
161+
></animateTransform>
162+
<animate
163+
begin="-0.11682242990654206s"
164+
values="1;0"
165+
repeatCount="indefinite"
166+
dur="0.9345794392523364s"
167+
keyTimes="0;1"
168+
attributeName="fill-opacity"
169+
></animate>
170+
</circle>
171+
</g>
172+
</g>
173+
<g transform="translate(71.21320343559643,28.78679656440357)">
174+
<g transform="rotate(315)">
175+
<circle class="loader-circle" fill-opacity="0.125" fill="#ffd943" r="6" cy="0" cx="0">
176+
<animateTransform
177+
repeatCount="indefinite"
178+
dur="0.9345794392523364s"
179+
keyTimes="0;1"
180+
values="1.5 1.5;1 1"
181+
begin="0s"
182+
type="scale"
183+
attributeName="transform"
184+
></animateTransform>
185+
<animate
186+
begin="0s"
187+
values="1;0"
188+
repeatCount="indefinite"
189+
dur="0.9345794392523364s"
190+
keyTimes="0;1"
191+
attributeName="fill-opacity"
192+
></animate>
193+
</circle>
194+
</g>
195+
</g>
196+
<g></g>
197+
</g>
198+
<!-- [ldio] generated by https://loading.io -->
199+
</svg>
200+
</template>
201+
<style scoped lang="scss">
202+
.loader-circle {
203+
fill: rgb(var(--v-theme-buttonActive));
204+
}
205+
</style>

photon-client/src/components/dashboard/StreamConfigCard.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const processingMode = computed<number>({
2828
<v-row class="pa-3 pb-0 align-center">
2929
<v-col class="pa-4">
3030
<p style="color: white">Processing Mode</p>
31-
<v-btn-toggle v-model="processingMode" mandatory base-color="surface-variant" class="fill w-100">
31+
<v-btn-toggle v-model="processingMode" mandatory class="fill w-100">
3232
<v-btn
3333
color="buttonPassive"
3434
:disabled="!useCameraSettingsStore().hasConnected"
@@ -59,7 +59,7 @@ const processingMode = computed<number>({
5959
<v-row class="pa-3 pt-0 align-center">
6060
<v-col class="pa-4 pt-0">
6161
<p style="color: white">Stream Display</p>
62-
<v-btn-toggle v-model="value" :multiple="true" mandatory base-color="surface-variant" class="fill w-100">
62+
<v-btn-toggle v-model="value" :multiple="true" mandatory class="fill w-100">
6363
<v-btn
6464
color="buttonPassive"
6565
class="fill w-50"

0 commit comments

Comments
 (0)