mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-11 02:12:00 +03:00
docs: update README and ARCHITECTURE for v2.0 features
This commit is contained in:
parent
fbeca06f2e
commit
b509a65fc5
4 changed files with 146 additions and 36 deletions
|
|
@ -29,10 +29,12 @@ src/
|
|||
updater.ts checks tauri-plugin-updater, installs + relaunches
|
||||
src-tauri/
|
||||
src/main.rs tray, hide-to-tray, storage commands, rates fetching
|
||||
(open.er-api.com + CoinGecko) with cache, backups,
|
||||
data-folder migration, file drops, plugin registration
|
||||
(autostart, global-shortcut, single-instance, opener,
|
||||
dialog, updater, process)
|
||||
(open.er-api.com + CoinGecko + Yahoo Finance) with
|
||||
cache, historical rates (frankfurter.app, permanent
|
||||
per-date cache), backups, data-folder migration,
|
||||
file drops, plugin registration (autostart,
|
||||
global-shortcut, single-instance, opener, dialog,
|
||||
updater, process)
|
||||
```
|
||||
|
||||
## How a line becomes a result
|
||||
|
|
@ -89,9 +91,25 @@ snapshot price) plus the mirror list in `src-tauri/src/main.rs`.
|
|||
because an "attosecond" symbol would shadow the word `as`.
|
||||
- `and`/`or` are dual-purpose: `and` adds (`5 and 3` = 8), `or` is bitwise.
|
||||
`of/off/on` only act on a percent left-hand side; otherwise they're prose.
|
||||
The same `on` token doubles as the historical-rate date separator (`in EUR on
|
||||
2024-01-01`) — `parseSeq` checks for `pctop(on)` + `datelit` right after a
|
||||
currency conv target and consumes both when present.
|
||||
- Currency rates are "units per USD"; a currency Unit's ratio is computed at
|
||||
tokenization time from the current rate map, so re-setting rates re-prices
|
||||
everything on the next evaluation.
|
||||
evaluation time from the current rate map, so re-setting rates re-prices
|
||||
everything on the next evaluation. Historical rates live in a separate
|
||||
`historicalRates: Map<date, Map<code, number>>` on `SumEngine` and are
|
||||
passed through `EvalCtx` to `evalConv`.
|
||||
- Live market data (stocks, commodities via Yahoo Finance) is merged into the
|
||||
same rate map as exchange rates via `applyAllRates()` in `main.ts` to avoid
|
||||
one `setRates()` call overwriting the other.
|
||||
- Goal seek (`? * 1.2 = 1000`) tokenizes `?` as `{ t: "unknown" }`, parsed
|
||||
into `{ k: "unknown" }` nodes. `parseLine` detects the presence of `unknown`
|
||||
+ `assign` and builds a `{ k: "goalseek", lhs, rhs }` node. The solver
|
||||
tries a linear probe (f(0), f(1) → exact root for affine expressions) and
|
||||
falls back to bisection for nonlinear ones.
|
||||
- `ChartValue` (`kind: "chart"`) is returned by `evalAgg("chart", …)` and
|
||||
bypasses `formatValue` — the results overlay renders an SVG sparkline
|
||||
instead of a text string.
|
||||
- The registry is rebuilt never; extensions mutate it (add phrases/functions)
|
||||
at startup.
|
||||
|
||||
|
|
@ -131,9 +149,24 @@ snapshot price) plus the mirror list in `src-tauri/src/main.rs`.
|
|||
(write-only — there is no durable copy in the repo). Losing that key means
|
||||
already-installed copies can never trust a future signed release again.
|
||||
|
||||
## Storage (cache files)
|
||||
|
||||
All files below live in `%APPDATA%/app.summarum.calc` (or the user-chosen folder):
|
||||
|
||||
| File | Contents | TTL |
|
||||
|------|----------|-----|
|
||||
| `settings.json` | app settings | persisted |
|
||||
| `documents.json` | all sheets | persisted (400 ms debounce) |
|
||||
| `rates.json` | live exchange rates | 1 hour |
|
||||
| `market.json` | stock/commodity prices | 15 minutes |
|
||||
| `rates-YYYY-MM-DD.json` | historical ECB rates for one date | permanent |
|
||||
| `backups/documents-YYYY-MM-DD.json` | daily snapshots | 14 days |
|
||||
| `backups/deleted/*.numi` | soft-deleted sheets | configurable |
|
||||
|
||||
## Tests
|
||||
|
||||
`npm test` runs ~96 vitest cases over the engine (`src/engine/*.test.ts`):
|
||||
`npm test` runs 113 vitest cases over the engine (`src/engine/*.test.ts`):
|
||||
every expression class, both languages, deterministic injected rates,
|
||||
plus a regression suite covering all known-fixed bugs.
|
||||
UI is exercised manually; the engine is where the complexity lives.
|
||||
goal seek, historical rates (injected), plus a regression suite covering
|
||||
all known-fixed bugs. UI is exercised manually; the engine is where the
|
||||
complexity lives.
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ src/
|
|||
updater.ts проверка tauri-plugin-updater, установка + перезапуск
|
||||
src-tauri/
|
||||
src/main.rs трей, скрытие в трей, команды хранилища, загрузка курсов
|
||||
(open.er-api.com + CoinGecko) с кэшем, резервные копии,
|
||||
миграция папки данных, открытие файлов, регистрация
|
||||
плагинов (autostart, global-shortcut, single-instance,
|
||||
opener, dialog, updater, process)
|
||||
(open.er-api.com + CoinGecko + Yahoo Finance) с кэшем,
|
||||
исторические курсы (frankfurter.app, постоянный кэш
|
||||
на каждую дату), резервные копии, миграция папки данных,
|
||||
открытие файлов, регистрация плагинов (autostart,
|
||||
global-shortcut, single-instance, opener, dialog,
|
||||
updater, process)
|
||||
```
|
||||
|
||||
## Как строка превращается в результат
|
||||
|
|
@ -89,9 +91,23 @@ src-tauri/
|
|||
только ms/µs/ns/ps, потому что символ «аттосекунды» затенил бы слово `as`.
|
||||
- `и`/`или` двойного назначения: `и` складывает (`5 и 3` = 8), `или` — побитовое.
|
||||
`от/без/на` работают только с процентом в левой части; иначе это просто текст.
|
||||
Тот же токен `на` используется как разделитель даты для исторических курсов
|
||||
(`в EUR на 2024-01-01`) — `parseSeq` проверяет `pctop(on)` + `datelit`
|
||||
сразу после цели конвертации валюты и поглощает оба при совпадении.
|
||||
- Курсы валют — «единиц за 1 USD»; соотношение единицы валюты вычисляется при
|
||||
токенизации из текущей карты курсов, поэтому обновление курсов пересчитывает
|
||||
всё при следующей оценке.
|
||||
вычислении из текущей карты курсов, поэтому обновление курсов пересчитывает
|
||||
всё при следующей оценке. Исторические курсы живут отдельно в
|
||||
`historicalRates: Map<date, Map<code, number>>` на `SumEngine` и передаются
|
||||
через `EvalCtx` в `evalConv`.
|
||||
- Живые рыночные данные (акции, сырьё через Yahoo Finance) объединяются в ту же
|
||||
карту курсов через `applyAllRates()` в `main.ts`, чтобы один вызов `setRates()`
|
||||
не перезаписывал другой.
|
||||
- Поиск неизвестного (`? * 1.2 = 1000`): `?` токенизируется как `{ t: "unknown" }`,
|
||||
разбирается в узел `{ k: "unknown" }`. `parseLine` обнаруживает `unknown` + `assign`
|
||||
и строит `{ k: "goalseek", lhs, rhs }`. Решатель пробует линейный метод (f(0),
|
||||
f(1) → точный корень для аффинных выражений) и переходит к бисекции для нелинейных.
|
||||
- `ChartValue` (`kind: "chart"`) возвращается из `evalAgg("chart", …)` и
|
||||
минует `formatValue` — оверлей результатов рисует SVG-спарклайн вместо текста.
|
||||
- Реестр не пересобирается никогда; расширения мутируют его (добавляют фразы/функции)
|
||||
при запуске.
|
||||
|
||||
|
|
@ -132,9 +148,24 @@ src-tauri/
|
|||
означает, что уже установленные копии никогда больше не смогут доверять
|
||||
будущему подписанному релизу.
|
||||
|
||||
## Хранилище (кэш-файлы)
|
||||
|
||||
Все файлы ниже живут в `%APPDATA%/app.summarum.calc` (или в папке данных пользователя):
|
||||
|
||||
| Файл | Содержимое | TTL |
|
||||
|------|-----------|-----|
|
||||
| `settings.json` | настройки | постоянно |
|
||||
| `documents.json` | все листы | постоянно (дебаунс 400 мс) |
|
||||
| `rates.json` | живые курсы валют | 1 час |
|
||||
| `market.json` | котировки акций/сырья | 15 минут |
|
||||
| `rates-YYYY-MM-DD.json` | исторические курсы ЕЦБ за одну дату | навсегда |
|
||||
| `backups/documents-YYYY-MM-DD.json` | ежедневные снимки | 14 дней |
|
||||
| `backups/deleted/*.numi` | мягко удалённые листы | настраивается |
|
||||
|
||||
## Тесты
|
||||
|
||||
`npm test` запускает ~96 vitest-кейсов по движку (`src/engine/*.test.ts`):
|
||||
`npm test` запускает 113 vitest-кейсов по движку (`src/engine/*.test.ts`):
|
||||
все классы выражений, оба языка, детерминированные инжектированные курсы,
|
||||
плюс набор регрессий по всем известным исправленным багам.
|
||||
UI проверяется вручную; движок — там где живёт вся сложность.
|
||||
поиск неизвестного, исторические курсы (инжектированные), плюс набор регрессий
|
||||
по всем известным исправленным багам. UI проверяется вручную; движок — там
|
||||
где живёт вся сложность.
|
||||
|
|
|
|||
41
README.md
41
README.md
|
|
@ -17,7 +17,11 @@ total → $1,066.97
|
|||
$2000 in rubles → 144,060 ₽
|
||||
20% off 1500 → 1,200
|
||||
today + 6 weeks → 22 July 2026
|
||||
time in Tokyo → 14:25
|
||||
1000 USD in EUR on 2024-01-01 → € 917.30
|
||||
? * 1.2 = 1000 → 833.33
|
||||
AAPL in USD → $213.55
|
||||
days until 2027-01-01 → 183 day
|
||||
chart → ▂▄▇█▆
|
||||
```
|
||||
|
||||
Works in **English and Russian** at the same time: `5 метров в см`, `20$ в евро`,
|
||||
|
|
@ -35,13 +39,14 @@ Requirements:
|
|||
- **[WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/)** —
|
||||
already part of Windows 11 and most updated Windows 10 systems; if it's
|
||||
missing, the installer downloads it automatically.
|
||||
- Internet connection is only needed for live currency rates — everything
|
||||
- Internet connection is only needed for live rates and market data — everything
|
||||
else works offline (rates fall back to a cached/bundled snapshot).
|
||||
|
||||
## What it understands
|
||||
|
||||
**Plain math** — `2 + 2`, `8 / (45 - 20%)`, parentheses, `^`, `sqrt`, `sin`,
|
||||
factorials. Decimal-exact: `0.1 + 0.2` is `0.3`, not `0.30000000000000004`.
|
||||
factorials, `random()`, `random(1; 6)`. Decimal-exact: `0.1 + 0.2` is `0.3`,
|
||||
not `0.30000000000000004`.
|
||||
|
||||
**Words as operators** — `12 plus 4`, `20 divided by 4`, `5 и 3`. Text around
|
||||
the math is ignored, so `spent 20 on pizza and 5 on coffee` just gives 25.
|
||||
|
|
@ -58,30 +63,48 @@ the math is ignored, so `spent 20 on pizza and 5 on coffee` just gives 25.
|
|||
(refreshed hourly, cached for offline). `$100 + 20%`, `5 eth in USD`,
|
||||
`9800 рублей в долларах`.
|
||||
|
||||
**Live market data** — stock and commodity prices refreshed every 15 minutes:
|
||||
`100 AAPL in USD`, `2 GC=F in USD` (gold futures), `MSFT + GOOGL`.
|
||||
|
||||
**Historical exchange rates** — `1000 USD in EUR on 2024-01-01` looks up the
|
||||
ECB rate for that exact date (via frankfurter.app) and caches it permanently.
|
||||
|
||||
**Percents the human way** — `20% of 80`, `20% off 1500`, `5 as a % of 25`,
|
||||
`20% of what is 30`.
|
||||
|
||||
**Dates and time** — `today + 2 weeks`, `tomorrow - today`, `time in New York`,
|
||||
`now in unix`, `1750000000 as date`.
|
||||
`now in unix`, `1750000000 as date`. ISO date literals: `2024-01-01 + 30 days`,
|
||||
`days until 2027-01-01`, `days since 2020-03-15`.
|
||||
|
||||
**Goal seek** — put `?` where the unknown is and an `=` with the target:
|
||||
`? * 1.2 = 1000` → `833.33`, `100 - ? = 30` → `70`. Works for any expression
|
||||
solvable by linear probe or bisection.
|
||||
|
||||
**Programmer stuff** — `0xFF & 0x0F`, `1 << 4`, `255 in hex`, `in binary`,
|
||||
plus `in fraction` (0.75 → 3/4) and `in roman` (2026 → MMXXVI).
|
||||
|
||||
**Sheets that calculate** — variables (`rent = $500`), running totals
|
||||
(`sum`, `avg`, `prev`), `#` headers, `//` comments. The status bar always shows
|
||||
the sheet total; select a few lines to see just their sum.
|
||||
(`sum`, `avg`, `prev`, `count`, `min`, `max`, `product`), `#` headers,
|
||||
`//` comments. The status bar always shows the sheet total; select a few lines
|
||||
to see just their sum. Add `chart` at the end of a block to draw a sparkline.
|
||||
|
||||
## Handy things
|
||||
|
||||
- **Ctrl+Alt+N** (configurable — the settings field records keys you press)
|
||||
shows/hides the window from anywhere. Closing hides to tray.
|
||||
- **Ctrl+F** (or the 🔍 button) searches every sheet at once, not just the
|
||||
one you're in — click a hit to jump straight to that line.
|
||||
- **Ctrl+W** closes the current sheet (asks first if it has content).
|
||||
- **Ctrl+F** (or the 🔍 button) searches every sheet at once — by text or by
|
||||
result value: `>1000`, `<50`, `=100`, `~100` (±1%). Click a hit to jump there.
|
||||
- **Click a result** to copy it. Copying a line copies it *with* the answer
|
||||
(`rent * 3 = $1,500`). **Ctrl+Shift+C** copies the whole sheet with answers.
|
||||
- **Export as image** — the ⤓ menu includes "Copy as image": renders the sheet
|
||||
to a PNG and puts it on the clipboard (falls back to file save if the browser
|
||||
blocks clipboard write).
|
||||
- **Drag the divider** to resize the results column.
|
||||
- **Drop a `.numi`, `.sum`, `.txt` or `.md` file** into the window to open it
|
||||
as a sheet.
|
||||
- **Pin and reorder sheets** — click the pin icon on any sheet in the sidebar
|
||||
to keep it at the top; use ↑↓ to rearrange.
|
||||
- **Backups are automatic**: a daily snapshot of all sheets (last 14 kept), and
|
||||
deleted sheets sit in a bin for 14 days (configurable) — restore one by
|
||||
dragging the file back in. Settings → Backups opens the folder.
|
||||
|
|
@ -120,7 +143,7 @@ see [ARCHITECTURE.md](ARCHITECTURE.md).
|
|||
|
||||
```bash
|
||||
npm install
|
||||
npm test # engine tests
|
||||
npm test # engine tests (113 cases)
|
||||
npm run tauri dev # run the app
|
||||
npm run tauri build # build the installer
|
||||
```
|
||||
|
|
|
|||
41
README.ru.md
41
README.ru.md
|
|
@ -16,7 +16,11 @@
|
|||
$2000 в рублях → 144,060 ₽
|
||||
скидка 20% от 1500 → 1,200
|
||||
сегодня + 6 недель → 22 Июля 2026
|
||||
время в Токио → 14:25
|
||||
1000 USD в EUR на 2024-01-01 → € 917.30
|
||||
? * 1.2 = 1000 → 833.33
|
||||
AAPL в USD → $213.55
|
||||
дней до 2027-01-01 → 183 day
|
||||
график → ▂▄▇█▆
|
||||
```
|
||||
|
||||
Работает на **английском и русском одновременно**: `5 метров в см`, `20$ в евро`, `сумма` — переключать язык не нужно.
|
||||
|
|
@ -31,13 +35,14 @@ $2000 в рублях → 144,060 ₽
|
|||
- **[WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/)** —
|
||||
уже встроен в Windows 11 и большинство обновлённых Windows 10; если отсутствует,
|
||||
установщик скачает его автоматически.
|
||||
- Интернет нужен только для живых курсов валют — всё остальное работает офлайн
|
||||
- Интернет нужен только для живых курсов и рыночных данных — всё остальное работает офлайн
|
||||
(курсы берутся из кэша или встроенного снимка).
|
||||
|
||||
## Что понимает
|
||||
|
||||
**Обычная математика** — `2 + 2`, `8 / (45 - 20%)`, скобки, `^`, `sqrt`, `sin`,
|
||||
факториалы. Точная десятичная арифметика: `0.1 + 0.2` это `0.3`, а не `0.30000000000000004`.
|
||||
факториалы, `случайное()`, `случайное(1; 6)`. Точная десятичная арифметика:
|
||||
`0.1 + 0.2` это `0.3`, а не `0.30000000000000004`.
|
||||
|
||||
**Слова как операторы** — `12 плюс 4`, `20 делить на 4`, `5 и 3`. Текст вокруг
|
||||
математики игнорируется: `потратил 20 на пиццу и 5 на кофе` даст 25.
|
||||
|
|
@ -54,29 +59,47 @@ $2000 в рублях → 144,060 ₽
|
|||
(обновляются раз в час, кэшируются для офлайна). `$100 + 20%`, `5 eth в USD`,
|
||||
`9800 рублей в долларах`.
|
||||
|
||||
**Живые рыночные данные** — котировки акций и сырья обновляются каждые 15 минут:
|
||||
`100 AAPL в USD`, `2 GC=F в USD` (фьючерс на золото), `MSFT + GOOGL`.
|
||||
|
||||
**Исторические курсы** — `1000 USD в EUR на 2024-01-01` запрашивает курс ЕЦБ
|
||||
за этот день (через frankfurter.app) и кэширует его навсегда.
|
||||
|
||||
**Проценты по-человечески** — `20% от 80`, `скидка 20% от 1500`, `5 это сколько % от 25`,
|
||||
`20% от какого числа равно 30`.
|
||||
|
||||
**Даты и время** — `сегодня + 2 недели`, `завтра - сегодня`, `время в Нью-Йорке`,
|
||||
`сейчас в unix`, `1750000000 как дата`.
|
||||
`сейчас в unix`, `1750000000 как дата`. ISO-даты: `2024-01-01 + 30 дней`,
|
||||
`дней до 2027-01-01`, `дней с 2020-03-15`.
|
||||
|
||||
**Поиск неизвестного** — поставь `?` на место неизвестного и `=` с нужным результатом:
|
||||
`? * 1.2 = 1000` → `833.33`, `100 - ? = 30` → `70`. Работает для любых выражений,
|
||||
решаемых линейным методом или бисекцией.
|
||||
|
||||
**Для программистов** — `0xFF & 0x0F`, `1 << 4`, `255 в hex`, `в binary`,
|
||||
плюс `в дроби` (0.75 → 3/4) и `в roman` (2026 → MMXXVI).
|
||||
|
||||
**Листы с переменными** — переменные (`аренда = $500`), нарастающие итоги
|
||||
(`сумма`, `avg`, `prev`), заголовки `#`, комментарии `//`. Строка статуса всегда
|
||||
показывает итог листа; выдели несколько строк — увидишь их сумму.
|
||||
(`сумма`, `avg`, `prev`, `количество`, `минимум`, `максимум`, `произведение`),
|
||||
заголовки `#`, комментарии `//`. Строка статуса всегда показывает итог листа;
|
||||
выдели несколько строк — увидишь их сумму. Добавь `график` в конце блока чтобы
|
||||
нарисовать спарклайн.
|
||||
|
||||
## Удобные мелочи
|
||||
|
||||
- **Ctrl+Alt+N** (настраивается — поле в настройках записывает нажатые клавиши)
|
||||
показывает/скрывает окно из любого места. Закрытие прячет в трей.
|
||||
- **Ctrl+F** (или кнопка 🔍) ищет сразу по всем листам, а не только по текущему —
|
||||
клик по результату переносит прямо на нужную строку.
|
||||
- **Ctrl+W** закрывает текущий лист (спрашивает подтверждение если есть содержимое).
|
||||
- **Ctrl+F** (или кнопка 🔍) ищет сразу по всем листам — по тексту или по значению
|
||||
результата: `>1000`, `<50`, `=100`, `~100` (±1%). Клик по результату — переходишь туда.
|
||||
- **Клик по результату** копирует его. Копирование строки копирует её *вместе с ответом*
|
||||
(`аренда * 3 = $1,500`). **Ctrl+Shift+C** копирует весь лист с ответами.
|
||||
- **Экспорт картинкой** — меню ⤓ содержит «Скопировать картинкой»: рендерит лист
|
||||
в PNG и кладёт в буфер обмена (при ошибке предлагает сохранить файл).
|
||||
- **Потяни разделитель** чтобы изменить ширину колонки результатов.
|
||||
- **Брось файл `.numi`, `.sum`, `.txt` или `.md`** в окно — откроется как лист.
|
||||
- **Закрепление и порядок листов** — нажми значок булавки на любом листе в сайдбаре
|
||||
чтобы закрепить его вверху; кнопки ↑↓ меняют порядок.
|
||||
- **Резервные копии автоматические**: ежедневный снимок всех листов (хранятся 14 дней),
|
||||
удалённые листы лежат в корзине 14 дней (настраивается) — верни перетащив обратно.
|
||||
Настройки → Backups откроет папку.
|
||||
|
|
@ -115,7 +138,7 @@ numi.addFunction({ id: "гипот", phrases: "гипот" },
|
|||
|
||||
```bash
|
||||
npm install
|
||||
npm test # тесты движка
|
||||
npm test # тесты движка (113 кейсов)
|
||||
npm run tauri dev # запустить приложение
|
||||
npm run tauri build # собрать установщик
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue