-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: backup import (except chords) feat: legacy layout import feat: separate layout, chord & setting backup downloads
- Loading branch information
1 parent
acd5864
commit c5d9def
Showing
20 changed files
with
299 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import type { | ||
CharaBackupFile, | ||
CharaChordFile, | ||
CharaSettingsFile, | ||
CharaLayoutFile, | ||
CharaFile, | ||
} from "$lib/share/chara-file.js" | ||
import {changes, ChangeType, chords, layout, settings} from "$lib/undo-redo.js" | ||
import type {Change} from "$lib/undo-redo.js" | ||
import {get} from "svelte/store" | ||
import {serialPort} from "../serial/connection" | ||
import {csvLayoutToJson, isCsvLayout} from "$lib/backup/compat/legacy-layout" | ||
|
||
export function downloadFile<T extends CharaFile<string>>(contents: T) { | ||
const downloadUrl = URL.createObjectURL(new Blob([JSON.stringify(contents)], {type: "application/json"})) | ||
const element = document.createElement("a") | ||
element.setAttribute( | ||
"download", | ||
`${contents.type}-${get(serialPort)?.device}-${new Date().toISOString()}.json`, | ||
) | ||
element.href = downloadUrl | ||
element.setAttribute("target", "_blank") | ||
element.click() | ||
URL.revokeObjectURL(downloadUrl) | ||
} | ||
|
||
export function downloadBackup() { | ||
downloadFile<CharaBackupFile>({ | ||
charaVersion: 1, | ||
type: "backup", | ||
history: [[createChordBackup(), createLayoutBackup(), createSettingsBackup()]], | ||
}) | ||
} | ||
|
||
export function createLayoutBackup(): CharaLayoutFile { | ||
return { | ||
charaVersion: 1, | ||
type: "layout", | ||
device: get(serialPort)?.device, | ||
layout: get(layout).map(it => it.map(it => it.action)) as [number[], number[], number[]], | ||
} | ||
} | ||
|
||
export function createChordBackup(): CharaChordFile { | ||
return {charaVersion: 1, type: "chords", chords: get(chords).map(it => [it.actions, it.phrase])} | ||
} | ||
|
||
export function createSettingsBackup(): CharaSettingsFile { | ||
return {charaVersion: 1, type: "settings", settings: get(settings).map(it => it.value)} | ||
} | ||
|
||
export async function restoreBackup(event: Event) { | ||
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)) | ||
} else if (isCsvLayout(text)) { | ||
restoreFromFile(csvLayoutToJson(text)) | ||
} | ||
} | ||
|
||
export function restoreFromFile( | ||
file: CharaBackupFile | CharaSettingsFile | CharaLayoutFile | CharaChordFile, | ||
) { | ||
if (file.charaVersion !== 1) throw new Error("Incompatible backup") | ||
switch (file.type) { | ||
case "backup": { | ||
const recent = file.history[0] | ||
if (recent[1].device !== get(serialPort)?.device) | ||
throw new Error("Backup is incompatible with this device") | ||
|
||
changes.update(changes => { | ||
changes.push( | ||
...getChangesFromChordFile(recent[0]), | ||
...getChangesFromLayoutFile(recent[1]), | ||
...getChangesFromSettingsFile(recent[2]), | ||
) | ||
return changes | ||
}) | ||
break | ||
} | ||
case "chords": { | ||
changes.update(changes => { | ||
changes.push(...getChangesFromChordFile(file)) | ||
return changes | ||
}) | ||
break | ||
} | ||
case "layout": { | ||
changes.update(changes => { | ||
changes.push(...getChangesFromLayoutFile(file)) | ||
return changes | ||
}) | ||
break | ||
} | ||
case "settings": { | ||
changes.update(changes => { | ||
changes.push(...getChangesFromSettingsFile(file)) | ||
return changes | ||
}) | ||
break | ||
} | ||
default: { | ||
throw new Error(`Unknown backup type "${(file as CharaFile<string>).type}"`) | ||
} | ||
} | ||
} | ||
|
||
export function getChangesFromChordFile(file: CharaChordFile) { | ||
const changes: Change[] = [] | ||
// TODO... | ||
return changes | ||
} | ||
|
||
export function getChangesFromSettingsFile(file: CharaSettingsFile) { | ||
const changes: Change[] = [] | ||
for (const [id, value] of file.settings.entries()) { | ||
if (get(settings)[id].value !== value) { | ||
changes.push({ | ||
type: ChangeType.Setting, | ||
id, | ||
setting: value, | ||
}) | ||
} | ||
} | ||
return changes | ||
} | ||
|
||
export function getChangesFromLayoutFile(file: CharaLayoutFile) { | ||
const changes: Change[] = [] | ||
for (const [layer, keys] of file.layout.entries()) { | ||
for (const [id, action] of keys.entries()) { | ||
if (get(layout)[layer][id].action !== action) { | ||
changes.push({ | ||
type: ChangeType.Layout, | ||
layer, | ||
id, | ||
action, | ||
}) | ||
} | ||
} | ||
} | ||
return changes | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import {describe, it, expect} from "vitest" | ||
import {deserializeActionArray, serializeActionArray} from "./action-array" | ||
|
||
describe("action array", () => { | ||
it("should work with number arrays", () => { | ||
expect(deserializeActionArray(serializeActionArray([62, 256, 1235]))).toEqual([62, 256, 1235]) | ||
}) | ||
|
||
it("should work with nested arrays", () => { | ||
expect(deserializeActionArray(serializeActionArray([[], [[]]]))).toEqual([[], [[]]]) | ||
}) | ||
|
||
it("should compress back and forth", () => { | ||
expect( | ||
deserializeActionArray( | ||
serializeActionArray([ | ||
[43, 746, 634], | ||
[34, 63], | ||
[332, 34], | ||
]), | ||
), | ||
).toEqual([ | ||
[43, 746, 634], | ||
[34, 63], | ||
[332, 34], | ||
]) | ||
}) | ||
|
||
it("should compress a full layout", () => { | ||
const layout = Object.freeze([ | ||
Object.freeze([ | ||
0, 0, 0, 0, 0, 53, 119, 45, 103, 122, 52, 107, 118, 109, 99, 51, 114, 36, 59, 101, 50, 105, 34, 46, | ||
111, 49, 39, 515, 44, 117, 0, 512, 514, 513, 550, 0, 319, 318, 321, 320, 326, 315, 314, 317, 316, 0, | ||
0, 0, 0, 0, 54, 98, 120, 536, 113, 55, 102, 112, 104, 100, 56, 97, 296, 544, 116, 57, 108, 299, 106, | ||
110, 48, 121, 297, 61, 115, 0, 518, 516, 517, 553, 0, 336, 338, 335, 337, 0, 325, 322, 323, 324, | ||
]), | ||
Object.freeze([ | ||
0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 53, 0, 47, 52, 0, 51, 298, 0, 50, 0, 0, 127, 0, 49, 0, 0, 515, 0, 0, | ||
0, 512, 514, 513, 550, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 536, 0, 0, 54, 0, 92, | ||
55, 0, 56, 296, 544, 57, 0, 96, 299, 0, 48, 0, 0, 297, 0, 0, 0, 518, 516, 517, 553, 0, 336, 338, 335, | ||
337, 0, 0, 0, 0, 0, | ||
]), | ||
Object.freeze([ | ||
0, 0, 0, 0, 0, 0, 64, 95, 43, 0, 0, 126, 38, 63, 40, 0, 35, 298, 36, 123, 0, 33, 127, 37, 60, 0, 34, | ||
515, 0, 0, 0, 512, 514, 513, 550, 0, 333, 331, 330, 334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 536, | ||
0, 0, 94, 58, 124, 41, 0, 42, 296, 544, 125, 0, 126, 299, 0, 62, 0, 0, 297, 0, 0, 0, 518, 516, 517, | ||
553, 0, 336, 338, 335, 337, 0, 0, 0, 0, 0, | ||
]), | ||
]) | ||
|
||
expect(deserializeActionArray(serializeActionArray(layout as number[][]))).toEqual(layout) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,14 @@ | ||
import type {LayoutLoad} from "./$types" | ||
import {browser} from "$app/environment" | ||
import {charaFileFromUriComponent} from "$lib/share/share-url" | ||
|
||
export const prerender = true | ||
export const trailingSlash = "always" | ||
|
||
export const load = (async ({url, data, fetch}) => { | ||
const importFile = new URLSearchParams(url.search).get("import") | ||
return { | ||
...data, | ||
importFile: browser && importFile ? await charaFileFromUriComponent(importFile, fetch) : undefined, | ||
} | ||
}) satisfies LayoutLoad |
Oops, something went wrong.