fix: crash when saving empty chords

This commit is contained in:
2025-12-12 17:41:54 +01:00
parent b13c34ca15
commit fe42dcd2ab
7 changed files with 379 additions and 150 deletions

View File

@@ -72,22 +72,26 @@ export function createSettingsBackup(): CharaSettingsFile {
};
}
export async function restoreBackup(event: Event) {
export async function restoreBackup(
event: Event,
only?: "chords" | "layout" | "settings",
) {
const input = (event.target as HTMLInputElement).files![0];
if (!input) return;
const text = await input.text();
if (input.name.endsWith(".json")) {
restoreFromFile(JSON.parse(text));
restoreFromFile(JSON.parse(text), only);
} else if (isCsvLayout(text)) {
restoreFromFile(csvLayoutToJson(text));
restoreFromFile(csvLayoutToJson(text), only);
} else if (isCsvChords(text)) {
restoreFromFile(csvChordsToJson(text));
restoreFromFile(csvChordsToJson(text), only);
} else {
}
}
export function restoreFromFile(
file: CharaBackupFile | CharaSettingsFile | CharaLayoutFile | CharaChordFile,
only?: "chords" | "layout" | "settings",
) {
if (file.charaVersion !== 1) throw new Error("Incompatible backup");
switch (file.type) {
@@ -112,33 +116,45 @@ export function restoreFromFile(
changes.update((changes) => {
changes.push([
...getChangesFromChordFile(recent[0]),
...getChangesFromLayoutFile(recent[1]),
...getChangesFromSettingsFile(recent[2]),
...(!only || only === "chords"
? getChangesFromChordFile(recent[0])
: []),
...(!only || only === "layout"
? getChangesFromLayoutFile(recent[1])
: []),
...(!only || only === "settings"
? getChangesFromSettingsFile(recent[2])
: []),
]);
return changes;
});
break;
}
case "chords": {
changes.update((changes) => {
changes.push(getChangesFromChordFile(file));
return changes;
});
if (!only || only === "chords") {
changes.update((changes) => {
changes.push(getChangesFromChordFile(file));
return changes;
});
}
break;
}
case "layout": {
changes.update((changes) => {
changes.push(getChangesFromLayoutFile(file));
return changes;
});
if (!only || only === "layout") {
changes.update((changes) => {
changes.push(getChangesFromLayoutFile(file));
return changes;
});
}
break;
}
case "settings": {
changes.update((changes) => {
changes.push(getChangesFromSettingsFile(file));
return changes;
});
if (!only || only === "settings") {
changes.update((changes) => {
changes.push(getChangesFromSettingsFile(file));
return changes;
});
}
break;
}
default: {

View File

@@ -1,7 +1,12 @@
<script lang="ts">
import { serialLog, serialPort } from "$lib/serial/connection";
import { onMount } from "svelte";
import { slide } from "svelte/transition";
onMount(() => {
io.scrollTo({ top: io.scrollHeight });
});
function submit(event: Event) {
event.preventDefault();
$serialPort?.send(0, value.trim());

View File

@@ -342,23 +342,24 @@ export class CharaDevice {
.join(" ")
.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
const readResult = await read(timeout);
if (readResult === undefined) {
return readResult
?.replace(new RegExp(`^${commandString} `), "")
.split(" ");
}).then((it) => {
if (it === undefined) {
console.error("No response");
return Array(expectedLength).fill("NO_RESPONSE") as LengthArray<
string,
T
>;
}
const array = readResult
.replace(new RegExp(`^${commandString} `), "")
.split(" ");
if (array.length < expectedLength) {
if (it.length < expectedLength) {
console.error("Response too short");
return array.concat(
Array(expectedLength - array.length).fill("TOO_SHORT"),
return it.concat(
Array(expectedLength - it.length).fill("TOO_SHORT"),
) as LengthArray<string, T>;
}
return array as LengthArray<string, T>;
return it as LengthArray<string, T>;
});
}
@@ -401,7 +402,7 @@ export class CharaDevice {
stringifyChordActions(chord.actions),
stringifyPhrase(chord.phrase),
]);
if (status !== "0") console.error(`Failed with status ${status}`);
if (status !== "0") throw new Error(`Failed with status ${status}`);
}
async deleteChord(chord: Pick<Chord, "actions">) {

View File

@@ -179,6 +179,22 @@ export const chords = derived(
},
);
export const duplicateChords = derived(chords, (chords) => {
const duplicates = new Set<string>();
const seen = new Set<string>();
for (const chord of chords) {
const key = JSON.stringify(chord.actions);
if (seen.has(key)) {
duplicates.add(key);
} else {
seen.add(key);
}
}
return duplicates;
});
export const chordHashes = derived(
chords,
(chords) =>