11<template >
2- <dialog id =" update_modal" class =" modal" >
3- <div class =" modal-box" >
4- <h3 class =" text-lg font-bold" >Attention</h3 >
5- <p class =" py-4" >
6- Updating the POG files will overwrite all files on your keyboard generated by POG (e.g.
7- kb.py, code.py, customkeys.py, etc.)
8- </p >
9- <p class =" py-4" >Be sure to backup your code if you still need any of it.</p >
10- <div class =" flex justify-between" >
11- <div class =" btn" @click =" closeModal" >Abort</div >
12- <div class =" btn btn-error" @click =" updatePOG" >Update POG files</div >
13- </div >
14- </div >
15- <form method =" dialog" class =" modal-backdrop" >
16- <button >close</button >
17- </form >
18- </dialog >
2+ <BaseModal
3+ :open =" isUpdateOpen"
4+ title =" Attention"
5+ confirm-text =" Update POG files"
6+ cancel-text =" Abort"
7+ @close =" isUpdateOpen = false"
8+ @confirm =" updatePOG"
9+ >
10+ <p >
11+ Updating the POG files will overwrite all files on your keyboard generated by POG (e.g. kb.py,
12+ code.py, customkeys.py, etc.)
13+ </p >
14+ <p class =" pt-2" >Be sure to backup your code if you still need any of it.</p >
15+ </BaseModal >
16+
17+ <BaseModal
18+ :open =" isRestoreOpen"
19+ title =" Restore Configuration"
20+ cancel-text =" Cancel"
21+ secondary-text =" Keep ID"
22+ confirm-text =" Generate New ID"
23+ @close =" isRestoreOpen = false"
24+ @secondary =" restoreConfig(false)"
25+ @confirm =" restoreConfig(true)"
26+ >
27+ <p >
28+ Do you want to generate new ID for the restored configuration? This is only recommended if you
29+ are restoring a configuration from another keyboard.
30+ </p >
31+ </BaseModal >
32+
1933 <div class =" mt-4 p-4 text-left" >
2034 <p >
2135 <a href =" https://kmkfw.io/" target =" _blank" class =" link" >KMK</a > is a capable firmware for
5266 v-if =" ['', 'done'].includes(kmkInstallState)"
5367 class =" mt-8 flex flex-col items-center justify-center"
5468 >
55- <div class =" mt-8 grid grid-cols-2 justify-center gap-4" >
69+ <div
70+ class =" mt-8 grid justify-center gap-4"
71+ :class =" {
72+ 'grid-cols-1': !keyboardStore.firmwareInstalled,
73+ 'grid-cols-2': keyboardStore.firmwareInstalled
74+ }"
75+ >
5676 <button class =" btn btn-primary" @click =" updateKMK" >
5777 {{ keyboardStore.firmwareInstalled ? 'update' : 'install' }} KMK
5878 </button >
59- <button class =" btn btn-primary" @click =" openModal" >update Firmware</button >
79+ <button v-if =" !initialSetup" class =" btn btn-primary" @click =" isUpdateOpen = true" >
80+ update Firmware
81+ </button >
82+ <button v-if =" !initialSetup" class =" btn btn-primary" @click =" backupConfiguration" >
83+ Backup Config
84+ </button >
85+ <button
86+ v-if =" !initialSetup || keyboardStore.firmwareInstalled"
87+ class =" btn btn-primary"
88+ @click =" restoreConfiguration"
89+ >
90+ Restore Config
91+ </button >
6092 </div >
61- <button class =" btn btn-primary mt-8" @click =" updateKMK" >install KMK</button >
93+ <input
94+ ref =" fileInput"
95+ type =" file"
96+ accept =" .json"
97+ style =" display : none "
98+ @change =" handleFileUpload"
99+ />
62100 </div >
63101 <div v-if =" initialSetup" class =" mt-8 flex justify-center" >
64102 <button
68106 >
69107 Next
70108 </button >
71- <button class =" btn mt-4 block" @click =" $emit('next')" >I installed KMK manually</button >
72109 </div >
73110
74111 <div
84121</template >
85122<script setup lang="ts">
86123import { ref } from ' vue'
87- import { keyboardStore } from ' ../store'
124+ import BaseModal from ' ./BaseModal.vue'
125+ import { keyboardStore , addToHistory } from ' ../store'
88126import dayjs from ' dayjs'
127+ import { ulid } from ' ulid'
128+ import { saveConfigurationWithLoading } from ' ../helpers/saveConfigurationWrapper'
89129
90130const progress = ref (0 )
91131const kmkInstallState = ref (' ' )
132+ const isUpdateOpen = ref (false )
133+ const isRestoreOpen = ref (false )
134+ const fileInput = ref <HTMLInputElement >()
135+ const pendingConfigData = ref <any >(null )
92136
93- defineProps <{ initialSetup: boolean }>()
94- defineEmits ([' next' ])
137+ const props = defineProps <{ initialSetup: boolean }>()
138+ const emit = defineEmits ([' next' , ' done ' ])
95139const startTime = ref (dayjs ())
96140const endTime = ref (dayjs ())
97141
@@ -102,18 +146,78 @@ const updateKMK = async () => {
102146}
103147
104148const updatePOG = async () => {
105- window . api . saveConfiguration (
149+ await saveConfigurationWithLoading (
106150 JSON .stringify ({ pogConfig: keyboardStore .serialize (), writeFirmware: true })
107151 )
108- closeModal ()
152+ isUpdateOpen . value = false
109153}
110154
111- const openModal = () => {
112- ;(document .getElementById (' update_modal' ) as HTMLDialogElement ).showModal ()
155+ const backupConfiguration = async () => {
156+ try {
157+ const configData = keyboardStore .serialize ()
158+ const jsonString = JSON .stringify (configData , null , 2 )
159+ const blob = new Blob ([jsonString ], { type: ' application/json' })
160+ const url = URL .createObjectURL (blob )
161+ const link = document .createElement (' a' )
162+ link .href = url
163+ link .download = ' pog_backup.json'
164+ document .body .appendChild (link )
165+ link .click ()
166+ document .body .removeChild (link )
167+ URL .revokeObjectURL (url )
168+ } catch (error ) {
169+ console .error (' Error downloading configuration:' , error )
170+ }
113171}
114172
115- const closeModal = () => {
116- ;(document .getElementById (' update_modal' ) as HTMLDialogElement ).close ()
173+ const restoreConfiguration = () => {
174+ fileInput .value ?.click ()
175+ }
176+
177+ const handleFileUpload = async (event : Event ) => {
178+ const target = event .target as HTMLInputElement
179+ const file = target .files ?.[0 ]
180+ if (! file ) return
181+ try {
182+ const configData = JSON .parse (await file .text ())
183+ pendingConfigData .value = configData
184+ isRestoreOpen .value = true
185+ } catch (error ) {
186+ console .error (' Error reading or parsing configuration file:' , error )
187+ }
188+ target .value = ' '
189+ }
190+
191+ const restoreConfig = async (generateNewIds : boolean ) => {
192+ if (! pendingConfigData .value ) return
193+
194+ const configData = generateNewIds ? { ... pendingConfigData .value } : pendingConfigData .value
195+ if (generateNewIds ) {
196+ configData .id = ulid ()
197+ }
198+
199+ try {
200+ isRestoreOpen .value = false
201+
202+ await saveConfigurationWithLoading (
203+ JSON .stringify ({ pogConfig: configData , serial: false , writeFirmware: true })
204+ )
205+ if (keyboardStore .path ) {
206+ const keyboardData = await window .api .selectKeyboard ({ path: keyboardStore .path })
207+ if (keyboardData && ! keyboardData .error ) {
208+ keyboardStore .import (keyboardData )
209+ }
210+ }
211+ if (keyboardStore .keymap .length === 0 ) keyboardStore .keymap = [[]]
212+ keyboardStore .coordMapSetup = false
213+
214+ if (props .initialSetup ) {
215+ addToHistory (keyboardStore )
216+ }
217+ emit (' done' )
218+ } catch (e ) {
219+ console .error (' restore failed' , e )
220+ }
117221}
118222
119223window .api .onUpdateFirmwareInstallProgress (
0 commit comments