mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-22 09:52:50 +00:00
feat: new blocking progress bar, fixes #18
feat: change cloud icon to history, fixes #15 fix: action search items overlap, fixes #16 feat: show tooltips immediately
This commit is contained in:
@@ -72,6 +72,8 @@ const config: IconsConfig = {
|
|||||||
"navigate_next",
|
"navigate_next",
|
||||||
"print",
|
"print",
|
||||||
"restore_from_trash",
|
"restore_from_trash",
|
||||||
|
"history",
|
||||||
|
"history_toggle_off",
|
||||||
],
|
],
|
||||||
codePoints: {
|
codePoints: {
|
||||||
speed: "e9e4",
|
speed: "e9e4",
|
||||||
|
|||||||
@@ -44,9 +44,6 @@
|
|||||||
"@sveltejs/vite-plugin-svelte": "^2.4.5",
|
"@sveltejs/vite-plugin-svelte": "^2.4.5",
|
||||||
"@tauri-apps/api": "^1.4.0",
|
"@tauri-apps/api": "^1.4.0",
|
||||||
"@tauri-apps/cli": "^1.4.0",
|
"@tauri-apps/cli": "^1.4.0",
|
||||||
"@tiptap/core": "^2.1.12",
|
|
||||||
"@tiptap/pm": "^2.1.12",
|
|
||||||
"@tiptap/starter-kit": "^2.1.12",
|
|
||||||
"@theaninova/prettier-config": "^1.0.0",
|
"@theaninova/prettier-config": "^1.0.0",
|
||||||
"@types/dom-view-transitions": "^1.0.1",
|
"@types/dom-view-transitions": "^1.0.1",
|
||||||
"@types/flexsearch": "^0.7.3",
|
"@types/flexsearch": "^0.7.3",
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ const de = {
|
|||||||
APPLY: "Anwenden",
|
APPLY: "Anwenden",
|
||||||
SAVE: "Änderungen auf das Gerät schreiben",
|
SAVE: "Änderungen auf das Gerät schreiben",
|
||||||
},
|
},
|
||||||
|
sync: {
|
||||||
|
TITLE_READ: "Neueste Änderungen werden abgerufen",
|
||||||
|
TITLE_WRITE: "Änderungen werden gebrannt",
|
||||||
|
DISCLAIMER_WRITE:
|
||||||
|
"Das Brennen von Änderungen ist nur für Layouts und Einstellungen erforderlich wenn diese Neustarts überdauern sollen. Bei Akkorden passiert das brennen automatisch beim anwenden.",
|
||||||
|
},
|
||||||
backup: {
|
backup: {
|
||||||
TITLE: "Sicherungskopie",
|
TITLE: "Sicherungskopie",
|
||||||
DISCLAIMER:
|
DISCLAIMER:
|
||||||
@@ -28,9 +34,13 @@ const de = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
share: {
|
share: {
|
||||||
|
TITLE: "Teilen",
|
||||||
URL_COPIED: "Teilbare URL kopiert!",
|
URL_COPIED: "Teilbare URL kopiert!",
|
||||||
EXTRA_DOWNLOAD: "Als Datei herunterladen",
|
EXTRA_DOWNLOAD: "Als Datei herunterladen",
|
||||||
},
|
},
|
||||||
|
print: {
|
||||||
|
TITLE: "Drucken",
|
||||||
|
},
|
||||||
profile: {
|
profile: {
|
||||||
TITLE: "Profil",
|
TITLE: "Profil",
|
||||||
LANGUAGE: "Sprache",
|
LANGUAGE: "Sprache",
|
||||||
@@ -67,12 +77,21 @@ const de = {
|
|||||||
DOWNLOAD_APP: "Desktop-app herunterladen",
|
DOWNLOAD_APP: "Desktop-app herunterladen",
|
||||||
},
|
},
|
||||||
changes: {
|
changes: {
|
||||||
TITLE: "Änderungen anwenden",
|
TITLE: "Änderungen importieren",
|
||||||
CHORD_ADD: "{0} Akkord{{|e}} hinzugefügt",
|
ALL_CHANGES: "Alle Änderungen",
|
||||||
CHORD_EDIT: "{0} Akkord{{|e}} bearbeitet",
|
layout: {
|
||||||
CHORD_DELETE: "{0} Akkord{{|e}} entfernt",
|
TITLE: "{0} veränderte Belegung{{:|en}}",
|
||||||
SETTING_CHANGE: "{0} Einstellung{{|en}} geändert",
|
LAYER: "{changes} Belegung{{changes:|en}} in Ebene {layer} ändern",
|
||||||
LAYOUT_CHANGE: "{0} Layout-belegung{{|en}} geändert",
|
},
|
||||||
|
settings: {
|
||||||
|
TITLE: "{0} Einstellung{{|en}} anpassen",
|
||||||
|
},
|
||||||
|
chords: {
|
||||||
|
TITLE: "{0} Akkorde",
|
||||||
|
NEW_CHORDS: "{0} neue Akkord{{|e}} hinzufügen",
|
||||||
|
CHANGED_CHORDS: "{0} Akkord{{|e}} ersetzen",
|
||||||
|
DELETED_CHORDS: "{0} Akkord{{|e}} zum löschen markieren",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
configure: {
|
configure: {
|
||||||
chords: {
|
chords: {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const en = {
|
|||||||
UNDO: "Undo (hold <kbd class='icon'>shift</kbd> to undo all changes)",
|
UNDO: "Undo (hold <kbd class='icon'>shift</kbd> to undo all changes)",
|
||||||
REDO: "Redo",
|
REDO: "Redo",
|
||||||
APPLY: "Apply",
|
APPLY: "Apply",
|
||||||
SAVE: "Write changes to your device",
|
SAVE: "Burn changes to your device",
|
||||||
},
|
},
|
||||||
backup: {
|
backup: {
|
||||||
TITLE: "Local Backup",
|
TITLE: "Local Backup",
|
||||||
@@ -15,6 +15,12 @@ const en = {
|
|||||||
DOWNLOAD: "Download Backup",
|
DOWNLOAD: "Download Backup",
|
||||||
RESTORE: "Restore",
|
RESTORE: "Restore",
|
||||||
},
|
},
|
||||||
|
sync: {
|
||||||
|
TITLE_READ: "Reading latest changes",
|
||||||
|
TITLE_WRITE: "Burning changes to device",
|
||||||
|
DISCLAIMER_WRITE:
|
||||||
|
"Burning is only necessary if you want your layout or settings to persist across reboots. Chords always persist automatically on apply.",
|
||||||
|
},
|
||||||
modal: {
|
modal: {
|
||||||
CLOSE: "Close",
|
CLOSE: "Close",
|
||||||
},
|
},
|
||||||
@@ -27,9 +33,13 @@ const en = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
share: {
|
share: {
|
||||||
|
TITLE: "Share",
|
||||||
URL_COPIED: "Sharable URL copied!",
|
URL_COPIED: "Sharable URL copied!",
|
||||||
EXTRA_DOWNLOAD: "Download as file",
|
EXTRA_DOWNLOAD: "Download as file",
|
||||||
},
|
},
|
||||||
|
print: {
|
||||||
|
TITLE: "Print",
|
||||||
|
},
|
||||||
profile: {
|
profile: {
|
||||||
TITLE: "Profile",
|
TITLE: "Profile",
|
||||||
LANGUAGE: "Language",
|
LANGUAGE: "Language",
|
||||||
@@ -65,12 +75,21 @@ const en = {
|
|||||||
DOWNLOAD_APP: "Download the desktop app",
|
DOWNLOAD_APP: "Download the desktop app",
|
||||||
},
|
},
|
||||||
changes: {
|
changes: {
|
||||||
TITLE: "Apply changes",
|
TITLE: "Import changes",
|
||||||
CHORD_ADD: "{0} chord{{|s}} added",
|
ALL_CHANGES: "All changes",
|
||||||
CHORD_EDIT: "{0} chord{{|s}} edited",
|
layout: {
|
||||||
CHORD_DELETE: "{0} chord{{|s}} deleted",
|
TITLE: "{0} layout change{{|s}}",
|
||||||
SETTING_CHANGE: "{0} setting{{|s}} changed",
|
LAYER: "Update {changes} key{{changes:|s}} in layer {layer}",
|
||||||
LAYOUT_CHANGE: "{0} layout key{{|s}} changed",
|
},
|
||||||
|
settings: {
|
||||||
|
TITLE: "Update {0} setting{{|s}}",
|
||||||
|
},
|
||||||
|
chords: {
|
||||||
|
TITLE: "{0} chords",
|
||||||
|
NEW_CHORDS: "Add {0} new chord{{|s}}",
|
||||||
|
CHANGED_CHORDS: "Replace {0} chord{{|s}}",
|
||||||
|
DELETED_CHORDS: "Mark {0} chord{{|s}} for deletion",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
configure: {
|
configure: {
|
||||||
chords: {
|
chords: {
|
||||||
|
|||||||
@@ -15,311 +15,408 @@ actions:
|
|||||||
title: Keyboard Error Undefined
|
title: Keyboard Error Undefined
|
||||||
260:
|
260:
|
||||||
id: "KEY_A"
|
id: "KEY_A"
|
||||||
|
keyCode: "KeyA"
|
||||||
title: Keyboard a and A (US English)
|
title: Keyboard a and A (US English)
|
||||||
description: Non US English keyboard users may prefer these Raw Scancodes
|
description: Non US English keyboard users may prefer these Raw Scancodes
|
||||||
261:
|
261:
|
||||||
id: "KEY_B"
|
id: "KEY_B"
|
||||||
|
keyCode: "KeyB"
|
||||||
title: Keyboard b and B (US English)
|
title: Keyboard b and B (US English)
|
||||||
262:
|
262:
|
||||||
id: "KEY_C"
|
id: "KEY_C"
|
||||||
|
keyCode: "KeyC"
|
||||||
title: Keyboard c and C (US English)
|
title: Keyboard c and C (US English)
|
||||||
263:
|
263:
|
||||||
id: "KEY_D"
|
id: "KEY_D"
|
||||||
|
keyCode: "KeyD"
|
||||||
title: Keyboard d and D (US English)
|
title: Keyboard d and D (US English)
|
||||||
264:
|
264:
|
||||||
id: "KEY_E"
|
id: "KEY_E"
|
||||||
|
keyCode: "KeyE"
|
||||||
title: Keyboard e and E (US English)
|
title: Keyboard e and E (US English)
|
||||||
265:
|
265:
|
||||||
id: "KEY_F"
|
id: "KEY_F"
|
||||||
|
keyCode: "KeyF"
|
||||||
title: Keyboard f and F (US English)
|
title: Keyboard f and F (US English)
|
||||||
266:
|
266:
|
||||||
id: "KEY_G"
|
id: "KEY_G"
|
||||||
|
keyCode: "KeyG"
|
||||||
title: Keyboard g and G (US English)
|
title: Keyboard g and G (US English)
|
||||||
267:
|
267:
|
||||||
id: "KEY_H"
|
id: "KEY_H"
|
||||||
|
keyCode: "KeyH"
|
||||||
title: Keyboard h and H (US English)
|
title: Keyboard h and H (US English)
|
||||||
268:
|
268:
|
||||||
id: "KEY_I"
|
id: "KEY_I"
|
||||||
|
keyCode: "KeyI"
|
||||||
title: Keyboard i and I (US English)
|
title: Keyboard i and I (US English)
|
||||||
269:
|
269:
|
||||||
id: "KEY_J"
|
id: "KEY_J"
|
||||||
|
keyCode: "KeyJ"
|
||||||
title: Keyboard j and J (US English)
|
title: Keyboard j and J (US English)
|
||||||
270:
|
270:
|
||||||
id: "KEY_K"
|
id: "KEY_K"
|
||||||
|
keyCode: "KeyK"
|
||||||
title: Keyboard k and K (US English)
|
title: Keyboard k and K (US English)
|
||||||
271:
|
271:
|
||||||
id: "KEY_L"
|
id: "KEY_L"
|
||||||
|
keyCode: "KeyL"
|
||||||
title: Keyboard l and L (US English)
|
title: Keyboard l and L (US English)
|
||||||
272:
|
272:
|
||||||
id: "KEY_M"
|
id: "KEY_M"
|
||||||
|
keyCode: "KeyM"
|
||||||
title: Keyboard m and M (US English)
|
title: Keyboard m and M (US English)
|
||||||
273:
|
273:
|
||||||
id: "KEY_N"
|
id: "KEY_N"
|
||||||
|
keyCode: "KeyN"
|
||||||
title: Keyboard n and N (US English)
|
title: Keyboard n and N (US English)
|
||||||
274:
|
274:
|
||||||
id: "KEY_O"
|
id: "KEY_O"
|
||||||
|
keyCode: "KeyO"
|
||||||
title: Keyboard o and O (US English)
|
title: Keyboard o and O (US English)
|
||||||
275:
|
275:
|
||||||
id: "KEY_P"
|
id: "KEY_P"
|
||||||
|
keyCode: "KeyP"
|
||||||
title: Keyboard p and P (US English)
|
title: Keyboard p and P (US English)
|
||||||
276:
|
276:
|
||||||
id: "KEY_Q"
|
id: "KEY_Q"
|
||||||
|
keyCode: "KeyQ"
|
||||||
title: Keyboard q and Q (US English)
|
title: Keyboard q and Q (US English)
|
||||||
277:
|
277:
|
||||||
id: "KEY_R"
|
id: "KEY_R"
|
||||||
|
keyCode: "KeyR"
|
||||||
title: Keyboard r and R (US English)
|
title: Keyboard r and R (US English)
|
||||||
278:
|
278:
|
||||||
id: "KEY_S"
|
id: "KEY_S"
|
||||||
|
keyCode: "KeyS"
|
||||||
title: Keyboard s and S (US English)
|
title: Keyboard s and S (US English)
|
||||||
279:
|
279:
|
||||||
id: "KEY_T"
|
id: "KEY_T"
|
||||||
|
keyCode: "KeyT"
|
||||||
title: Keyboard t and T (US English)
|
title: Keyboard t and T (US English)
|
||||||
280:
|
280:
|
||||||
id: "KEY_U"
|
id: "KEY_U"
|
||||||
|
keyCode: "KeyU"
|
||||||
title: Keyboard u and U (US English)
|
title: Keyboard u and U (US English)
|
||||||
281:
|
281:
|
||||||
id: "KEY_V"
|
id: "KEY_V"
|
||||||
|
keyCode: "KeyV"
|
||||||
title: Keyboard v and V (US English)
|
title: Keyboard v and V (US English)
|
||||||
282:
|
282:
|
||||||
id: "KEY_W"
|
id: "KEY_W"
|
||||||
|
keyCode: "KeyW"
|
||||||
title: Keyboard w and W (US English)
|
title: Keyboard w and W (US English)
|
||||||
283:
|
283:
|
||||||
id: "KEY_X"
|
id: "KEY_X"
|
||||||
|
keyCode: "KeyX"
|
||||||
title: Keyboard x and X (US English)
|
title: Keyboard x and X (US English)
|
||||||
284:
|
284:
|
||||||
id: "KEY_Y"
|
id: "KEY_Y"
|
||||||
|
keyCode: "KeyY"
|
||||||
title: Keyboard y and Y (US English)
|
title: Keyboard y and Y (US English)
|
||||||
285:
|
285:
|
||||||
id: "KEY_Z"
|
id: "KEY_Z"
|
||||||
|
keyCode: "KeyZ"
|
||||||
title: Keyboard z and Z (US English)
|
title: Keyboard z and Z (US English)
|
||||||
286:
|
286:
|
||||||
id: "KEY_1"
|
id: "KEY_1"
|
||||||
|
keyCode: "Digit1"
|
||||||
title: Keyboard 1 and ! (US English)
|
title: Keyboard 1 and ! (US English)
|
||||||
287:
|
287:
|
||||||
id: "KEY_2"
|
id: "KEY_2"
|
||||||
|
keyCode: "Digit2"
|
||||||
title: Keyboard 2 and @ (US English)
|
title: Keyboard 2 and @ (US English)
|
||||||
288:
|
288:
|
||||||
id: "KEY_3"
|
id: "KEY_3"
|
||||||
|
keyCode: "Digit3"
|
||||||
title: Keyboard 3 and # (US English)
|
title: Keyboard 3 and # (US English)
|
||||||
289:
|
289:
|
||||||
id: "KEY_4"
|
id: "KEY_4"
|
||||||
|
keyCode: "Digit4"
|
||||||
title: Keyboard 4 and $ (US English)
|
title: Keyboard 4 and $ (US English)
|
||||||
290:
|
290:
|
||||||
id: "KEY_5"
|
id: "KEY_5"
|
||||||
|
keyCode: "Digit5"
|
||||||
title: Keyboard 5 and % (US English)
|
title: Keyboard 5 and % (US English)
|
||||||
291:
|
291:
|
||||||
id: "KEY_6"
|
id: "KEY_6"
|
||||||
|
keyCode: "Digit6"
|
||||||
title: Keyboard 6 and ^ (US English)
|
title: Keyboard 6 and ^ (US English)
|
||||||
292:
|
292:
|
||||||
id: "KEY_7"
|
id: "KEY_7"
|
||||||
|
keyCode: "Digit7"
|
||||||
title: Keyboard 7 and & (US English)
|
title: Keyboard 7 and & (US English)
|
||||||
293:
|
293:
|
||||||
id: "KEY_8"
|
id: "KEY_8"
|
||||||
|
keyCode: "Digit8"
|
||||||
title: Keyboard 8 and * (US English)
|
title: Keyboard 8 and * (US English)
|
||||||
294:
|
294:
|
||||||
id: "KEY_9"
|
id: "KEY_9"
|
||||||
|
keyCode: "Digit9"
|
||||||
title: Keyboard 9 and ( (US English)
|
title: Keyboard 9 and ( (US English)
|
||||||
295:
|
295:
|
||||||
id: "KEY_0"
|
id: "KEY_0"
|
||||||
|
keyCode: "Digit0"
|
||||||
title: Keyboard 0 and ) (US English)
|
title: Keyboard 0 and ) (US English)
|
||||||
296:
|
296:
|
||||||
id: "ENTER"
|
id: "ENTER"
|
||||||
|
keyCode: "Enter"
|
||||||
title: Keyboard Return (US English)
|
title: Keyboard Return (US English)
|
||||||
icon: keyboard_return
|
icon: keyboard_return
|
||||||
297:
|
297:
|
||||||
id: "ESC"
|
id: "ESC"
|
||||||
|
keyCode: "Escape"
|
||||||
title: Keyboard Escape (US English)
|
title: Keyboard Escape (US English)
|
||||||
298:
|
298:
|
||||||
id: "BKSP"
|
id: "BKSP"
|
||||||
|
keyCode: "Backspace"
|
||||||
title: Keyboard Backspace (US English)
|
title: Keyboard Backspace (US English)
|
||||||
icon: backspace
|
icon: backspace
|
||||||
299:
|
299:
|
||||||
id: "TAB"
|
id: "TAB"
|
||||||
|
keyCode: "Tab"
|
||||||
title: Keyboard Tab (US English)
|
title: Keyboard Tab (US English)
|
||||||
icon: keyboard_tab
|
icon: keyboard_tab
|
||||||
300:
|
300:
|
||||||
id: "KSC_2C"
|
id: "KSC_2C"
|
||||||
|
keyCode: "Space"
|
||||||
title: Keyboard Space (US English)
|
title: Keyboard Space (US English)
|
||||||
description: |
|
description: |
|
||||||
The ASCII space is preferred over this raw scancode for the space bar.
|
The ASCII space is preferred over this raw scancode for the space bar.
|
||||||
icon: space_bar
|
icon: space_bar
|
||||||
301:
|
301:
|
||||||
id: "KSC_2D"
|
id: "KSC_2D"
|
||||||
|
keyCode: "Minus"
|
||||||
title: Keyboard - and _ (US English)
|
title: Keyboard - and _ (US English)
|
||||||
302:
|
302:
|
||||||
id: "KSC_2E"
|
id: "KSC_2E"
|
||||||
|
keyCode: "Equal"
|
||||||
title: Keyboard = and + (US English)
|
title: Keyboard = and + (US English)
|
||||||
303:
|
303:
|
||||||
id: "KSC_2F"
|
id: "KSC_2F"
|
||||||
|
keyCode: "BracketLeft"
|
||||||
title: Keyboard [ and { (US English)
|
title: Keyboard [ and { (US English)
|
||||||
304:
|
304:
|
||||||
id: "KSC_30"
|
id: "KSC_30"
|
||||||
|
keyCode: "BracketRight"
|
||||||
title: Keyboard ] and } (US English)
|
title: Keyboard ] and } (US English)
|
||||||
305:
|
305:
|
||||||
id: "KSC_31"
|
id: "KSC_31"
|
||||||
|
keyCode: "Backslash"
|
||||||
title: Keyboard \ and | (US English)
|
title: Keyboard \ and | (US English)
|
||||||
306:
|
306:
|
||||||
id: "KSC_32"
|
id: "KSC_32"
|
||||||
|
# TODO: also backslash?
|
||||||
title: Keyboard Non-US \# and ~ (US English)
|
title: Keyboard Non-US \# and ~ (US English)
|
||||||
307:
|
307:
|
||||||
id: "KSC_33"
|
id: "KSC_33"
|
||||||
|
keyCode: "Semicolon"
|
||||||
title: "Keyboard ; and : (US English)"
|
title: "Keyboard ; and : (US English)"
|
||||||
308:
|
308:
|
||||||
id: "KSC_34"
|
id: "KSC_34"
|
||||||
|
keyCode: "Quote"
|
||||||
title: Keyboard ' and " (US English)
|
title: Keyboard ' and " (US English)
|
||||||
309:
|
309:
|
||||||
id: "KSC_35"
|
id: "KSC_35"
|
||||||
|
keyCode: "Backquote"
|
||||||
title: Keyboard ` and ~ (US English)
|
title: Keyboard ` and ~ (US English)
|
||||||
310:
|
310:
|
||||||
id: "KSC_36"
|
id: "KSC_36"
|
||||||
|
keyCode: "Comma"
|
||||||
title: Keyboard , and < (US English)
|
title: Keyboard , and < (US English)
|
||||||
311:
|
311:
|
||||||
id: "KSC_37"
|
id: "KSC_37"
|
||||||
|
keyCode: "Period"
|
||||||
title: Keyboard . and > (US English)
|
title: Keyboard . and > (US English)
|
||||||
312:
|
312:
|
||||||
id: "KSC_38"
|
id: "KSC_38"
|
||||||
|
keyCode: "Slash"
|
||||||
title: Keyboard / and ? (US English)
|
title: Keyboard / and ? (US English)
|
||||||
313:
|
313:
|
||||||
id: "CAPSLOCK"
|
id: "CAPSLOCK"
|
||||||
|
keyCode: "CapsLock"
|
||||||
title: Keyboard Caps Lock
|
title: Keyboard Caps Lock
|
||||||
icon: shift_lock
|
icon: shift_lock
|
||||||
314:
|
314:
|
||||||
id: "F1"
|
id: "F1"
|
||||||
|
keyCode: "F1"
|
||||||
title: Keyboard F1
|
title: Keyboard F1
|
||||||
315:
|
315:
|
||||||
id: "F2"
|
id: "F2"
|
||||||
|
keyCode: "F2"
|
||||||
title: Keyboard F2
|
title: Keyboard F2
|
||||||
316:
|
316:
|
||||||
id: "F3"
|
id: "F3"
|
||||||
|
keyCode: "F3"
|
||||||
title: Keyboard F3
|
title: Keyboard F3
|
||||||
317:
|
317:
|
||||||
id: "F4"
|
id: "F4"
|
||||||
|
keyCode: "F4"
|
||||||
title: Keyboard F4
|
title: Keyboard F4
|
||||||
318:
|
318:
|
||||||
id: "F5"
|
id: "F5"
|
||||||
|
keyCode: "F5"
|
||||||
title: Keyboard F5
|
title: Keyboard F5
|
||||||
319:
|
319:
|
||||||
id: "F6"
|
id: "F6"
|
||||||
|
keyCode: "F6"
|
||||||
title: Keyboard F6
|
title: Keyboard F6
|
||||||
320:
|
320:
|
||||||
id: "F7"
|
id: "F7"
|
||||||
|
keyCode: "F7"
|
||||||
title: Keyboard F7
|
title: Keyboard F7
|
||||||
321:
|
321:
|
||||||
id: "F8"
|
id: "F8"
|
||||||
|
keyCode: "F8"
|
||||||
title: Keyboard F8
|
title: Keyboard F8
|
||||||
322:
|
322:
|
||||||
id: "F9"
|
id: "F9"
|
||||||
|
keyCode: "F9"
|
||||||
title: Keyboard F9
|
title: Keyboard F9
|
||||||
323:
|
323:
|
||||||
id: "F10"
|
id: "F10"
|
||||||
|
keyCode: "F10"
|
||||||
title: Keyboard F10
|
title: Keyboard F10
|
||||||
324:
|
324:
|
||||||
id: "F11"
|
id: "F11"
|
||||||
|
keyCode: "F11"
|
||||||
title: Keyboard F11
|
title: Keyboard F11
|
||||||
325:
|
325:
|
||||||
id: "F12"
|
id: "F12"
|
||||||
|
keyCode: "F12"
|
||||||
title: Keyboard F12
|
title: Keyboard F12
|
||||||
326:
|
326:
|
||||||
id: "PRTSCN"
|
id: "PRTSCN"
|
||||||
|
keyCode: "PrintScreen"
|
||||||
title: Keyboard Print Screen
|
title: Keyboard Print Screen
|
||||||
icon: screenshot_monitor
|
icon: screenshot_monitor
|
||||||
327:
|
327:
|
||||||
id: "SCRLK"
|
id: "SCRLK"
|
||||||
|
keyCode: "ScrollLock"
|
||||||
title: Keyboard Scroll Lock
|
title: Keyboard Scroll Lock
|
||||||
328:
|
328:
|
||||||
id: "PAUSE"
|
id: "PAUSE"
|
||||||
|
keyCode: "Pause"
|
||||||
title: Keyboard Pause
|
title: Keyboard Pause
|
||||||
329:
|
329:
|
||||||
id: "INSERT"
|
id: "INSERT"
|
||||||
|
keyCode: "Insert"
|
||||||
title: Keyboard Insert
|
title: Keyboard Insert
|
||||||
icon: insert_text
|
icon: insert_text
|
||||||
330:
|
330:
|
||||||
id: "HOME"
|
id: "HOME"
|
||||||
|
keyCode: "Home"
|
||||||
title: Keyboard Home
|
title: Keyboard Home
|
||||||
icon: home
|
icon: home
|
||||||
331:
|
331:
|
||||||
id: "PGUP"
|
id: "PGUP"
|
||||||
|
keyCode: "PageUp"
|
||||||
title: Keyboard Page Up
|
title: Keyboard Page Up
|
||||||
icon: move_up
|
icon: move_up
|
||||||
332:
|
332:
|
||||||
id: "DELETE"
|
id: "DELETE"
|
||||||
|
keyCode: "Delete"
|
||||||
title: Keyboard Delete Forward
|
title: Keyboard Delete Forward
|
||||||
333:
|
333:
|
||||||
id: "END"
|
id: "END"
|
||||||
|
keyCode: "End"
|
||||||
title: Keyboard End
|
title: Keyboard End
|
||||||
334:
|
334:
|
||||||
id: "PGDN"
|
id: "PGDN"
|
||||||
|
keyCode: "PageDown"
|
||||||
title: Keyboard Page Down
|
title: Keyboard Page Down
|
||||||
icon: move_down
|
icon: move_down
|
||||||
335:
|
335:
|
||||||
id: "ARROW_RT"
|
id: "ARROW_RT"
|
||||||
|
keyCode: "ArrowRight"
|
||||||
title: Keyboard Right Arrow
|
title: Keyboard Right Arrow
|
||||||
icon: keyboard_arrow_right
|
icon: keyboard_arrow_right
|
||||||
336:
|
336:
|
||||||
id: "ARROW_LF"
|
id: "ARROW_LF"
|
||||||
|
keyCode: "ArrowLeft"
|
||||||
title: Keyboard Left Arrow
|
title: Keyboard Left Arrow
|
||||||
icon: keyboard_arrow_left
|
icon: keyboard_arrow_left
|
||||||
337:
|
337:
|
||||||
id: "ARROW_DN"
|
id: "ARROW_DN"
|
||||||
|
keyCode: "ArrowDown"
|
||||||
title: Keyboard Down Arrow
|
title: Keyboard Down Arrow
|
||||||
icon: keyboard_arrow_down
|
icon: keyboard_arrow_down
|
||||||
338:
|
338:
|
||||||
id: "ARROW_UP"
|
id: "ARROW_UP"
|
||||||
|
keyCode: "ArrowUp"
|
||||||
title: Keyboard Up Arrow
|
title: Keyboard Up Arrow
|
||||||
icon: keyboard_arrow_up
|
icon: keyboard_arrow_up
|
||||||
339:
|
339:
|
||||||
id: "NUMLOCK"
|
id: "NUMLOCK"
|
||||||
|
keyCode: "NumLock"
|
||||||
title: Keyboard Num Lock and Clear
|
title: Keyboard Num Lock and Clear
|
||||||
340:
|
340:
|
||||||
id: "KP_SLASH"
|
id: "KP_SLASH"
|
||||||
|
keyCode: "NumpadDivide"
|
||||||
title: Keypad /
|
title: Keypad /
|
||||||
341:
|
341:
|
||||||
id: "KP_ASTER"
|
id: "KP_ASTER"
|
||||||
|
keyCode: "NumpadStar"
|
||||||
title: Keypad *
|
title: Keypad *
|
||||||
342:
|
342:
|
||||||
id: "KP_MINUS"
|
id: "KP_MINUS"
|
||||||
|
keyCode: "NumpadSubtract"
|
||||||
title: Keypad -
|
title: Keypad -
|
||||||
343:
|
343:
|
||||||
id: "KP_PLUS"
|
id: "KP_PLUS"
|
||||||
|
keyCode: "NumpadAdd"
|
||||||
title: Keypad +
|
title: Keypad +
|
||||||
344:
|
344:
|
||||||
id: "KP_ENTER"
|
id: "KP_ENTER"
|
||||||
|
keyCode: "NumpadEnter"
|
||||||
title: Keypad Enter
|
title: Keypad Enter
|
||||||
345:
|
345:
|
||||||
id: "KP_1"
|
id: "KP_1"
|
||||||
|
keyCode: "Numpad1"
|
||||||
title: Keypad 1 and End
|
title: Keypad 1 and End
|
||||||
346:
|
346:
|
||||||
id: "KP_2"
|
id: "KP_2"
|
||||||
|
keyCode: "Numpad2"
|
||||||
title: Keypad 2 and Down Arrow
|
title: Keypad 2 and Down Arrow
|
||||||
347:
|
347:
|
||||||
id: "KP_3"
|
id: "KP_3"
|
||||||
|
keyCode: "Numpad3"
|
||||||
title: Keypad 3 and Page Down
|
title: Keypad 3 and Page Down
|
||||||
348:
|
348:
|
||||||
id: "KP_4"
|
id: "KP_4"
|
||||||
|
keyCode: "Numpad4"
|
||||||
title: Keypad 4 and Left Arrow
|
title: Keypad 4 and Left Arrow
|
||||||
349:
|
349:
|
||||||
id: "KP_5"
|
id: "KP_5"
|
||||||
|
keyCode: "Numpad5"
|
||||||
title: Keypad 5
|
title: Keypad 5
|
||||||
350:
|
350:
|
||||||
id: "KP_6"
|
id: "KP_6"
|
||||||
|
keyCode: "Numpad6"
|
||||||
title: Keypad 6 and Rigth Arrow
|
title: Keypad 6 and Rigth Arrow
|
||||||
351:
|
351:
|
||||||
id: "KP_7"
|
id: "KP_7"
|
||||||
|
keyCode: "Numpad7"
|
||||||
title: Keypad 7 and Home
|
title: Keypad 7 and Home
|
||||||
352:
|
352:
|
||||||
id: "KP_8"
|
id: "KP_8"
|
||||||
|
keyCode: "Numpad8"
|
||||||
title: Keypad 8 and Up Arrow
|
title: Keypad 8 and Up Arrow
|
||||||
353:
|
353:
|
||||||
id: "KP_9"
|
id: "KP_9"
|
||||||
|
keyCode: "Numpad9"
|
||||||
title: Keypad 9 and Page Up
|
title: Keypad 9 and Page Up
|
||||||
354:
|
354:
|
||||||
id: "KP_0"
|
id: "KP_0"
|
||||||
|
keyCode: "Numpad0"
|
||||||
title: Keypad 0 and Insert
|
title: Keypad 0 and Insert
|
||||||
355:
|
355:
|
||||||
id: "KP_DOT"
|
id: "KP_DOT"
|
||||||
|
keyCode: "NumpadDecimal"
|
||||||
title: Keypad . and Delete
|
title: Keypad . and Delete
|
||||||
356:
|
356:
|
||||||
id: "KSC_64"
|
id: "KSC_64"
|
||||||
|
keyCode: "IntlBackslash"
|
||||||
title: Keyboard Non-US \ and | (US English)
|
title: Keyboard Non-US \ and | (US English)
|
||||||
357:
|
357:
|
||||||
id: "COMPOSE"
|
id: "COMPOSE"
|
||||||
@@ -327,10 +424,12 @@ actions:
|
|||||||
description: Officially supported by Win, Unix, and Boot
|
description: Officially supported by Win, Unix, and Boot
|
||||||
358:
|
358:
|
||||||
id: "POWER"
|
id: "POWER"
|
||||||
|
keyCode: "Power"
|
||||||
title: Keyboard Power
|
title: Keyboard Power
|
||||||
description: Only officially supported by Mac and Unix
|
description: Only officially supported by Mac and Unix
|
||||||
359:
|
359:
|
||||||
id: "KP_EQUAL"
|
id: "KP_EQUAL"
|
||||||
|
keyCode: "NumpadEqual"
|
||||||
title: Keypad =
|
title: Keypad =
|
||||||
description: Only officially supported by Mac
|
description: Only officially supported by Mac
|
||||||
360:
|
360:
|
||||||
@@ -787,10 +886,12 @@ actions:
|
|||||||
description: Not required to be supported by any OS
|
description: Not required to be supported by any OS
|
||||||
472:
|
472:
|
||||||
id: "KSC_D8"
|
id: "KSC_D8"
|
||||||
|
keyCode: "NumpadClear"
|
||||||
title: Keypad Clear
|
title: Keypad Clear
|
||||||
description: Not required to be supported by any OS
|
description: Not required to be supported by any OS
|
||||||
473:
|
473:
|
||||||
id: "KSC_D9"
|
id: "KSC_D9"
|
||||||
|
keyCode: "NumpadClearEntry"
|
||||||
title: Keypad Clear Entry
|
title: Keypad Clear Entry
|
||||||
description: Not required to be supported by any OS
|
description: Not required to be supported by any OS
|
||||||
474:
|
474:
|
||||||
@@ -817,58 +918,74 @@ actions:
|
|||||||
description: Not required to be supported by any OS
|
description: Not required to be supported by any OS
|
||||||
480:
|
480:
|
||||||
id: "KSC_E0"
|
id: "KSC_E0"
|
||||||
|
keyCode: "ControlLeft"
|
||||||
title: Keyboard Left Control
|
title: Keyboard Left Control
|
||||||
481:
|
481:
|
||||||
id: "KSC_E1"
|
id: "KSC_E1"
|
||||||
|
keyCode: "ShiftLeft"
|
||||||
title: Keyboard Left Shift
|
title: Keyboard Left Shift
|
||||||
482:
|
482:
|
||||||
id: "KSC_E2"
|
id: "KSC_E2"
|
||||||
|
keyCode: "AltLeft"
|
||||||
title: Keyboard Left Alt
|
title: Keyboard Left Alt
|
||||||
483:
|
483:
|
||||||
id: "KSC_E3"
|
id: "KSC_E3"
|
||||||
|
keyCode: "MetaLeft"
|
||||||
title: Keyboard Left GUI
|
title: Keyboard Left GUI
|
||||||
484:
|
484:
|
||||||
id: "KSC_E4"
|
id: "KSC_E4"
|
||||||
|
keyCode: "ControlRight"
|
||||||
title: Keyboard Right Control
|
title: Keyboard Right Control
|
||||||
485:
|
485:
|
||||||
id: "KSC_E5"
|
id: "KSC_E5"
|
||||||
|
keyCode: "ShiftRight"
|
||||||
title: Keyboard Right Shift
|
title: Keyboard Right Shift
|
||||||
486:
|
486:
|
||||||
id: "KSC_E6"
|
id: "KSC_E6"
|
||||||
|
keyCode: "AltRight"
|
||||||
title: Keyboard Right Alt
|
title: Keyboard Right Alt
|
||||||
487:
|
487:
|
||||||
id: "KSC_E7"
|
id: "KSC_E7"
|
||||||
|
keyCode: "MetaRight"
|
||||||
title: Keyboard Right GUI
|
title: Keyboard Right GUI
|
||||||
488:
|
488:
|
||||||
id: "KSC_E8"
|
id: "KSC_E8"
|
||||||
|
keyCode: "MediaPlayPause"
|
||||||
title: Media Play Pause
|
title: Media Play Pause
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
489:
|
489:
|
||||||
id: "KSC_E9"
|
id: "KSC_E9"
|
||||||
|
keyCode: "MediaStop"
|
||||||
title: Media Stop CD
|
title: Media Stop CD
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
490:
|
490:
|
||||||
id: "KSC_EA"
|
id: "KSC_EA"
|
||||||
|
keyCode: "MediaTrackPrevious"
|
||||||
title: Media Previous Song
|
title: Media Previous Song
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
491:
|
491:
|
||||||
id: "KSC_EB"
|
id: "KSC_EB"
|
||||||
|
keyCode: "MediaTrackNext"
|
||||||
title: Media Next Song
|
title: Media Next Song
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
492:
|
492:
|
||||||
id: "KSC_EC"
|
id: "KSC_EC"
|
||||||
|
keyCode: "Eject"
|
||||||
title: Media Eject CD
|
title: Media Eject CD
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
493:
|
493:
|
||||||
id: "KSC_ED"
|
id: "KSC_ED"
|
||||||
|
keyCode: "AudioVolumeUp"
|
||||||
title: Media Volume Up
|
title: Media Volume Up
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
494:
|
494:
|
||||||
id: "KSC_EE"
|
id: "KSC_EE"
|
||||||
|
keyCode: "AudioVolumeDown"
|
||||||
title: Media Volume Down
|
title: Media Volume Down
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
495:
|
495:
|
||||||
id: "KSC_EF"
|
id: "KSC_EF"
|
||||||
|
keyCode: "AudioVolumeMute"
|
||||||
title: Media Mute
|
title: Media Mute
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
496:
|
496:
|
||||||
@@ -877,18 +994,22 @@ actions:
|
|||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
497:
|
497:
|
||||||
id: "KSC_F1"
|
id: "KSC_F1"
|
||||||
|
keyCode: "BrowserBack"
|
||||||
title: Media Back
|
title: Media Back
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
498:
|
498:
|
||||||
id: "KSC_F2"
|
id: "KSC_F2"
|
||||||
|
keyCode: "BrowserForward"
|
||||||
title: Media Forward
|
title: Media Forward
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
499:
|
499:
|
||||||
id: "KSC_F3"
|
id: "KSC_F3"
|
||||||
|
keyCode: "BrowserStop"
|
||||||
title: Media Stop
|
title: Media Stop
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
500:
|
500:
|
||||||
id: "KSC_F4"
|
id: "KSC_F4"
|
||||||
|
keyCode: "BrowserSearch"
|
||||||
title: Media Find
|
title: Media Find
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
501:
|
501:
|
||||||
@@ -905,14 +1026,17 @@ actions:
|
|||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
504:
|
504:
|
||||||
id: "KSC_F8"
|
id: "KSC_F8"
|
||||||
|
keyCode: "Sleep"
|
||||||
title: Media Sleep
|
title: Media Sleep
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
505:
|
505:
|
||||||
id: "KSC_F9"
|
id: "KSC_F9"
|
||||||
|
keyCode: "WakeUp"
|
||||||
title: Media Coffee
|
title: Media Coffee
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
506:
|
506:
|
||||||
id: "KSC_FA"
|
id: "KSC_FA"
|
||||||
|
keyCode: "BrowserRefresh"
|
||||||
title: Media Refresh
|
title: Media Refresh
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
507:
|
507:
|
||||||
|
|||||||
39
src/lib/components/Action.svelte
Normal file
39
src/lib/components/Action.svelte
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
|
||||||
|
import type {KeyInfo} from "$lib/serial/keymap-codes"
|
||||||
|
|
||||||
|
export let action: number | KeyInfo
|
||||||
|
export let display: "inline-keys" | "keys" = "inline-keys"
|
||||||
|
|
||||||
|
$: info = typeof action === "number" ? KEYMAP_CODES[action] ?? {code: action} : action
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if display === "keys"}
|
||||||
|
<kbd class:icon={!!info.icon}>
|
||||||
|
{info.icon ?? info.id ?? `0x${info.code.toString(16)}`}
|
||||||
|
</kbd>
|
||||||
|
{:else if display === "inline-keys"}
|
||||||
|
{#if !info.icon && info.id?.length === 1}
|
||||||
|
<span>{info.id}</span>
|
||||||
|
{:else}
|
||||||
|
<kbd class="inline-kbd" class:icon={!!info.icon}
|
||||||
|
>{info.icon ?? info.id ?? `0x${info.code.toString(16)}`}</kbd
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
kbd:not(.inline-kbd) {
|
||||||
|
height: 24px;
|
||||||
|
padding-block: auto;
|
||||||
|
transition: color 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-kbd {
|
||||||
|
margin-inline-end: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(span) + .inline-kbd {
|
||||||
|
margin-inline-start: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
@@ -61,4 +62,8 @@
|
|||||||
|
|
||||||
text-align: start;
|
text-align: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
11
src/lib/components/ActionString.svelte
Normal file
11
src/lib/components/ActionString.svelte
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Action from "$lib/components/Action.svelte"
|
||||||
|
import type {KeyInfo} from "$lib/serial/keymap-codes"
|
||||||
|
|
||||||
|
export let actions: Array<number | KeyInfo>
|
||||||
|
export let display: "keys" | "inline-keys" = "inline-keys"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each actions as action, i (`${typeof action === "number" ? action : action.code}:${i}`)}
|
||||||
|
<Action {action} {display} />
|
||||||
|
{/each}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import {Extension, Node} from "@tiptap/core"
|
|
||||||
|
|
||||||
const CharaAction = Node.create({
|
|
||||||
name: "Action",
|
|
||||||
renderHTML({HTMLAttributes}) {
|
|
||||||
return ["kbd", HTMLAttributes, 0]
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {createEventDispatcher} from "svelte"
|
import {createEventDispatcher} from "svelte"
|
||||||
|
import Dialog from "$lib/dialogs/Dialog.svelte"
|
||||||
|
|
||||||
export let title: string
|
export let title: string
|
||||||
export let message: string | undefined
|
export let message: string | undefined
|
||||||
@@ -7,15 +8,9 @@
|
|||||||
export let confirmTitle: string
|
export let confirmTitle: string
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export function show() {
|
|
||||||
modal.showModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
let modal: HTMLDialogElement
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<dialog bind:this={modal}>
|
<Dialog>
|
||||||
<h1>{@html title}</h1>
|
<h1>{@html title}</h1>
|
||||||
{#if message}
|
{#if message}
|
||||||
<p>{@html message}</p>
|
<p>{@html message}</p>
|
||||||
@@ -24,7 +19,7 @@
|
|||||||
<button on:click={() => dispatch("abort")}>{abortTitle}</button>
|
<button on:click={() => dispatch("abort")}>{abortTitle}</button>
|
||||||
<button class="primary" on:click={() => dispatch("confirm")}>{confirmTitle}</button>
|
<button class="primary" on:click={() => dispatch("confirm")}>{confirmTitle}</button>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</Dialog>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
h1 {
|
h1 {
|
||||||
32
src/lib/dialogs/Dialog.svelte
Normal file
32
src/lib/dialogs/Dialog.svelte
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {onMount} from "svelte"
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
modal.showModal()
|
||||||
|
})
|
||||||
|
|
||||||
|
let modal: HTMLDialogElement
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<dialog bind:this={modal}>
|
||||||
|
<slot />
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
dialog {
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 512px;
|
||||||
|
|
||||||
|
color: var(--md-sys-color-on-background);
|
||||||
|
|
||||||
|
background: var(--md-sys-color-background);
|
||||||
|
border: none;
|
||||||
|
border-radius: 38px;
|
||||||
|
box-shadow: 0 0 48px rgba(0 0 0 / 60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog::backdrop {
|
||||||
|
opacity: 0.5;
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
161
src/lib/dialogs/PickChangesDialog.svelte
Normal file
161
src/lib/dialogs/PickChangesDialog.svelte
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Dialog from "$lib/dialogs/Dialog.svelte"
|
||||||
|
import type {Change, ChordChange, LayoutChange, SettingChange} from "$lib/undo-redo"
|
||||||
|
import {ChangeType, chords} from "$lib/undo-redo"
|
||||||
|
import ActionString from "$lib/components/ActionString.svelte"
|
||||||
|
import LL from "../../i18n/i18n-svelte"
|
||||||
|
import {KEYMAP_IDS} from "$lib/serial/keymap-codes"
|
||||||
|
|
||||||
|
export let changes: Change[] = [
|
||||||
|
{type: ChangeType.Layout, layer: 0, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Layout, layer: 1, id: 1, action: 1},
|
||||||
|
{type: ChangeType.Setting, id: 0, setting: 2},
|
||||||
|
{type: ChangeType.Setting, id: 0, setting: 2},
|
||||||
|
{type: ChangeType.Setting, id: 0, setting: 2},
|
||||||
|
{type: ChangeType.Setting, id: 0, setting: 2},
|
||||||
|
{type: ChangeType.Chord, id: [1], actions: [55], phrase: [55, 63, 37, 36]},
|
||||||
|
{
|
||||||
|
type: ChangeType.Chord,
|
||||||
|
id: [KEYMAP_IDS.get("y")!.code, KEYMAP_IDS.get("r")!.code, KEYMAP_IDS.get("t")!.code],
|
||||||
|
actions: [KEYMAP_IDS.get("y")!.code, KEYMAP_IDS.get("r")!.code, KEYMAP_IDS.get("t")!.code],
|
||||||
|
phrase: [55, 63, 37, 36],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ChangeType.Chord,
|
||||||
|
id: [KEYMAP_IDS.get("y")!.code, KEYMAP_IDS.get("r")!.code, KEYMAP_IDS.get("t")!.code],
|
||||||
|
actions: [KEYMAP_IDS.get("y")!.code, KEYMAP_IDS.get("r")!.code, KEYMAP_IDS.get("t")!.code],
|
||||||
|
phrase: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
$: existingChords = new Set($chords.map(it => JSON.stringify(it.id)))
|
||||||
|
|
||||||
|
$: layoutChanges = Array.from(
|
||||||
|
{length: 3},
|
||||||
|
(_, i) => changes.filter(it => it.type === ChangeType.Layout && it.layer === i) as LayoutChange[],
|
||||||
|
)
|
||||||
|
$: settingChanges = changes.filter(it => it.type === ChangeType.Setting) as SettingChange[]
|
||||||
|
$: chordChanges = {
|
||||||
|
added: changes.filter(
|
||||||
|
it =>
|
||||||
|
it.type === ChangeType.Chord && it.phrase.length > 0 && !existingChords.has(JSON.stringify(it.id)),
|
||||||
|
) as ChordChange[],
|
||||||
|
changed: changes.filter(
|
||||||
|
it => it.type === ChangeType.Chord && it.phrase.length > 0 && existingChords.has(JSON.stringify(it.id)),
|
||||||
|
) as ChordChange[],
|
||||||
|
deleted: changes.filter(it => it.type === ChangeType.Chord && it.phrase.length === 0) as ChordChange[],
|
||||||
|
}
|
||||||
|
$: totalChordChanges = Object.values(chordChanges).reduce((acc, curr) => acc + curr.length, 0)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog>
|
||||||
|
<h1>{$LL.changes.TITLE()}</h1>
|
||||||
|
<h2>
|
||||||
|
<label><input type="checkbox" class="checkbox" />{$LL.changes.ALL_CHANGES()}</label>
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
{#if layoutChanges.some(it => it.length > 0)}
|
||||||
|
<li>
|
||||||
|
<h3>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" class="checkbox" />
|
||||||
|
{$LL.changes.layout.TITLE(layoutChanges.reduce((acc, curr) => acc + curr.length, 0))}
|
||||||
|
</label>
|
||||||
|
</h3>
|
||||||
|
<ul>
|
||||||
|
{#each layoutChanges
|
||||||
|
.map((it, i) => /** @type {const} */ ([it, i + 1]))
|
||||||
|
.filter(([it]) => it.length > 0) as [changes, layer]}
|
||||||
|
<li>
|
||||||
|
<h4>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" class="checkbox" />
|
||||||
|
{$LL.changes.layout.LAYER({changes: changes.length, layer})}
|
||||||
|
</label>
|
||||||
|
</h4>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
{#if settingChanges.length > 0}
|
||||||
|
<li>
|
||||||
|
<h3>
|
||||||
|
<label
|
||||||
|
><input type="checkbox" class="checkbox" />{$LL.changes.settings.TITLE(
|
||||||
|
settingChanges.length,
|
||||||
|
)}</label
|
||||||
|
>
|
||||||
|
</h3>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
{#if totalChordChanges > 0}
|
||||||
|
<li>
|
||||||
|
<h3>
|
||||||
|
<label
|
||||||
|
><input type="checkbox" class="checkbox" />{$LL.changes.chords.TITLE(totalChordChanges)}</label
|
||||||
|
>
|
||||||
|
</h3>
|
||||||
|
<ul>
|
||||||
|
{#each Object.entries(chordChanges) as [category, changes]}
|
||||||
|
{#if changes.length > 0}
|
||||||
|
<li>
|
||||||
|
<h4>
|
||||||
|
<label
|
||||||
|
><input type="checkbox" class="checkbox" />
|
||||||
|
{#if category === "added"}
|
||||||
|
{$LL.changes.chords.NEW_CHORDS(changes.length)}
|
||||||
|
{:else if category === "changed"}
|
||||||
|
{$LL.changes.chords.CHANGED_CHORDS(changes.length)}
|
||||||
|
{:else if category === "deleted"}
|
||||||
|
{$LL.changes.chords.DELETED_CHORDS(changes.length)}
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
|
</h4>
|
||||||
|
<ul>
|
||||||
|
{#each changes as change}
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" class="checkbox" />
|
||||||
|
<ActionString display="keys" actions={change.actions} />
|
||||||
|
<ActionString actions={change.phrase} />
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
</ul>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-inline-start: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-inline-start: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import ConfirmDialog from "$lib/ConfirmDialog.svelte"
|
import ConfirmDialog from "$lib/dialogs/ConfirmDialog.svelte"
|
||||||
|
|
||||||
export async function askForConfirmation(
|
export async function askForConfirmation(
|
||||||
title: string,
|
title: string,
|
||||||
39
src/lib/os-layout.ts
Normal file
39
src/lib/os-layout.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {persistentWritable} from "$lib/storage"
|
||||||
|
import {get} from "svelte/store"
|
||||||
|
|
||||||
|
export const osLayout = persistentWritable<Record<string, string>>("os-layout", {})
|
||||||
|
|
||||||
|
const keysCurrentlyDown = new Set<string>()
|
||||||
|
|
||||||
|
function keydown({code, key}: KeyboardEvent) {
|
||||||
|
const keys = [...keysCurrentlyDown]
|
||||||
|
keysCurrentlyDown.add(code)
|
||||||
|
|
||||||
|
const keyString = JSON.stringify([...keys.sort(), code])
|
||||||
|
if (keyString in get(osLayout) || get(osLayout)[JSON.stringify([code])] === key) return
|
||||||
|
|
||||||
|
osLayout.update(layout => {
|
||||||
|
layout[keyString] = key
|
||||||
|
return layout
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyup({code}: KeyboardEvent) {
|
||||||
|
keysCurrentlyDown.delete(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runLayoutDetection() {
|
||||||
|
if ("keyboard" in navigator) {
|
||||||
|
;(navigator.keyboard as any).getLayoutMap().then((layout: Map<string, string>) => {
|
||||||
|
osLayout.update(osLayout => {
|
||||||
|
Object.assign(
|
||||||
|
osLayout,
|
||||||
|
Object.fromEntries([...layout.entries()].map(([key, value]) => [JSON.stringify([key]), value])),
|
||||||
|
)
|
||||||
|
return osLayout
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
window.addEventListener("keydown", keydown)
|
||||||
|
window.addEventListener("keyup", keyup)
|
||||||
|
}
|
||||||
@@ -45,17 +45,33 @@ export const deviceSettings = persistentWritable<number[]>(
|
|||||||
|
|
||||||
export const syncStatus: Writable<"done" | "error" | "downloading" | "uploading"> = writable("done")
|
export const syncStatus: Writable<"done" | "error" | "downloading" | "uploading"> = writable("done")
|
||||||
|
|
||||||
|
export interface ProgressInfo {
|
||||||
|
max: number
|
||||||
|
current: number
|
||||||
|
}
|
||||||
|
export const syncProgress = writable<ProgressInfo | undefined>(undefined)
|
||||||
|
|
||||||
export async function initSerial(manual = false) {
|
export async function initSerial(manual = false) {
|
||||||
const device = get(serialPort) ?? new CharaDevice()
|
const device = get(serialPort) ?? new CharaDevice()
|
||||||
await device.init(manual)
|
await device.init(manual)
|
||||||
serialPort.set(device)
|
serialPort.set(device)
|
||||||
|
const chordCount = await device.getChordCount()
|
||||||
syncStatus.set("downloading")
|
syncStatus.set("downloading")
|
||||||
|
|
||||||
|
const max = Object.keys(settingInfo.settings).length + device.keyCount * 3 + chordCount
|
||||||
|
let current = 0
|
||||||
|
syncProgress.set({max, current})
|
||||||
|
function progressTick() {
|
||||||
|
current++
|
||||||
|
syncProgress.set({max, current})
|
||||||
|
}
|
||||||
|
|
||||||
const parsedSettings: number[] = []
|
const parsedSettings: number[] = []
|
||||||
for (const key in settingInfo.settings) {
|
for (const key in settingInfo.settings) {
|
||||||
try {
|
try {
|
||||||
parsedSettings[Number.parseInt(key)] = await device.getSetting(Number.parseInt(key))
|
parsedSettings[Number.parseInt(key)] = await device.getSetting(Number.parseInt(key))
|
||||||
} catch {}
|
} catch {}
|
||||||
|
progressTick()
|
||||||
}
|
}
|
||||||
deviceSettings.set(parsedSettings)
|
deviceSettings.set(parsedSettings)
|
||||||
|
|
||||||
@@ -63,15 +79,17 @@ export async function initSerial(manual = false) {
|
|||||||
for (let layer = 1; layer <= 3; layer++) {
|
for (let layer = 1; layer <= 3; layer++) {
|
||||||
for (let i = 0; i < device.keyCount; i++) {
|
for (let i = 0; i < device.keyCount; i++) {
|
||||||
parsedLayout[layer - 1][i] = await device.getLayoutKey(layer, i)
|
parsedLayout[layer - 1][i] = await device.getLayoutKey(layer, i)
|
||||||
|
progressTick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deviceLayout.set(parsedLayout)
|
deviceLayout.set(parsedLayout)
|
||||||
|
|
||||||
const chordCount = await device.getChordCount()
|
|
||||||
const chordInfo = []
|
const chordInfo = []
|
||||||
for (let i = 0; i < chordCount; i++) {
|
for (let i = 0; i < chordCount; i++) {
|
||||||
chordInfo.push(await device.getChord(i))
|
chordInfo.push(await device.getChord(i))
|
||||||
|
progressTick()
|
||||||
}
|
}
|
||||||
deviceChords.set(chordInfo)
|
deviceChords.set(chordInfo)
|
||||||
syncStatus.set("done")
|
syncStatus.set("done")
|
||||||
|
syncProgress.set(undefined)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@import "./form/button";
|
@import "./form/button";
|
||||||
@import "./form/toggle";
|
@import "./form/toggle";
|
||||||
|
@import "./form/checkbox";
|
||||||
@import "./kbd";
|
@import "./kbd";
|
||||||
@import "./print";
|
@import "./print";
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export const action: Action<HTMLElement, {title?: string; shortcut?: string}> =
|
|||||||
arrow: false,
|
arrow: false,
|
||||||
theme: "tooltip",
|
theme: "tooltip",
|
||||||
animation: "fade",
|
animation: "fade",
|
||||||
delay: [500, 0],
|
|
||||||
onShow(instance) {
|
onShow(instance) {
|
||||||
component ??= new Tooltip({
|
component ??= new Tooltip({
|
||||||
target: instance.popper.querySelector(".tippy-content") as HTMLElement,
|
target: instance.popper.querySelector(".tippy-content") as HTMLElement,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import Navigation from "./Navigation.svelte"
|
import Navigation from "./Navigation.svelte"
|
||||||
import {canAutoConnect} from "$lib/serial/device"
|
import {canAutoConnect} from "$lib/serial/device"
|
||||||
import {initSerial} from "$lib/serial/connection"
|
import {initSerial} from "$lib/serial/connection"
|
||||||
import type {LayoutServerData} from "./$types"
|
import type {LayoutData} from "./$types"
|
||||||
import {browser} from "$app/environment"
|
import {browser} from "$app/environment"
|
||||||
import BrowserWarning from "./BrowserWarning.svelte"
|
import BrowserWarning from "./BrowserWarning.svelte"
|
||||||
import "tippy.js/animations/shift-away.css"
|
import "tippy.js/animations/shift-away.css"
|
||||||
@@ -21,12 +21,16 @@
|
|||||||
import {detectLocale} from "../i18n/i18n-util"
|
import {detectLocale} from "../i18n/i18n-util"
|
||||||
import type {Locales} from "../i18n/i18n-types"
|
import type {Locales} from "../i18n/i18n-types"
|
||||||
import Footer from "./Footer.svelte"
|
import Footer from "./Footer.svelte"
|
||||||
|
import {runLayoutDetection} from "$lib/os-layout.js"
|
||||||
|
import PageTransition from "./PageTransition.svelte"
|
||||||
|
import SyncOverlay from "./SyncOverlay.svelte"
|
||||||
|
|
||||||
const locale = ((browser && localStorage.getItem("locale")) as Locales) || detectLocale()
|
const locale = ((browser && localStorage.getItem("locale")) as Locales) || detectLocale()
|
||||||
loadLocale(locale)
|
loadLocale(locale)
|
||||||
setLocale(locale)
|
setLocale(locale)
|
||||||
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
|
runLayoutDetection()
|
||||||
tippy.setDefaultProps({
|
tippy.setDefaultProps({
|
||||||
animation: "shift-away",
|
animation: "shift-away",
|
||||||
theme: "surface-variant",
|
theme: "surface-variant",
|
||||||
@@ -37,7 +41,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export let data: LayoutServerData
|
export let data: LayoutData
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
theme.subscribe(it => {
|
theme.subscribe(it => {
|
||||||
@@ -63,11 +67,15 @@
|
|||||||
<meta name="theme-color" content={data.themeColor} />
|
<meta name="theme-color" content={data.themeColor} />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
<SyncOverlay />
|
||||||
|
|
||||||
<Navigation />
|
<Navigation />
|
||||||
|
|
||||||
<main>
|
<!-- <PickChangesDialog /> -->
|
||||||
|
|
||||||
|
<PageTransition>
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</PageTransition>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,15 @@
|
|||||||
import type {Change} from "$lib/undo-redo"
|
import type {Change} from "$lib/undo-redo"
|
||||||
import {fly} from "svelte/transition"
|
import {fly} from "svelte/transition"
|
||||||
import {action} from "$lib/title"
|
import {action} from "$lib/title"
|
||||||
import {deviceChords, deviceLayout, deviceSettings, serialPort, syncStatus} from "$lib/serial/connection"
|
import {
|
||||||
import {askForConfirmation} from "$lib/confirm-dialog"
|
deviceChords,
|
||||||
|
deviceLayout,
|
||||||
|
deviceSettings,
|
||||||
|
serialPort,
|
||||||
|
syncProgress,
|
||||||
|
syncStatus,
|
||||||
|
} from "$lib/serial/connection"
|
||||||
|
import {askForConfirmation} from "$lib/dialogs/confirm-dialog"
|
||||||
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
|
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
|
||||||
|
|
||||||
function undo(event: MouseEvent) {
|
function undo(event: MouseEvent) {
|
||||||
@@ -94,7 +101,23 @@
|
|||||||
// would be if they click it every time they change a setting.
|
// would be if they click it every time they change a setting.
|
||||||
// Because of that, we don't need to show a fearmongering message such as
|
// Because of that, we don't need to show a fearmongering message such as
|
||||||
// "Your device will break after you click this 10,000 times!"
|
// "Your device will break after you click this 10,000 times!"
|
||||||
await new Promise(resolve => setTimeout(resolve, 6000))
|
const virtualWriteTime = 6000
|
||||||
|
const startStamp = performance.now()
|
||||||
|
await new Promise<void>(resolve => {
|
||||||
|
function animate() {
|
||||||
|
const delta = performance.now() - startStamp
|
||||||
|
syncProgress.set({
|
||||||
|
max: virtualWriteTime,
|
||||||
|
current: delta,
|
||||||
|
})
|
||||||
|
if (delta >= virtualWriteTime) {
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
requestAnimationFrame(animate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestAnimationFrame(animate)
|
||||||
|
})
|
||||||
if ($serialPort) {
|
if ($serialPort) {
|
||||||
await $serialPort.commit()
|
await $serialPort.commit()
|
||||||
$changes = []
|
$changes = []
|
||||||
|
|||||||
@@ -1,5 +1,34 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import {version} from "$app/environment"
|
import {browser, version} from "$app/environment"
|
||||||
|
import {action} from "$lib/title"
|
||||||
|
import LL, {setLocale} from "../i18n/i18n-svelte"
|
||||||
|
import {theme} from "$lib/preferences.js"
|
||||||
|
import type {Locales} from "../i18n/i18n-types"
|
||||||
|
import {detectLocale, locales} from "../i18n/i18n-util"
|
||||||
|
import {loadLocaleAsync} from "../i18n/i18n-util.async"
|
||||||
|
import {tick} from "svelte"
|
||||||
|
|
||||||
|
let locale = (browser && (localStorage.getItem("locale") as Locales)) || detectLocale()
|
||||||
|
$: if (browser)
|
||||||
|
(async () => {
|
||||||
|
localStorage.setItem("locale", locale)
|
||||||
|
await loadLocaleAsync(locale)
|
||||||
|
setLocale(locale)
|
||||||
|
})()
|
||||||
|
|
||||||
|
function switchTheme() {
|
||||||
|
const mode = $theme.mode === "light" ? "dark" : "light"
|
||||||
|
if (document.startViewTransition) {
|
||||||
|
document.startViewTransition(async () => {
|
||||||
|
$theme.mode = mode
|
||||||
|
await tick()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
$theme.mode = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let languageSelect: HTMLSelectElement
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
@@ -13,22 +42,107 @@
|
|||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<input use:action={{title: $LL.profile.theme.COLOR_SCHEME()}} type="color" bind:value={$theme.color} />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{#if $theme.mode === "light"}
|
||||||
|
<button use:action={{title: $LL.profile.theme.DARK_MODE()}} class="icon" on:click={switchTheme}>
|
||||||
|
dark_mode
|
||||||
|
</button>
|
||||||
|
{:else if $theme.mode === "dark"}
|
||||||
|
<button use:action={{title: $LL.profile.theme.LIGHT_MODE()}} class="icon" on:click={switchTheme}>
|
||||||
|
light_mode
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="icon"
|
||||||
|
use:action={{title: $LL.profile.LANGUAGE()}}
|
||||||
|
on:click={() => languageSelect.click()}
|
||||||
|
>translate
|
||||||
|
|
||||||
|
<select bind:value={locale} bind:this={languageSelect}>
|
||||||
|
{#each locales as code}
|
||||||
|
<option value={code}>{code}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style>
|
<style lang="scss">
|
||||||
|
select {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="color"] {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
inline-size: 20px;
|
||||||
|
block-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
&::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-color-swatch {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul:last-child {
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
height: 24px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
import {canAutoConnect} from "$lib/serial/device"
|
import {canAutoConnect} from "$lib/serial/device"
|
||||||
import {browser} from "$app/environment"
|
import {browser} from "$app/environment"
|
||||||
import {userPreferences} from "$lib/preferences"
|
import {userPreferences} from "$lib/preferences"
|
||||||
|
import {action} from "$lib/title"
|
||||||
import LL from "../i18n/i18n-svelte"
|
import LL from "../i18n/i18n-svelte"
|
||||||
import Profile from "./Profile.svelte"
|
|
||||||
import ConfigTabs from "./ConfigTabs.svelte"
|
import ConfigTabs from "./ConfigTabs.svelte"
|
||||||
import EditActions from "./EditActions.svelte"
|
import EditActions from "./EditActions.svelte"
|
||||||
|
|
||||||
@@ -29,8 +29,18 @@
|
|||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
{#if $canShare}
|
{#if $canShare}
|
||||||
<button transition:fly={{x: -8}} class="icon" on:click={triggerShare}>share</button>
|
<button
|
||||||
<button transition:fly={{x: -8}} class="icon" on:click={() => print()}>print</button>
|
use:action={{title: $LL.share.TITLE()}}
|
||||||
|
transition:fly={{x: -8}}
|
||||||
|
class="icon"
|
||||||
|
on:click={triggerShare}>share</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
use:action={{title: $LL.print.TITLE()}}
|
||||||
|
transition:fly={{x: -8}}
|
||||||
|
class="icon"
|
||||||
|
on:click={() => print()}>print</button
|
||||||
|
>
|
||||||
<div transition:slide class="separator" />
|
<div transition:slide class="separator" />
|
||||||
{/if}
|
{/if}
|
||||||
{#if import.meta.env.TAURI_FAMILY === undefined}
|
{#if import.meta.env.TAURI_FAMILY === undefined}
|
||||||
@@ -39,90 +49,31 @@
|
|||||||
{/await}
|
{/await}
|
||||||
{/if}
|
{/if}
|
||||||
{#if $serialPort}
|
{#if $serialPort}
|
||||||
<button title={$LL.backup.TITLE()} use:popup={BackupPopup} class="icon {$syncStatus}">
|
<button use:action={{title: $LL.backup.TITLE()}} use:popup={BackupPopup} class="icon {$syncStatus}">
|
||||||
{#if $syncStatus === "downloading"}
|
{#if $userPreferences.backup}
|
||||||
backup
|
history
|
||||||
{:else if $syncStatus === "uploading"}
|
|
||||||
cloud_download
|
|
||||||
{:else if $userPreferences.backup}
|
|
||||||
cloud_done
|
|
||||||
{:else}
|
{:else}
|
||||||
cloud_off
|
history_toggle_off
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
<button
|
||||||
bind:this={connectButton}
|
bind:this={connectButton}
|
||||||
title="Devices"
|
use:action={{title: $LL.deviceManager.TITLE()}}
|
||||||
use:popup={ConnectionPopup}
|
use:popup={ConnectionPopup}
|
||||||
class="icon connect"
|
class="icon connect"
|
||||||
class:error={$serialPort === undefined}
|
class:error={$serialPort === undefined}
|
||||||
>
|
>
|
||||||
cable
|
cable
|
||||||
</button>
|
</button>
|
||||||
<button title={$LL.profile.TITLE()} use:popup={Profile} class="icon account">person</button>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@keyframes sync {
|
|
||||||
0% {
|
|
||||||
scale: 1 1;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
85% {
|
|
||||||
scale: 1 0;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
86% {
|
|
||||||
scale: 1 1;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
scale: 1 1;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.uploading::after,
|
|
||||||
.downloading::after {
|
|
||||||
content: "";
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
left: 50%;
|
|
||||||
transform-origin: top;
|
|
||||||
translate: -50% 0;
|
|
||||||
|
|
||||||
width: 8px;
|
|
||||||
height: 10px;
|
|
||||||
|
|
||||||
background: var(--md-sys-color-background);
|
|
||||||
|
|
||||||
animation: sync 1s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uploading::after {
|
|
||||||
transform-origin: bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
.downloading.active::after,
|
|
||||||
.uploading.active::after {
|
|
||||||
background: var(--md-sys-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sync.downloading::after {
|
|
||||||
top: 10px;
|
|
||||||
transform-origin: bottom;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
margin-inline: 4px;
|
||||||
background: var(--md-sys-color-outline-variant);
|
background: var(--md-sys-color-outline-variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,12 +135,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon.account {
|
|
||||||
font-size: 32px;
|
|
||||||
color: var(--md-sys-color-on-secondary-container);
|
|
||||||
background: var(--md-sys-color-secondary-container);
|
|
||||||
}
|
|
||||||
|
|
||||||
:disabled {
|
:disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
|||||||
51
src/routes/PageTransition.svelte
Normal file
51
src/routes/PageTransition.svelte
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {fly} from "svelte/transition"
|
||||||
|
import {afterNavigate, beforeNavigate} from "$app/navigation"
|
||||||
|
import {expoIn, expoOut, quadIn, quadOut} from "svelte/easing"
|
||||||
|
|
||||||
|
let inDirection = 0
|
||||||
|
let outDirection = 0
|
||||||
|
let outroEnd: undefined | (() => void) = undefined
|
||||||
|
let animationDone: Promise<void>
|
||||||
|
|
||||||
|
let isNavigating = false
|
||||||
|
|
||||||
|
const routeOrder = ["/config/chords/", "/config/layout/", "/config/settings/"]
|
||||||
|
|
||||||
|
beforeNavigate(navigation => {
|
||||||
|
const from = navigation.from?.url.pathname
|
||||||
|
const to = navigation.to?.url.pathname
|
||||||
|
isNavigating = true
|
||||||
|
|
||||||
|
if (!(from && to && routeOrder.includes(from) && routeOrder.includes(to))) {
|
||||||
|
inDirection = 0
|
||||||
|
outDirection = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromIndex = routeOrder.indexOf(from)
|
||||||
|
const toIndex = routeOrder.indexOf(to)
|
||||||
|
|
||||||
|
inDirection = fromIndex > toIndex ? -1 : 1
|
||||||
|
outDirection = fromIndex > toIndex ? 1 : -1
|
||||||
|
|
||||||
|
animationDone = new Promise(resolve => {
|
||||||
|
outroEnd = resolve
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterNavigate(async () => {
|
||||||
|
await animationDone
|
||||||
|
isNavigating = false
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if !isNavigating}
|
||||||
|
<main
|
||||||
|
in:fly={{x: inDirection * 24, duration: 150, easing: expoOut}}
|
||||||
|
out:fly={{x: outDirection * 24, duration: 150, easing: expoIn}}
|
||||||
|
on:outroend={outroEnd}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
{/if}
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import LL, {setLocale} from "../i18n/i18n-svelte"
|
|
||||||
import {theme} from "$lib/preferences"
|
|
||||||
import {tick} from "svelte"
|
|
||||||
import {detectLocale, locales} from "../i18n/i18n-util"
|
|
||||||
import {loadLocaleAsync} from "../i18n/i18n-util.async"
|
|
||||||
import type {Locales} from "../i18n/i18n-types"
|
|
||||||
|
|
||||||
let locale = (localStorage.getItem("locale") as Locales) || detectLocale()
|
|
||||||
$: (async () => {
|
|
||||||
localStorage.setItem("locale", locale)
|
|
||||||
await loadLocaleAsync(locale)
|
|
||||||
setLocale(locale)
|
|
||||||
})()
|
|
||||||
|
|
||||||
function switchTheme() {
|
|
||||||
const mode = $theme.mode === "light" ? "dark" : "light"
|
|
||||||
if (document.startViewTransition) {
|
|
||||||
document.startViewTransition(async () => {
|
|
||||||
$theme.mode = mode
|
|
||||||
await tick()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
$theme.mode = mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>{$LL.profile.TITLE()}</h2>
|
|
||||||
<fieldset>
|
|
||||||
<legend>
|
|
||||||
<span class="icon">format_paint</span>
|
|
||||||
{$LL.profile.theme.TITLE()}
|
|
||||||
</legend>
|
|
||||||
|
|
||||||
<input title={$LL.profile.theme.COLOR_SCHEME()} type="color" bind:value={$theme.color} />
|
|
||||||
<button
|
|
||||||
title={$theme.mode === "light" ? $LL.profile.theme.LIGHT_MODE() : $LL.profile.theme.DARK_MODE()}
|
|
||||||
class="icon"
|
|
||||||
on:click={switchTheme}
|
|
||||||
>
|
|
||||||
{#if $theme.mode === "light"}
|
|
||||||
light_mode
|
|
||||||
{:else if $theme.mode === "dark"}
|
|
||||||
dark_mode
|
|
||||||
{:else}
|
|
||||||
TODO
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<legend>
|
|
||||||
<span class="icon">translate</span>
|
|
||||||
{$LL.profile.LANGUAGE()}
|
|
||||||
</legend>
|
|
||||||
{#each locales as code}
|
|
||||||
<label>{code}<input bind:group={locale} type="radio" value={code} name="language" /></label>
|
|
||||||
{/each}
|
|
||||||
</fieldset>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
h2 {
|
|
||||||
grid-column: 1 / span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto auto;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
border: 1px solid var(--md-sys-color-outline);
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
input[type="color"] {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
inline-size: 24px;
|
|
||||||
block-size: 24px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
color: inherit;
|
|
||||||
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
&::-webkit-color-swatch-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-color-swatch {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
68
src/routes/SyncOverlay.svelte
Normal file
68
src/routes/SyncOverlay.svelte
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {syncProgress, syncStatus} from "$lib/serial/connection"
|
||||||
|
import LL from "../i18n/i18n-svelte"
|
||||||
|
|
||||||
|
$: if (dialog) toggleDialog($syncStatus)
|
||||||
|
|
||||||
|
async function toggleDialog(status: "uploading" | "downloading" | string) {
|
||||||
|
// debounce
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 150))
|
||||||
|
if ($syncStatus !== status) return
|
||||||
|
|
||||||
|
if (!dialog.open && ($syncStatus === "uploading" || $syncStatus === "downloading")) {
|
||||||
|
message = $syncStatus
|
||||||
|
dialog.showModal()
|
||||||
|
dialog.animate([{opacity: 0}, {opacity: 1}], {duration: 250, easing: "ease"})
|
||||||
|
} else if (dialog.open) {
|
||||||
|
const animation = dialog.animate([{opacity: 1}, {opacity: 0}], {duration: 250, easing: "ease"})
|
||||||
|
animation.addEventListener("finish", () => {
|
||||||
|
dialog.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let message: "downloading" | "uploading"
|
||||||
|
let dialog: HTMLDialogElement
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<dialog bind:this={dialog}>
|
||||||
|
{#if message === "downloading"}
|
||||||
|
<h2>{$LL.sync.TITLE_READ()}</h2>
|
||||||
|
{:else}
|
||||||
|
<h2>{$LL.sync.TITLE_WRITE()}</h2>
|
||||||
|
<p>{$LL.sync.DISCLAIMER_WRITE()}</p>
|
||||||
|
{/if}
|
||||||
|
<progress max={$syncProgress?.max ?? 1} value={$syncProgress?.current ?? 1}></progress>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
dialog::backdrop {
|
||||||
|
background: rgba(0 0 0 / 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress::-webkit-progress-bar {
|
||||||
|
background: var(--md-sys-color-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress::-webkit-progress-value {
|
||||||
|
background: var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog {
|
||||||
|
max-width: 14cm;
|
||||||
|
padding: 2cm;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {KEYMAP_CODES, KEYMAP_IDS} from "$lib/serial/keymap-codes"
|
import {KEYMAP_IDS} from "$lib/serial/keymap-codes"
|
||||||
import type {ChordInfo} from "$lib/undo-redo"
|
import type {ChordInfo} from "$lib/undo-redo"
|
||||||
import {changes, ChangeType} from "$lib/undo-redo"
|
import {changes, ChangeType} from "$lib/undo-redo"
|
||||||
import {createEventDispatcher} from "svelte"
|
import {createEventDispatcher} from "svelte"
|
||||||
import LL from "../../../i18n/i18n-svelte"
|
import LL from "../../../i18n/i18n-svelte"
|
||||||
|
import ActionString from "$lib/components/ActionString.svelte"
|
||||||
|
|
||||||
export let chord: ChordInfo | undefined = undefined
|
export let chord: ChordInfo | undefined = undefined
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
changes.push({
|
changes.push({
|
||||||
type: ChangeType.Chord,
|
type: ChangeType.Chord,
|
||||||
id: chord!.id,
|
id: chord!.id,
|
||||||
actions: [...pressedKeys],
|
actions: [...pressedKeys].sort(),
|
||||||
phrase: chord!.phrase,
|
phrase: chord!.phrase,
|
||||||
})
|
})
|
||||||
return changes
|
return changes
|
||||||
@@ -53,12 +54,7 @@
|
|||||||
{:else if !editing && !chord}
|
{:else if !editing && !chord}
|
||||||
<span>{$LL.configure.chords.NEW_CHORD()}</span>
|
<span>{$LL.configure.chords.NEW_CHORD()}</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#each editing ? [...pressedKeys].sort() : chord?.actions ?? [] as actionId}
|
<ActionString display="keys" actions={editing ? [...pressedKeys].sort() : chord?.actions ?? []} />
|
||||||
{@const {icon, id, code} = KEYMAP_CODES[actionId] ?? {code: actionId}}
|
|
||||||
<kbd class:icon={!!icon}>
|
|
||||||
{icon ?? id ?? `0x${code.toString(16)}`}
|
|
||||||
</kbd>
|
|
||||||
{/each}
|
|
||||||
<sup>•</sup>
|
<sup>•</sup>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -87,12 +83,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
|
||||||
height: 24px;
|
|
||||||
padding-block: auto;
|
|
||||||
transition: color 250ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
button::after {
|
button::after {
|
||||||
content: "";
|
content: "";
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {KEYMAP_CODES, KEYMAP_IDS, specialKeycodes} from "$lib/serial/keymap-codes"
|
import {KEYMAP_IDS, specialKeycodes} from "$lib/serial/keymap-codes"
|
||||||
import {tick} from "svelte"
|
import {tick} from "svelte"
|
||||||
import ActionSelector from "$lib/components/layout/ActionSelector.svelte"
|
import ActionSelector from "$lib/components/layout/ActionSelector.svelte"
|
||||||
import {changes, ChangeType} from "$lib/undo-redo"
|
import {changes, ChangeType} from "$lib/undo-redo"
|
||||||
import type {ChordInfo} from "$lib/undo-redo"
|
import type {ChordInfo} from "$lib/undo-redo"
|
||||||
import {scale} from "svelte/transition"
|
import {scale} from "svelte/transition"
|
||||||
|
import ActionString from "$lib/components/ActionString.svelte"
|
||||||
|
|
||||||
export let chord: ChordInfo
|
export let chord: ChordInfo
|
||||||
|
|
||||||
@@ -149,14 +150,7 @@
|
|||||||
<div />
|
<div />
|
||||||
<!-- placeholder for cursor placement -->
|
<!-- placeholder for cursor placement -->
|
||||||
{/if}
|
{/if}
|
||||||
{#each chord.phrase as actionId, i (`${actionId}:${i}`)}
|
<ActionString actions={chord.phrase} />
|
||||||
{@const {icon, id, code} = KEYMAP_CODES[actionId] ?? {code: actionId}}
|
|
||||||
{#if !icon && id?.length === 1}
|
|
||||||
<span>{id}</span>
|
|
||||||
{:else}
|
|
||||||
<kbd class:icon={!!icon}>{icon ?? id ?? `0x${code.toString(16)}`}</kbd>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
<sup>•</sup>
|
<sup>•</sup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -203,14 +197,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:not(.cursor) + kbd {
|
|
||||||
margin-inline-start: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
kbd + * {
|
|
||||||
margin-inline-start: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[role="textbox"] {
|
[role="textbox"] {
|
||||||
cursor: text;
|
cursor: text;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user