Skip to content

Commit 4573203

Browse files
committed
Merge branch 'develop'
Signed-off-by: Pedro Lamas <[email protected]>
2 parents b743dbe + 9682b5b commit 4573203

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1768
-1473
lines changed

package-lock.json

Lines changed: 578 additions & 773 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,15 @@
1717
"serve:docs": "cd ./docs; bundle exec jekyll serve",
1818
"lint": "eslint .",
1919
"bootstrap": "husky",
20-
"copy:host:config": "shx cp -f ./server/config.json ./dist/config.json",
2120
"i18n-extract": "vue-i18n-extract use-config",
2221
"release": "standard-version",
2322
"release:major": "npm run release -- --release-as major",
2423
"release:minor": "npm run release -- --release-as minor",
2524
"release:patch": "npm run release -- --release-as patch",
2625
"release:rc": "npm run release -- --prerelease rc",
2726
"test": "npm run test:unit",
28-
"test:unit": "vitest --environment jsdom",
29-
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json",
27+
"test:unit": "vitest --environment jsdom --config ./vitest.config.ts",
28+
"type-check": "vue-tsc --build --noEmit",
3029
"circular-check": "skott --displayMode=file-tree --showCircularDependencies --tsconfig=./tsconfig.app.json",
3130
"theme:convert": "cd tools; node convertTheme.mjs"
3231
},
@@ -38,14 +37,14 @@
3837
"@irojs/iro-core": "^1.2.1",
3938
"@jaames/iro": "^5.5.2",
4039
"axios": "^1.7.9",
41-
"consola": "^3.2.3",
40+
"consola": "^3.3.1",
4241
"dompurify": "^3.2.3",
4342
"echarts": "^5.5.1",
4443
"echarts-gl": "^2.0.9",
45-
"hls.js": "^1.5.17",
44+
"hls.js": "^1.5.18",
4645
"jwt-decode": "^4.0.0",
4746
"lodash-es": "^4.17.21",
48-
"marked": "^15.0.3",
47+
"marked": "^15.0.4",
4948
"marked-base-url": "^1.1.6",
5049
"md5": "^2.3.0",
5150
"monaco-editor": "^0.52.2",
@@ -81,34 +80,33 @@
8180
"@types/jsdom": "^21.1.7",
8281
"@types/lodash-es": "^4.17.12",
8382
"@types/md5": "^2.3.5",
84-
"@types/node": "^22.10.1",
83+
"@types/node": "^22.10.2",
8584
"@types/semver": "^7.5.8",
8685
"@types/sortablejs": "^1.15.8",
8786
"@vitejs/plugin-vue2": "^2.3.3",
88-
"@vue/eslint-config-typescript": "^14.1.4",
87+
"@vue/eslint-config-typescript": "^14.2.0",
8988
"@vue/test-utils": "^1.3.6",
9089
"@vue/tsconfig": "~0.1.3",
91-
"eslint": "^9.16.0",
90+
"eslint": "^9.17.0",
9291
"eslint-plugin-regexp": "^2.7.0",
9392
"eslint-plugin-vue": "^9.32.0",
9493
"husky": "^9.1.7",
9594
"jsdom": "^25.0.1",
9695
"mockdate": "^3.0.5",
9796
"monaco-vscode-textmate-theme-converter": "^0.1.7",
9897
"neostandard": "^0.12.0",
99-
"rollup": "^4.28.1",
98+
"rollup": "^4.29.1",
10099
"sass": "~1.32.13",
101-
"shx": "^0.3.4",
102100
"skott": "^0.35.4",
103101
"standard-version": "^9.5.0",
104102
"typescript": "^5.6.3",
105-
"typescript-eslint": "^8.18.0",
106-
"unplugin-vue-components": "^0.27.5",
107-
"vite": "^6.0.3",
103+
"typescript-eslint": "^8.18.2",
104+
"unplugin-vue-components": "^0.28.0",
105+
"vite": "^6.0.6",
108106
"vite-plugin-checker": "^0.8.0",
109107
"vite-plugin-monaco-editor": "^1.1.0",
110108
"vite-plugin-pwa": "^0.21.1",
111-
"vitest": "^1.6.0",
109+
"vitest": "^2.1.8",
112110
"vue-debounce-decorator": "^1.0.1",
113111
"vue-i18n-extract": "^2.0.7",
114112
"vue-template-compiler": "^2.7.16",

src/components/common/PeripheralsDialog.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ export default class ManualProbeDialog extends Mixins(StateMixin) {
414414
break
415415
416416
case 'can':
417-
for (const canbusInterface in this.canbusInterfaces) {
417+
for (const canbusInterface of this.canbusInterfaces) {
418418
SocketActions.machinePeripheralsCanbus(canbusInterface)
419419
}
420420
break

src/components/widgets/camera/CameraItem.vue

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
:camera="camera"
1212
:crossorigin="crossorigin"
1313
class="camera-image"
14+
@update:status="status = $event"
1415
@update:camera-name="cameraName = $event"
1516
@update:camera-name-menu-items="cameraNameMenuItems = $event"
1617
@update:raw-camera-url="rawCameraUrl = $event"
1718
@update:frames-per-second="framesPerSecond = $event"
18-
@playback="setupFrameEvents()"
1919
/>
2020
</template>
2121
<div v-else>
@@ -35,7 +35,7 @@
3535
class="camera-name"
3636
v-on="on"
3737
>
38-
{{ cameraName || camera.name }}
38+
{{ cameraNameAndStatus }}
3939
<v-icon
4040
small
4141
class="ml-1"
@@ -49,11 +49,11 @@
4949
<v-list-item
5050
v-for="(item, index) in cameraNameMenuItems"
5151
:key="index"
52-
@click="cameraNameMenuItemClick(item.value)"
52+
@click="cameraNameMenuItemClick(item)"
5353
>
5454
<v-list-item-icon>
5555
<v-icon>
56-
$camera
56+
{{ item.icon }}
5757
</v-icon>
5858
</v-list-item-icon>
5959
<v-list-item-content>
@@ -69,7 +69,7 @@
6969
v-else
7070
class="camera-name"
7171
>
72-
{{ cameraName || camera.name }}
72+
{{ cameraNameAndStatus }}
7373
</div>
7474
</template>
7575

@@ -108,6 +108,8 @@ import type { WebcamConfig } from '@/store/webcams/types'
108108
import type { CameraFullscreenAction } from '@/store/config/types'
109109
import { CameraComponents } from '@/dynamicImports'
110110
import CameraMixin from '@/mixins/camera'
111+
import type { CameraConnectionStatus, CameraNameMenuItem } from '@/types'
112+
import { startCase } from 'lodash-es'
111113
112114
@Component({})
113115
export default class CameraItem extends Vue {
@@ -123,19 +125,17 @@ export default class CameraItem extends Vue {
123125
@Ref('component-instance')
124126
readonly componentInstance!: CameraMixin
125127
126-
rawCameraUrl: string | null = null
127-
framesPerSecond: string | null = null
128-
cameraName: string | null = null
129-
cameraNameMenuItems: { text: string, value: string }[] = []
128+
status: CameraConnectionStatus = 'disconnected'
129+
rawCameraUrl = ''
130+
framesPerSecond = ''
131+
cameraName = ''
132+
cameraNameMenuItems: CameraNameMenuItem[] = []
130133
131-
mounted () {
132-
this.setupFrameEvents()
133-
}
134-
135-
setupFrameEvents () {
136-
if (this.$listeners?.frame && this.componentInstance) {
134+
@Watch('status')
135+
onStatus (value: CameraConnectionStatus) {
136+
if (value === 'connected' && this.$listeners?.frame && this.componentInstance) {
137137
if (this.componentInstance.streamingElement instanceof HTMLImageElement) {
138-
this.componentInstance.streamingElement.addEventListener('load', () => this.handleFrame())
138+
this.handleFrame()
139139
} else if (this.componentInstance.streamingElement instanceof HTMLVideoElement) {
140140
this.handleFrame(true)
141141
}
@@ -153,12 +153,13 @@ export default class CameraItem extends Vue {
153153
}
154154
}
155155
156-
cameraNameMenuItemClick (value: string) {
157-
this.componentInstance.menuItemClick(value)
156+
cameraNameMenuItemClick (item: CameraNameMenuItem) {
157+
this.componentInstance.menuItemClick(item)
158158
}
159159
160160
@Watch('camera')
161161
onCamera () {
162+
this.status = 'disconnected'
162163
this.rawCameraUrl = ''
163164
this.framesPerSecond = ''
164165
this.cameraName = ''
@@ -171,13 +172,23 @@ export default class CameraItem extends Vue {
171172
172173
get cameraComponent () {
173174
if (this.camera.service) {
174-
const componentName = `${this.$filters.startCase(this.camera.service).replace(/ /g, '')}Camera`
175+
const componentName = `${startCase(this.camera.service).replace(/ /g, '')}Camera`
175176
176177
if (componentName in CameraComponents) {
177178
return CameraComponents[componentName]
178179
}
179180
}
180181
}
182+
183+
get cameraNameAndStatus () {
184+
const cameraName = this.cameraName || this.camera.name
185+
186+
if (this.status !== 'connected') {
187+
return `${cameraName} (${this.status})`
188+
}
189+
190+
return cameraName
191+
}
181192
}
182193
</script>
183194

@@ -196,6 +207,7 @@ export default class CameraItem extends Vue {
196207
.camera-container {
197208
position: relative;
198209
background: rgba(0, 0, 0, 1);
210+
min-height: 70px;
199211
}
200212
201213
.camera-name,

src/components/widgets/camera/services/DeviceCamera.vue

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,41 @@
44
autoplay
55
muted
66
:style="cameraStyle"
7+
@play="updateStatus('connected')"
8+
@error="updateStatus('error')"
79
/>
810
</template>
911

1012
<script lang="ts">
1113
import { Component, Ref, Mixins } from 'vue-property-decorator'
1214
import CameraMixin from '@/mixins/camera'
1315
import consola from 'consola'
16+
import type { CameraNameMenuItem } from '@/types'
1417
1518
@Component({})
1619
export default class DeviceCamera extends Mixins(CameraMixin) {
1720
@Ref('streamingElement')
1821
readonly cameraVideo!: HTMLVideoElement
1922
20-
cameraNameMenuItems: { text: string, value: string }[] = []
21-
2223
async startPlayback () {
23-
const stream = await this.getUserMedia()
24+
this.updateStatus('connecting')
25+
26+
try {
27+
const stream = await this.getUserMedia()
2428
25-
this.cameraVideo.srcObject = stream
26-
this.$emit('playback')
29+
this.cameraVideo.srcObject = stream
30+
31+
this.updateCameraName(await this.getDeviceLabel() ?? '')
32+
} catch (e) {
33+
consola.error(`[DeviceCamera] failed to start playback "${this.getSelectedDeviceCamera()}"`, e)
2734
28-
this.$emit('update:camera-name', await this.getDeviceLabel())
35+
this.updateStatus('error')
36+
}
2937
}
3038
3139
stopPlayback () {
40+
this.updateStatus('disconnected')
41+
3242
try {
3343
const stream = this.cameraVideo.srcObject as MediaStream
3444
@@ -39,7 +49,7 @@ export default class DeviceCamera extends Mixins(CameraMixin) {
3949
}
4050
}
4151
} catch (e) {
42-
consola.error('[Device Camera] failed to stop and remove all tracks', e)
52+
consola.error('[DeviceCamera] failed to stop and remove all tracks', e)
4353
}
4454
4555
this.cameraVideo.srcObject = null
@@ -59,7 +69,7 @@ export default class DeviceCamera extends Mixins(CameraMixin) {
5969
}
6070
})
6171
} catch (e) {
62-
consola.error(`[Device Camera] failed to select device ${selectedDeviceCamera}`, e)
72+
consola.error(`[DeviceCamera] failed to select device "${selectedDeviceCamera}"`, e)
6373
6474
this.setSelectedDeviceCamera(null)
6575
@@ -78,7 +88,7 @@ export default class DeviceCamera extends Mixins(CameraMixin) {
7888
return devices
7989
.filter(device => device.kind === 'videoinput')
8090
} catch (e) {
81-
consola.error('[Device Camera] failed to enumerate devices', e)
91+
consola.error('[DeviceCamera] failed to enumerate devices', e)
8292
8393
return []
8494
}
@@ -88,23 +98,26 @@ export default class DeviceCamera extends Mixins(CameraMixin) {
8898
if (this.cameraNameMenuItems.length === 0) {
8999
const devices = await this.enumerateDevices()
90100
91-
this.cameraNameMenuItems = [
101+
const cameraNameMenuItems = [
92102
{
103+
icon: '$camera',
93104
text: this.$tc('app.general.label.environment_facing'),
94105
value: 'environment'
95106
},
96107
{
108+
icon: '$camera',
97109
text: this.$tc('app.general.label.user_facing'),
98110
value: 'user'
99111
},
100112
...devices
101113
.map(device => ({
114+
icon: '$camera',
102115
text: device.label,
103116
value: device.deviceId
104117
}))
105118
]
106119
107-
this.$emit('update:camera-name-menu-items', this.cameraNameMenuItems)
120+
this.updateCameraNameMenuItems(cameraNameMenuItems)
108121
}
109122
110123
const selectedDeviceCamera = this.getSelectedDeviceCamera()
@@ -125,11 +138,14 @@ export default class DeviceCamera extends Mixins(CameraMixin) {
125138
}
126139
}
127140
128-
menuItemClick (value: string) {
129-
if (this.getSelectedDeviceCamera() !== value) {
130-
this.setSelectedDeviceCamera(value)
141+
menuItemClick (item: CameraNameMenuItem) {
142+
if (this.getSelectedDeviceCamera() !== item.value) {
143+
this.setSelectedDeviceCamera(item.value)
131144
132145
this.stopPlayback()
146+
147+
this.updateCameraName(item.text)
148+
133149
this.startPlayback()
134150
}
135151
}

0 commit comments

Comments
 (0)