diff --git a/electron/preload.ts b/electron/preload.ts
index a26e42dce..b3929e0a1 100644
--- a/electron/preload.ts
+++ b/electron/preload.ts
@@ -31,4 +31,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
keys: async (subFolders?: string[]) => {
return await ipcRenderer.invoke('keys', { subFolders })
},
+ openCockpitFolder: () => ipcRenderer.invoke('open-cockpit-folder'),
+ openVideoFolder: () => ipcRenderer.invoke('open-video-folder'),
})
diff --git a/electron/services/storage.ts b/electron/services/storage.ts
index f005494df..f6349f72e 100644
--- a/electron/services/storage.ts
+++ b/electron/services/storage.ts
@@ -1,10 +1,10 @@
-import { ipcMain } from 'electron'
+import { ipcMain, shell } from 'electron'
import { app } from 'electron'
import * as fs from 'fs/promises'
import { dirname, join } from 'path'
// Create a new storage interface for filesystem
-const cockpitFolderPath = join(app.getPath('home'), 'Cockpit')
+export const cockpitFolderPath = join(app.getPath('home'), 'Cockpit')
fs.mkdir(cockpitFolderPath, { recursive: true })
export const filesystemStorage = {
@@ -58,4 +58,13 @@ export const setupFilesystemStorage = (): void => {
ipcMain.handle('keys', async (_, data) => {
return await filesystemStorage.keys(data.subFolders)
})
+ ipcMain.handle('open-cockpit-folder', async () => {
+ await fs.mkdir(cockpitFolderPath, { recursive: true })
+ await shell.openPath(cockpitFolderPath)
+ })
+ ipcMain.handle('open-video-folder', async () => {
+ const videoFolderPath = join(cockpitFolderPath, 'videos')
+ await fs.mkdir(videoFolderPath, { recursive: true })
+ await shell.openPath(videoFolderPath)
+ })
}
diff --git a/src/components/VideoLibraryModal.vue b/src/components/VideoLibraryModal.vue
index 2ea3d5379..a44575a30 100644
--- a/src/components/VideoLibraryModal.vue
+++ b/src/components/VideoLibraryModal.vue
@@ -484,6 +484,7 @@ import { ref, watch } from 'vue'
import { useInteractionDialog } from '@/composables/interactionDialog'
import { useSnackbar } from '@/composables/snackbar'
+import { isElectron } from '@/libs/utils'
import { useAppInterfaceStore } from '@/stores/appInterface'
import { useVideoStore } from '@/stores/video'
import { DialogActions } from '@/types/general'
@@ -580,8 +581,31 @@ const fileActionButtons = computed(() => [
disabled: showOnScreenProgress.value === true || isPreparingDownload.value === true,
action: () => downloadVideoAndTelemetryFiles(),
},
+ {
+ name: 'Open Folder',
+ icon: 'mdi-folder-outline',
+ size: 28,
+ tooltip: 'Open videos folder',
+ confirmAction: false,
+ show: isElectron(),
+ disabled: false,
+ action: () => openVideoFolder(),
+ },
])
+const openVideoFolder = (): void => {
+ if (isElectron() && window.electronAPI) {
+ window.electronAPI?.openVideoFolder()
+ } else {
+ showSnackbar({
+ message: 'This feature is only available in the desktop version of Cockpit.',
+ duration: 3000,
+ variant: 'error',
+ closeButton: true,
+ })
+ }
+}
+
const closeModal = (): void => {
isVisible.value = false
emits('update:openModal', false)
diff --git a/src/libs/cosmos.ts b/src/libs/cosmos.ts
index b74b2f9e8..ecd0c0a97 100644
--- a/src/libs/cosmos.ts
+++ b/src/libs/cosmos.ts
@@ -224,6 +224,14 @@ declare global {
* Register callback for download progress event
*/
onDownloadProgress: (callback: (info: any) => void) => void
+ /**
+ * Open cockpit folder
+ */
+ openCockpitFolder: () => void
+ /**
+ * Open video folder
+ */
+ openVideoFolder: () => void
}
}
}
diff --git a/src/views/ConfigurationGeneralView.vue b/src/views/ConfigurationGeneralView.vue
index 029378016..1548bf100 100644
--- a/src/views/ConfigurationGeneralView.vue
+++ b/src/views/ConfigurationGeneralView.vue
@@ -32,9 +32,20 @@
@click="missionStore.changeUsername"
/>
-