Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.2.1] - 2026-09-08

### Changed

- Jog step is chosen with 0.1 / 1 / 5 / 10 mm buttons (the 3D-printer convention)
instead of a typed number, in the Jog panel and in the registration wizard.
Sighting a pen tip onto a crosshair goes 10 → 1 → 0.1 in turn; a button per
size beats retyping while watching the tip.

## [1.2.0] - 2026-09-08

### Changed
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "penplotter271",
"private": true,
"version": "1.2.0",
"version": "1.2.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
3 changes: 2 additions & 1 deletion src/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { loadCalibration, saveCalibration } from './calibrationStore';
import { loadSession, saveSession, type Session, type PersistedArt } from './sessionStore';
import { flattenSvg, ABORTED } from '../plot/svg';
import { RegistrationWizard } from './RegistrationWizard';
import { StepPicker } from './StepPicker';
import { btn, btnPrimary, field } from './styles';
import { imageToField, traceField, type FieldSource } from '../plot/raster';
import { applyDetail } from '../plot/detail';
Expand Down Expand Up @@ -979,7 +980,7 @@ export function App() {
</Section>

<Section title="Jog">
<NumberField label="Step (mm)" value={jogStep} onChange={setJogStep} />
<StepPicker value={jogStep} onChange={setJogStep} />
<div className="mt-2 grid w-44 grid-cols-3 gap-1 md:w-36">
<span />
<JogBtn label="↑" onPress={() => jogBy(0, -1)} disabled={!connected} />
Expand Down
15 changes: 3 additions & 12 deletions src/ui/RegistrationWizard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useEffect, useMemo, useState } from 'react';
import { fitRegistration } from '../plot/register';
import type { CalibrationPoint, Placement, Point } from '../plot/types';
import { btn, btnPrimary, field } from './styles';
import { btn, btnPrimary } from './styles';
import { StepPicker } from './StepPicker';

/**
* RMS residual above which the fit is flagged. A sighted pen tip repeats to
Expand Down Expand Up @@ -144,17 +145,7 @@ export function RegistrationWizard(p: RegistrationWizardProps) {
{p.penPos ? `${p.penPos.x.toFixed(2)}, ${p.penPos.y.toFixed(2)}` : '—'}
</span>
</span>
<label className="flex items-center gap-1">
Step (mm)
<input
type="number"
className={`${field} w-16`}
value={p.jogStep}
step={0.1}
min={0.01}
onChange={(e) => p.setJogStep(Math.max(0.01, Number(e.target.value) || 0.01))}
/>
</label>
<StepPicker value={p.jogStep} onChange={p.setJogStep} />
</div>
<div className="mt-2 grid w-36 grid-cols-3 gap-1">
<span />
Expand Down
30 changes: 30 additions & 0 deletions src/ui/StepPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** Jog step presets, mm — the 3D-printer convention operators already know. */
export const JOG_STEPS = [0.1, 1, 5, 10] as const;

/**
* Step-size button group. Fixed presets rather than a free number: sighting a
* pen tip onto a crosshair is 10 → 1 → 0.1 mm in turn, and a button per size
* beats retyping a field while watching the tip.
*/
export function StepPicker(props: { value: number; onChange: (mm: number) => void }) {
return (
<div className="flex items-center gap-1 text-xs">
<span className="mr-1 text-slate-600">Step</span>
{JOG_STEPS.map((s) => (
<button
key={s}
className={`rounded border px-2 py-1 ${
props.value === s
? 'border-blue-600 bg-blue-600 text-white'
: 'border-slate-300 bg-white hover:bg-slate-50'
}`}
onClick={() => props.onChange(s)}
aria-pressed={props.value === s}
>
{s}
</button>
))}
<span className="text-slate-500">mm</span>
</div>
);
}