UPD: Working key LED config!

This commit is contained in:
Robert Kossessa
2024-03-13 00:52:42 +01:00
parent 97088ded94
commit 7aa8b8a40b
8 changed files with 158 additions and 82 deletions

View File

@@ -26,6 +26,7 @@
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@radix-icons/vue": "^1.0.0",
"@serialport/bindings-cpp": "^12.0.1",
"@types/color": "^3.0.6",
"@vueuse/core": "^10.9.0",
"ajv": "^8.12.0",
"class-variance-authority": "^0.7.0",

19
pnpm-lock.yaml generated
View File

@@ -20,6 +20,9 @@ dependencies:
'@serialport/bindings-cpp':
specifier: ^12.0.1
version: 12.0.1
'@types/color':
specifier: ^3.0.6
version: 3.0.6
'@vueuse/core':
specifier: ^10.9.0
version: 10.9.0(vue@3.4.21)
@@ -1204,6 +1207,22 @@ packages:
'@types/node': 18.19.21
'@types/responselike': 1.0.3
/@types/color-convert@2.0.3:
resolution: {integrity: sha512-2Q6wzrNiuEvYxVQqhh7sXM2mhIhvZR/Paq4FdsQkOMgWsCIkKvSGj8Le1/XalulrmgOzPMqNa0ix+ePY4hTrfg==}
dependencies:
'@types/color-name': 1.1.3
dev: false
/@types/color-name@1.1.3:
resolution: {integrity: sha512-87W6MJCKZYDhLAx/J1ikW8niMvmGRyY+rpUxWpL1cO7F8Uu5CHuQoFv+R0/L5pgNdW4jTyda42kv60uwVIPjLw==}
dev: false
/@types/color@3.0.6:
resolution: {integrity: sha512-NMiNcZFRUAiUUCCf7zkAelY8eV3aKqfbzyFQlXpPIEeoNDbsEHGpb854V3gzTsGKYj830I5zPuOwU/TP5/cW6A==}
dependencies:
'@types/color-convert': 2.0.3
dev: false
/@types/debug@4.1.12:
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
dependencies:

View File

@@ -187,13 +187,19 @@ import { SliderRoot, SliderThumb, SliderTrack } from 'radix-vue'
import { MoreHorizontal } from 'lucide-vue-next'
import { Separator } from '@renderer/components/ui/separator'
defineProps({
const props = defineProps({
color: {
type: Color,
default: () => Color.rgb(255, 0, 0)
},
roundedTop: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['input'])
const hueSliderValue = ref(0)
const saturationSliderValue = ref(100)
const valueSliderValue = ref(50)
@@ -204,7 +210,7 @@ const hueSliderModel = computed({
},
set(hue) {
hueSliderValue.value = hue[0]
color.value = color.value.hue(hue[0])
emit('input', props.color.hue(hue[0]))
}
})
@@ -214,7 +220,7 @@ const saturationSliderModel = computed({
},
set(saturation) {
saturationSliderValue.value = saturation[0]
color.value = color.value.saturationv(saturation[0])
emit('input', props.color.saturationv(saturation[0]))
}
})
@@ -224,15 +230,10 @@ const valueSliderModel = computed({
},
set(value) {
valueSliderValue.value = value[0]
color.value = color.value.value(value[0])
emit('input', props.color.value(value[0]))
}
})
const color = defineModel({
type: Color,
default: () => Color.rgb(255, 0, 0)
})
const saturationSliderColor = computed(() => {
return Color.hsv(hueSliderModel.value[0], 100, valueSliderModel.value[0])
})
@@ -255,7 +256,7 @@ function onSubmitHexInput() {
input = '#' + input
}
if (input.match(/^#[0-9A-F]{6}$/i)) {
color.value = Color(input)
emit('input', Color(input))
} else shake()
}
@@ -266,10 +267,10 @@ function onSubmitHueInput() {
return
}
const newHue = Math.max(0, Math.min(input, 360))
if (newHue === color.value.hue()) {
if (newHue === props.color.hue()) {
updateInputs()
}
color.value = color.value.hue(newHue)
emit('input', props.color.hue(newHue))
}
function onSubmitSaturationInput() {
@@ -279,10 +280,10 @@ function onSubmitSaturationInput() {
return
}
const newSaturation = Math.max(0, Math.min(input, 100))
if (newSaturation === color.value.saturationv()) {
if (newSaturation === props.color.saturationv()) {
updateInputs()
}
color.value = color.value.saturationv(newSaturation)
emit('input', props.color.saturationv(newSaturation))
}
function onSubmitValueInput() {
@@ -292,10 +293,10 @@ function onSubmitValueInput() {
return
}
const newValue = Math.max(0, Math.min(input, 100))
if (newValue === color.value.value()) {
if (newValue === props.color.value()) {
updateInputs()
}
color.value = color.value.value(newValue)
emit('input', props.color.value(newValue))
}
function onSubmitRGBInput() {
@@ -307,26 +308,27 @@ function onSubmitRGBInput() {
return
}
const newColor = Color.rgb(r, g, b)
if (newColor.hex() === color.value.hex()) {
if (newColor.hex() === props.color.hex()) {
updateInputs()
}
color.value = newColor
emit('input', newColor)
}
function updateInputs() {
hexInput.value = color.value.hex().substring(1, 7)
hueInput.value = String(parseInt(color.value.hue())).padStart(3, '0')
saturationInput.value = String(parseInt(color.value.saturationv())).padStart(3, '0')
valueInput.value = String(parseInt(color.value.value())).padStart(3, '0')
rInput.value = String(parseInt(color.value.red())).padStart(3, '0')
gInput.value = String(parseInt(color.value.green())).padStart(3, '0')
bInput.value = String(parseInt(color.value.blue())).padStart(3, '0')
hueSliderValue.value = color.value.hue()
saturationSliderValue.value = color.value.saturationv()
valueSliderValue.value = color.value.value()
console.log('COLORRR', props.color)
hexInput.value = props.color.hex().substring(1, 7)
hueInput.value = String(parseInt(props.color.hue())).padStart(3, '0')
saturationInput.value = String(parseInt(props.color.saturationv())).padStart(3, '0')
valueInput.value = String(parseInt(props.color.value())).padStart(3, '0')
rInput.value = String(parseInt(props.color.red())).padStart(3, '0')
gInput.value = String(parseInt(props.color.green())).padStart(3, '0')
bInput.value = String(parseInt(props.color.blue())).padStart(3, '0')
hueSliderValue.value = props.color.hue()
saturationSliderValue.value = props.color.saturationv()
valueSliderValue.value = props.color.value()
}
watch(color, updateInputs)
watch(props.color, updateInputs)
onBeforeMount(updateInputs)
const colorFieldText = ref(null)

View File

@@ -32,40 +32,45 @@
@click="currentOption = key"
/>
</div>
<HSVInput v-model="options[currentOption].color" />
<HSVInput
:color="options[currentOption].color"
@input="(color) => $emit('input', currentOption, color)"
/>
</div>
</template>
<script setup>
import HSVInput from '@renderer/components/common/HSVInput.vue'
import Color from 'color'
import { computed, onBeforeMount, reactive, ref } from 'vue'
import { computed, onBeforeMount, ref } from 'vue'
defineEmits(['input'])
const currentOption = ref(null)
const currentColorHex = computed(() => options[currentOption.value].color.hex())
const currentColorHex = computed(() => props.options[currentOption.value].color.hex())
const model = defineModel({
type: Object,
default: () => ({
one: {
titleKey: 'One',
color: Color('#ff0000')
},
two: {
titleKey: 'Two',
color: Color('#00ff00')
},
three: {
titleKey: 'Three',
color: Color('#0000ff')
}
})
const props = defineProps({
options: {
type: Object,
default: () => ({
one: {
titleKey: 'One',
color: Color('#ff0000')
},
two: {
titleKey: 'Two',
color: Color('#00ff00')
},
three: {
titleKey: 'Three',
color: Color('#0000ff')
}
})
}
})
const options = reactive(model.value)
onBeforeMount(() => {
if (currentOption.value === null) currentOption.value = Object.keys(options)[0]
if (currentOption.value === null) currentOption.value = Object.keys(props.options)[0]
})
</script>
<style scoped>

View File

@@ -1,6 +1,19 @@
<template>
<ConfigSection title="Key Colors" :icon-component="Palette">
<PaletteInput v-model="keyColors" />
<PaletteInput
:options="keyColors"
@input="
(optionKey, color) => {
keyColors = {
...keyColors,
[optionKey]: {
...keyColors[optionKey],
color
}
}
}
"
/>
</ConfigSection>
</template>
<script setup>
@@ -8,28 +21,31 @@ import { Palette } from 'lucide-vue-next'
import ConfigSection from '@renderer/components/common/ConfigSection.vue'
import PaletteInput from '@renderer/components/common/PaletteInput.vue'
import Color from 'color'
import { ref, watch } from 'vue'
import { useDeviceStore } from '@renderer/deviceStore'
import { useAppStore } from '@renderer/appStore'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
const appStore = useAppStore()
const deviceStore = useDeviceStore()
const { keyColor } = storeToRefs(deviceStore)
const keyColors = ref({
default: {
titleKey: 'default',
color: Color('#4f25ef')
const keyColors = computed({
get() {
return {
default: {
titleKey: 'default',
color: Color(keyColor.value(appStore.selectedKey, false))
},
pressed: {
titleKey: 'pressed',
color: Color(keyColor.value(appStore.selectedKey, true))
}
}
},
pressed: {
titleKey: 'pressed',
color: Color('#d0078f')
set(newValue) {
deviceStore.setKeyColor(appStore.selectedKey, false, Color(newValue.default.color).rgbNumber())
deviceStore.setKeyColor(appStore.selectedKey, true, Color(newValue.pressed.color).rgbNumber())
}
})
watch(
keyColors,
(newVal) => {
// store.setKeyDefaultColor(newVal.default.color)
// store.setKeyPressedColor(newVal.pressed.color)
},
{ deep: true }
)
</script>

View File

@@ -13,7 +13,7 @@
:class="{ 'bg-zinc-300': selected }"
@submit.prevent="
() => {
$emit('rename', { profile: profile.name, name: nameInput })
$emit('rename', profile.name, nameInput)
editing = false
}
"
@@ -90,7 +90,7 @@
'rounded-l-lg': !nameEditable
}"
class="flex w-0 shrink-0 items-center justify-center rounded-lg transition-all"
@click="$emit('duplicate')"
@click="$emit('duplicate', profile.name, profile)"
>
<Copy class="size-4" />
</button>
@@ -114,7 +114,7 @@
'group-focus-within:w-12 group-hover:w-12': !editing
}"
class="flex w-0 shrink-0 items-center justify-center rounded-lg transition-all"
@click="$emit('delete', profile.id)"
@click="$emit('delete', profile.name)"
>
<Check class="size-4" />
</button>

View File

@@ -19,7 +19,7 @@
scramble-on-mount
:fill-interval="20"
:delay="500"
:text="`(${deviceStore.profileNames.length}/${maxProfiles})`"
:text="`(${deviceStore.profiles.length}/${maxProfiles})`"
/>
</button>
<DropdownMenu>
@@ -44,7 +44,7 @@
</div>
<div class="relative grow overflow-y-auto">
<div v-if="renderProfileList" class="absolute w-full">
<div v-if="deviceStore.profileNames.length === 0">
<div v-if="deviceStore.profiles.length === 0">
<div class="flex h-32 flex-col items-center justify-center">
<ScrambleText
scramble-on-mount
@@ -114,14 +114,18 @@
}
"
@rename="
(event) => {
deviceStore.renameProfile(event.profile, event.name)
if (deviceStore.currentProfileName === event.profile) {
deviceStore.selectProfile(event.name)
(oldName, newName) => {
deviceStore.renameProfile(oldName, newName)
if (deviceStore.currentProfileName === oldName) {
deviceStore.selectProfile(newName)
}
}
"
@duplicate="console.log('Duplicate profile not implemented!')"
@duplicate="
(originalName, profile) => {
deviceStore.duplicateProfile(originalName, profile)
}
"
@delete="console.log('Delete profile not implemented!')"
/>
</div>

View File

@@ -67,7 +67,7 @@ export const useDeviceStore = defineStore('device', {
state.currentProfileName
? state.profiles.find((profile) => profile.name === state.currentProfileName)
: null,
profileTags: (state) => state.profiles.map((profile) => profile.profileTag),
profileTags: (state) => [...new Set(state.profiles.map((profile) => profile.profileTag))],
profilesByTag: (state) =>
state.profiles.reduce((acc, profile) => {
if (!acc[profile.profileTag]) {
@@ -75,7 +75,11 @@ export const useDeviceStore = defineStore('device', {
}
acc[profile.profileTag].push(profile)
return acc
}, {})
}, {}),
keyColor: (state) => (key: string, pressed: boolean) => {
const propertyName = `button${key.toUpperCase()}${pressed ? 'Press' : 'Idle'}`
return state.currentProfile ? state.currentProfile[propertyName] : 0
}
},
actions: {
setAttachedDeviceIds(deviceIds: string[]) {
@@ -103,9 +107,13 @@ export const useDeviceStore = defineStore('device', {
this.profiles.push(profile)
}
if (updateDevice) {
const newProfile = JSON.parse(JSON.stringify(profile))
delete newProfile.name
console.log('Sending new profile:', newProfile)
console.log('with name', profile.name)
nanoIpc.send(
this.currentDeviceId!,
JSON.stringify({ profile: profile.name, updates: profile })
JSON.stringify({ profile: profile.name, updates: newProfile })
)
}
},
@@ -124,6 +132,17 @@ export const useDeviceStore = defineStore('device', {
}
}
},
duplicateProfile(profileName: string, updateDevice: boolean = true) {
const profile = this.profiles.find((p) => p.name === profileName)
if (profile) {
const newProfile = JSON.parse(JSON.stringify(profile))
newProfile.name = profileName + ' Copy'
this.addProfile(newProfile, updateDevice)
if (this.currentProfileName === profileName) {
this.selectProfile(newProfile.name, updateDevice)
}
}
},
detachDevice(deviceId: string) {
const index = this.attachedDeviceIds.indexOf(deviceId)
if (index !== -1) {
@@ -170,6 +189,16 @@ export const useDeviceStore = defineStore('device', {
},
setAngle(angle: number) {
this.angle = angle
},
setKeyColor(key: string, pressed: boolean, color: number, updateDevice: boolean = true) {
const propertyName = `button${key.toUpperCase()}${pressed ? 'Press' : 'Idle'}`
this.currentProfile![propertyName] = color
if (updateDevice) {
nanoIpc.send(
this.currentDeviceId!,
JSON.stringify({ profile: this.currentProfileName, updates: { [propertyName]: color } })
)
}
}
}
})