From ccdb91a24350ac3f3699a0c0e9d99d906df609ae Mon Sep 17 00:00:00 2001 From: daylily Date: Thu, 3 Apr 2025 18:06:00 -0400 Subject: [PATCH] Multiple refactors - Use aria-labelledby instead of aria-label where possible - Replace magic numbers with explicitly defined constants - Add input textboxes for values previously only changed with sliders --- src/App.svelte | 2 +- .../components/ImplicitNumericInput.svelte | 43 +++++++++++++++++++ src/lib/components/MoreInfo.svelte | 13 ++++-- src/lib/constants.ts | 10 +++++ src/lib/contexts/device.svelte.ts | 3 +- src/lib/contexts/rendered.svelte.ts | 7 +-- src/lib/image/transform.ts | 9 +++- src/lib/layouts/connect/ConnectButton.svelte | 5 ++- .../controls/BackgroundColorSlider.svelte | 15 +++++-- .../edit/controls/ControlsSection.svelte | 7 +-- .../controls/conversion/BiasSlider.svelte | 13 +++++- .../controls/conversion/ContrastSlider.svelte | 13 +++++- .../conversion/dither/DitherSwitch.svelte | 2 +- .../dither/DitheringKernelDropdown.svelte | 6 ++- .../dimensions/AspectRatioAlert.svelte | 4 +- .../dimensions/TransformControls.svelte | 2 +- .../layouts/edit/preview/FileSelect.svelte | 2 +- .../edit/preview/PreviewCanvas1x.svelte | 11 +++-- .../edit/preview/PreviewCanvas2x.svelte | 13 ++++-- src/lib/layouts/edit/preview/common.svelte.ts | 20 +++++---- src/lib/layouts/write/WriteButton.svelte | 13 +++--- src/lib/utils.ts | 4 ++ 22 files changed, 168 insertions(+), 49 deletions(-) create mode 100644 src/lib/components/ImplicitNumericInput.svelte create mode 100644 src/lib/constants.ts diff --git a/src/App.svelte b/src/App.svelte index b94e72c..1d3f108 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -12,7 +12,7 @@ -
+
{#if unsupported} {:else} diff --git a/src/lib/components/ImplicitNumericInput.svelte b/src/lib/components/ImplicitNumericInput.svelte new file mode 100644 index 0000000..552b99b --- /dev/null +++ b/src/lib/components/ImplicitNumericInput.svelte @@ -0,0 +1,43 @@ + + + { + let n = Number(e.currentTarget.value) + + if (isNaN(n)) { + e.currentTarget.value = showNumber(value) + return + } + + if (n < min) n = min + if (n > max) n = max + + e.currentTarget.value = showNumber(n) + onchange(n) + }} + {...restProps} +/> diff --git a/src/lib/components/MoreInfo.svelte b/src/lib/components/MoreInfo.svelte index 888b3a8..280ebd7 100644 --- a/src/lib/components/MoreInfo.svelte +++ b/src/lib/components/MoreInfo.svelte @@ -1,15 +1,22 @@ - + - + {#if children} + {@render children()} + {/if} diff --git a/src/lib/constants.ts b/src/lib/constants.ts new file mode 100644 index 0000000..8d4d8b2 --- /dev/null +++ b/src/lib/constants.ts @@ -0,0 +1,10 @@ +export const INKCLIP_VID = 0x1209 +export const INKCLIP_PID = 0xc9c9 + +export const INKCLIP_WIDTH = 200 // px +export const INKCLIP_HEIGHT = 200 // px + +export const INKCLIP_ASPECT_RATIO = INKCLIP_WIDTH / INKCLIP_HEIGHT +export const BYTES_IN_A_ROW = INKCLIP_WIDTH / 8 + +export const WRITE_TIME = 2000 // ms diff --git a/src/lib/contexts/device.svelte.ts b/src/lib/contexts/device.svelte.ts index c6ec1be..0241c1c 100644 --- a/src/lib/contexts/device.svelte.ts +++ b/src/lib/contexts/device.svelte.ts @@ -1,8 +1,9 @@ +import { INKCLIP_PID, INKCLIP_VID } from '$lib/constants' import { getContext, setContext } from 'svelte' import { toast } from 'svelte-sonner' function isInkclip(dev: HIDDevice) { - return dev.vendorId == 0xc0de && dev.productId == 0xcafe + return dev.vendorId == INKCLIP_VID && dev.productId == INKCLIP_PID } export interface DeviceContext { diff --git a/src/lib/contexts/rendered.svelte.ts b/src/lib/contexts/rendered.svelte.ts index 2a18110..34fc423 100644 --- a/src/lib/contexts/rendered.svelte.ts +++ b/src/lib/contexts/rendered.svelte.ts @@ -4,6 +4,7 @@ import { withTransform } from '$lib/image/transform' import type { ConversionConfig } from './config.svelte' import { Scaler } from '$lib/image/scaler' import { Quantizer } from '$lib/image/quantizer' +import { INKCLIP_HEIGHT, INKCLIP_WIDTH } from '$lib/constants' export interface RenderedContext { rendered: number[] | null @@ -15,7 +16,7 @@ export function getRenderedContext(): Readonly { return getContext(RenderedContextToken) } -const scaler = new Scaler(200, 200) +const scaler = new Scaler(INKCLIP_WIDTH, INKCLIP_HEIGHT) export function createRenderedContext( imageCtx: Readonly, @@ -33,7 +34,7 @@ export function createRenderedContext( }) ) - const canvas = new OffscreenCanvas(200, 200) + const canvas = new OffscreenCanvas(INKCLIP_WIDTH, INKCLIP_HEIGHT) const canvasCtx = canvas.getContext('2d', { willReadFrequently: true, })! @@ -49,7 +50,7 @@ export function createRenderedContext( withTransform(canvasCtx, config.transform, () => { const bg = config.backgroundColor canvasCtx.fillStyle = `rgb(${bg} ${bg} ${bg})` - canvasCtx.fillRect(0, 0, 200, 200) + canvasCtx.fillRect(0, 0, INKCLIP_WIDTH, INKCLIP_HEIGHT) const { dx, dy, dWidth, dHeight } = scaler.scale(config.scaleMode, bitmap) canvasCtx.drawImage(bitmap, dx, dy, dWidth, dHeight) }) diff --git a/src/lib/image/transform.ts b/src/lib/image/transform.ts index 1988c13..20c25b9 100644 --- a/src/lib/image/transform.ts +++ b/src/lib/image/transform.ts @@ -1,3 +1,5 @@ +import { INKCLIP_HEIGHT, INKCLIP_WIDTH } from '$lib/constants' + export type Rotation = 0 | 90 | 180 | 270 export type Side = 'obverse' | 'reverse' export type Operation = 'cw' | 'ccw' | 'h' | 'v' @@ -52,15 +54,18 @@ function withCtx(ctx: Context2D, pre: () => void, action: () => T): T { } export function withTransform(ctx: Context2D, transform: Transform, action: () => T): T { + const centerX = INKCLIP_WIDTH / 2 + const centerY = INKCLIP_HEIGHT / 2 + return withCtx( ctx, () => { - ctx.translate(100, 100) + ctx.translate(centerX, centerY) if (transform.side === 'reverse') { ctx.scale(-1, 1) } ctx.rotate((transform.rotation / 180) * Math.PI) - ctx.translate(-100, -100) + ctx.translate(-centerX, -centerY) }, action ) diff --git a/src/lib/layouts/connect/ConnectButton.svelte b/src/lib/layouts/connect/ConnectButton.svelte index b17160f..4cad155 100644 --- a/src/lib/layouts/connect/ConnectButton.svelte +++ b/src/lib/layouts/connect/ConnectButton.svelte @@ -1,6 +1,7 @@ -
+
- = {value} + + = (config.bias = value = v)} + aria-labelledby="background-color-input-label" + /> + The color used for transparent pixels.
@@ -22,7 +31,7 @@ bind:value onValueCommit={() => (config.backgroundColor = value)} min={0} - max={255} + max={0xff} step={1} aria-labelledby="background-color-input-label" /> diff --git a/src/lib/layouts/edit/controls/ControlsSection.svelte b/src/lib/layouts/edit/controls/ControlsSection.svelte index 42fb039..bba1110 100644 --- a/src/lib/layouts/edit/controls/ControlsSection.svelte +++ b/src/lib/layouts/edit/controls/ControlsSection.svelte @@ -1,5 +1,4 @@ -
+
- = {value}% + + = (config.bias = (value = v) / 100)} + aria-labelledby="bias-input-label" + />% + Whether the conversion algorithm should bias the entire image towards white (negative) or black (positive). diff --git a/src/lib/layouts/edit/controls/conversion/ContrastSlider.svelte b/src/lib/layouts/edit/controls/conversion/ContrastSlider.svelte index 2b290ec..ca131e5 100644 --- a/src/lib/layouts/edit/controls/conversion/ContrastSlider.svelte +++ b/src/lib/layouts/edit/controls/conversion/ContrastSlider.svelte @@ -2,6 +2,7 @@ import { Label } from '$lib/components/ui/label' import { Slider } from '$lib/components/ui/slider' import MoreInfo from '$lib/components/MoreInfo.svelte' + import ImplicitNumericInput from '$lib/components/ImplicitNumericInput.svelte' import { getConversionConfig } from '$lib/contexts/config.svelte' @@ -10,10 +11,18 @@ let value = $derived(Math.round(config.contrast * 100)) -
+
- = {value}% + + = (config.contrast = (value = v) / 100)} + aria-labelledby="contrast-input-label" + />% + How eager the conversion algorithm should push colors towards the two ends (black and white) of the grayscale. diff --git a/src/lib/layouts/edit/controls/conversion/dither/DitherSwitch.svelte b/src/lib/layouts/edit/controls/conversion/dither/DitherSwitch.svelte index 8fc9ea9..958a925 100644 --- a/src/lib/layouts/edit/controls/conversion/dither/DitherSwitch.svelte +++ b/src/lib/layouts/edit/controls/conversion/dither/DitherSwitch.svelte @@ -11,7 +11,7 @@ let { checked, onchange }: Props = $props() -
+
Dithering uses different dot densities to simulate shades of gray. diff --git a/src/lib/layouts/edit/controls/conversion/dither/DitheringKernelDropdown.svelte b/src/lib/layouts/edit/controls/conversion/dither/DitheringKernelDropdown.svelte index d1ee52b..467cc25 100644 --- a/src/lib/layouts/edit/controls/conversion/dither/DitheringKernelDropdown.svelte +++ b/src/lib/layouts/edit/controls/conversion/dither/DitheringKernelDropdown.svelte @@ -28,7 +28,11 @@ } -