Skip to content

Commit ff4dd7d

Browse files
committed
Merge branch 'develop'
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
2 parents fe2aba1 + 8d5c08f commit ff4dd7d

File tree

14 files changed

+2078
-1862
lines changed

14 files changed

+2078
-1862
lines changed

package-lock.json

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

package.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,25 @@
3434
"main": "index.js",
3535
"dependencies": {
3636
"@ctrl/tinycolor": "^3.6.0",
37-
"@fontsource/raleway": "^4.5.12",
38-
"@fontsource/roboto": "^4.5.8",
37+
"@fontsource/raleway": "^5.0.2",
38+
"@fontsource/roboto": "^5.0.2",
3939
"@jaames/iro": "^5.5.2",
4040
"axios": "^0.27.2",
4141
"consola": "^3.1.0",
4242
"deepmerge": "^4.3.1",
4343
"echarts": "^5.4.2",
4444
"echarts-gl": "^2.0.9",
45-
"hls.js": "^1.4.2",
45+
"hls.js": "^1.4.5",
4646
"jwt-decode": "^3.1.2",
4747
"lodash-es": "^4.17.21",
4848
"md5": "^2.3.0",
49-
"monaco-editor": "^0.38.0",
49+
"monaco-editor": "^0.39.0",
5050
"monaco-editor-textmate": "^4.0.0",
5151
"monaco-textmate": "^3.0.1",
5252
"onigasm": "^2.2.5",
5353
"panzoom": "^9.4.3",
5454
"qrcode.vue": "^1.7.0",
55-
"semver": "^7.5.0",
55+
"semver": "^7.5.1",
5656
"shlex": "^2.1.2",
5757
"sortablejs": "^1.15.0",
5858
"uuid": "^9.0.0",
@@ -77,38 +77,38 @@
7777
"@types/jsdom": "^21.1.1",
7878
"@types/lodash-es": "^4.17.7",
7979
"@types/md5": "^2.3.2",
80-
"@types/node": "^20.1.2",
80+
"@types/node": "^18.16.16",
8181
"@types/semver": "^7.5.0",
8282
"@types/sortablejs": "^1.15.1",
8383
"@types/uuid": "^9.0.1",
84-
"@typescript-eslint/eslint-plugin": "^5.59.5",
85-
"@typescript-eslint/parser": "^5.59.5",
84+
"@typescript-eslint/eslint-plugin": "^5.59.9",
85+
"@typescript-eslint/parser": "^5.59.9",
8686
"@vitejs/plugin-vue2": "^2.2.0",
8787
"@vue/eslint-config-standard": "^8.0.1",
8888
"@vue/eslint-config-typescript": "^11.0.3",
89-
"@vue/test-utils": "^1.3.5",
89+
"@vue/test-utils": "^1.3.6",
9090
"@vue/tsconfig": "^0.1.3",
91-
"eslint": "^8.40.0",
92-
"eslint-plugin-vue": "^9.12.0",
91+
"eslint": "^8.42.0",
92+
"eslint-plugin-vue": "^9.14.1",
9393
"husky": "^8.0.3",
94-
"jsdom": "^22.0.0",
94+
"jsdom": "^22.1.0",
9595
"madge": "^5.0.1",
9696
"mockdate": "^3.0.5",
9797
"monaco-vscode-textmate-theme-converter": "^0.1.7",
9898
"sass": "~1.32",
9999
"shx": "^0.3.4",
100100
"standard-version": "^9.5.0",
101-
"typescript": "^5.0.4",
102-
"unplugin-vue-components": "^0.24.1",
103-
"vite": "^4.3.5",
101+
"typescript": "^5.1.3",
102+
"unplugin-vue-components": "^0.25.1",
103+
"vite": "^4.3.9",
104104
"vite-plugin-checker": "^0.6.0",
105105
"vite-plugin-monaco-editor": "^1.1.0",
106-
"vite-plugin-pwa": "^0.14.7",
107-
"vitest": "^0.31.0",
106+
"vite-plugin-pwa": "^0.16.3",
107+
"vitest": "^0.32.0",
108108
"vue-debounce-decorator": "^1.0.1",
109109
"vue-i18n-extract": "^2.0.7",
110110
"vue-template-compiler": "^2.7.14",
111-
"vue-tsc": "^1.6.4"
111+
"vue-tsc": "^1.6.5"
112112
},
113113
"_id": "fluidd@0.0.3",
114114
"bugs": {

src/components/settings/cameras/CameraConfigDialog.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
:title="(camera.id != '') ? $t('app.general.label.edit_camera') : $t('app.general.label.add_camera')"
55
:cancel-button-text="camera.source === 'config' ? $t('app.general.btn.close') : $t('app.general.btn.cancel')"
66
:save-button-text="(camera.id !== '') ? $t('app.general.btn.save') : $t('app.general.btn.add')"
7-
max-width="500"
7+
max-width="600"
88
:disabled="camera.source === 'config'"
99
@save="handleSave"
1010
>
@@ -83,6 +83,7 @@
8383
{ text: $t('app.setting.camera_type_options.mjpegadaptive'), value: 'mjpegstreamer-adaptive' },
8484
{ text: $t('app.setting.camera_type_options.mjpegstream'), value: 'mjpegstreamer' },
8585
{ text: $t('app.setting.camera_type_options.hlsstream'), value: 'hlsstream' },
86+
{ text: $t('app.setting.camera_type_options.webrtc_camera_streamer'), value: 'webrtc-camerastreamer' },
8687
{ text: $t('app.setting.camera_type_options.video'), value: 'ipstream' },
8788
{ text: $t('app.setting.camera_type_options.iframe'), value: 'iframe' }
8889
]"

src/components/widgets/camera/CameraItem.vue

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
>
1414

1515
<video
16-
v-else-if="camera.service === 'ipstream' || camera.service === 'hlsstream'"
16+
v-else-if="camera.service === 'ipstream' || camera.service === 'hlsstream' || camera.service === 'webrtc-camerastreamer'"
1717
ref="camera_image"
1818
:src="cameraUrl"
1919
autoplay
@@ -66,6 +66,7 @@ import { CameraConfig } from '@/store/cameras/types'
6666
import { noop } from 'vue-class-component/lib/util'
6767
import { CameraFullscreenAction } from '@/store/config/types'
6868
import type HlsType from 'hls.js'
69+
import consola from 'consola'
6970
let Hls: typeof HlsType
7071
7172
/**
@@ -91,6 +92,8 @@ export default class CameraItem extends Vue {
9192
request_time_smoothing = 0.1
9293
currentFPS = '0'
9394
hls: HlsType | null = null
95+
pc: RTCPeerConnection | null = null
96+
remoteId: string | null = null
9497
9598
// URL used by camera
9699
cameraUrl = ''
@@ -157,6 +160,7 @@ export default class CameraItem extends Vue {
157160
*/
158161
beforeDestroy () {
159162
this.hls?.destroy()
163+
this.pc?.close()
160164
this.cancelCameraTransform()
161165
this.cameraUrl = this.cameraImage.src = ''
162166
this.cameraFullScreenUrl = ''
@@ -286,9 +290,82 @@ export default class CameraItem extends Vue {
286290
}
287291
break
288292
}
293+
294+
case 'webrtc-camerastreamer': {
295+
const cameraVideo = this.cameraImage as HTMLVideoElement
296+
297+
this.pc?.close()
298+
299+
const config = {
300+
sdpSemantics: 'unified-plan'
301+
} as RTCConfiguration
302+
303+
this.pc = new RTCPeerConnection(config)
304+
305+
this.pc.addTransceiver('video', {
306+
direction: 'recvonly'
307+
})
308+
309+
this.pc.ontrack = (evt: RTCTrackEvent) => {
310+
if (evt.track.kind === 'video' && cameraVideo) {
311+
cameraVideo.srcObject = evt.streams[0]
312+
}
313+
}
314+
315+
fetch(baseUrl, {
316+
body: JSON.stringify({
317+
type: 'request'
318+
}),
319+
headers: {
320+
'Content-Type': 'application/json'
321+
},
322+
method: 'POST'
323+
})
324+
.then(response => response.json())
325+
.then((answer: RTCSessionDescriptionInit) => {
326+
this.remoteId = 'id' in answer && typeof (answer.id) === 'string' ? answer.id : null
327+
328+
return this.pc?.setRemoteDescription(answer)
329+
})
330+
.then(() => this.pc?.createAnswer())
331+
.then(answer => this.pc?.setLocalDescription(answer))
332+
.then(() => new Promise(resolve => {
333+
const checkState = () => {
334+
if (this.pc?.iceGatheringState === 'complete') {
335+
this.pc?.removeEventListener('icegatheringstatechange', checkState)
336+
337+
resolve(true)
338+
}
339+
}
340+
341+
if (this.pc?.iceGatheringState === 'complete') {
342+
resolve(true)
343+
} else {
344+
this.pc?.addEventListener('icegatheringstatechange', checkState)
345+
}
346+
}))
347+
.then(() => {
348+
const offer = this.pc?.localDescription
349+
350+
return fetch(baseUrl, {
351+
body: JSON.stringify({
352+
type: offer?.type,
353+
id: this.remoteId,
354+
sdp: offer?.sdp
355+
}),
356+
headers: {
357+
'Content-Type': 'application/json'
358+
},
359+
method: 'POST'
360+
})
361+
})
362+
.then(response => response.json())
363+
.catch(e => consola.error('[CameraItem] setUrl', e))
364+
}
289365
}
290366
} else {
291367
this.hls?.destroy()
368+
this.pc?.close()
292369
this.cameraUrl = ''
293370
}
294371
}

src/components/widgets/filesystem/FileEditor.vue

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ export default class FileEditor extends Mixins(BrowserMixin) {
101101
rulers: (this.isMobileViewport) ? [80, 120] : []
102102
})
103103
104+
this.editor.addAction({
105+
id: 'action-save-file',
106+
label: this.$tc('app.general.btn.save'),
107+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],
108+
run: () => {
109+
this.$emit('save')
110+
}
111+
})
112+
104113
const filename = this.path ? `${this.path}/${this.filename}` : this.filename
105114
const apiFileUrl = `${this.activeInstance.apiUrl}/server/files/${filename}`
106115
@@ -126,16 +135,22 @@ export default class FileEditor extends Mixins(BrowserMixin) {
126135
127136
// Focus the editor.
128137
this.$nextTick(() => {
129-
this.editor?.focus()
138+
focus()
130139
})
131140
132141
this.$emit('ready')
133-
this.editor.onDidChangeModelContent(event => {
142+
143+
this.editor.onDidChangeModelContent(() => {
134144
const value = this.editor?.getValue()
135-
this.emitChange(value, event)
145+
146+
this.$emit('input', value)
136147
})
137148
}
138149
150+
focus () {
151+
this.editor?.focus()
152+
}
153+
139154
showCommandPalette () {
140155
if (this.editor) {
141156
this.editor.focus()
@@ -162,11 +177,6 @@ export default class FileEditor extends Mixins(BrowserMixin) {
162177
if (monaco) monaco.editor.getModels().forEach(model => model.dispose())
163178
if (this.editor) this.editor.dispose()
164179
}
165-
166-
emitChange (value: string | undefined, event: Monaco.editor.IModelContentChangedEvent) {
167-
this.$emit('change', value, event)
168-
this.$emit('input', value)
169-
}
170180
}
171181
</script>
172182

src/components/widgets/filesystem/FileEditorDialog.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
>
2828
<v-icon>$close</v-icon>
2929
</app-btn>
30-
<v-toolbar-title>{{ filename }}</v-toolbar-title>
30+
<v-toolbar-title>{{ filename }}{{ updatedContent !== lastSavedContent ? " *" : "" }}</v-toolbar-title>
3131
<v-spacer />
3232
<v-toolbar-items>
3333
<app-btn
@@ -104,6 +104,7 @@
104104
:readonly="readonly"
105105
:code-lens="codeLens"
106106
@ready="editorReady = true"
107+
@save="emitSave(false)"
107108
/>
108109

109110
<file-editor-text-only
@@ -118,7 +119,7 @@
118119
</template>
119120

120121
<script lang="ts">
121-
import { Component, Mixins, Prop, Ref, VModel } from 'vue-property-decorator'
122+
import { Component, Mixins, Prop, Ref, VModel, Watch } from 'vue-property-decorator'
122123
import StateMixin from '@/mixins/state'
123124
import BrowserMixin from '@/mixins/browser'
124125
import FileEditor from './FileEditor.vue'
@@ -168,6 +169,13 @@ export default class FileEditorDialog extends Mixins(StateMixin, BrowserMixin) {
168169
)
169170
}
170171
172+
@Watch('ready')
173+
onReady (value: boolean) {
174+
if (value) {
175+
this.editor?.focus()
176+
}
177+
}
178+
171179
get isWebAssemblySupported () {
172180
return isWebAssemblySupported()
173181
}

src/components/widgets/filesystem/FileEditorTextOnly.vue

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,24 @@
11
<template>
22
<textarea
3+
v-model="content"
34
class="v-input v-textarea px-2"
45
:class="{'theme--dark': $vuetify.theme.dark}"
56
:readonly="readonly"
6-
:value="value"
77
:spellcheck="false"
8-
@change="emitChange($event)"
98
/>
109
</template>
1110

1211
<script lang="ts">
13-
import { Vue, Component, Prop } from 'vue-property-decorator'
12+
import { Vue, Component, Prop, VModel } from 'vue-property-decorator'
1413
1514
@Component({})
1615
export default class FileEditorText extends Vue {
17-
@Prop({ type: String, required: true })
18-
readonly value!: string
16+
@VModel({ type: String, required: true })
17+
content!: string
1918
2019
@Prop({ type: Boolean, default: false })
2120
readonly readonly!: boolean
2221
23-
emitChange (e: Event) {
24-
const element = e.target as HTMLTextAreaElement
25-
26-
if (element) {
27-
this.$emit('change', element.value)
28-
this.$emit('input', element.value)
29-
}
30-
}
31-
3222
mounted () {
3323
this.$emit('ready')
3424
}

src/components/widgets/filesystem/FileSystemBrowser.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ export default class FileSystemBrowser extends Mixins(FilesMixin) {
403403
}
404404
} else if (item.name === '..') {
405405
return '$folderUp'
406+
} else if (item.permissions === 'r' || this.readonly) {
407+
return '$folderLock'
406408
} else {
407409
return '$folder'
408410
}

src/components/widgets/filesystem/setupMonaco.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import themeLight from '@/monaco/theme/editor.light.theme.json'
1111

1212
import { MonacoLanguageImports } from '@/dynamicImports'
1313

14-
type CodeLensSupportedService = 'klipper' | 'moonraker' | 'moonraker-telegram-bot'
14+
type CodeLensSupportedService = 'klipper' | 'moonraker' | 'moonraker-telegram-bot' | 'crowsnest'
1515

1616
const isCodeLensSupportedService = (service: string) : service is CodeLensSupportedService => [
1717
'klipper',
1818
'moonraker',
19-
'moonraker-telegram-bot'
19+
'moonraker-telegram-bot',
20+
'crowsnest'
2021
].includes(service)
2122

2223
const getDocsSection = (service: CodeLensSupportedService, sectionName: string) => {

0 commit comments

Comments
 (0)