diff --git a/src/lib/components/Navigation.svelte b/src/lib/components/Navigation.svelte index aea097fa..32f408f3 100644 --- a/src/lib/components/Navigation.svelte +++ b/src/lib/components/Navigation.svelte @@ -1,5 +1,5 @@ diff --git a/src/lib/components/RingInput.svelte b/src/lib/components/RingInput.svelte index dfecea3a..be6b63e5 100644 --- a/src/lib/components/RingInput.svelte +++ b/src/lib/components/RingInput.svelte @@ -1,43 +1,32 @@ - diff --git a/src/lib/components/Terminal.svelte b/src/lib/components/Terminal.svelte index 7b0f4ddd..a52847d4 100644 --- a/src/lib/components/Terminal.svelte +++ b/src/lib/components/Terminal.svelte @@ -1,22 +1,16 @@ - diff --git a/src/lib/icons.js b/src/lib/icons.js deleted file mode 100644 index 5a42d295..00000000 --- a/src/lib/icons.js +++ /dev/null @@ -1,3 +0,0 @@ -export const Icon = { - close: 0xe5cd, -} diff --git a/src/lib/serial/connection.js b/src/lib/serial/connection.ts similarity index 52% rename from src/lib/serial/connection.js rename to src/lib/serial/connection.ts index 09720113..327546c7 100644 --- a/src/lib/serial/connection.js +++ b/src/lib/serial/connection.ts @@ -1,19 +1,26 @@ import {writable} from "svelte/store" -import {CharaDevice} from "$lib/serial/device.js" +import {CharaDevice} from "$lib/serial/device" -/** @type {import('svelte/store').Writable} */ -export const serialPort = writable() +export const serialPort = writable() -/** @type {import('svelte/store').Writable>} */ -export const serialLog = writable([]) +export interface SerialLogEntry { + type: "input" | "output" | "system" + value: string +} -/** @type {import('svelte/store').Writable>} */ -export const chords = writable([]) +export const serialLog = writable([]) -/** @type {import('svelte/store').Writable<[number[], number[], number[]]>} */ -export const layout = writable([[], [], []]) +export interface Chord { + actions: number[] + phrase: string +} + +export const chords = writable([]) + +export type CharaLayout = [number[], number[], number[]] + +export const layout = writable([[], [], []]) -/** @type {import('svelte/store').Writable} */ export const syncing = writable(false) /** @type {CharaDevice} */ @@ -24,7 +31,7 @@ export async function initSerial() { device ??= new CharaDevice() serialPort.set(device) - const parsedLayout = [[], [], []] + const parsedLayout: CharaLayout = [[], [], []] for (let layer = 1; layer <= 3; layer++) { for (let i = 0; i < 90; i++) { parsedLayout[layer - 1][i] = await device.getLayoutKey(layer, i) diff --git a/src/lib/serial/device.js b/src/lib/serial/device.ts similarity index 53% rename from src/lib/serial/device.js rename to src/lib/serial/device.ts index 5cbfc1a4..4eca5c8e 100644 --- a/src/lib/serial/device.js +++ b/src/lib/serial/device.ts @@ -1,5 +1,6 @@ -import {LineBreakTransformer} from "$lib/serial/line-break-transformer.js" -import {serialLog} from "$lib/serial/connection.js" +import {LineBreakTransformer} from "$lib/serial/line-break-transformer" +import {serialLog} from "$lib/serial/connection" +import type {Chord} from "$lib/serial/connection" export const VENDOR_ID = 0x239a @@ -11,29 +12,22 @@ export async function hasSerialPermission() { } export class CharaDevice { - /** @type {Promise} */ - #port - /** @type {Promise>} */ - #reader + private readonly port: Promise + private readonly reader: Promise> - #encoder = new TextEncoder() + private readonly abortController1 = new AbortController() + private readonly abortController2 = new AbortController() - #abortController1 = new AbortController() - #abortController2 = new AbortController() + private lock?: Promise - /** @type {Promise | undefined} */ - #lock - - /** @type {Promise} */ - version - /** @type {Promise} */ - deviceId + version: Promise + deviceId: Promise /** * @param baudRate */ constructor(baudRate = 115200) { - this.#port = navigator.serial.getPorts().then(async ports => { + this.port = navigator.serial.getPorts().then(async ports => { const port = ports.find(it => it.getInfo().usbVendorId === VENDOR_ID) ?? (await navigator.serial.requestPort({filters: [{usbVendorId: VENDOR_ID}]})) @@ -42,7 +36,7 @@ export class CharaDevice { serialLog.update(it => { it.push({ type: "system", - value: `Connected; ID: 0x${info.usbProductId.toString(16)}; Vendor: 0x${info.usbVendorId.toString( + value: `Connected; ID: 0x${info.usbProductId?.toString(16)}; Vendor: 0x${info.usbVendorId?.toString( 16, )}`, }) @@ -50,29 +44,27 @@ export class CharaDevice { }) return port }) - this.#reader = this.#port.then(async port => { + this.reader = this.port.then(async port => { const decoderStream = new TextDecoderStream() - void port.readable.pipeTo(decoderStream.writable, {signal: this.#abortController1.signal}) + void port.readable!.pipeTo(decoderStream.writable, {signal: this.abortController1.signal}) - return decoderStream.readable - .pipeThrough(new TransformStream(new LineBreakTransformer()), {signal: this.#abortController2.signal}) + return decoderStream + .readable!.pipeThrough(new TransformStream(new LineBreakTransformer()), { + signal: this.abortController2.signal, + }) .getReader() }) - this.#lock = this.#reader.then(() => { - this.#lock = undefined + this.lock = this.reader.then(() => { + delete this.lock return true }) this.version = this.send("VERSION") this.deviceId = this.send("ID") } - /** - * @returns {Promise} - */ - async #read() { - return this.#reader.then(async it => { - /** @type {string} */ - const result = await it.read().then(({value}) => value) + private async internalRead() { + return this.reader.then(async it => { + const result: string = await it.read().then(({value}) => value!) serialLog.update(it => { it.push({ type: "output", @@ -86,12 +78,10 @@ export class CharaDevice { /** * Send a command to the device - * @param command {string} - * @returns {Promise} */ - async #send(...command) { - const port = await this.#port - const writer = port.writable.getWriter() + private async internalSend(...command: string[]) { + const port = await this.port + const writer = port.writable!.getWriter() try { serialLog.update(it => { it.push({ @@ -108,35 +98,32 @@ export class CharaDevice { /** * Read/write to serial port - * @template T - * @param callback {(send: (...commands: string) => Promise, read: () => Promise) => T | Promise} - * @returns Promise */ - async runWith(callback) { - while (this.#lock) { - await this.#lock + async runWith( + callback: (send: typeof this.internalSend, read: typeof this.internalRead) => T | Promise, + ): Promise { + while (this.lock) { + await this.lock } - const send = this.#send.bind(this) - const read = this.#read.bind(this) - const exec = new Promise(async resolve => { - let result + const send = this.internalSend.bind(this) + const read = this.internalRead.bind(this) + const exec = new Promise(async resolve => { + let result!: T try { result = await callback(send, read) } finally { - this.#lock = undefined + this.lock = undefined resolve(result) } }) - this.#lock = exec.then(() => true) + this.lock = exec.then(() => true) return exec } /** * Send to serial port - * @param command {string} - * @returns Promise */ - async send(...command) { + async send(...command: string[]) { return this.runWith(async (send, read) => { await send(...command) const commandString = command.join(" ").replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") @@ -144,20 +131,13 @@ export class CharaDevice { }) } - /** - * @returns {Promise} - */ - async getChordCount() { + async getChordCount(): Promise { return Number.parseInt(await this.send("CML C0")) } - /** - * @param index {number} - * @returns {Promise<{actions: number[]; phrase: string, unk: number}>} - */ - async getChord(index) { + async getChord(index: number): Promise { const chord = await this.send(`CML C1 ${index}`) - const [keys, rawPhrase, b] = chord.split(" ") + const [keys, rawPhrase] = chord.split(" ") let phrase = [] for (let i = 0; i < rawPhrase.length; i += 2) { phrase.push(Number.parseInt(rawPhrase.substring(i, i + 2), 16)) @@ -175,16 +155,10 @@ export class CharaDevice { return { actions, phrase: String.fromCodePoint(...phrase), - unk: Number(b), } } - /** - * @param layer {number} - * @param id {number} - * @returns {Promise} - */ - async getLayoutKey(layer, id) { + async getLayoutKey(layer: number, id: number) { const layout = await this.send(`VAR B3 A${layer} ${id}`) const [position] = layout.split(" ").map(Number) return position diff --git a/src/lib/serial/keymap-codes.js b/src/lib/serial/keymap-codes.js deleted file mode 100644 index 2bee31a4..00000000 --- a/src/lib/serial/keymap-codes.js +++ /dev/null @@ -1,17 +0,0 @@ -import keymapCodes from "$lib/assets/keymap_codes.json" -import keySymbols from "$lib/assets/key-symbols.json" - -/** @type {Record} */ -export const KEYMAP_CODES = Object.fromEntries( - keymapCodes.map(([code, charset, id, title, description]) => [ - code, - { - code: Number(code), - title: title || undefined, - charset: charset || undefined, - id: id || undefined, - symbol: id ? keySymbols[id] || undefined : undefined, - description: description || undefined, - }, - ]), -) diff --git a/src/lib/serial/keymap.d.ts b/src/lib/serial/keymap-codes.ts similarity index 59% rename from src/lib/serial/keymap.d.ts rename to src/lib/serial/keymap-codes.ts index be09db9e..5475d0fb 100644 --- a/src/lib/serial/keymap.d.ts +++ b/src/lib/serial/keymap-codes.ts @@ -1,3 +1,6 @@ +import keymapCodes from "$lib/assets/keymap_codes.json" +import keySymbols from "$lib/assets/key-symbols.json" + export interface KeyInfo { /** * Numeric action code @@ -45,3 +48,17 @@ export type CharsetCategory = | "Keybard" | "CharaChorder" | "CharaChorder One" + +export const KEYMAP_CODES: Record = Object.fromEntries( + keymapCodes.map(([code, charset, id, title, description]) => [ + code, + { + code: Number(code), + title: title || undefined, + charset: (charset || undefined) as CharsetCategory, + id: id || undefined, + symbol: id ? keySymbols[id as keyof typeof keySymbols] || undefined : undefined, + description: description || undefined, + }, + ]), +) diff --git a/src/lib/serial/line-break-transformer.js b/src/lib/serial/line-break-transformer.ts similarity index 62% rename from src/lib/serial/line-break-transformer.js rename to src/lib/serial/line-break-transformer.ts index 36c6bad7..473d447d 100644 --- a/src/lib/serial/line-break-transformer.js +++ b/src/lib/serial/line-break-transformer.ts @@ -1,21 +1,18 @@ -// @ts-check export class LineBreakTransformer { - constructor() { - this.chunks = "" - } + private chunks = "" // noinspection JSUnusedGlobalSymbols - transform(chunk, controller) { + transform(chunk: string, controller: TransformStreamDefaultController) { this.chunks += chunk const lines = this.chunks.split("\r\n") - this.chunks = lines.pop() + this.chunks = lines.pop()! for (const line of lines) { controller.enqueue(line) } } // noinspection JSUnusedGlobalSymbols - flush(controller) { + flush(controller: TransformStreamDefaultController) { controller.enqueue(this.chunks) } } diff --git a/src/routes/+layout.server.js b/src/routes/+layout.server.js deleted file mode 100644 index 3cf134e9..00000000 --- a/src/routes/+layout.server.js +++ /dev/null @@ -1,10 +0,0 @@ -import {themeBase, themeColor, themeSuccessBase} from "$lib/style/theme.server.js" - -/** @type {import("./$types").LayoutServerLoad} */ -export async function load() { - return { - themeSuccessBase, - themeBase, - themeColor, - } -} diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts new file mode 100644 index 00000000..b4fef69b --- /dev/null +++ b/src/routes/+layout.server.ts @@ -0,0 +1,8 @@ +import {themeBase, themeColor, themeSuccessBase} from "$lib/style/theme.server" +import type {LayoutServerLoad} from "./$types" + +export const load = (async () => ({ + themeSuccessBase, + themeBase, + themeColor, +})) satisfies LayoutServerLoad diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index ca55c790..5b52648c 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,15 +1,17 @@ - diff --git a/jsconfig.json b/tsconfig.json similarity index 100% rename from jsconfig.json rename to tsconfig.json