diff --git a/.typesafe-i18n.json b/.typesafe-i18n.json index 7e08fafe..c335ee6f 100644 --- a/.typesafe-i18n.json +++ b/.typesafe-i18n.json @@ -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", "adapter": "svelte" } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc2328cf..5c642b70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,8 +132,8 @@ importers: specifier: ^4.1.1 version: 4.1.1 jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.0.1 + version: 29.0.1 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -2651,8 +2651,8 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@5.1.1: - resolution: {integrity: sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==} + immutable@5.1.5: + resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -2978,8 +2978,8 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - keyv@5.5.5: - resolution: {integrity: sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==} + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} @@ -3002,6 +3002,76 @@ packages: lie@3.3.0: 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: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -7164,7 +7234,7 @@ snapshots: immediate@3.0.6: {} - immutable@5.1.1: {} + immutable@5.1.5: {} import-fresh@3.3.0: dependencies: @@ -7488,7 +7558,7 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - keyv@5.5.5: + keyv@5.6.0: dependencies: '@keyv/serialize': 1.1.1 @@ -7506,6 +7576,55 @@ snapshots: dependencies: 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: {} listr2@3.14.0(enquirer@2.4.1): diff --git a/src/lib/serial/device.ts b/src/lib/serial/device.ts index c6bd711a..4348dcc8 100644 --- a/src/lib/serial/device.ts +++ b/src/lib/serial/device.ts @@ -1,5 +1,5 @@ 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 { parseChordActions, @@ -143,12 +143,15 @@ export class CharaDevice { constructor( readonly port: SerialPortLike, - public baudRate = navigator.userAgent.includes("Mac") ? 38400 : 115200, + public baudRate = 921600, ) {} async init() { try { - await this.port.open({ baudRate: this.baudRate }); + await this.port.open({ + baudRate: this.baudRate, + parity: "even", + }); const info = this.port.getInfo(); serialLog.update((it) => { it.push({ @@ -554,48 +557,37 @@ export class CharaDevice { const writer = this.port.writable!.getWriter(); try { - const start = performance.now(); - writer.write(new TextEncoder().encode(`RST OTA\r\n`)); + await 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 const signal = await this.reader.read(); - const signalTime = performance.now(); + serialLog.update((it) => { + it.push({ + type: "output", + value: signal.value!.trim(), + }); + return it; + }); const chunkSize = 128; - const chunks: Promise[] = []; 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)), - ); + const chunk = file.slice(i, i + chunkSize); + await writer.write(new Uint8Array(chunk)); + progress(i + chunk.byteLength, file.byteLength); } - await Promise.all(chunks); serialLog.update((it) => { - it.push( - { - type: "input", - value: "RST OTA", - }, - { - type: "system", - value: `+${(signalTime - start).toFixed(0)} ms`, - }, - { - type: "output", - value: signal.value!.trim(), - }, - { - type: "system", - value: `+${(performance.now() - signalTime).toFixed(0)} ms`, - }, - { - type: "input", - value: `...${file.byteLength} bytes`, - }, - ); + it.push({ + type: "input", + value: `...${file.byteLength} bytes`, + }); return it; }); @@ -622,8 +614,9 @@ export class CharaDevice { }); } finally { writer.releaseLock(); - await this.suspend(); } + + await this.suspend(); } finally { delete this.lock; resolveLock!(true); diff --git a/src/routes/(app)/config/EditActions.svelte b/src/routes/(app)/config/EditActions.svelte index e2ac18a3..552b4b97 100644 --- a/src/routes/(app)/config/EditActions.svelte +++ b/src/routes/(app)/config/EditActions.svelte @@ -104,114 +104,6 @@ return true; } - async function safeDeleteChord(actions: number[]): Promise { - 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 { - const port = $serialPort; - if (!port) return false; - let ok = true; - - const empty = new Set(); - 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(); - const changed = new Map(); - 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() { try { const port = $serialPort;