mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-14 14:02:49 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
f0ad19e6c2
|
|||
|
9022a09b4c
|
|||
|
7e3e61afd7
|
|||
|
08f594d164
|
|||
|
046595b51f
|
|||
|
fbc5303690
|
|||
|
ad41d39bfb
|
|||
|
6faaa18b3b
|
|||
|
6ab6959129
|
|||
|
44d89d3f35
|
|||
|
eaf0adaf01
|
|||
|
5b6a5ea36d
|
|||
|
14cbb5553b
|
|||
|
|
8ed72fe958 | ||
|
06b83f79ef
|
@@ -92,6 +92,7 @@ const config = {
|
||||
"stat_2",
|
||||
"description",
|
||||
"add_circle",
|
||||
"refresh",
|
||||
],
|
||||
codePoints: {
|
||||
speed: "e9e4",
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "charachorder-device-manager",
|
||||
"version": "1.2.0",
|
||||
"version": "1.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "charachorder-device-manager",
|
||||
"version": "1.2.0",
|
||||
"version": "1.4.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "charachorder-device-manager",
|
||||
"version": "1.2.0",
|
||||
"version": "1.4.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"private": true,
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "app"
|
||||
version = "1.2.0"
|
||||
version = "1.4.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["Thea Schöbl <dev@theaninova.de>"]
|
||||
license = "AGPL-3"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"devPath": "http://localhost:5173",
|
||||
"distDir": "../build"
|
||||
},
|
||||
"package": { "productName": "amacc1ng", "version": "1.2.0" },
|
||||
"package": { "productName": "amacc1ng", "version": "1.4.0" },
|
||||
"tauri": {
|
||||
"allowlist": { "all": false },
|
||||
"bundle": {
|
||||
|
||||
1
src/env.d.ts
vendored
1
src/env.d.ts
vendored
@@ -12,6 +12,7 @@ interface ImportMetaEnv {
|
||||
readonly VITE_BUGS_URL: string
|
||||
readonly VITE_DOCS_URL: string
|
||||
readonly VIET_LEARN_URL: string
|
||||
readonly VITE_LATEST_FIRMWARE: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
@@ -14,12 +14,12 @@ const de = {
|
||||
sync: {
|
||||
TITLE_READ: "Neueste Änderungen werden abgerufen",
|
||||
TITLE_WRITE: "Änderungen werden gespeichert",
|
||||
RELOAD: "Neu laden",
|
||||
},
|
||||
backup: {
|
||||
TITLE: "Verlauf speichern",
|
||||
TITLE: "Lokale Kopie",
|
||||
INDIVIDUAL: "Einzeldateien",
|
||||
DISCLAIMER:
|
||||
"Der Verlauf wird als Backup in diesem Browser gespeichert. Der Verlauf bleibt auf diesem Computer.",
|
||||
DISCLAIMER: "Das Backup in diesem Browser gespeichert und bleibt nur auf diesem Computer.",
|
||||
DOWNLOAD: "Alles herunterladen",
|
||||
RESTORE: "Wiederherstellen",
|
||||
},
|
||||
|
||||
@@ -12,15 +12,16 @@ const en = {
|
||||
TITLE: "Update your device",
|
||||
},
|
||||
backup: {
|
||||
TITLE: "Store History",
|
||||
TITLE: "Local backup",
|
||||
INDIVIDUAL: "Individual backups",
|
||||
DISCLAIMER: "Your history is stored as a backup in this browser. The history remains on your computer.",
|
||||
DISCLAIMER: "A backup is made and stored in this browser, and always remains only on your computer.",
|
||||
DOWNLOAD: "Download Everything",
|
||||
RESTORE: "Restore",
|
||||
},
|
||||
sync: {
|
||||
TITLE_READ: "Reading latest changes",
|
||||
TITLE_WRITE: "Saving changes to device",
|
||||
RELOAD: "Reload",
|
||||
},
|
||||
modal: {
|
||||
CLOSE: "Close",
|
||||
|
||||
@@ -6,41 +6,49 @@ actions:
|
||||
id: "LEFT_CTRL"
|
||||
display: CTRL
|
||||
title: Control Keyboard Modifier
|
||||
keyCode: ControlLeft
|
||||
variant: left
|
||||
513: &left_shift
|
||||
id: "LEFT_SHIFT"
|
||||
title: Shift Keyboard Modifier
|
||||
keyCode: ShiftLeft
|
||||
variant: left
|
||||
icon: shift
|
||||
514: &left_alt
|
||||
id: "LEFT_ALT"
|
||||
display: ALT
|
||||
title: Alt Keyboard Modifier
|
||||
keyCode: AltLeft
|
||||
variant: left
|
||||
515: &left_gui
|
||||
id: "LEFT_GUI"
|
||||
title: GUI Keyboard Modifier
|
||||
keyCode: MetaLeft
|
||||
icon: apps
|
||||
variant: left
|
||||
516:
|
||||
variationOf: 512
|
||||
<<: *left_ctrl
|
||||
id: "RIGHT_CTRL"
|
||||
keyCode: ControlRight
|
||||
variant: right
|
||||
517:
|
||||
variationOf: 513
|
||||
<<: *left_shift
|
||||
id: "RIGHT_SHIFT"
|
||||
keyCode: ShiftRight
|
||||
variant: right
|
||||
518:
|
||||
variationOf: 514
|
||||
<<: *left_alt
|
||||
id: "RIGHT_ALT"
|
||||
keyCode: AltRight
|
||||
variant: right
|
||||
519:
|
||||
variationOf: 515
|
||||
<<: *left_gui
|
||||
id: "RIGHT_GUI"
|
||||
keyCode: MetaRight
|
||||
variant: right
|
||||
520:
|
||||
id: "RELEASE_MOD"
|
||||
|
||||
@@ -3,7 +3,9 @@ description: OS-Layout sensitive keycodes
|
||||
actions:
|
||||
256:
|
||||
id: "KSC_00"
|
||||
icon: block
|
||||
title: No Key Pressed
|
||||
description: Also commonly used at the end of a chord to remove auto-spaces
|
||||
257:
|
||||
id: "KSC_01"
|
||||
title: Keyboard Error Roll Over
|
||||
@@ -918,35 +920,27 @@ actions:
|
||||
description: Not required to be supported by any OS
|
||||
480:
|
||||
id: "KSC_E0"
|
||||
keyCode: "ControlLeft"
|
||||
title: Keyboard Left Control
|
||||
481:
|
||||
id: "KSC_E1"
|
||||
keyCode: "ShiftLeft"
|
||||
title: Keyboard Left Shift
|
||||
482:
|
||||
id: "KSC_E2"
|
||||
keyCode: "AltLeft"
|
||||
title: Keyboard Left Alt
|
||||
483:
|
||||
id: "KSC_E3"
|
||||
keyCode: "MetaLeft"
|
||||
title: Keyboard Left GUI
|
||||
484:
|
||||
id: "KSC_E4"
|
||||
keyCode: "ControlRight"
|
||||
title: Keyboard Right Control
|
||||
485:
|
||||
id: "KSC_E5"
|
||||
keyCode: "ShiftRight"
|
||||
title: Keyboard Right Shift
|
||||
486:
|
||||
id: "KSC_E6"
|
||||
keyCode: "AltRight"
|
||||
title: Keyboard Right Alt
|
||||
487:
|
||||
id: "KSC_E7"
|
||||
keyCode: "MetaRight"
|
||||
title: Keyboard Right GUI
|
||||
488:
|
||||
id: "KSC_E8"
|
||||
|
||||
@@ -61,7 +61,6 @@ export async function restoreBackup(event: Event) {
|
||||
} else if (isCsvChords(text)) {
|
||||
restoreFromFile(csvChordsToJson(text))
|
||||
} else {
|
||||
alert("Unknown backup format")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,10 @@ export function csvChordsToJson(csv: string): CharaChordFile {
|
||||
.map(line => {
|
||||
const [input, output] = line.split(/,(?=[^,]*$)/, 2)
|
||||
return [
|
||||
input.split("+").map(it => KEYMAP_IDS.get(it.trim())?.code ?? 0),
|
||||
input
|
||||
.split("+")
|
||||
.map(it => KEYMAP_IDS.get(it.trim())?.code ?? 0)
|
||||
.sort((a, b) => a - b),
|
||||
output
|
||||
.trim()
|
||||
.split("")
|
||||
|
||||
@@ -48,9 +48,7 @@ export function deserializeActions(native: bigint): number[] {
|
||||
const actions = []
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const action = Number(native & 0x3ffn)
|
||||
if (action !== 0) {
|
||||
actions.push(action)
|
||||
}
|
||||
actions.push(action)
|
||||
native >>= 10n
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,12 @@ export async function initSerial(manual = false) {
|
||||
const device = get(serialPort) ?? new CharaDevice()
|
||||
await device.init(manual)
|
||||
serialPort.set(device)
|
||||
sync()
|
||||
}
|
||||
|
||||
export async function sync() {
|
||||
const device = get(serialPort)
|
||||
if (!device) return
|
||||
const chordCount = await device.getChordCount()
|
||||
syncStatus.set("downloading")
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ export class CharaDevice {
|
||||
async deleteChord(chord: Pick<Chord, "actions">) {
|
||||
const status = await this.send(`CML C4 ${stringifyChordActions(chord.actions)}`)
|
||||
console.log(status)
|
||||
if (status.at(-1) !== "2") throw new Error(`Failed with status ${status}`)
|
||||
if (status.at(-1) !== "2" && status.at(-1) !== "0") throw new Error(`Failed with status ${status}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,3 @@ export const KEYMAP_IDS: Map<string, KeyInfo> = new Map(
|
||||
),
|
||||
).filter(([id]) => id !== undefined),
|
||||
)
|
||||
|
||||
export const specialKeycodes = new Map([
|
||||
[" ", 32], // Space
|
||||
])
|
||||
|
||||
@@ -9,7 +9,6 @@ export const setting: Action<HTMLInputElement, {id: number; inverse?: number; sc
|
||||
const type = node.getAttribute("type") as "number" | "checkbox"
|
||||
const min = node.hasAttribute("min") ? Number(node.getAttribute("min")) : undefined
|
||||
const max = node.hasAttribute("max") ? Number(node.getAttribute("max")) : undefined
|
||||
console.log(min, max, "|", id, "|", node.getAttribute("min"), node.getAttribute("max"))
|
||||
|
||||
const unsubscribe = settings.subscribe(async settings => {
|
||||
if (id in settings) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import {slide, fade} from "svelte/transition"
|
||||
import {preference} from "$lib/preferences"
|
||||
import LL from "../i18n/i18n-svelte"
|
||||
import {downloadBackup} from "$lib/backup/backup"
|
||||
|
||||
function reboot() {
|
||||
$serialPort?.reboot()
|
||||
@@ -15,6 +16,7 @@
|
||||
}
|
||||
|
||||
function bootloader() {
|
||||
downloadBackup()
|
||||
$serialPort?.bootloader()
|
||||
$serialPort = undefined
|
||||
rebootInfo = true
|
||||
@@ -51,6 +53,11 @@
|
||||
<br />
|
||||
Version {$serialPort.version}
|
||||
</p>
|
||||
{#if $serialPort.version.toString() !== import.meta.env.VITE_LATEST_FIRMWARE}
|
||||
<a href="https://docs.charachorder.com/CharaChorder%20One.html#updating-the-firmware"
|
||||
>Firmware Update Instructions</a
|
||||
>
|
||||
{/if}
|
||||
<!--<button on:click={updateFirmware}>Update</button>-->
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
<script lang="ts">
|
||||
import {syncProgress, syncStatus} from "$lib/serial/connection"
|
||||
import {serialPort, syncProgress, syncStatus, sync} from "$lib/serial/connection"
|
||||
import LL from "../i18n/i18n-svelte"
|
||||
import {fly} from "svelte/transition"
|
||||
import {slide} from "svelte/transition"
|
||||
</script>
|
||||
|
||||
{#if $syncStatus !== "done"}
|
||||
<div transition:fly={{y: 40}}>
|
||||
<progress max={$syncProgress?.max ?? 1} value={$syncProgress?.current ?? 1}></progress>
|
||||
{#if $syncStatus === "downloading"}
|
||||
<div>{$LL.sync.TITLE_READ()}</div>
|
||||
{:else}
|
||||
<div>{$LL.sync.TITLE_WRITE()}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="container">
|
||||
{#if $syncStatus !== "done"}
|
||||
<div transition:slide>
|
||||
<progress max={$syncProgress?.max ?? 1} value={$syncProgress?.current ?? 1}></progress>
|
||||
{#if $syncStatus === "downloading"}
|
||||
<div>{$LL.sync.TITLE_READ()}</div>
|
||||
{:else}
|
||||
<div>{$LL.sync.TITLE_WRITE()}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $serialPort}
|
||||
<button transition:slide on:click={sync}><span class="icon">refresh</span>{$LL.sync.RELOAD()}</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,12 @@
|
||||
function keydown(event: KeyboardEvent) {
|
||||
if (!editing) return
|
||||
event.preventDefault()
|
||||
pressedKeys.add(inputToAction(event, get(serialPort)?.device === "X")!)
|
||||
const input = inputToAction(event, get(serialPort)?.device === "X")
|
||||
if (input == undefined) {
|
||||
alert("Invalid key")
|
||||
return
|
||||
}
|
||||
pressedKeys.add(input)
|
||||
pressedKeys = pressedKeys
|
||||
}
|
||||
|
||||
@@ -61,12 +66,19 @@
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
$: chordActions = chord?.actions.slice(chord.actions.lastIndexOf(0) + 1).toSorted(compare)
|
||||
$: compoundIndices = chord?.actions.slice(0, chord.actions.indexOf(0))
|
||||
|
||||
$: {
|
||||
console.log(chord?.actions, chordActions, compoundIndices)
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
class:deleted={chord && chord.deleted}
|
||||
class:edited={chord && chord.actionsChanged}
|
||||
class:invalid={chord && chord.actions.toSorted(compare).some((it, i) => chord?.actions[i] !== it)}
|
||||
class:invalid={chord && chordActions?.some((it, i) => chordActions?.[i] !== it)}
|
||||
class="chord"
|
||||
on:click={edit}
|
||||
on:keydown={keydown}
|
||||
@@ -77,7 +89,15 @@
|
||||
{:else if !editing && !chord}
|
||||
<span>{$LL.configure.chords.NEW_CHORD()}</span>
|
||||
{/if}
|
||||
<ActionString display="keys" actions={editing ? [...pressedKeys].sort(compare) : chord?.actions ?? []} />
|
||||
{#if !editing}
|
||||
{#each compoundIndices ?? [] as index}
|
||||
<sub>{index}</sub>
|
||||
{/each}
|
||||
{#if compoundIndices?.length}
|
||||
<span>→</span>
|
||||
{/if}
|
||||
{/if}
|
||||
<ActionString display="keys" actions={editing ? [...pressedKeys].sort(compare) : chordActions ?? []} />
|
||||
<button class="icon add" on:click|stopPropagation={addSpecial}>add_circle</button>
|
||||
<sup>•</sup>
|
||||
</button>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {KEYMAP_IDS, specialKeycodes} from "$lib/serial/keymap-codes"
|
||||
import {tick} from "svelte"
|
||||
import ActionSelector from "$lib/components/layout/ActionSelector.svelte"
|
||||
import {changes, ChangeType} from "$lib/undo-redo"
|
||||
import type {ChordInfo} from "$lib/undo-redo"
|
||||
import {scale} from "svelte/transition"
|
||||
@@ -26,6 +24,7 @@
|
||||
} else if (event.key === "Delete") {
|
||||
deleteAction(cursorPosition)
|
||||
} else {
|
||||
if (event.key === "Shift") return
|
||||
const action = inputToAction(event, get(serialPort)?.device === "X")
|
||||
if (action !== undefined) {
|
||||
insertAction(cursorPosition, action)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {KEYMAP_IDS, KEYMAP_KEYCODES, specialKeycodes} from "$lib/serial/keymap-codes"
|
||||
import {KEYMAP_IDS, KEYMAP_KEYCODES} from "$lib/serial/keymap-codes"
|
||||
|
||||
export function inputToAction(event: KeyboardEvent, useKeycodes?: boolean): number | undefined {
|
||||
if (useKeycodes) {
|
||||
return KEYMAP_KEYCODES.get(event.code)
|
||||
} else {
|
||||
return KEYMAP_IDS.get(event.key)?.code ?? specialKeycodes.get(event.key)
|
||||
return KEYMAP_IDS.get(event.key)?.code ?? KEYMAP_KEYCODES.get(event.code)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<h3>Type the following to confim the action</h3>
|
||||
<h3>Type the following to confirm the action</h3>
|
||||
|
||||
<p>{challengeString}</p>
|
||||
<input type="text" bind:value={challengeInput} placeholder={challengeString} />
|
||||
|
||||
@@ -17,6 +17,7 @@ process.env.VITE_HOMEPAGE_URL = repository.url.replace(/\.git$/, "")
|
||||
process.env.VITE_DOCS_URL = homepage
|
||||
process.env.VITE_BUGS_URL = bugs.url
|
||||
process.env.VITE_LEARN_URL = "https://www.iq-eq.io/"
|
||||
process.env.VITE_LATEST_FIRMWARE = "1.1.3"
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
|
||||
Reference in New Issue
Block a user