diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1f475a2060..d7aa188171 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -91,19 +91,34 @@ jobs:
run: |
./gradlew build
./gradlew clean
-
+ typecheck-client:
+ name: "Typecheck Client"
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 10
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: 22
+ cache: pnpm
+ cache-dependency-path: photon-client/pnpm-lock.yaml
+ - name: Typecheck Client
+ working-directory: photon-client
+ run: |
+ pnpm install
+ pnpm type-check
playwright-tests:
name: "Playwright E2E tests"
runs-on: ubuntu-24.04
needs: [validation]
steps:
- # Checkout code.
- name: Checkout code
uses: actions/checkout@v6
- with:
- fetch-depth: 0
- - name: Fetch tags
- run: git fetch --tags --force
- name: Install Java 17
uses: actions/setup-java@v5
with:
@@ -138,7 +153,6 @@ jobs:
runs-on: ubuntu-24.04
needs: [validation]
steps:
- # Checkout code.
- name: Checkout code
uses: actions/checkout@v6
with:
diff --git a/photon-client/env.d.ts b/photon-client/env.d.ts
index 11f02fe2a0..c5d1606877 100644
--- a/photon-client/env.d.ts
+++ b/photon-client/env.d.ts
@@ -1 +1,3 @@
///
+
+declare module "vue3-virtual-scroll-list";
diff --git a/photon-client/eslint.config.mjs b/photon-client/eslint.config.mjs
index 7191125429..4721ca0a8a 100644
--- a/photon-client/eslint.config.mjs
+++ b/photon-client/eslint.config.mjs
@@ -5,7 +5,7 @@ import skipFormattingConfig from "@vue/eslint-config-prettier/skip-formatting";
export default defineConfigWithVueTs(
pluginVue.configs["flat/recommended-error"],
- vueTsConfigs.recommended,
+ vueTsConfigs.recommendedTypeChecked,
skipFormattingConfig,
{
ignores: ["**/dist/**", "playwright-report"]
@@ -39,13 +39,16 @@ export default defineConfigWithVueTs(
"vue/no-undef-properties": "error",
"vue/no-unused-properties": "error",
"vue/no-unused-refs": "error",
+ "vue/prefer-use-template-ref": "error",
"vue/no-use-v-else-with-v-for": "error",
"vue/no-useless-mustaches": "error",
"vue/no-useless-v-bind": "error",
"vue/require-default-prop": "off",
"vue/v-for-delimiter-style": "error",
"vue/v-on-event-hyphenation": "off",
- "@typescript-eslint/no-explicit-any": "off",
+ "vue/require-typed-ref": "error",
+ "@typescript-eslint/no-empty-object-type": "error",
+ "@typescript-eslint/no-explicit-any": "error",
"vue/valid-v-slot": ["error", { allowModifiers: true }]
}
}
diff --git a/photon-client/package.json b/photon-client/package.json
index b5b11dbade..0000df4f9f 100644
--- a/photon-client/package.json
+++ b/photon-client/package.json
@@ -14,8 +14,8 @@
"format-ci": "prettier --check src/",
"test": "playwright test",
"test-ui": "playwright test --ui",
- "test-setup": "playwright install --with-deps"
-
+ "test-setup": "playwright install --with-deps",
+ "type-check": "vue-tsc"
},
"dependencies": {
"@fontsource/prompt": "^5.2.6",
@@ -37,6 +37,7 @@
"@types/node": "^22.15.14",
"@types/three": "^0.178.0",
"@vitejs/plugin-vue": "^6.0.0",
+ "vue-tsc": "^3.2.5",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.0",
"@vue/tsconfig": "^0.7.0",
diff --git a/photon-client/pnpm-lock.yaml b/photon-client/pnpm-lock.yaml
index 1168ca0b1d..cdb50493ba 100644
--- a/photon-client/pnpm-lock.yaml
+++ b/photon-client/pnpm-lock.yaml
@@ -90,6 +90,9 @@ importers:
vite-plugin-vuetify:
specifier: ^2.1.1
version: 2.1.1(vite@7.0.5(@types/node@22.15.14)(sass@1.89.2))(vue@3.5.13(typescript@5.8.3))(vuetify@3.8.3)
+ vue-tsc:
+ specifier: ^3.2.5
+ version: 3.2.5(typescript@5.8.3)
packages:
@@ -379,36 +382,42 @@ packages:
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.1':
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
+ libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.1':
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.1':
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.1':
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.1':
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@parcel/watcher-win32-arm64@2.5.1':
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
@@ -478,56 +487,67 @@ packages:
resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.40.2':
resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==}
cpu: [arm]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.40.2':
resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.40.2':
resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-loongarch64-gnu@4.40.2':
resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==}
cpu: [loong64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-powerpc64le-gnu@4.40.2':
resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.40.2':
resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.40.2':
resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.40.2':
resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.40.2':
resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.40.2':
resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.40.2':
resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==}
@@ -625,6 +645,15 @@ packages:
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
vue: ^3.2.25
+ '@volar/language-core@2.4.28':
+ resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==}
+
+ '@volar/source-map@2.4.28':
+ resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==}
+
+ '@volar/typescript@2.4.28':
+ resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==}
+
'@vue/compiler-core@3.5.13':
resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
@@ -666,6 +695,9 @@ packages:
typescript:
optional: true
+ '@vue/language-core@3.2.5':
+ resolution: {integrity: sha512-d3OIxN/+KRedeM5wQ6H6NIpwS3P5gC9nmyaHgBk+rO6dIsjY+tOh4UlPpiZbAh3YtLdCGEX4M16RmsBqPmJV+g==}
+
'@vue/reactivity@3.5.13':
resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
@@ -721,6 +753,9 @@ packages:
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+ alien-signals@3.1.2:
+ resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
+
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
@@ -1205,6 +1240,9 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -1235,6 +1273,9 @@ packages:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -1503,6 +1544,9 @@ packages:
yaml:
optional: true
+ vscode-uri@3.1.0:
+ resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
vue-eslint-parser@10.1.3:
resolution: {integrity: sha512-dbCBnd2e02dYWsXoqX5yKUZlOt+ExIpq7hmHKPb5ZqKcjf++Eo0hMseFTZMLKThrUk61m+Uv6A2YSBve6ZvuDQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1514,6 +1558,12 @@ packages:
peerDependencies:
vue: ^3.2.0
+ vue-tsc@3.2.5:
+ resolution: {integrity: sha512-/htfTCMluQ+P2FISGAooul8kO4JMheOTCbCy4M6dYnYYjqLe3BExZudAua6MSIKSFYQtFOYAll7XobYwcpokGA==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
vue3-virtual-scroll-list@0.2.1:
resolution: {integrity: sha512-G4KxITUOy9D4ro15zOp40D6ogmMefzjIyMsBKqN3xGbV1P6dlKYMx+BBXCKm3Nr/6iipcUKM272Sh2AJRyWMyQ==}
peerDependencies:
@@ -1976,6 +2026,18 @@ snapshots:
vite: 7.0.5(@types/node@22.15.14)(sass@1.89.2)
vue: 3.5.13(typescript@5.8.3)
+ '@volar/language-core@2.4.28':
+ dependencies:
+ '@volar/source-map': 2.4.28
+
+ '@volar/source-map@2.4.28': {}
+
+ '@volar/typescript@2.4.28':
+ dependencies:
+ '@volar/language-core': 2.4.28
+ path-browserify: 1.0.1
+ vscode-uri: 3.1.0
+
'@vue/compiler-core@3.5.13':
dependencies:
'@babel/parser': 7.27.2
@@ -2048,6 +2110,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@vue/language-core@3.2.5':
+ dependencies:
+ '@volar/language-core': 2.4.28
+ '@vue/compiler-dom': 3.5.13
+ '@vue/shared': 3.5.13
+ alien-signals: 3.1.2
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ picomatch: 4.0.2
+
'@vue/reactivity@3.5.13':
dependencies:
'@vue/shared': 3.5.13
@@ -2104,6 +2176,8 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
+ alien-signals@3.1.2: {}
+
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
@@ -2601,6 +2675,8 @@ snapshots:
ms@2.1.3: {}
+ muggle-string@0.4.1: {}
+
nanoid@3.3.11: {}
natural-compare@1.4.0: {}
@@ -2633,6 +2709,8 @@ snapshots:
dependencies:
callsites: 3.1.0
+ path-browserify@1.0.1: {}
+
path-exists@4.0.0: {}
path-key@3.1.1: {}
@@ -2863,6 +2941,8 @@ snapshots:
fsevents: 2.3.3
sass: 1.89.2
+ vscode-uri@3.1.0: {}
+
vue-eslint-parser@10.1.3(eslint@9.31.0):
dependencies:
debug: 4.4.0
@@ -2881,6 +2961,12 @@ snapshots:
'@vue/devtools-api': 6.6.4
vue: 3.5.13(typescript@5.8.3)
+ vue-tsc@3.2.5(typescript@5.8.3):
+ dependencies:
+ '@volar/typescript': 2.4.28
+ '@vue/language-core': 3.2.5
+ typescript: 5.8.3
+
vue3-virtual-scroll-list@0.2.1(vue@3.5.13(typescript@5.8.3)):
dependencies:
vue: 3.5.13(typescript@5.8.3)
diff --git a/photon-client/src/App.vue b/photon-client/src/App.vue
index f24875a0c9..edcdc365c1 100644
--- a/photon-client/src/App.vue
+++ b/photon-client/src/App.vue
@@ -11,9 +11,10 @@ import { useTheme } from "vuetify";
import { restoreThemeConfig } from "@/lib/ThemeManager";
const is_demo = import.meta.env.MODE === "demo";
+const backendHost = inject("backendHost");
if (!is_demo) {
const websocket = new AutoReconnectingWebsocket(
- `ws://${inject("backendHost")}/websocket_data`,
+ `ws://${backendHost}/websocket_data`,
() => {
useStateStore().$patch({ backendConnected: true });
},
diff --git a/photon-client/src/components/app/photon-3d-visualizer.vue b/photon-client/src/components/app/photon-3d-visualizer.vue
index 9f457ded77..cde1d3c720 100644
--- a/photon-client/src/components/app/photon-3d-visualizer.vue
+++ b/photon-client/src/components/app/photon-3d-visualizer.vue
@@ -3,7 +3,7 @@ import type { PhotonTarget } from "@/types/PhotonTrackingTypes";
// @ts-expect-error Intellisense says these conflict with the dynamic imports below
import type { Mesh, Object3D, PerspectiveCamera, Scene, WebGLRenderer } from "three";
// @ts-expect-error Intellisense says these conflict with the dynamic imports below
-import type { TrackballControls } from "three/examples/jsm/controls/TrackballControls";
+import type { TrackballControls } from "three/examples/jsm/controls/TrackballControls.js";
import { onBeforeUnmount, onMounted, watchEffect } from "vue";
const {
ArrowHelper,
@@ -20,7 +20,7 @@ const {
Scene,
WebGLRenderer
} = await import("three");
-const { TrackballControls } = await import("three/examples/jsm/controls/TrackballControls");
+const { TrackballControls } = await import("three/examples/jsm/controls/TrackballControls.js");
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { createPerspectiveCamera } from "@/lib/ThreeUtils";
@@ -213,14 +213,14 @@ onMounted(async () => {
renderer.render(scene, camera);
};
- drawTargets(props.targets);
+ await drawTargets(props.targets);
animate();
});
onBeforeUnmount(() => {
window.removeEventListener("resize", onWindowResize);
});
watchEffect(() => {
- drawTargets(props.targets);
+ void drawTargets(props.targets);
});
diff --git a/photon-client/src/components/app/photon-calibration-visualizer.vue b/photon-client/src/components/app/photon-calibration-visualizer.vue
index 31910269ce..246a722f69 100644
--- a/photon-client/src/components/app/photon-calibration-visualizer.vue
+++ b/photon-client/src/components/app/photon-calibration-visualizer.vue
@@ -1,5 +1,13 @@
diff --git a/photon-client/src/components/app/photon-camera-stream.vue b/photon-client/src/components/app/photon-camera-stream.vue
index 5c0e76d3fe..ffbbf959f0 100644
--- a/photon-client/src/components/app/photon-camera-stream.vue
+++ b/photon-client/src/components/app/photon-camera-stream.vue
@@ -1,5 +1,5 @@
diff --git a/photon-client/src/components/app/photon-log-view.vue b/photon-client/src/components/app/photon-log-view.vue
index 5d219fc104..0f9a17b797 100644
--- a/photon-client/src/components/app/photon-log-view.vue
+++ b/photon-client/src/components/app/photon-log-view.vue
@@ -1,5 +1,5 @@
diff --git a/photon-client/src/components/common/pv-camera-match-card.vue b/photon-client/src/components/common/pv-camera-match-card.vue
index 644741bd3f..dc14b13e36 100644
--- a/photon-client/src/components/common/pv-camera-match-card.vue
+++ b/photon-client/src/components/common/pv-camera-match-card.vue
@@ -1,5 +1,6 @@
diff --git a/photon-client/src/components/common/pv-input.vue b/photon-client/src/components/common/pv-input.vue
index 4285585af3..a47894e48e 100644
--- a/photon-client/src/components/common/pv-input.vue
+++ b/photon-client/src/components/common/pv-input.vue
@@ -25,7 +25,7 @@ const emit = defineEmits<{
(e: "onEscape"): void;
}>();
-const handleKeydown = ({ key }) => {
+const handleKeydown = ({ key }: KeyboardEvent) => {
switch (key) {
case "Enter":
// Explicitly check that all rule props return true
diff --git a/photon-client/src/components/common/pv-select.vue b/photon-client/src/components/common/pv-select.vue
index 2f7bd9e821..3d84aaa783 100644
--- a/photon-client/src/components/common/pv-select.vue
+++ b/photon-client/src/components/common/pv-select.vue
@@ -1,13 +1,16 @@
-
@@ -49,7 +57,6 @@ const items = computed(() => {
:items="items"
item-title="name"
item-value="value"
- item-props.disabled="disabled"
:disabled="disabled"
hide-details="auto"
variant="underlined"
diff --git a/photon-client/src/components/common/pv-slider.vue b/photon-client/src/components/common/pv-slider.vue
index 4503f8cc44..534f6dea3b 100644
--- a/photon-client/src/components/common/pv-slider.vue
+++ b/photon-client/src/components/common/pv-slider.vue
@@ -18,11 +18,11 @@ const props = withDefaults(
const emit = defineEmits<{ (e: "update:modelValue", value: number): void }>();
// Debounce function
-function debounce(func: (...args: any[]) => void, wait: number) {
+function debounce(func: (...args: number[]) => void, wait: number) {
let timeout: ReturnType;
- return function (...args: any[]) {
+ return function (...args: number[]) {
clearTimeout(timeout);
- timeout = setTimeout(() => func.apply(this, args), wait);
+ timeout = setTimeout(() => func(...args), wait);
};
}
diff --git a/photon-client/src/components/dashboard/CameraAndPipelineSelectCard.vue b/photon-client/src/components/dashboard/CameraAndPipelineSelectCard.vue
index 71bc543e23..fc883edd67 100644
--- a/photon-client/src/components/dashboard/CameraAndPipelineSelectCard.vue
+++ b/photon-client/src/components/dashboard/CameraAndPipelineSelectCard.vue
@@ -13,7 +13,8 @@ import PvDeleteModal from "@/components/common/pv-delete-modal.vue";
const theme = useTheme();
-const changeCurrentCameraUniqueName = (cameraUniqueName: string) => {
+const changeCurrentCameraUniqueName = (cameraUniqueName: string | number | undefined) => {
+ if (typeof cameraUniqueName !== "string") return;
useCameraSettingsStore().setCurrentCameraUniqueName(cameraUniqueName, true);
switch (useCameraSettingsStore().cameras[cameraUniqueName].pipelineSettings.pipelineType) {
@@ -303,7 +304,7 @@ const wrappedCameras = computed(() =>
!useCameraSettingsStore().hasConnected
"
:items="pipelineNamesWrapper"
- @update:modelValue="(args) => useCameraSettingsStore().changeCurrentPipelineIndex(args, true)"
+ @update:modelValue="(args) => (args ? useCameraSettingsStore().changeCurrentPipelineIndex(args, true) : null)"
/>
(() => {
.filter((it) => it.length); // Remove empty tab groups
});
+// This boolean is used to satisfy type-checking requirements.
+const shouldUseWideSecondTabGroup = computed(() => {
+ const currentPipelineSettings = useCameraSettingsStore().currentPipelineSettings;
+
+ return (
+ (currentPipelineSettings.pipelineType === PipelineType.AprilTag ||
+ currentPipelineSettings.pipelineType === PipelineType.Aruco) &&
+ currentPipelineSettings.doMultiTarget
+ );
+});
+
const onBeforeTabUpdate = () => {
// Force the current tab to the input tab on driver mode change
if (useCameraSettingsStore().isDriverMode) {
@@ -129,7 +140,7 @@ const onBeforeTabUpdate = () => {
diff --git a/photon-client/src/components/dashboard/tabs/AprilTagTab.vue b/photon-client/src/components/dashboard/tabs/AprilTagTab.vue
index 249f80607e..2c0cace150 100644
--- a/photon-client/src/components/dashboard/tabs/AprilTagTab.vue
+++ b/photon-client/src/components/dashboard/tabs/AprilTagTab.vue
@@ -1,18 +1,17 @@