mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-06-04 04:48:55 +00:00
update
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json",
|
"$schema": "https://unpkg.com/typesafe-i18n@5.27.1/schema/typesafe-i18n.json",
|
||||||
"baseLocale": "en",
|
"baseLocale": "en",
|
||||||
"adapter": "svelte"
|
"adapter": "svelte"
|
||||||
}
|
}
|
||||||
135
pnpm-lock.yaml
generated
135
pnpm-lock.yaml
generated
@@ -132,8 +132,8 @@ importers:
|
|||||||
specifier: ^4.1.1
|
specifier: ^4.1.1
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: ^26.1.0
|
specifier: ^29.0.1
|
||||||
version: 26.1.0
|
version: 29.0.1
|
||||||
jszip:
|
jszip:
|
||||||
specifier: ^3.10.1
|
specifier: ^3.10.1
|
||||||
version: 3.10.1
|
version: 3.10.1
|
||||||
@@ -2651,8 +2651,8 @@ packages:
|
|||||||
immediate@3.0.6:
|
immediate@3.0.6:
|
||||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||||
|
|
||||||
immutable@5.1.1:
|
immutable@5.1.5:
|
||||||
resolution: {integrity: sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==}
|
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
|
||||||
|
|
||||||
import-fresh@3.3.0:
|
import-fresh@3.3.0:
|
||||||
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||||
@@ -2978,8 +2978,8 @@ packages:
|
|||||||
jszip@3.10.1:
|
jszip@3.10.1:
|
||||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||||
|
|
||||||
keyv@5.5.5:
|
keyv@5.6.0:
|
||||||
resolution: {integrity: sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==}
|
resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==}
|
||||||
|
|
||||||
kind-of@6.0.3:
|
kind-of@6.0.3:
|
||||||
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
|
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
|
||||||
@@ -3002,6 +3002,76 @@ packages:
|
|||||||
lie@3.3.0:
|
lie@3.3.0:
|
||||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||||
|
|
||||||
|
lightningcss-android-arm64@1.32.0:
|
||||||
|
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
lightningcss-darwin-arm64@1.32.0:
|
||||||
|
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
lightningcss-darwin-x64@1.32.0:
|
||||||
|
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
lightningcss-freebsd-x64@1.32.0:
|
||||||
|
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||||
|
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-gnu@1.32.0:
|
||||||
|
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-musl@1.32.0:
|
||||||
|
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
lightningcss-linux-x64-gnu@1.32.0:
|
||||||
|
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
lightningcss-linux-x64-musl@1.32.0:
|
||||||
|
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
lightningcss-win32-arm64-msvc@1.32.0:
|
||||||
|
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
lightningcss-win32-x64-msvc@1.32.0:
|
||||||
|
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
lightningcss@1.32.0:
|
||||||
|
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
|
||||||
lines-and-columns@1.2.4:
|
lines-and-columns@1.2.4:
|
||||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||||
|
|
||||||
@@ -7164,7 +7234,7 @@ snapshots:
|
|||||||
|
|
||||||
immediate@3.0.6: {}
|
immediate@3.0.6: {}
|
||||||
|
|
||||||
immutable@5.1.1: {}
|
immutable@5.1.5: {}
|
||||||
|
|
||||||
import-fresh@3.3.0:
|
import-fresh@3.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7488,7 +7558,7 @@ snapshots:
|
|||||||
readable-stream: 2.3.8
|
readable-stream: 2.3.8
|
||||||
setimmediate: 1.0.5
|
setimmediate: 1.0.5
|
||||||
|
|
||||||
keyv@5.5.5:
|
keyv@5.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@keyv/serialize': 1.1.1
|
'@keyv/serialize': 1.1.1
|
||||||
|
|
||||||
@@ -7506,6 +7576,55 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
immediate: 3.0.6
|
immediate: 3.0.6
|
||||||
|
|
||||||
|
lightningcss-android-arm64@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-darwin-arm64@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-darwin-x64@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-freebsd-x64@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-gnu@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-musl@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-x64-gnu@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-x64-musl@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-win32-arm64-msvc@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-win32-x64-msvc@1.32.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss@1.32.0:
|
||||||
|
dependencies:
|
||||||
|
detect-libc: 2.1.2
|
||||||
|
optionalDependencies:
|
||||||
|
lightningcss-android-arm64: 1.32.0
|
||||||
|
lightningcss-darwin-arm64: 1.32.0
|
||||||
|
lightningcss-darwin-x64: 1.32.0
|
||||||
|
lightningcss-freebsd-x64: 1.32.0
|
||||||
|
lightningcss-linux-arm-gnueabihf: 1.32.0
|
||||||
|
lightningcss-linux-arm64-gnu: 1.32.0
|
||||||
|
lightningcss-linux-arm64-musl: 1.32.0
|
||||||
|
lightningcss-linux-x64-gnu: 1.32.0
|
||||||
|
lightningcss-linux-x64-musl: 1.32.0
|
||||||
|
lightningcss-win32-arm64-msvc: 1.32.0
|
||||||
|
lightningcss-win32-x64-msvc: 1.32.0
|
||||||
|
|
||||||
lines-and-columns@1.2.4: {}
|
lines-and-columns@1.2.4: {}
|
||||||
|
|
||||||
listr2@3.14.0(enquirer@2.4.1):
|
listr2@3.14.0(enquirer@2.4.1):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LineBreakTransformer } from "$lib/serial/line-break-transformer";
|
import { LineBreakTransformer } from "$lib/serial/line-break-transformer";
|
||||||
import { serialLog, type SerialLogEntry } from "$lib/serial/connection";
|
import { serialLog } from "$lib/serial/connection";
|
||||||
import type { Chord } from "$lib/serial/chord";
|
import type { Chord } from "$lib/serial/chord";
|
||||||
import {
|
import {
|
||||||
parseChordActions,
|
parseChordActions,
|
||||||
@@ -143,12 +143,15 @@ export class CharaDevice {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly port: SerialPortLike,
|
readonly port: SerialPortLike,
|
||||||
public baudRate = navigator.userAgent.includes("Mac") ? 38400 : 115200,
|
public baudRate = 921600,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
try {
|
try {
|
||||||
await this.port.open({ baudRate: this.baudRate });
|
await this.port.open({
|
||||||
|
baudRate: this.baudRate,
|
||||||
|
parity: "even",
|
||||||
|
});
|
||||||
const info = this.port.getInfo();
|
const info = this.port.getInfo();
|
||||||
serialLog.update((it) => {
|
serialLog.update((it) => {
|
||||||
it.push({
|
it.push({
|
||||||
@@ -554,48 +557,37 @@ export class CharaDevice {
|
|||||||
|
|
||||||
const writer = this.port.writable!.getWriter();
|
const writer = this.port.writable!.getWriter();
|
||||||
try {
|
try {
|
||||||
const start = performance.now();
|
await writer.write(new TextEncoder().encode(`RST OTA\r\n`));
|
||||||
writer.write(new TextEncoder().encode(`RST OTA\r\n`));
|
serialLog.update((it) => {
|
||||||
|
it.push({
|
||||||
|
type: "input",
|
||||||
|
value: "RST OTA",
|
||||||
|
});
|
||||||
|
return it;
|
||||||
|
});
|
||||||
|
|
||||||
// Wait for the device to be ready
|
// Wait for the device to be ready
|
||||||
const signal = await this.reader.read();
|
const signal = await this.reader.read();
|
||||||
const signalTime = performance.now();
|
|
||||||
|
|
||||||
const chunkSize = 128;
|
|
||||||
const chunks: Promise<void>[] = [];
|
|
||||||
for (let i = 0; i < file.byteLength; i += chunkSize) {
|
|
||||||
const size = Math.min(chunkSize, file.byteLength - i);
|
|
||||||
chunks.push(
|
|
||||||
writer
|
|
||||||
.write(new Uint8Array(file, i, size))
|
|
||||||
.then(() => progress(i + size, file.byteLength)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await Promise.all(chunks);
|
|
||||||
|
|
||||||
serialLog.update((it) => {
|
serialLog.update((it) => {
|
||||||
it.push(
|
it.push({
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
value: "RST OTA",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "system",
|
|
||||||
value: `+${(signalTime - start).toFixed(0)} ms`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "output",
|
type: "output",
|
||||||
value: signal.value!.trim(),
|
value: signal.value!.trim(),
|
||||||
},
|
});
|
||||||
{
|
return it;
|
||||||
type: "system",
|
});
|
||||||
value: `+${(performance.now() - signalTime).toFixed(0)} ms`,
|
|
||||||
},
|
const chunkSize = 128;
|
||||||
{
|
for (let i = 0; i < file.byteLength; i += chunkSize) {
|
||||||
|
const chunk = file.slice(i, i + chunkSize);
|
||||||
|
await writer.write(new Uint8Array(chunk));
|
||||||
|
progress(i + chunk.byteLength, file.byteLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
serialLog.update((it) => {
|
||||||
|
it.push({
|
||||||
type: "input",
|
type: "input",
|
||||||
value: `...${file.byteLength} bytes`,
|
value: `...${file.byteLength} bytes`,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
return it;
|
return it;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -622,8 +614,9 @@ export class CharaDevice {
|
|||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
writer.releaseLock();
|
writer.releaseLock();
|
||||||
await this.suspend();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.suspend();
|
||||||
} finally {
|
} finally {
|
||||||
delete this.lock;
|
delete this.lock;
|
||||||
resolveLock!(true);
|
resolveLock!(true);
|
||||||
|
|||||||
@@ -104,114 +104,6 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function safeDeleteChord(actions: number[]): Promise<boolean> {
|
|
||||||
const port = $serialPort;
|
|
||||||
if (!port) return false;
|
|
||||||
try {
|
|
||||||
await port.deleteChord({ actions });
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
try {
|
|
||||||
if ((await port.getChordPhrase(actions)) === undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveChords(progress: () => void): Promise<boolean> {
|
|
||||||
const port = $serialPort;
|
|
||||||
if (!port) return false;
|
|
||||||
let ok = true;
|
|
||||||
|
|
||||||
const empty = new Set<string>();
|
|
||||||
for (const [id, chord] of $overlay.chords) {
|
|
||||||
if (chord.actions.length === 0 || chord.phrase.length === 0) {
|
|
||||||
empty.add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
changes.update((changes) => {
|
|
||||||
changes.push([
|
|
||||||
...empty.keys().map(
|
|
||||||
(id) =>
|
|
||||||
({
|
|
||||||
type: ChangeType.Chord,
|
|
||||||
id: JSON.parse(id),
|
|
||||||
deleted: true,
|
|
||||||
actions: [],
|
|
||||||
phrase: [],
|
|
||||||
}) satisfies ChordChange,
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
return changes;
|
|
||||||
});
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
const deleted = new Set<string>();
|
|
||||||
const changed = new Map<string, number[]>();
|
|
||||||
for (const [id, chord] of $overlay.chords) {
|
|
||||||
if (!chord.deleted) continue;
|
|
||||||
if (await safeDeleteChord(JSON.parse(id))) {
|
|
||||||
deleted.add(id);
|
|
||||||
} else {
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
progress();
|
|
||||||
}
|
|
||||||
deviceChords.update((chords) =>
|
|
||||||
chords.filter((chord) => !deleted.has(JSON.stringify(chord.actions))),
|
|
||||||
);
|
|
||||||
deleted.clear();
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
for (const [id, chord] of $overlay.chords) {
|
|
||||||
if (chord.deleted) continue;
|
|
||||||
if ($duplicateChords.has(JSON.stringify(chord.actions))) {
|
|
||||||
ok = false;
|
|
||||||
} else {
|
|
||||||
let skip = false;
|
|
||||||
if (id !== JSON.stringify(chord.actions)) {
|
|
||||||
if (await safeDeleteChord(JSON.parse(id))) {
|
|
||||||
deleted.add(id);
|
|
||||||
} else {
|
|
||||||
skip = true;
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!skip) {
|
|
||||||
try {
|
|
||||||
await port.setChord({
|
|
||||||
actions: chord.actions,
|
|
||||||
phrase: chord.phrase,
|
|
||||||
});
|
|
||||||
deleted.add(JSON.stringify(chord.actions));
|
|
||||||
changed.set(JSON.stringify(chord.actions), chord.phrase);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progress();
|
|
||||||
}
|
|
||||||
deviceChords.update((chords) => {
|
|
||||||
chords.filter((chord) => !deleted.has(JSON.stringify(chord.actions)));
|
|
||||||
for (const [id, phrase] of changed) {
|
|
||||||
chords.push({ actions: JSON.parse(id), phrase });
|
|
||||||
}
|
|
||||||
return chords;
|
|
||||||
});
|
|
||||||
await tick();
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
try {
|
try {
|
||||||
const port = $serialPort;
|
const port = $serialPort;
|
||||||
|
|||||||
Reference in New Issue
Block a user