mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-19 08:22:53 +00:00
@@ -1,4 +1,4 @@
|
||||
import type {Translation} from "../i18n-types"
|
||||
import type { Translation } from "../i18n-types"
|
||||
|
||||
const de = {
|
||||
TITLE: "CharaChorder Gerätemanager",
|
||||
@@ -11,9 +11,7 @@ const de = {
|
||||
},
|
||||
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.",
|
||||
TITLE_WRITE: "Änderungen werden gespeichert",
|
||||
},
|
||||
backup: {
|
||||
TITLE: "Sicherungskopie",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type {BaseTranslation} from "../i18n-types"
|
||||
import type { BaseTranslation } from "../i18n-types"
|
||||
|
||||
const en = {
|
||||
TITLE: "CharaChorder Device Manager",
|
||||
@@ -18,9 +18,7 @@ const en = {
|
||||
},
|
||||
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.",
|
||||
TITLE_WRITE: "Saving changes to device",
|
||||
},
|
||||
modal: {
|
||||
CLOSE: "Close",
|
||||
|
||||
26
src/lib/backup/compat/legacy-chords.sample.csv
Normal file
26
src/lib/backup/compat/legacy-chords.sample.csv
Normal file
@@ -0,0 +1,26 @@
|
||||
e + b + a,babe
|
||||
e + c + b,because
|
||||
f + e + c + a,face
|
||||
h + e + c + a,each
|
||||
i + d + ',I'd
|
||||
i + g + b,big
|
||||
i + g + e,give
|
||||
k + b + a,back
|
||||
k + e + a,take
|
||||
l + e + a,late
|
||||
l + e + d + a,lead
|
||||
l + f + e,feel
|
||||
l + g + e + a,large
|
||||
l + h + e,help
|
||||
l + i + a,Lia
|
||||
l + i + f,fill
|
||||
l + i + f + e,life
|
||||
l + i + g + b + a,gitlab
|
||||
l + k + i + e,like
|
||||
m + e + a,make
|
||||
m + i + ',I'm
|
||||
n + c + a,can
|
||||
n + d + a,and
|
||||
n + e + b,been
|
||||
n + e + b + a,enable
|
||||
n + e + d,end
|
||||
|
26
src/lib/backup/compat/legacy-chords.sample.json
Normal file
26
src/lib/backup/compat/legacy-chords.sample.json
Normal file
@@ -0,0 +1,26 @@
|
||||
e + b + a,babe
|
||||
e + c + b,because
|
||||
f + e + c + a,face
|
||||
h + e + c + a,each
|
||||
i + d + ',I'd
|
||||
i + g + b,big
|
||||
i + g + e,give
|
||||
k + b + a,back
|
||||
k + e + a,take
|
||||
l + e + a,late
|
||||
l + e + d + a,lead
|
||||
l + f + e,feel
|
||||
l + g + e + a,large
|
||||
l + h + e,help
|
||||
l + i + a,Lia
|
||||
l + i + f,fill
|
||||
l + i + f + e,life
|
||||
l + i + g + b + a,gitlab
|
||||
l + k + i + e,like
|
||||
m + e + a,make
|
||||
m + i + ',I'm
|
||||
n + c + a,can
|
||||
n + d + a,and
|
||||
n + e + b,been
|
||||
n + e + b + a,enable
|
||||
n + e + d,end
|
||||
21
src/lib/backup/compat/legacy-chords.ts
Normal file
21
src/lib/backup/compat/legacy-chords.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { KEYMAP_IDS } from "$lib/serial/keymap-codes"
|
||||
import type { CharaChordFile } from "$lib/share/chara-file"
|
||||
|
||||
|
||||
export function csvChordsToJson(csv: string): CharaChordFile {
|
||||
return {
|
||||
charaVersion: 1,
|
||||
type: 'chords',
|
||||
chords: csv.split('\n').map(line => {
|
||||
const [input, output] = line.split(',', 2)
|
||||
return [
|
||||
input.split('+').map(it => KEYMAP_IDS.get(it.trim())?.code ?? 0),
|
||||
output.split('').map(it => KEYMAP_IDS.get(it.trim())?.code ?? 0),
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function isCsvLayout(csv: string): boolean {
|
||||
return /^((\s*\w+\s*+?),\s*\w+\n?)+$/.test(csv)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
import LL from "../i18n/i18n-svelte"
|
||||
import {changes, ChangeType, chords, layout, overlay, settings} from "$lib/undo-redo"
|
||||
import type {Change} from "$lib/undo-redo"
|
||||
import {fly} from "svelte/transition"
|
||||
import {fly, slide} from "svelte/transition"
|
||||
import {action} from "$lib/title"
|
||||
import {
|
||||
deviceChords,
|
||||
@@ -34,10 +34,9 @@
|
||||
}
|
||||
let redoQueue: Change[] = []
|
||||
|
||||
async function apply() {
|
||||
async function save() {
|
||||
const port = $serialPort
|
||||
if (!port) return
|
||||
|
||||
$syncStatus = "uploading"
|
||||
|
||||
for (const [id, {actions, phrase}] of $overlay.chords) {
|
||||
@@ -79,21 +78,7 @@
|
||||
await port.setSetting(id, setting)
|
||||
}
|
||||
|
||||
$deviceLayout = $layout.map(layer => layer.map<number>(({action}) => action)) as [
|
||||
number[],
|
||||
number[],
|
||||
number[],
|
||||
]
|
||||
$deviceChords = $chords
|
||||
.map(({actions, phrase}) => ({actions, phrase}))
|
||||
.filter(({phrase}) => phrase.length > 1)
|
||||
$deviceSettings = $settings.map(({value}) => value)
|
||||
$changes = []
|
||||
$syncStatus = "done"
|
||||
}
|
||||
|
||||
async function flashChanges() {
|
||||
$syncStatus = "uploading"
|
||||
// Yes, this is a completely arbitrary and unnecessary delay.
|
||||
// The only purpose of it is to create a sense of weight,
|
||||
// aka make it more "energy intensive" to click.
|
||||
@@ -101,7 +86,7 @@
|
||||
// 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
|
||||
// "Your device will break after you click this 10,000 times!"
|
||||
const virtualWriteTime = 6000
|
||||
const virtualWriteTime = 1000
|
||||
const startStamp = performance.now()
|
||||
await new Promise<void>(resolve => {
|
||||
function animate() {
|
||||
@@ -118,83 +103,22 @@
|
||||
}
|
||||
requestAnimationFrame(animate)
|
||||
})
|
||||
if ($serialPort) {
|
||||
await $serialPort.commit()
|
||||
$changes = []
|
||||
}
|
||||
await port.commit()
|
||||
|
||||
$deviceLayout = $layout.map(layer => layer.map<number>(({action}) => action)) as [
|
||||
number[],
|
||||
number[],
|
||||
number[],
|
||||
]
|
||||
$deviceChords = $chords
|
||||
.map(({actions, phrase}) => ({actions, phrase}))
|
||||
.filter(({phrase}) => phrase.length > 1)
|
||||
$deviceSettings = $settings.map(({value}) => value)
|
||||
$changes = []
|
||||
$syncStatus = "done"
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- <svg viewBox="0 0 36 36" style="width: 48px">
|
||||
<defs>
|
||||
<rect
|
||||
id="mouth"
|
||||
x="13"
|
||||
y="13"
|
||||
width="512"
|
||||
height="10"
|
||||
rx="5"
|
||||
style="transform-origin: center; animation-direction: alternate-reverse"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="scale"
|
||||
values="1; 0.25; 1"
|
||||
keyTimes="0; 0.33; 1"
|
||||
dur="0.4"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
<mask id="inner-mask">
|
||||
<rect x="0" y="0" width="36" height="36" />
|
||||
<use fill="white" href="#mouth" />
|
||||
</mask>
|
||||
<mask id="clip">
|
||||
<rect x="0" y="0" width="36" height="36" fill="white" />
|
||||
<use fill="black" href="#mouth" />
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
<g mask="url(#clip)" style="transform-origin: center">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="scale"
|
||||
values="1 1;0.9 0.8; 1 1"
|
||||
keyTimes="0; 0.33; 1"
|
||||
dur="0.4"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
<circle cx="18" cy="18" r="14" stroke="currentcolor" fill="none" stroke-width="5" />
|
||||
<circle cx="18" cy="18" r="10" fill="currentcolor" stroke-width="6" />
|
||||
</g>
|
||||
<g mask="url(#inner-mask)">
|
||||
<text
|
||||
mask="url(#inner-mask)"
|
||||
x="18"
|
||||
y="17.2"
|
||||
fill="currentcolor"
|
||||
text-anchor="start"
|
||||
dominant-baseline="central"
|
||||
font-size="8"
|
||||
font-weight="bold"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="translate"
|
||||
from="0"
|
||||
to="-76.8"
|
||||
dur="1.6"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
c c o s c c o s
|
||||
</text>
|
||||
</g>
|
||||
</svg> -->
|
||||
|
||||
<button
|
||||
use:action={{title: $LL.saveActions.UNDO(), shortcut: "ctrl+z"}}
|
||||
class="icon"
|
||||
@@ -207,46 +131,17 @@
|
||||
disabled={redoQueue.length === 0}
|
||||
on:click={redo}>redo</button
|
||||
>
|
||||
<div class="separator" />
|
||||
<button
|
||||
use:action={{title: $LL.saveActions.SAVE(), shortcut: "ctrl+shift+s"}}
|
||||
on:click={flashChanges}
|
||||
class="icon">save</button
|
||||
>
|
||||
{#if $changes.length !== 0}
|
||||
<div transition:slide class="separator" />
|
||||
<button
|
||||
class="click-me"
|
||||
transition:fly={{x: 8}}
|
||||
on:click={apply}
|
||||
use:action={{
|
||||
title: $LL.changes.TITLE(),
|
||||
shortcut: "ctrl+s",
|
||||
}}><span class="icon">bolt</span>{$LL.saveActions.APPLY()}</button
|
||||
transition:fly={{x: 10}}
|
||||
use:action={{title: $LL.saveActions.SAVE(), shortcut: "ctrl+shift+s"}}
|
||||
on:click={save}
|
||||
class="icon">save</button
|
||||
>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.click-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
height: fit-content;
|
||||
margin-inline: 8px;
|
||||
padding-block: 2px;
|
||||
padding-inline-start: 4px;
|
||||
padding-inline-end: 8px;
|
||||
|
||||
font-family: inherit;
|
||||
font-weight: bold;
|
||||
color: var(--md-sys-color-primary);
|
||||
|
||||
border: 2px solid var(--md-sys-color-primary);
|
||||
border-radius: 18px;
|
||||
outline: 2px dashed var(--md-sys-color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user