mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-21 17:32:41 +00:00
@@ -1,4 +1,4 @@
|
|||||||
import type {Translation} from "../i18n-types"
|
import type { Translation } from "../i18n-types"
|
||||||
|
|
||||||
const de = {
|
const de = {
|
||||||
TITLE: "CharaChorder Gerätemanager",
|
TITLE: "CharaChorder Gerätemanager",
|
||||||
@@ -11,9 +11,7 @@ const de = {
|
|||||||
},
|
},
|
||||||
sync: {
|
sync: {
|
||||||
TITLE_READ: "Neueste Änderungen werden abgerufen",
|
TITLE_READ: "Neueste Änderungen werden abgerufen",
|
||||||
TITLE_WRITE: "Änderungen werden gebrannt",
|
TITLE_WRITE: "Änderungen werden gespeichert",
|
||||||
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",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type {BaseTranslation} from "../i18n-types"
|
import type { BaseTranslation } from "../i18n-types"
|
||||||
|
|
||||||
const en = {
|
const en = {
|
||||||
TITLE: "CharaChorder Device Manager",
|
TITLE: "CharaChorder Device Manager",
|
||||||
@@ -18,9 +18,7 @@ const en = {
|
|||||||
},
|
},
|
||||||
sync: {
|
sync: {
|
||||||
TITLE_READ: "Reading latest changes",
|
TITLE_READ: "Reading latest changes",
|
||||||
TITLE_WRITE: "Burning changes to device",
|
TITLE_WRITE: "Saving 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",
|
||||||
|
|||||||
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 LL from "../i18n/i18n-svelte"
|
||||||
import {changes, ChangeType, chords, layout, overlay, settings} from "$lib/undo-redo"
|
import {changes, ChangeType, chords, layout, overlay, settings} from "$lib/undo-redo"
|
||||||
import type {Change} 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 {action} from "$lib/title"
|
||||||
import {
|
import {
|
||||||
deviceChords,
|
deviceChords,
|
||||||
@@ -34,10 +34,9 @@
|
|||||||
}
|
}
|
||||||
let redoQueue: Change[] = []
|
let redoQueue: Change[] = []
|
||||||
|
|
||||||
async function apply() {
|
async function save() {
|
||||||
const port = $serialPort
|
const port = $serialPort
|
||||||
if (!port) return
|
if (!port) return
|
||||||
|
|
||||||
$syncStatus = "uploading"
|
$syncStatus = "uploading"
|
||||||
|
|
||||||
for (const [id, {actions, phrase}] of $overlay.chords) {
|
for (const [id, {actions, phrase}] of $overlay.chords) {
|
||||||
@@ -79,21 +78,7 @@
|
|||||||
await port.setSetting(id, setting)
|
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.
|
// Yes, this is a completely arbitrary and unnecessary delay.
|
||||||
// The only purpose of it is to create a sense of weight,
|
// The only purpose of it is to create a sense of weight,
|
||||||
// aka make it more "energy intensive" to click.
|
// aka make it more "energy intensive" to click.
|
||||||
@@ -101,7 +86,7 @@
|
|||||||
// 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!"
|
||||||
const virtualWriteTime = 6000
|
const virtualWriteTime = 1000
|
||||||
const startStamp = performance.now()
|
const startStamp = performance.now()
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
function animate() {
|
function animate() {
|
||||||
@@ -118,83 +103,22 @@
|
|||||||
}
|
}
|
||||||
requestAnimationFrame(animate)
|
requestAnimationFrame(animate)
|
||||||
})
|
})
|
||||||
if ($serialPort) {
|
await port.commit()
|
||||||
await $serialPort.commit()
|
|
||||||
$changes = []
|
$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"
|
$syncStatus = "done"
|
||||||
}
|
}
|
||||||
</script>
|
</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
|
<button
|
||||||
use:action={{title: $LL.saveActions.UNDO(), shortcut: "ctrl+z"}}
|
use:action={{title: $LL.saveActions.UNDO(), shortcut: "ctrl+z"}}
|
||||||
class="icon"
|
class="icon"
|
||||||
@@ -207,46 +131,17 @@
|
|||||||
disabled={redoQueue.length === 0}
|
disabled={redoQueue.length === 0}
|
||||||
on:click={redo}>redo</button
|
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}
|
{#if $changes.length !== 0}
|
||||||
|
<div transition:slide class="separator" />
|
||||||
<button
|
<button
|
||||||
class="click-me"
|
transition:fly={{x: 10}}
|
||||||
transition:fly={{x: 8}}
|
use:action={{title: $LL.saveActions.SAVE(), shortcut: "ctrl+shift+s"}}
|
||||||
on:click={apply}
|
on:click={save}
|
||||||
use:action={{
|
class="icon">save</button
|
||||||
title: $LL.changes.TITLE(),
|
|
||||||
shortcut: "ctrl+s",
|
|
||||||
}}><span class="icon">bolt</span>{$LL.saveActions.APPLY()}</button
|
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<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 {
|
.separator {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
<h2>{$LL.sync.TITLE_READ()}</h2>
|
<h2>{$LL.sync.TITLE_READ()}</h2>
|
||||||
{:else}
|
{:else}
|
||||||
<h2>{$LL.sync.TITLE_WRITE()}</h2>
|
<h2>{$LL.sync.TITLE_WRITE()}</h2>
|
||||||
<p>{$LL.sync.DISCLAIMER_WRITE()}</p>
|
|
||||||
{/if}
|
{/if}
|
||||||
<progress max={$syncProgress?.max ?? 1} value={$syncProgress?.current ?? 1}></progress>
|
<progress max={$syncProgress?.max ?? 1} value={$syncProgress?.current ?? 1}></progress>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|||||||
Reference in New Issue
Block a user