Reset all data stages a wipe behind a typed RESET confirmation and applies it
on the next start, before the database is opened. It deletes the database,
assets, PDF archive, imported fonts and every backup, then the setup wizard
shows again.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The new CI runs cargo fmt --check. QuantityInput also reported its validity only when it changed, so removing a line
whose quantity box held bad text left that box counted as invalid and blocked issuing; it now reports valid when it
unmounts, and no longer writes a ref during render.
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
Version bump, the README line about units, and two new manual checks in the release checklist (export then New invoice
through the real save dialog, and units).
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
Quantities can now be time, count, distance, area, weight or volume, or a custom unit, instead of only
second/minute/hour/session/unit.
- Registry in src/lib/units.ts (mirrored in src-tauri/src/units.rs): grouped built-in units plus a custom unit of 1-12
characters. A rate line's unit is validated when an invoice or a preset is saved; migration M12 drops the old unit
allow-list from item_presets and keeps every row.
- Time is typed as h:mm: "4:30" hours counts as 4.5, "4:20" as 4.333... (kept exact, so rate x quantity rounds once, the
same in TypeScript and Rust). Other units take plain decimals up to 3 places. A bad quantity blocks issuing.
- The PDF quantity column shows the unit ("4.5 hr", "12.75 km"); the rate keeps "per hour" / "/hr". RenderItem carries
priceText, perText and rateShort, so no template parses the rate text with a regex over five hard-coded units.
- Quantity columns are sized from the widest word once a quantity with its unit is wider than 72 pt, so one long
custom unit cannot squeeze the description (the serenity and citrus-split tables overflowed in the new fixture).
Classic's quantity column is now content-sized.
- ERPNext: the default UOM map comes from the registry, with fractional-capable UOMs for time, distance, area, weight
and volume. A custom unit is sent as Nos. A quantity that is not exact to 3 decimals (4:20 hours) is still refused for
push, as before.
- Goldens and template thumbnails regenerated; the template harness has a "units" fixture with the longest texts.
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
Every successful export left the window blank and unresponsive, so the app had to be killed. The "Saved to ..." notice
(and the export-failed notice) held buttons inside a Carbon InlineNotification. Carbon throws "component should have no
interactive child nodes" in an effect, and an uncaught throw makes React unmount the whole tree. Found by driving the
real debug binary through WebKitWebDriver: the page body was empty and the error listener showed that throw.
- The buttons now sit beside the notice, and the failed-export notice uses ActionableNotification's own Retry action.
- ErrorBoundary around the app (reload screen instead of a blank window) and around each export notice.
- A lint test that fails if a Carbon notification wraps an interactive element.
- The real-webview self-test mounts the saved and failed notices and fails on an uncaught React error, and issues and
exports a second invoice after the first.
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
The saved-client box showed "Timber Media Pv" for "Timber Media Pvt Ltd": the form sat on a Carbon Grid nested in a
Column, whose tracks came out much narrower than the page grid, so the combo box was about 230 px wide at the default
window size and the date, GSTIN and PO fields were just as tight. The fields now sit on a plain 12-column CSS grid
(.voiced-fields), the client fields use the full width, place of supply gets its own row, and the line type select is
wide enough for "Rate x quantity".
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5