Compare commits

...

13 Commits

14 changed files with 1080 additions and 185 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ out
.DS_Store .DS_Store
*.log* *.log*
electron.vite.config.1716830836044.mjs electron.vite.config.1716830836044.mjs
.eslintrc.cjs

View File

@@ -2,3 +2,4 @@ singleQuote: true
semi: false semi: false
printWidth: 100 printWidth: 100
trailingComma: none trailingComma: none
endOfLine: "auto"

857
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,15 @@
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0" "@electron-toolkit/utils": "^3.0.0",
"@node-steam/vdf": "^2.2.0",
"find-steam-app": "^1.0.2",
"get-installed-apps": "^1.1.0",
"get-startapps": "^1.0.4",
"glob": "^10.4.1",
"node-fetch": "^2.6.1",
"serialport": "^12.0.0",
"vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config": "^1.0.1", "@electron-toolkit/eslint-config": "^1.0.1",
@@ -31,6 +39,7 @@
"electron-vite": "^2.0.0", "electron-vite": "^2.0.0",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-plugin-vue": "^9.20.1", "eslint-plugin-vue": "^9.20.1",
"log-timestamp": "^0.3.0",
"prettier": "^3.2.4", "prettier": "^3.2.4",
"vite": "^5.0.12", "vite": "^5.0.12",
"vue": "^3.4.15" "vue": "^3.4.15"

View File

@@ -16,4 +16,4 @@ baud_rate: 9600
# adjust the amount of signal noise reduction depending on your hardware quality # adjust the amount of signal noise reduction depending on your hardware quality
# supported values are "low" (excellent hardware), "default" (regular hardware) or "high" (bad, noisy hardware) # supported values are "low" (excellent hardware), "default" (regular hardware) or "high" (bad, noisy hardware)
noise_reduction: default noise_reduction: default

3
resources/test.js Normal file
View File

@@ -0,0 +1,3 @@
import { SerialPort } from 'serialport'
console.log(SerialPort.list())

42
src/main/Steam.js Normal file
View File

@@ -0,0 +1,42 @@
import * as VDF from '@node-steam/vdf'
import fs from 'fs'
import { glob, globSync } from 'glob'
import { findSteam } from 'find-steam-app'
// const VDF = require('@node-steam/vdf')
// const fs = require('fs')
// const glob = require('glob')
export async function getLibraryFolders(steamPath) {
if (!steamPath) steamPath = await findSteam()
if (!fs.existsSync(steamPath)) throw new Error(`Steam path not found: ${steamPath}`)
if (!fs.existsSync(`${steamPath}/steamapps/libraryfolders.vdf`))
throw new Error(`Steam config.vdf not found: ${steamPath}/steanapps/libraryfolders.vdf`)
const libraryFolders = VDF.parse(
fs.readFileSync(`${steamPath}/steamapps/libraryfolders.vdf`, 'utf8')
)
console.log(typeof libraryFolders)
return Object.values(libraryFolders.libraryfolders)
}
export function getGames(libraryFolder) {
if (!libraryFolder) return null
if (!fs.existsSync(libraryFolder.path)) throw new Error(`Steam path not found: ${libraryFolder}`)
const appManifests = glob.globSync(`${libraryFolder.path}/steamapps/*.acf`)
const gamesCollection = []
appManifests.forEach((appManifest) => {
const appManifestData = VDF.parse(fs.readFileSync(appManifest, 'utf8'))
gamesCollection.push({
baseDirectory: libraryFolder.path.replaceAll('\\\\', '\\'),
appid: appManifestData.AppState.appid,
name: appManifestData.AppState.name,
installdir: appManifestData.AppState.installdir.replaceAll('\\\\', '\\')
})
})
return gamesCollection
}

View File

@@ -3,18 +3,54 @@ import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils' import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset' import icon from '../../resources/icon.png?asset'
import fs from 'node:fs/promises' import fs from 'node:fs/promises'
// import {getInstalledApps} from 'get-installed-apps'
import getapps from 'get-startapps'
import * as steam from './Steam.js'
import { SerialPort } from 'serialport'
import * as logTimestamp from 'log-timestamp'
import { globSync } from 'glob'
async function loadConfigurationYAML() { async function loadConfigurationYAML() {
try { try {
const data = await fs.readFile('./resources//config.yaml', { encoding: 'utf8' }); const data = await fs.readFile('./resources//config.yaml', { encoding: 'utf8' })
console.log(data); console.log(data)
return data return data
} catch (err) { } catch (err) {
console.log(err); console.log(err)
return (`There was an error reading the file: <br> ${err} `) return `There was an error reading the file: <br> ${err} `
} }
}
async function getSerialPorts() {
return await SerialPort.list()
}
async function getApps() {
// let appCollection = await getInstalledApps()
let appCollection = await getapps()
let filters = [(a) => a.appid.includes('.exe'), (a) => a.appid.includes('steam')]
console.log(` Total apps ${appCollection.length}`)
return appCollection.filter((app) => filters.some((fn) => fn(app)))
}
async function getSteamApps() {
let steamGames = []
let steamLibraries = await steam.getLibraryFolders()
steamLibraries.forEach((libraryFolder) => {
let games = steam.getGames(libraryFolder)
games.forEach((game) => {
const dir = `${game.baseDirectory}\\steamapps\\common\\${game.installdir}\\`
const exeFiles = globSync(`${dir}/**/*.exe`)
exeFiles.forEach((file) => {
steamGames.push({
name: game.name,
app: file.split('\\').pop()
})
})
})
})
return steamGames
} }
function createWindow() { function createWindow() {
@@ -65,10 +101,12 @@ app.whenReady().then(() => {
// IPC test // IPC test
ipcMain.on('ping', () => console.log('pong')) ipcMain.on('ping', () => console.log('pong'))
ipcMain.handle("loadConfigurationYML", loadConfigurationYAML) ipcMain.handle('loadConfigurationYML', loadConfigurationYAML)
ipcMain.handle('getSerialPorts', getSerialPorts)
ipcMain.handle('getInstalledApps', getApps)
ipcMain.handle('getSteamApps', getSteamApps)
createWindow() createWindow()
app.on('activate', function () { app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the // On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open. // dock icon is clicked and there are no other windows open.

View File

@@ -2,12 +2,8 @@
<html> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Electron</title> <title>Deej Configurator</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<!-- <meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
/> -->
<link <link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet" rel="stylesheet"

View File

@@ -1,40 +1,146 @@
<script setup lang="ts"> <script setup lang="ts">
import Versions from './components/Versions.vue' import COMselect from './components/COMselect.vue'
import MovieCard from './components/MovieCard.vue' import ProgramSelector from './components/ProgramSelector.vue'
import { onMounted } from 'vue' import { getTransitionRawChildren, onMounted } from 'vue'
import { ref } from 'vue' import { ref } from 'vue'
import * as logTimestamp from 'log-timestamp'
import draggable from 'vuedraggable'
import ProgramGroup from './components/ProgramGroup.vue'
// import MovieCard from './components/MovieCard.vue'
const movies = ref([]) // const movies = ref([])
const apiKey = 'a0eb411ca9c81896004dce1d27a7245b' // const apiKey = 'a0eb411ca9c81896004dce1d27a7245b'
const configuration = ref('Loading....') const configuration = ref('Loading....')
const serialPorts = ref('Loading Serial ports.... ')
const installedApps = ref([])
const steamApps = ref([])
const steamPath = ref('')
//model variables for user provided input
const programCounter = ref([
'python.exe',
'nvm.exe',
'narrator.exe',
['TimeCamp.exe', 'charmap.exe'],
'nvm.exe'
])
const serialPortSelection = ref('select the COM port... ')
const ipcHandle = () => window.electron.ipcRenderer.send('ping') const ipcHandle = () => window.electron.ipcRenderer.send('ping')
const getConfigFileContents = async () => { // const getConfigFileContents = async () => {
const returnValue = await window.electron.ipcRenderer.invoke('loadConfigurationYML') // const returnValue = await window.electron.ipcRenderer.invoke('loadConfigurationYML')
configuration.value = returnValue // configuration.value = returnValue
// }
const getSerialPorts = async () => {
serialPorts.value = await window.electron.ipcRenderer.invoke('getSerialPorts')
console.log(`the returned serial ports are ${serialPorts.value}`)
} }
const getTrendingMovies = (category) => { // const getTrendingMovies = (category) => {
return fetch(`https://api.themoviedb.org/3/trending/movie/${category}?api_key=${apiKey}`) // return fetch(`https://api.themoviedb.org/3/trending/movie/${category}?api_key=${apiKey}`)
.then((response) => response.json()) // .then((response) => response.json())
.then((data) => { // .then((data) => {
movies.value = data.results // movies.value = data.results
}) // })
// }
async function getInstalledApps() {
installedApps.value = await window.electron.ipcRenderer.invoke('getInstalledApps')
// console.log(installedApps.value)
installedApps.value.forEach((app) => {
app.appid = app.appid.split('\\').pop()
})
getSteamApps()
} }
async function getSteamApps() {
steamApps.value = await window.electron.ipcRenderer.invoke('getSteamApps')
steamApps.value.forEach((game) => {
console.log(`the game is ${Object.entries(game)}`)
})
}
function deleteProgramRow(index) {
programCounter.value.splice(index, 1)
// Vue.delete(programCounter.value, index)
console.log(`Got event to delete row for index : ${index}`)
}
function deleteAppFromCollection(index, childIndex) {
console.log(`The Indexi is ${index} and the child index is ${childIndex}`)
programCounter.value[index].splice(childIndex, 1)
}
function addProgram() {
programCounter.value.push('')
}
function addAppToCollection(index, childIndex) {
programCounter.value[index].push('')
}
function updateCollectionApp(index, childIndex, appName) {
programCounter.value[index][childIndex] = appName
}
onMounted(() => { onMounted(() => {
getTrendingMovies('day') // getTrendingMovies('day')
getSerialPorts()
getInstalledApps()
}) })
</script> </script>
<template> <template>
<img alt="logo" class="logo" src="./assets/electron.svg" /> <img alt="logo" class="logo" src="./assets/electron.svg" />
<div class="creator">Powered by electron-vite</div>
<Versions /> <COMselect
<button type="button" @click="ipcHandle">Send Ping</button> v-model="serialPortSelection"
<button type="button" @click="getConfigFileContents">Load config</button> :serialports="serialPorts"
<div>{{ configuration }}</div> :installed-apps="installedApps"
/>
<!-- <div class="row mt-2">
<button type="button" class="col-auto" @click="ipcHandle">Send Ping</button>
<button type="button" class="col-auto" @click="getSerialPorts">Load config</button>
</div>
<div>{{ configuration }}</div> -->
<draggable v-model="programCounter">
<template #item="{ element, index }">
<div v-if="typeof element === 'string'">
<ProgramSelector
:installed-apps="installedApps"
:steam-apps="steamApps"
:selected-app="element"
@update-app="programCounter[index] = $event"
@delete-row="deleteProgramRow(index)"
/>
</div>
<div v-else class="border-top border-bottom py-2 mt-2">
<ProgramGroup
:installed-apps="installedApps"
:program-counter="element"
:steam-apps="steamApps"
@delete-row="deleteProgramRow(index)"
@update-app="(appName, childIndex) => updateCollectionApp(index, childIndex, appName)"
@add-app="(childIndex) => addAppToCollection(index, childIndex)"
@delete-app="(childIndex) => deleteAppFromCollection(index, childIndex)"
/>
</div>
</template>
</draggable>
<div class="row mt-2">
<button class="col-auto" @click="addProgram">Add</button>
<!-- <button class="col-auto" click="removeProgram">Remove</button>/ -->
</div>
<div class="row">
<div class="col-auto">COM Port</div>
<div class="col-auto">{{ serialPortSelection }}</div>
</div>
<div>{{ programCounter }}</div>
<div>{{ steamPath }}</div>
<!--
<div class="container"> <div class="container">
<div class="text-center"> <div class="text-center">
<h2 class="text-center mt-5">Trending Movies 🍿</h2> <h2 class="text-center mt-5">Trending Movies 🍿</h2>
@@ -48,8 +154,10 @@ onMounted(() => {
</div> </div>
<div v-if="movies.length > 0" class="row"> <div v-if="movies.length > 0" class="row">
<div v-for="(movie, i) in movies" :key="i" class="col-md-4"> <draggable v-model="movies">
<MovieCard :movie="movie" /> <template #item="{ element }">
</div> <MovieCard :movie="element" />
</div> </template>
</draggable>
</div> -->
</template> </template>

View File

@@ -0,0 +1,29 @@
<template>
<div class="row">
<div class="col-auto">
<label class="form-label">Select COM Port for your deej device</label>
</div>
<div class="col-auto">
<select v-model="com_port" class="form-select">
<option
v-for="(serialport, i) in props.serialports"
:key="i"
:value="serialport.path"
class="form-option"
>
{{ serialport.friendlyName }}
</option>
</select>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps(['serialports', 'installedApps'])
console.log (`Inside COMSelect the length of installedApps is ${props.installedApps.length}`)
const com_port = defineModel()
</script>

View File

@@ -0,0 +1,56 @@
<script setup>
import ProgramSelector from './ProgramSelector.vue'
import draggable from 'vuedraggable'
import { ref } from 'vue'
const props = defineProps({
installedApps: {
type: Array,
required: true
},
programCounter: {
type: Array,
required: false
},
steamApps: {
type: Array,
required: false
}
})
const emit = defineEmits(['update-app', 'delete-row', 'add-app', 'delete-app'])
// function deleteProgramRow(index) {
// programCounter.value.splice(index, 1)
// // Vue.delete(programCounter.value, index)
// console.log(`Got event to delete row for index : ${index}`)
// }
// function addProgramRow() {
// programCounter.value.push('')
// }
</script>
<template>
<div>{{ programCounter }}</div>
<div class="row">
<div class="col">Select programs</div>
<div class="col text-end">
<i class="fas fa-plus p-2" @click="$emit('add-app', index)"></i>
<i class="fas fa-trash-alt" @click="$emit('delete-row')"></i>
</div>
</div>
<draggable :list="programCounter">
<template #item="{ element, index }">
<div>
<ProgramSelector
:installed-apps="installedApps"
:selected-app="element"
:steam-apps="steamApps"
@delete-row="$emit('delete-app', index)"
@update-app="$emit('update-app', $event, index)"
/>
</div>
</template>
</draggable>
</template>

View File

@@ -0,0 +1,47 @@
<script setup>
defineEmits(['delete-row', 'update-app'])
const props = defineProps({ installedApps: Object, selectedApp: String, steamApps: Object })
console.log(props.installedApps)
</script>
<template>
<div class="row mt-2 d-flex align-items-center">
<div class="col-auto"><i class="fas fa-bars"></i></div>
<div class="col-auto">Program</div>
<div class="col-auto">
<select class="form-select" @change="$emit('update-app', $event.target.value)">
<optgroup label="Deej defaults">
<option value="master">Master</option>
<option value="deej.unmapped">deej.unmapped</option>
<option value="mic">Mic</option>
<option value="system">System</option>
</optgroup>
<optgroup label="Steam Games">
<option
v-for="(app, i) in props.steamApps"
:key="i"
class="col-auto mt-3"
:value="app.app"
:selected="app.app == selectedApp"
>
<b>{{ app.name }}:</b> {{ app.app }}
</option>
</optgroup>
<optgroup label="Installed apps">
<option
v-for="(app, i) in props.installedApps"
:key="i"
class="col-auto mt-3"
:value="app.appid"
:selected="app.appid == selectedApp"
>
{{ app.name }}
</option>
</optgroup>
</select>
</div>
<div class="col-auto"><i class="fas fa-trash-alt" @click="$emit('delete-row')"></i></div>
</div>
<!-- <div>{{ selectedApp }}</div> -->
</template>

View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
import { reactive } from 'vue' import { reactive, ref } from 'vue'
const versions = reactive({ ...window.electron.process.versions }) const versions = reactive({ ...window.electron.process.versions })
</script> </script>