Toolbar buttons (☰ 🔍 ⚙ ✕ ─ ⤓ +) and the untitled doc-title/search
inputs had no accessible name beyond a title tooltip, which screen
readers don't reliably announce.
The manual rebind flow in Settings already toasts hotkeyFailed when a
combo is taken by another app, but the initial registerHotkey() call
at boot discarded its success/failure result — if the saved hotkey
couldn't be registered (already bound elsewhere, etc.), there was no
error and no indication the shortcut just wasn't active.
Tauri's window-level file-drop hook (needed for dragging a .numi file
in from Explorer) intercepts WebView2's own drag-and-drop session, so
native HTML5 draggable="true" elements never get a valid drop target
inside the webview — the OS always shows "not allowed", confirmed
with an actual mouse-drag simulation before this fix.
Replaced with plain pointer tracking (mousedown/mousemove/mouseup),
the same pattern already used for the results-column divider — full
control over the cursor and the drag-over indicator, no dependency on
the webview's native DnD stack, and it doesn't touch the unrelated
OS file-drop feature at all.
Move-up/move-down felt disconnected from the result — clicking a
button and having two rows silently swap position doesn't read as
motion. Dropped both buttons for native HTML5 drag-and-drop: drag a
sheet, an accent line shows where it'll land, drop to reorder. Still
respects the pinned/unpinned grouping — dragging across that boundary
is a no-op, same as the old up/down buttons.
#market-info had no CSS at all — different font size and color from
#rates-info right next to it, and its own hardcoded English "Market"
text ignored the language setting. Merged its styling into the same
rule as #rates-info and gave it the same .spin pulse while a fetch
is in flight, so a market refresh is as visible as a rates refresh.
Updates tab, below the check-update button. Reads from
tauri.conf.json via getVersion() in a real build (the source of
truth for what's actually installed); falls back to package.json's
version under vite dev, where there's no Tauri runtime to ask.
The flat settings list had grown to 14 fields plus footer buttons —
split it into General / Format / Data / Updates tabs (same pill style
as the pinned-doc sidebar, active tab gets the selection color).
Wired the date-format setting into the engine, and reworked update
checks per an actual bug report: a boot-only check is easy to miss on
a tray app that can run for weeks without a cold restart, and every
failure was swallowed silently with no logging at all. Now: failures
are logged (console.warn) and successful "no update" checks say why
(console.info, current version vs. latest published release) so the
next report has something to go on; checks also rerun every 6 hours;
Settings has both a manual "Check for updates" button (reports back
via toast) and a toggle to turn automatic checking off entirely —
manual checks keep working either way.
Both were self-contained enough to live in their own modules —
src/ui/search.ts (Ctrl+F overlay, text and result-value queries) and
src/ui/export.ts (render the sheet + results to a PNG, clipboard or
file). Trims main.ts by about 150 lines.
Also fixes a real bug while it's in there: the PNG export's clipboard
fallback did btoa(String.fromCharCode(...bytes)), which blows the
call stack on large sheets (spreading a big array as call arguments).
Chunks the conversion in 0x8000-byte pieces instead.
v7/finance/quote returns 401 now — Yahoo stopped issuing anonymous
clients the cookie+crumb pair it requires. v8 chart still answers
without auth, one symbol per request instead of one bulk call.
Also dropped SBER/GAZP from the tracked symbols; Yahoo delisted
Russian tickers in 2022, so those two always missed anyway.
api.frankfurter.app 301-redirects to frankfurter.dev now, and the old
host is flaky over reqwest/rustls-tls — talk to the new one directly
(Rust backend, the vite-dev web fallback, and the CSP allowlist).
Separately, a sheet reopened on boot with "on 2024-01-01" in it never
fetched historical rates at all: the fetch only ran from switchDoc()
and the editor's onChange, and the editor's initial text is seeded
directly, bypassing both.
Ctrl+F opens an overlay that searches every document's lines, not just
the active one, and jumps to the hit on click. Always-on-top is a
plain window pin, off by default, toggleable in settings.
Signed by a new key (public half embedded in tauri.conf.json, private
half + password live in GitHub secrets). CI now signs release artifacts
and publishes latest.json; the app checks on boot and prompts to
install and restart.
- vocab: EN+RU aliases for abs/log/ln/round; RU already had sqrt/cbrt
- engine: "half"/"a half"/"one half"/"половина" → 0.5; "one and a half"/"полтора"/"полторы" → 1.5 as named constants; widen const union in parser/tokenizer/evaluator/registry
- export: ⤓ button in statusbar dropdown — copy sheet, save .sum, export .txt, print/PDF; write_text_file Tauri command; print media CSS hides chrome
Write plain text, get live results per line. English + Russian, units,
live currency rates with crypto, percents, dates and timezones, bitwise
ops, variables and sheet totals. Tauri 2 + TypeScript engine +
CodeMirror 6; daily backups and a deleted-sheets bin.
Inspired by Numi for macOS (numi.app); built from scratch for Windows.