diff --git a/src/lib/serial/connection.ts b/src/lib/serial/connection.ts index f0196aa1..dde8bd53 100644 --- a/src/lib/serial/connection.ts +++ b/src/lib/serial/connection.ts @@ -1,4 +1,4 @@ -import { get, writable } from "svelte/store"; +import { derived, get, writable } from "svelte/store"; import { CharaDevice, type SerialPortLike } from "$lib/serial/device"; import type { Chord } from "$lib/serial/chord"; import type { Writable } from "svelte/store"; @@ -7,12 +7,27 @@ import { persistentWritable } from "$lib/storage"; import { userPreferences } from "$lib/preferences"; import { getMeta } from "$lib/meta/meta-storage"; import type { VersionMeta } from "$lib/meta/types/meta"; +import { serial as serialPolyfill } from "web-serial-polyfill"; export const serialPort = writable(); -navigator.serial?.addEventListener("disconnect", async (event) => { +export const forceWebUSB = persistentWritable("force-webusb", false); + +async function onSerialDisconnect() { serialPort.set(undefined); -}); +} + +export const serialObject = derived( + forceWebUSB, + (forceWebUSB) => + forceWebUSB || !("serial" in navigator) + ? (serialPolyfill as any as Serial) + : navigator.serial, +); + +if ("serial" in navigator) { + navigator.serial.addEventListener("disconnect", onSerialDisconnect); +} export interface SerialLogEntry { type: "input" | "output" | "system"; diff --git a/src/lib/serial/device.ts b/src/lib/serial/device.ts index 2a3f0330..9f77419f 100644 --- a/src/lib/serial/device.ts +++ b/src/lib/serial/device.ts @@ -7,7 +7,6 @@ import { stringifyChordActions, stringifyPhrase, } from "$lib/serial/chord"; -import { browser } from "$app/environment"; import { showConnectionFailedDialog } from "$lib/dialogs/connection-failed-dialog"; import semverGte from "semver/functions/gte"; @@ -71,23 +70,8 @@ const KEY_COUNTS = { ZERO: 256, } as const; -if ( - browser && - navigator.serial === undefined && - import.meta.env.TAURI_FAMILY !== undefined -) { - await import("./tauri-serial"); -} - -if (browser && navigator.serial === undefined && navigator.usb !== undefined) { - // @ts-expect-error polyfill - navigator.serial = await import("web-serial-polyfill").then( - ({ serial }) => serial, - ); -} - -export async function getViablePorts(): Promise { - return navigator.serial.getPorts().then((ports) => +export async function getViablePorts(serial: Serial): Promise { + return serial.getPorts().then((ports) => ports.filter((it) => { const { usbProductId, usbVendorId } = it.getInfo(); for (const filter of PORT_FILTERS.values()) { @@ -109,8 +93,8 @@ type LengthArray = number extends N ? R : LengthArray; -export async function canAutoConnect() { - return getViablePorts().then((it) => it.length === 1); +export async function canAutoConnect(serial: Serial) { + return getViablePorts(serial).then((it) => it.length === 1); } async function timeout(promise: Promise, ms: number): Promise { diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 22a187b5..32580d95 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -12,7 +12,7 @@ themeFromSourceColor, } from "@material/material-color-utilities"; import { canAutoConnect, getViablePorts } from "$lib/serial/device"; - import { initSerial } from "$lib/serial/connection"; + import { initSerial, serialObject } from "$lib/serial/connection"; import type { LayoutData } from "./$types"; import { browser } from "$app/environment"; import "tippy.js/animations/shift-away.css"; @@ -74,8 +74,12 @@ webManifestLink = await initPwa(); } - if (browser && $userPreferences.autoConnect && (await canAutoConnect())) { - const [port] = await getViablePorts(); + if ( + browser && + $userPreferences.autoConnect && + (await canAutoConnect($serialObject)) + ) { + const [port] = await getViablePorts($serialObject); await initSerial(port!, true); } diff --git a/src/routes/(app)/ConnectPopup.svelte b/src/routes/(app)/ConnectPopup.svelte index 1e8b24e8..57ee2bbd 100644 --- a/src/routes/(app)/ConnectPopup.svelte +++ b/src/routes/(app)/ConnectPopup.svelte @@ -1,21 +1,27 @@ @@ -60,55 +66,79 @@
refreshPorts()} + onmouseenter={() => refreshPorts($serialObject)} role="region" > - {#if ports.length === 1} + {#if supportsWebSerial || supportsWebUSB}
- + {#if ports.length === 1} + -
- {/if} - {#if ports.length !== 0} -

Recent Devices

-
- - {#each ports as port} -
- - -
- {/each} + {#each ports as port} +
+ + +
+ {/each} +
+ {/if} +
+ +
+ {#if !supportsWebSerial} +

Browser with limited support detected.

+ {/if} + {:else} +

Your browser is missing support for critical features.

+

+ Please use a Chromium-based browser such as Chrome, Edge or Chromium + instead. +

{/if} -
- - -