feat: use factory default meta

feat: clear chords button
resolves #64
This commit is contained in:
2025-02-14 14:52:07 +01:00
parent 9d5b0e01d2
commit ac16cfd3bf
12 changed files with 173 additions and 87 deletions

View File

@@ -73,6 +73,10 @@ const config = {
"reply", "reply",
"navigate_before", "navigate_before",
"navigate_next", "navigate_next",
"library_add",
"reset_wrench",
"reset_settings",
"delete_sweep",
"print", "print",
"restore_from_trash", "restore_from_trash",
"history", "history",

View File

@@ -107,32 +107,32 @@ export function restoreFromFile(
} }
changes.update((changes) => { changes.update((changes) => {
changes.push( changes.push([
...getChangesFromChordFile(recent[0]), ...getChangesFromChordFile(recent[0]),
...getChangesFromLayoutFile(recent[1]), ...getChangesFromLayoutFile(recent[1]),
...getChangesFromSettingsFile(recent[2]), ...getChangesFromSettingsFile(recent[2]),
); ]);
return changes; return changes;
}); });
break; break;
} }
case "chords": { case "chords": {
changes.update((changes) => { changes.update((changes) => {
changes.push(...getChangesFromChordFile(file)); changes.push(getChangesFromChordFile(file));
return changes; return changes;
}); });
break; break;
} }
case "layout": { case "layout": {
changes.update((changes) => { changes.update((changes) => {
changes.push(...getChangesFromLayoutFile(file)); changes.push(getChangesFromLayoutFile(file));
return changes; return changes;
}); });
break; break;
} }
case "settings": { case "settings": {
changes.update((changes) => { changes.update((changes) => {
changes.push(...getChangesFromSettingsFile(file)); changes.push(getChangesFromSettingsFile(file));
return changes; return changes;
}); });
break; break;

View File

@@ -137,12 +137,14 @@
}, },
onselect(action) { onselect(action) {
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Layout, {
id: keyInfo.id, type: ChangeType.Layout,
layer: get(activeLayer), id: keyInfo.id,
action, layer: get(activeLayer),
}); action,
},
]);
return changes; return changes;
}); });
closed(); closed();

View File

@@ -1,11 +1,12 @@
<script lang="ts"> <script lang="ts">
import { serialPort } from "$lib/serial/connection"; import { deviceMeta, serialPort } from "$lib/serial/connection";
import { action } from "$lib/title"; import { action } from "$lib/title";
import GenericLayout from "$lib/components/layout/GenericLayout.svelte"; import GenericLayout from "$lib/components/layout/GenericLayout.svelte";
import { getContext } from "svelte"; import { getContext } from "svelte";
import type { Writable } from "svelte/store"; import type { Writable } from "svelte/store";
import type { VisualLayout } from "$lib/serialization/visual-layout"; import type { VisualLayout } from "$lib/serialization/visual-layout";
import { fade } from "svelte/transition"; import { fade, fly } from "svelte/transition";
import { restoreFromFile } from "$lib/backup/backup";
let device = $derived($serialPort?.device); let device = $derived($serialPort?.device);
const activeLayer = getContext<Writable<number>>("active-layer"); const activeLayer = getContext<Writable<number>>("active-layer");
@@ -58,6 +59,16 @@
{icon} {icon}
</button> </button>
{/each} {/each}
{#if $deviceMeta?.factoryDefaults?.layout}
<button
use:action={{ title: "Reset Layout" }}
transition:fly={{ x: -8 }}
class="icon reset-layout"
onclick={() =>
restoreFromFile($deviceMeta!.factoryDefaults!.layout)}
>reset_wrench</button
>
{/if}
</fieldset> </fieldset>
<GenericLayout {visualLayout} /> <GenericLayout {visualLayout} />
@@ -113,7 +124,7 @@
} }
&:first-child, &:first-child,
&:last-child { &:nth-child(3) {
aspect-ratio: unset; aspect-ratio: unset;
height: unset; height: unset;
} }
@@ -124,12 +135,21 @@
border-radius: 16px 0 0 16px; border-radius: 16px 0 0 16px;
} }
&:last-child { &:nth-child(3) {
margin-inline-start: -8px; margin-inline-start: -8px;
padding-inline: 24px 4px; padding-inline: 24px 4px;
border-radius: 0 16px 16px 0; border-radius: 0 16px 16px 0;
} }
&.reset-layout {
position: absolute;
top: 50%;
right: 0;
transform: translate(100%, -50%);
background: none;
font-size: 24px;
}
&.active { &.active {
font-weight: 900; font-weight: 900;
color: var(--md-sys-color-on-tertiary); color: var(--md-sys-color-on-tertiary);

View File

@@ -63,11 +63,13 @@ export const setting: Action<
} }
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Setting, {
id: id, type: ChangeType.Setting,
setting: value, id: id,
}); setting: value,
},
]);
return changes; return changes;
}); });
} }

View File

@@ -42,7 +42,7 @@ export interface ChangeInfo {
export type Change = LayoutChange | ChordChange | SettingChange; export type Change = LayoutChange | ChordChange | SettingChange;
export const changes = persistentWritable<Change[]>("changes", []); export const changes = persistentWritable<Change[][]>("changes", []);
export interface Overlay { export interface Overlay {
layout: [Map<number, number>, Map<number, number>, Map<number, number>]; layout: [Map<number, number>, Map<number, number>, Map<number, number>];
@@ -57,21 +57,23 @@ export const overlay = derived(changes, (changes) => {
settings: new Map(), settings: new Map(),
}; };
for (const change of changes) { for (const changeset of changes) {
switch (change.type) { for (const change of changeset) {
case ChangeType.Layout: switch (change.type) {
overlay.layout[change.layer]?.set(change.id, change.action); case ChangeType.Layout:
break; overlay.layout[change.layer]?.set(change.id, change.action);
case ChangeType.Chord: break;
overlay.chords.set(JSON.stringify(change.id), { case ChangeType.Chord:
actions: change.actions, overlay.chords.set(JSON.stringify(change.id), {
phrase: change.phrase, actions: change.actions,
deleted: change.deleted ?? false, phrase: change.phrase,
}); deleted: change.deleted ?? false,
break; });
case ChangeType.Setting: break;
overlay.settings.set(change.id, change.setting); case ChangeType.Setting:
break; overlay.settings.set(change.id, change.setting);
break;
}
} }
} }

View File

@@ -4,7 +4,6 @@
import { action } from "$lib/title"; import { action } from "$lib/title";
import LL from "$i18n/i18n-svelte"; import LL from "$i18n/i18n-svelte";
import EditActions from "./EditActions.svelte"; import EditActions from "./EditActions.svelte";
import { sync, syncStatus } from "$lib/serial/connection";
</script> </script>
<nav> <nav>

View File

@@ -5,7 +5,7 @@
import { action } from "$lib/title"; import { action } from "$lib/title";
import { onDestroy, onMount, setContext, tick } from "svelte"; import { onDestroy, onMount, setContext, tick } from "svelte";
import { changes, ChangeType, chords } from "$lib/undo-redo"; import { changes, ChangeType, chords } from "$lib/undo-redo";
import type { ChordInfo } from "$lib/undo-redo"; import type { ChordChange, ChordInfo } from "$lib/undo-redo";
import { derived, writable } from "svelte/store"; import { derived, writable } from "svelte/store";
import ChordEdit from "./ChordEdit.svelte"; import ChordEdit from "./ChordEdit.svelte";
import { crossfade, fly } from "svelte/transition"; import { crossfade, fly } from "svelte/transition";
@@ -14,6 +14,8 @@
import { expoOut } from "svelte/easing"; import { expoOut } from "svelte/easing";
import { osLayout } from "$lib/os-layout"; import { osLayout } from "$lib/os-layout";
import randomTips from "$lib/assets/random-tips/en.json"; import randomTips from "$lib/assets/random-tips/en.json";
import { deviceMeta } from "$lib/serial/connection";
import { restoreFromFile } from "$lib/backup/backup";
const resultSize = 38; const resultSize = 38;
let results: HTMLElement; let results: HTMLElement;
@@ -182,12 +184,14 @@
return; return;
} }
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Chord, {
id: actions, type: ChangeType.Chord,
actions, id: actions,
phrase: [], actions,
}); phrase: [],
},
]);
return changes; return changes;
}); });
} }
@@ -212,6 +216,21 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
function clearChords() {
changes.update((changes) => {
changes.push(
$chords.map<ChordChange>((it) => ({
type: ChangeType.Chord,
id: it.id,
actions: it.actions,
phrase: it.phrase,
deleted: true,
})),
);
return changes;
});
}
const items = derived( const items = derived(
[searchFilter, chords], [searchFilter, chords],
([filter, chords]) => ([filter, chords]) =>
@@ -292,6 +311,17 @@
"\n\nDid you know? " + "\n\nDid you know? " +
randomTips[Math.floor(randomTips.length * Math.random())]} randomTips[Math.floor(randomTips.length * Math.random())]}
></textarea> ></textarea>
<button onclick={clearChords}
><span class="icon">delete_sweep</span>
Clear Chords</button
>
<div>
{#each Object.entries($deviceMeta?.factoryDefaults?.chords ?? {}) as [title, library]}
<button onclick={() => restoreFromFile(library)}
><span class="icon">library_add</span>{title}</button
>
{/each}
</div>
<button onclick={downloadVocabulary} <button onclick={downloadVocabulary}
><span class="icon">download</span> ><span class="icon">download</span>
{$LL.configure.chords.VOCABULARY()}</button {$LL.configure.chords.VOCABULARY()}</button

View File

@@ -61,12 +61,14 @@
if (pressedKeys.size < 1) return; if (pressedKeys.size < 1) return;
if (!chord) return onsubmit(makeChordInput(...pressedKeys)); if (!chord) return onsubmit(makeChordInput(...pressedKeys));
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Chord, {
id: chord!.id, type: ChangeType.Chord,
actions: makeChordInput(...pressedKeys), id: chord!.id,
phrase: chord!.phrase, actions: makeChordInput(...pressedKeys),
}); phrase: chord!.phrase,
},
]);
return changes; return changes;
}); });
return undefined; return undefined;
@@ -76,12 +78,14 @@
event.stopPropagation(); event.stopPropagation();
selectAction(event, (action) => { selectAction(event, (action) => {
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Chord, {
id: chord!.id, type: ChangeType.Chord,
actions: makeChordInput(...chordActions!, action), id: chord!.id,
phrase: chord!.phrase, actions: makeChordInput(...chordActions!, action),
}); phrase: chord!.phrase,
},
]);
return changes; return changes;
}); });
}); });

View File

@@ -15,13 +15,15 @@
function remove() { function remove() {
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Chord, {
id: chord.id, type: ChangeType.Chord,
actions: chord.actions, id: chord.id,
phrase: chord.phrase, actions: chord.actions,
deleted: true, phrase: chord.phrase,
}); deleted: true,
},
]);
return changes; return changes;
}); });
} }
@@ -35,9 +37,13 @@
function restore() { function restore() {
changes.update((changes) => changes.update((changes) =>
changes.filter( changes
(it) => !(it.type === ChangeType.Chord && isSameChord(it, chord)), .map((it) =>
), it.filter(
(it) => !(it.type === ChangeType.Chord && isSameChord(it, chord)),
),
)
.filter((it) => it.length > 0),
); );
} }
@@ -50,12 +56,14 @@
} }
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Chord, {
id, type: ChangeType.Chord,
actions: [...chord.actions], id,
phrase: [...chord.phrase], actions: [...chord.actions],
}); phrase: [...chord.phrase],
},
]);
return changes; return changes;
}); });

View File

@@ -49,24 +49,28 @@
function deleteAction(at: number, count = 1) { function deleteAction(at: number, count = 1) {
if (!(at in chord.phrase)) return; if (!(at in chord.phrase)) return;
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Chord, {
id: chord.id, type: ChangeType.Chord,
actions: chord.actions, id: chord.id,
phrase: chord.phrase.toSpliced(at, count), actions: chord.actions,
}); phrase: chord.phrase.toSpliced(at, count),
},
]);
return changes; return changes;
}); });
} }
function insertAction(at: number, action: number) { function insertAction(at: number, action: number) {
changes.update((changes) => { changes.update((changes) => {
changes.push({ changes.push([
type: ChangeType.Chord, {
id: chord.id, type: ChangeType.Chord,
actions: chord.actions, id: chord.id,
phrase: chord.phrase.toSpliced(at, 0, action), actions: chord.actions,
}); phrase: chord.phrase.toSpliced(at, 0, action),
},
]);
return changes; return changes;
}); });
} }

View File

@@ -1,7 +1,7 @@
<script> <script lang="ts">
import Action from "$lib/components/Action.svelte"; import Action from "$lib/components/Action.svelte";
import { popup } from "$lib/popup"; import { popup } from "$lib/popup";
import { serialPort } from "$lib/serial/connection"; import { deviceMeta, 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 LL from "$i18n/i18n-svelte";
@@ -12,8 +12,11 @@
downloadBackup, downloadBackup,
downloadFile, downloadFile,
restoreBackup, restoreBackup,
restoreFromFile,
} from "$lib/backup/backup"; } from "$lib/backup/backup";
import { preference } from "$lib/preferences"; import { preference } from "$lib/preferences";
import { action } from "$lib/title";
import { fly } from "svelte/transition";
</script> </script>
<svelte:head> <svelte:head>
@@ -77,7 +80,15 @@
use:setting={{ id: 0x92 }} use:setting={{ id: 0x92 }}
/></label /></label
> >
<button class="outline" use:popup={ResetPopup}>Reset...</button> {#if $deviceMeta?.factoryDefaults?.settings}
<button
use:action={{ title: "Reset Settings" }}
transition:fly={{ x: -8 }}
onclick={() => restoreFromFile($deviceMeta.factoryDefaults!.settings)}
><span class="icon">reset_settings</span>Reset Settings</button
>
{/if}
<button class="outline" use:popup={ResetPopup}>Recovery...</button>
{/if} {/if}
</fieldset> </fieldset>