mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-22 09:52:50 +00:00
polish
This commit is contained in:
@@ -17,11 +17,11 @@ const de = {
|
|||||||
RELOAD: "Neu laden",
|
RELOAD: "Neu laden",
|
||||||
},
|
},
|
||||||
backup: {
|
backup: {
|
||||||
TITLE: "Lokale Kopie",
|
TITLE: "Backup",
|
||||||
INDIVIDUAL: "Einzeldateien",
|
AUTO_BACKUP: "Auto-backup",
|
||||||
DISCLAIMER:
|
DISCLAIMER:
|
||||||
"Das Backup in diesem Browser gespeichert und bleibt nur auf diesem Computer.",
|
"Das Backup in diesem Browser gespeichert und bleibt nur auf diesem Computer.",
|
||||||
DOWNLOAD: "Alles herunterladen",
|
DOWNLOAD: "Alles",
|
||||||
RESTORE: "Wiederherstellen",
|
RESTORE: "Wiederherstellen",
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
@@ -109,7 +109,7 @@ const de = {
|
|||||||
},
|
},
|
||||||
configure: {
|
configure: {
|
||||||
chords: {
|
chords: {
|
||||||
TITLE: "Akkorde",
|
TITLE: "Bibliothek",
|
||||||
HOLD_KEYS: "Akkord halten",
|
HOLD_KEYS: "Akkord halten",
|
||||||
NEW_CHORD: "Neuer Akkord",
|
NEW_CHORD: "Neuer Akkord",
|
||||||
DUPLICATE: "Akkord existiert bereits",
|
DUPLICATE: "Akkord existiert bereits",
|
||||||
@@ -131,7 +131,7 @@ const de = {
|
|||||||
TITLE: "Layout",
|
TITLE: "Layout",
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
TITLE: "Einstellungen",
|
TITLE: "Gerät",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugin: {
|
plugin: {
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ const en = {
|
|||||||
TITLE: "Update your device",
|
TITLE: "Update your device",
|
||||||
},
|
},
|
||||||
backup: {
|
backup: {
|
||||||
TITLE: "Local backup",
|
TITLE: "Backup",
|
||||||
INDIVIDUAL: "Individual backups",
|
AUTO_BACKUP: "Auto-backup",
|
||||||
DISCLAIMER:
|
DISCLAIMER:
|
||||||
"A backup is made and stored in this browser, and always remains only on your computer.",
|
"Whenever you connect this device to browser, a backup is made locally and kept only on your computer.",
|
||||||
DOWNLOAD: "Download Everything",
|
DOWNLOAD: "Everything",
|
||||||
RESTORE: "Restore",
|
RESTORE: "Restore",
|
||||||
},
|
},
|
||||||
sync: {
|
sync: {
|
||||||
@@ -108,7 +108,7 @@ const en = {
|
|||||||
},
|
},
|
||||||
configure: {
|
configure: {
|
||||||
chords: {
|
chords: {
|
||||||
TITLE: "Chords",
|
TITLE: "Library",
|
||||||
HOLD_KEYS: "Hold chord",
|
HOLD_KEYS: "Hold chord",
|
||||||
NEW_CHORD: "New chord",
|
NEW_CHORD: "New chord",
|
||||||
DUPLICATE: "Chord already exists",
|
DUPLICATE: "Chord already exists",
|
||||||
@@ -130,7 +130,7 @@ const en = {
|
|||||||
TITLE: "Layout",
|
TITLE: "Layout",
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
TITLE: "Settings",
|
TITLE: "Device",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugin: {
|
plugin: {
|
||||||
|
|||||||
@@ -17,5 +17,11 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
p {
|
p {
|
||||||
margin-block: 0;
|
margin-block: 0;
|
||||||
|
|
||||||
|
:global(kbd.icon) {
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: inherit;
|
||||||
|
translate: 0 0.2em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -70,7 +70,6 @@
|
|||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-bottom: 96px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { preference } from "$lib/preferences";
|
|
||||||
import LL from "$i18n/i18n-svelte";
|
|
||||||
import {
|
|
||||||
createChordBackup,
|
|
||||||
createLayoutBackup,
|
|
||||||
createSettingsBackup,
|
|
||||||
downloadBackup,
|
|
||||||
downloadFile,
|
|
||||||
restoreBackup,
|
|
||||||
} from "$lib/backup/backup";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>
|
|
||||||
<label
|
|
||||||
><input
|
|
||||||
type="checkbox"
|
|
||||||
use:preference={"backup"}
|
|
||||||
/>{$LL.backup.TITLE()}</label
|
|
||||||
>
|
|
||||||
</h2>
|
|
||||||
<p class="disclaimer">
|
|
||||||
<i>{$LL.backup.DISCLAIMER()}</i>
|
|
||||||
</p>
|
|
||||||
<fieldset>
|
|
||||||
<legend>{$LL.backup.INDIVIDUAL()}</legend>
|
|
||||||
<button onclick={() => downloadFile(createChordBackup())}>
|
|
||||||
<span class="icon">piano</span>
|
|
||||||
{$LL.configure.chords.TITLE()}
|
|
||||||
</button>
|
|
||||||
<button onclick={() => downloadFile(createLayoutBackup())}>
|
|
||||||
<span class="icon">keyboard</span>
|
|
||||||
{$LL.configure.layout.TITLE()}
|
|
||||||
</button>
|
|
||||||
<button onclick={() => downloadFile(createSettingsBackup())}>
|
|
||||||
<span class="icon">settings</span>
|
|
||||||
{$LL.configure.settings.TITLE()}
|
|
||||||
</button>
|
|
||||||
</fieldset>
|
|
||||||
<div class="save">
|
|
||||||
<button class="primary" onclick={downloadBackup}
|
|
||||||
><span class="icon">download</span>{$LL.backup.DOWNLOAD()}</button
|
|
||||||
>
|
|
||||||
<label class="button"
|
|
||||||
><input oninput={restoreBackup} type="file" /><span class="icon"
|
|
||||||
>settings_backup_restore</span
|
|
||||||
>{$LL.backup.RESTORE()}</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
h2 {
|
|
||||||
margin-block-end: 0;
|
|
||||||
|
|
||||||
> label {
|
|
||||||
gap: 10px;
|
|
||||||
font-size: 24px;
|
|
||||||
|
|
||||||
> input {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
display: flex;
|
|
||||||
margin-block: 16px;
|
|
||||||
border: 1px solid currentcolor;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
width: min-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disclaimer {
|
|
||||||
max-width: 16cm;
|
|
||||||
font-size: 12px;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="file"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.save {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { initSerial, serialPort } from "$lib/serial/connection";
|
|
||||||
import { browser } from "$app/environment";
|
|
||||||
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();
|
|
||||||
$serialPort = undefined;
|
|
||||||
powerDialog = false;
|
|
||||||
setTimeout(() => {
|
|
||||||
initSerial();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function bootloader() {
|
|
||||||
downloadBackup();
|
|
||||||
$serialPort?.bootloader();
|
|
||||||
$serialPort = undefined;
|
|
||||||
rebootInfo = true;
|
|
||||||
powerDialog = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function connect() {
|
|
||||||
try {
|
|
||||||
await initSerial(true);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
alert(
|
|
||||||
"Connection failed. Is your device maybe pre-CCOS? Refer to the doc link in the bottom left for more information on your device.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rebootInfo = $derived($serialPort !== undefined);
|
|
||||||
let terminal = $state(false);
|
|
||||||
let powerDialog = $state(false);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<div class="row">
|
|
||||||
<h2>{$LL.deviceManager.TITLE()}</h2>
|
|
||||||
<label
|
|
||||||
>{$LL.deviceManager.AUTO_CONNECT()}<input
|
|
||||||
type="checkbox"
|
|
||||||
use:preference={"autoConnect"}
|
|
||||||
/></label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if $serialPort}
|
|
||||||
<p transition:slide>
|
|
||||||
{$serialPort.company}
|
|
||||||
{$serialPort.device}
|
|
||||||
{$serialPort.chipset}
|
|
||||||
<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}
|
|
||||||
|
|
||||||
{#if browser}
|
|
||||||
{#if navigator.userAgent.includes("Linux") && !$serialPort}
|
|
||||||
<div class="linux-info">
|
|
||||||
<p>{@html $LL.deviceManager.LINUX_PERMISSIONS()}</p>
|
|
||||||
<p>
|
|
||||||
In most cases you can simply follow the <a
|
|
||||||
target="_blank"
|
|
||||||
href="https://docs.arduino.cc/software/ide-v1/tutorials/Linux#please-read"
|
|
||||||
>Arduino Guide</a
|
|
||||||
> on serial port permissions.
|
|
||||||
</p>
|
|
||||||
<p>Special systems:</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://wiki.archlinux.org/title/Arduino#Accessing_serial"
|
|
||||||
>Arch and Arch-based like Manjaro or EndeavourOS</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://gist.github.com/CMCDragonkai/d00201ec143c9f749fc49533034e5009?permalink_comment_id=4670311#gistcomment-4670311"
|
|
||||||
>NixOS</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://wiki.gentoo.org/wiki/Arduino#Grant_access_to_non-root_users"
|
|
||||||
>Gentoo</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if rebootInfo}
|
|
||||||
<p transition:slide>
|
|
||||||
<b>{$LL.deviceManager.bootMenu.POWER_WARNING()}</b>
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
<div class="row">
|
|
||||||
{#if $serialPort}
|
|
||||||
<button
|
|
||||||
class="secondary"
|
|
||||||
onclick={() => {
|
|
||||||
$serialPort?.forget();
|
|
||||||
$serialPort = undefined;
|
|
||||||
}}
|
|
||||||
><span class="icon">usb_off</span
|
|
||||||
>{$LL.deviceManager.DISCONNECT()}</button
|
|
||||||
>
|
|
||||||
{:else}
|
|
||||||
<button class="error" onclick={connect}
|
|
||||||
><span class="icon">usb</span>{$LL.deviceManager.CONNECT()}</button
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
<div class="row" style="justify-content: flex-end">
|
|
||||||
<a
|
|
||||||
href="/terminal"
|
|
||||||
title={$LL.deviceManager.TERMINAL()}
|
|
||||||
class="icon"
|
|
||||||
class:disabled={$serialPort === undefined}
|
|
||||||
onclick={() => (terminal = !terminal)}>terminal</a
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
title={$LL.deviceManager.bootMenu.TITLE()}
|
|
||||||
disabled={$serialPort === undefined}
|
|
||||||
onclick={() => (powerDialog = !powerDialog)}>settings_power</button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{#if powerDialog}
|
|
||||||
<div
|
|
||||||
class="backdrop"
|
|
||||||
role="button"
|
|
||||||
tabindex="-1"
|
|
||||||
transition:fade={{ duration: 250 }}
|
|
||||||
onclick={() => (powerDialog = !powerDialog)}
|
|
||||||
onkeypress={(event) => {
|
|
||||||
if (event.key === "Enter") powerDialog = !powerDialog;
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<dialog open transition:slide={{ duration: 250 }}>
|
|
||||||
<h3>{$LL.deviceManager.bootMenu.TITLE()}</h3>
|
|
||||||
<button onclick={reboot}
|
|
||||||
><span class="icon">restart_alt</span
|
|
||||||
>{$LL.deviceManager.bootMenu.REBOOT()}</button
|
|
||||||
>
|
|
||||||
<button onclick={bootloader}
|
|
||||||
><span class="icon">rule_settings</span
|
|
||||||
>{$LL.deviceManager.bootMenu.BOOTLOADER()}</button
|
|
||||||
>
|
|
||||||
</dialog>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
h2 {
|
|
||||||
margin-block: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-block: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.linux-info a {
|
|
||||||
display: inline;
|
|
||||||
padding-inline: 0;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.backdrop {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
inset: 0;
|
|
||||||
|
|
||||||
background: #0005;
|
|
||||||
border-radius: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
margin-block-start: 16px;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
color: var(--md-sys-color-on-secondary-container);
|
|
||||||
|
|
||||||
background: var(--md-sys-color-secondary-container);
|
|
||||||
border: none;
|
|
||||||
border-radius: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
gap: 0;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog > * {
|
|
||||||
margin-inline: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog > :first-child {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding-block: 8px;
|
|
||||||
|
|
||||||
color: var(--md-sys-color-on-secondary);
|
|
||||||
|
|
||||||
background: var(--md-sys-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
button:active:not(:disabled) {
|
|
||||||
color: var(--md-sys-color-on-surface-variant);
|
|
||||||
background: var(--md-sys-color-surface-variant);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -8,11 +8,25 @@
|
|||||||
import { loadLocaleAsync } from "$i18n/i18n-util.async";
|
import { loadLocaleAsync } from "$i18n/i18n-util.async";
|
||||||
import { tick } from "svelte";
|
import { tick } from "svelte";
|
||||||
import SyncOverlay from "./SyncOverlay.svelte";
|
import SyncOverlay from "./SyncOverlay.svelte";
|
||||||
import { serialPort } from "$lib/serial/connection";
|
import {
|
||||||
|
initSerial,
|
||||||
|
serialPort,
|
||||||
|
sync,
|
||||||
|
syncProgress,
|
||||||
|
syncStatus,
|
||||||
|
} from "$lib/serial/connection";
|
||||||
|
import { fade, slide } from "svelte/transition";
|
||||||
|
|
||||||
let locale = $state(
|
let locale = $state(
|
||||||
(browser && (localStorage.getItem("locale") as Locales)) || detectLocale(),
|
(browser && (localStorage.getItem("locale") as Locales)) || detectLocale(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let currentDevice = $derived(
|
||||||
|
$serialPort
|
||||||
|
? `${$serialPort.device.toLowerCase()}_${$serialPort.chipset.toLowerCase()}`
|
||||||
|
: undefined,
|
||||||
|
);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!browser) return;
|
if (!browser) return;
|
||||||
localStorage.setItem("locale", locale);
|
localStorage.setItem("locale", locale);
|
||||||
@@ -33,6 +47,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function connect() {
|
||||||
|
try {
|
||||||
|
await initSerial(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
alert(
|
||||||
|
"Connection failed. Is your device maybe pre-CCOS? Refer to the doc link in the bottom left for more information on your device.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnect(event: MouseEvent) {
|
||||||
|
if (event.shiftKey) {
|
||||||
|
sync();
|
||||||
|
} else {
|
||||||
|
$serialPort?.forget();
|
||||||
|
$serialPort = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let languageSelect: HTMLSelectElement;
|
let languageSelect: HTMLSelectElement;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -40,39 +74,58 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
|
use:action={{ title: "Branch" }}
|
||||||
href={import.meta.env.VITE_HOMEPAGE_URL}
|
href={import.meta.env.VITE_HOMEPAGE_URL}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"><span class="icon">commit</span> v{version}</a
|
target="_blank"><span class="icon">commit</span> v{version}</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href={import.meta.env.VITE_BUGS_URL} rel="noreferrer" target="_blank"
|
<a
|
||||||
><span class="icon">bug_report</span> Issues</a
|
href="/firmware/{currentDevice ? `${currentDevice}/` : ''}"
|
||||||
>
|
use:action={{ title: "Updates" }}
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href={import.meta.env.VITE_DOCS_URL} rel="noreferrer" target="_blank"
|
|
||||||
><span class="icon">description</span> Docs</a
|
|
||||||
>
|
>
|
||||||
|
CCOS {$serialPort?.version ?? "Updates"}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div>
|
<div class="sync-box">
|
||||||
{#if !$serialPort}
|
{#if !$serialPort}
|
||||||
<div class="warning">
|
<button class="warning" onclick={connect} transition:slide={{ axis: "x" }}
|
||||||
<span class="icon">warning</span>{$LL.deviceManager.NO_DEVICE()}
|
><span class="icon">usb</span>{$LL.deviceManager.CONNECT()}</button
|
||||||
</div>
|
>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
transition:slide={{ axis: "x" }}
|
||||||
|
onclick={disconnect}
|
||||||
|
use:action={{
|
||||||
|
title: "Disconnect<br><kbd class='icon'>shift</kbd> Sync",
|
||||||
|
}}
|
||||||
|
><b
|
||||||
|
>{$serialPort.company}
|
||||||
|
{$serialPort.device}
|
||||||
|
{$serialPort.chipset}</b
|
||||||
|
><span class="icon">usb_off</span></button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $syncStatus !== "done"}
|
||||||
|
<progress
|
||||||
|
transition:fade
|
||||||
|
max={$syncProgress?.max ?? 1}
|
||||||
|
value={$syncProgress?.current ?? 1}
|
||||||
|
></progress>
|
||||||
{/if}
|
{/if}
|
||||||
<SyncOverlay />
|
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href={import.meta.env.VITE_STORE_URL} rel="noreferrer" target="_blank"
|
<a href={import.meta.env.VITE_BUGS_URL} rel="noreferrer" target="_blank"
|
||||||
><span class="icon">shopping_bag</span> Store</a
|
><span class="icon">bug_report</span> Bugs</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href={import.meta.env.VITE_LEARN_URL} rel="noreferrer" target="_blank"
|
<a href={import.meta.env.VITE_STORE_URL} rel="noreferrer" target="_blank"
|
||||||
><span class="icon">school</span> Train</a
|
><span class="icon">shopping_bag</span> Store</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li class="hide-forced-colors">
|
<li class="hide-forced-colors">
|
||||||
@@ -101,7 +154,7 @@
|
|||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<!--<li>
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
class="icon"
|
class="icon"
|
||||||
@@ -116,7 +169,7 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>-->
|
||||||
</ul>
|
</ul>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
@@ -126,6 +179,37 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sync-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
button {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
bottom: 0;
|
||||||
|
left: 16px;
|
||||||
|
right: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: calc(100% - 32px);
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress::-webkit-progress-bar {
|
||||||
|
background: var(--md-sys-color-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress::-webkit-progress-value {
|
||||||
|
background: var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
color: var(--md-sys-color-error);
|
color: var(--md-sys-color-error);
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { LL } from "$i18n/i18n-svelte";
|
import { page } from "$app/stores";
|
||||||
import { popup } from "$lib/popup";
|
|
||||||
import { userPreferences } from "$lib/preferences";
|
import { userPreferences } from "$lib/preferences";
|
||||||
import { serialPort, syncStatus } from "$lib/serial/connection";
|
|
||||||
import { action } from "$lib/title";
|
|
||||||
import BackupPopup from "./BackupPopup.svelte";
|
|
||||||
import ConnectionPopup from "./ConnectionPopup.svelte";
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
@@ -17,20 +12,43 @@
|
|||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
[
|
[
|
||||||
{ href: "/config/chords/", icon: "dictionary", title: "Chords" },
|
{
|
||||||
|
href: "/config/settings/",
|
||||||
|
icon: "cable",
|
||||||
|
title: "Device",
|
||||||
|
primary: true,
|
||||||
|
},
|
||||||
|
{ href: "/config/chords/", icon: "dictionary", title: "Library" },
|
||||||
{ href: "/config/layout/", icon: "keyboard", title: "Layout" },
|
{ href: "/config/layout/", icon: "keyboard", title: "Layout" },
|
||||||
{ href: "/config/settings/", icon: "tune", title: "Config" },
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ href: "/learn", icon: "school", title: "Learn", wip: true },
|
// { href: "/learn", icon: "school", title: "Learn", wip: true },
|
||||||
{ href: "/learn", icon: "description", title: "Docs" },
|
{
|
||||||
|
href: import.meta.env.VITE_LEARN_URL,
|
||||||
|
icon: "school",
|
||||||
|
title: "Learn",
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: import.meta.env.VITE_DOCS_URL,
|
||||||
|
icon: "description",
|
||||||
|
title: "Docs",
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
{ href: "/editor", icon: "edit_document", title: "Editor", wip: true },
|
{ href: "/editor", icon: "edit_document", title: "Editor", wip: true },
|
||||||
],
|
],
|
||||||
[
|
/*[
|
||||||
{ href: "/chat", icon: "chat", title: "Chat", wip: true },
|
{ href: "/chat", icon: "chat", title: "Chat", wip: true },
|
||||||
{ href: "/plugin", icon: "code", title: "Plugin", wip: true },
|
{ href: "/plugin", icon: "code", title: "Plugin", wip: true },
|
||||||
],
|
],*/
|
||||||
] satisfies { href: string; icon: string; title: string; wip?: boolean }[][];
|
] satisfies {
|
||||||
|
href: string;
|
||||||
|
icon: string;
|
||||||
|
title: string;
|
||||||
|
wip?: boolean;
|
||||||
|
external?: boolean;
|
||||||
|
primary?: boolean;
|
||||||
|
}[][];
|
||||||
|
|
||||||
let connectButton: HTMLButtonElement;
|
let connectButton: HTMLButtonElement;
|
||||||
</script>
|
</script>
|
||||||
@@ -39,10 +57,18 @@
|
|||||||
<nav>
|
<nav>
|
||||||
{#each routes as group}
|
{#each routes as group}
|
||||||
<ul>
|
<ul>
|
||||||
{#each group as { href, icon, title, wip }}
|
{#each group as { href, icon, title, wip, external }}
|
||||||
<li>
|
<li>
|
||||||
<a class:wip {href}>
|
<a
|
||||||
<div class="icon">{icon}</div>
|
class:wip
|
||||||
|
{href}
|
||||||
|
rel={external ? "noreferrer" : undefined}
|
||||||
|
target={external ? "_blank" : undefined}
|
||||||
|
class:active={$page.url.pathname.startsWith(href)}
|
||||||
|
>
|
||||||
|
<div class="icon">
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
@@ -52,28 +78,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{/each}
|
{/each}
|
||||||
</nav>
|
</nav>
|
||||||
<ul class="sidebar-footer">
|
|
||||||
<li>
|
|
||||||
<button
|
|
||||||
bind:this={connectButton}
|
|
||||||
use:action={{ title: $LL.deviceManager.TITLE() }}
|
|
||||||
use:popup={ConnectionPopup}
|
|
||||||
class="icon connect"
|
|
||||||
class:error={$serialPort === undefined}
|
|
||||||
>
|
|
||||||
cable
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button
|
|
||||||
use:action={{ title: $LL.backup.TITLE() }}
|
|
||||||
use:popup={BackupPopup}
|
|
||||||
class="icon {$syncStatus}"
|
|
||||||
>
|
|
||||||
account_circle
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -109,12 +113,29 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
transition: all 250ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .content {
|
> .content {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
translate: 0 -8px;
|
||||||
|
transition: all 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
> .content {
|
||||||
|
translate: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
background: var(--md-sys-color-primary);
|
||||||
|
color: var(--md-sys-color-on-primary);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { page } from "$app/stores";
|
|
||||||
import LL from "$i18n/i18n-svelte";
|
|
||||||
import type { Snippet } from "svelte";
|
|
||||||
|
|
||||||
let { children }: { children?: Snippet } = $props();
|
|
||||||
|
|
||||||
let paths = $derived([
|
|
||||||
{
|
|
||||||
href: "/config/chords/",
|
|
||||||
title: $LL.configure.chords.TITLE(),
|
|
||||||
icon: "piano",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "/config/layout/",
|
|
||||||
title: $LL.configure.layout.TITLE(),
|
|
||||||
icon: "keyboard",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "/config/settings/",
|
|
||||||
title: $LL.configure.settings.TITLE(),
|
|
||||||
icon: "settings",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
{#each paths as { href, title, icon }}
|
|
||||||
<a {href} class:active={$page.url.pathname.startsWith(href)}>
|
|
||||||
<span class="icon">{icon}</span>
|
|
||||||
{title}
|
|
||||||
</a>
|
|
||||||
{/each}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{#if children}
|
|
||||||
{@render children()}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
nav {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
color: var(--md-sys-color-on-surface-variant);
|
|
||||||
|
|
||||||
background: var(--md-sys-color-surface-variant);
|
|
||||||
border: none;
|
|
||||||
border-radius: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.active {
|
|
||||||
--icon-fill: 1;
|
|
||||||
|
|
||||||
color: var(--md-sys-color-on-primary);
|
|
||||||
background: var(--md-sys-color-primary);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
import { canShare, triggerShare } from "$lib/share";
|
import { canShare, triggerShare } from "$lib/share";
|
||||||
import { action } from "$lib/title";
|
import { action } from "$lib/title";
|
||||||
import LL from "$i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import ConfigTabs from "./ConfigTabs.svelte";
|
|
||||||
import EditActions from "./EditActions.svelte";
|
import EditActions from "./EditActions.svelte";
|
||||||
|
import { sync, syncStatus } from "$lib/serial/connection";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
@@ -12,8 +12,6 @@
|
|||||||
<EditActions />
|
<EditActions />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ConfigTabs />
|
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
{#if $canShare}
|
{#if $canShare}
|
||||||
<button
|
<button
|
||||||
@@ -40,7 +38,7 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
nav {
|
nav {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|
||||||
width: calc(min(100%, 28cm));
|
width: calc(min(100%, 28cm));
|
||||||
margin-block: 8px;
|
margin-block: 8px;
|
||||||
@@ -48,6 +46,20 @@
|
|||||||
padding-inline: 16px;
|
padding-inline: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes syncing {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.syncing {
|
||||||
|
transform-origin: 50% 49%;
|
||||||
|
animation: syncing 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
let isNavigating = $state(false);
|
let isNavigating = $state(false);
|
||||||
|
|
||||||
const routeOrder = [
|
const routeOrder = [
|
||||||
|
"/config/settings/",
|
||||||
"/config/chords/",
|
"/config/chords/",
|
||||||
"/config/layout/",
|
"/config/layout/",
|
||||||
"/config/settings/",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
beforeNavigate((navigation) => {
|
beforeNavigate((navigation) => {
|
||||||
@@ -49,8 +49,8 @@
|
|||||||
|
|
||||||
{#if !isNavigating}
|
{#if !isNavigating}
|
||||||
<main
|
<main
|
||||||
in:fly={{ x: inDirection * 24, duration: 150, easing: expoOut }}
|
in:fly={{ y: inDirection * 24, duration: 150, easing: expoOut }}
|
||||||
out:fly={{ x: outDirection * 24, duration: 150, easing: expoIn }}
|
out:fly={{ y: outDirection * 24, duration: 150, easing: expoIn }}
|
||||||
onoutroend={outroEnd}
|
onoutroend={outroEnd}
|
||||||
>
|
>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
|
|||||||
@@ -4,6 +4,16 @@
|
|||||||
import { serialPort } from "$lib/serial/connection";
|
import { serialPort } from "$lib/serial/connection";
|
||||||
import { setting } from "$lib/setting";
|
import { setting } from "$lib/setting";
|
||||||
import ResetPopup from "./ResetPopup.svelte";
|
import ResetPopup from "./ResetPopup.svelte";
|
||||||
|
import LL from "$i18n/i18n-svelte";
|
||||||
|
import {
|
||||||
|
createChordBackup,
|
||||||
|
createLayoutBackup,
|
||||||
|
createSettingsBackup,
|
||||||
|
downloadBackup,
|
||||||
|
downloadFile,
|
||||||
|
restoreBackup,
|
||||||
|
} from "$lib/backup/backup";
|
||||||
|
import { preference } from "$lib/preferences";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -11,8 +21,67 @@
|
|||||||
<meta name="description" content="Change your device's settings" />
|
<meta name="description" content="Change your device's settings" />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#if $serialPort}
|
<section>
|
||||||
<section>
|
<fieldset>
|
||||||
|
<legend>{$LL.backup.TITLE()}</legend>
|
||||||
|
<label
|
||||||
|
><input
|
||||||
|
type="checkbox"
|
||||||
|
use:preference={"backup"}
|
||||||
|
/>{$LL.backup.AUTO_BACKUP()}</label
|
||||||
|
>
|
||||||
|
<p class="disclaimer">
|
||||||
|
{$LL.backup.DISCLAIMER()}
|
||||||
|
</p>
|
||||||
|
<div class="row" style="margin-top: auto">
|
||||||
|
<button onclick={() => downloadFile(createChordBackup())}>
|
||||||
|
<span class="icon">piano</span>
|
||||||
|
{$LL.configure.chords.TITLE()}
|
||||||
|
</button>
|
||||||
|
<button onclick={() => downloadFile(createLayoutBackup())}>
|
||||||
|
<span class="icon">keyboard</span>
|
||||||
|
{$LL.configure.layout.TITLE()}
|
||||||
|
</button>
|
||||||
|
<button onclick={() => downloadFile(createSettingsBackup())}>
|
||||||
|
<span class="icon">settings</span>
|
||||||
|
Settings
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<button class="primary" onclick={downloadBackup}
|
||||||
|
><span class="icon">download</span>{$LL.backup.DOWNLOAD()}</button
|
||||||
|
>
|
||||||
|
<label class="button"
|
||||||
|
><input oninput={restoreBackup} type="file" /><span class="icon"
|
||||||
|
>settings_backup_restore</span
|
||||||
|
>{$LL.backup.RESTORE()}</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Device</legend>
|
||||||
|
<label
|
||||||
|
>{$LL.deviceManager.AUTO_CONNECT()}<input
|
||||||
|
type="checkbox"
|
||||||
|
use:preference={"autoConnect"}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
{#if $serialPort}
|
||||||
|
<label
|
||||||
|
>Boot message<input type="checkbox" use:setting={{ id: 0x93 }} /></label
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
>GTM Realtime Feedback<input
|
||||||
|
type="checkbox"
|
||||||
|
use:setting={{ id: 0x92 }}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<button class="outline" use:popup={ResetPopup}>Reset...</button>
|
||||||
|
{/if}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{#if $serialPort}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend
|
<legend
|
||||||
><label
|
><label
|
||||||
@@ -231,20 +300,6 @@
|
|||||||
>
|
>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>Device</legend>
|
|
||||||
<label
|
|
||||||
>Boot message<input type="checkbox" use:setting={{ id: 0x93 }} /></label
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
>GTM Realtime Feedback<input
|
|
||||||
type="checkbox"
|
|
||||||
use:setting={{ id: 0x92 }}
|
|
||||||
/></label
|
|
||||||
>
|
|
||||||
<button class="outline" use:popup={ResetPopup}>Reset...</button>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
{#if $serialPort.device === "LITE"}
|
{#if $serialPort.device === "LITE"}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend
|
<legend
|
||||||
@@ -275,8 +330,8 @@
|
|||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
{/if}
|
||||||
{/if}
|
</section>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
section {
|
section {
|
||||||
@@ -319,14 +374,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
border: 1px solid var(--md-sys-color-outline);
|
border: 1px solid var(--md-sys-color-outline);
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
|
|
||||||
&:has(> legend input:not(:checked)) > :not(legend) {
|
/*&:has(> legend input:not(:checked)) > :not(legend) {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
> label {
|
> label {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -429,4 +487,14 @@
|
|||||||
content: "•";
|
content: "•";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
margin-block: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="file"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1><a href="/ota-update/">Firmware Update</a></h1>
|
<h1><a href="/firmware">Firmware Updates</a></h1>
|
||||||
|
|
||||||
{@render children()}
|
{@render children()}
|
||||||
|
|
||||||
@@ -11,6 +11,5 @@
|
|||||||
margin-block: 1em;
|
margin-block: 1em;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 3em;
|
font-size: 3em;
|
||||||
font-weight: 400;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user