Files
PhotonVision/photon-client/src/App.vue
Devon Doyle b43d0dde20 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"
/>
2025-09-07 00:33:37 -04:00

121 lines
3.4 KiB
Vue

<script setup lang="ts">
import { useStateStore } from "@/stores/StateStore";
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { AutoReconnectingWebsocket } from "@/lib/AutoReconnectingWebsocket";
import { inject, onBeforeMount } from "vue";
import PhotonSidebar from "@/components/app/photon-sidebar.vue";
import PhotonLogView from "@/components/app/photon-log-view.vue";
import PhotonErrorSnackbar from "@/components/app/photon-error-snackbar.vue";
import { useTheme } from "vuetify";
import { restoreThemeConfig } from "@/lib/ThemeManager";
const is_demo = import.meta.env.MODE === "demo";
if (!is_demo) {
const websocket = new AutoReconnectingWebsocket(
`ws://${inject("backendHost")}/websocket_data`,
() => {
useStateStore().$patch({ backendConnected: true });
},
(data) => {
if (data.log !== undefined) {
useStateStore().addLogFromWebsocket(data.log);
}
if (data.settings !== undefined) {
useSettingsStore().updateGeneralSettingsFromWebsocket(data.settings);
}
if (data.cameraSettings !== undefined) {
useCameraSettingsStore().updateCameraSettingsFromWebsocket(data.cameraSettings);
}
if (data.ntConnectionInfo !== undefined) {
useStateStore().updateNTConnectionStatusFromWebsocket(data.ntConnectionInfo);
}
if (data.metrics !== undefined) {
useSettingsStore().updateMetricsFromWebsocket(data.metrics);
}
if (data.updatePipelineResult !== undefined) {
useStateStore().updateBackendResultsFromWebsocket(data.updatePipelineResult);
}
if (data.mutatePipelineSettings !== undefined && data.cameraUniqueName !== undefined) {
useCameraSettingsStore().changePipelineSettingsInStore(data.mutatePipelineSettings, data.cameraUniqueName);
}
if (data.calibrationData !== undefined) {
useStateStore().updateCalibrationStateValuesFromWebsocket(data.calibrationData);
}
if (data.visionSourceManager !== undefined) {
useStateStore().updateDiscoveredCameras(data.visionSourceManager);
}
},
() => {
useStateStore().$patch({ backendConnected: false });
}
);
useStateStore().$patch({ websocket: websocket });
}
const theme = useTheme();
onBeforeMount(() => {
restoreThemeConfig(theme);
});
</script>
<template>
<v-app>
<photon-sidebar />
<v-main>
<v-container class="main-container" fluid fill-height>
<v-layout>
<v-container class="align-start pa-0 ma-0" fluid>
<router-view />
</v-container>
</v-layout>
</v-container>
</v-main>
<photon-log-view />
<photon-error-snackbar />
</v-app>
</template>
<style lang="scss">
@use "@/assets/styles/settings";
@use "@/assets/styles/variables";
@use "sass:map";
@media #{map.get(settings.$display-breakpoints, 'md-and-down')} {
html {
font-size: 14px !important;
}
}
/* Custom scrollbar styles */
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
background: rgb(var(--v-theme-background));
}
::-webkit-scrollbar-thumb {
background-color: rgb(var(--v-theme-accent));
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background-color: rgb(var(--v-theme-primary));
}
.main-container {
padding: 0 !important;
}
.v-overlay__scrim {
background-color: #111111;
}
div.v-layout {
overflow: unset !important;
}
</style>