Repository navigation
Releases
Subly v1.0.8
Fixed
- Android: app stuck on the splash screen. The keyring store panicked because the app Context was never registered with
ndk-context. The panic poisoned the state lock, so every command failed. - Android: crash on widget refresh.
WidgetSnapshotNotifierwas looked up from a Rust thread with the system class loader, which threwClassNotFoundException. - Android: header under the status bar. Content now stays inside the system bars and above the keyboard on WebViews that report zero safe-area insets.
- Dashboard spending trend no longer adds current subscriptions to months before they started or after they were cancelled.
- Tooltips no longer stay on screen after a tap on touch devices.
Full changelog: v1.0.7...v1.0.8
Subly v1.0.7
Added
- Redesigned UI — one control system (36px buttons/fields, pressed state, labelled primary header action), expressive dashboard KPIs, tinted cards, gradient letter avatars, settings split into six tabs.
- Subscriptions list — prices in the main currency from the backend (
priceMain,monthlyPriceMain), colored due hints, per-group monthly totals, mobile-friendly rows; cycle progress bars kept in all three view modes. - Expenses — per-day totals in day headers; calendar month stats strip.
- Rate history is now recorded on every rates update (one point per day, pruned to the configured window).
- Proper day pluralization (en/ru/de/fr/es/pt).
Changed
- Dashboard "amount due" counts every projected charge left this month (matches the Calendar).
- Changing the main currency rebases all rates so the new main is 1.
- Deleting a currency converts amounts into the main currency instead of relabelling them.
- Renaming a tag updates it in all subscriptions and expenses.
- Identical rates from the provider are a successful no-op (no error, no 30-minute re-fetch loop).
Fixed
- Closing the window no longer destroys it (the JS close hook let
@tauri-apps/apidestroy the window): the Dock icon (macOS) and tray bring it back; the splash screen now really closes. - CI: Android release workflow no longer fails in "Setup Android SDK" (legacy
toolspackage removed). - Desktop notifications: no burst after renewal, "N days before" reminders delivered; mobile scheduling fallback.
- Sync connectors: no auto-push over an empty remote, HTTP timeouts, WebDAV MOVE fallback/redirects, 401 mapping, OneDrive direct PUT, Google Drive
trashed=false. - Dashboard budget ignored the Budget setting; over-budget state never showed.
- Subscriptions page filter leaked into the Calendar; price sort ignored currency; stale selections in batch actions.
- Expenses: empty trailing page after delete, duplicates on the wrong day, stale catalogs, duplicated default names.
- Subscription form accepted frequency 0/blank and blank price; manual payment records were not validated.
- Converter parsed "1,5" as 15; "Update now" did not refresh the page; settings sections reverted the main currency.
- Calendar week view jumped to the wrong week across months; settings deep links missed usage counts; unsaved AI provider leaked to other pages.
Downloads
- macOS (Apple Silicon):
Subly_1.0.7_aarch64.dmg— signed with Developer ID and notarized. - Other platforms are attached by CI when their builds finish.
Full changelog: v1.0.6...v1.0.7
Subly v1.0.6
Subly v1.0.6
Subscription & expense tracker desktop application.
Downloads
See the assets below to download for your platform.
Note: macOS builds are unsigned. You may need to right-click → Open on first launch.
v1.0.5
v1.0.4
Full Changelog: v1.0.3...v1.0.4
Added
- Shared
dashboardIpcTypeScript types for dashboard command payloads socommandClientstays free of circular imports withdashboardClient. readClipboardText()inuseClipboard, backed by@tauri-apps/plugin-clipboard-managerwith anavigator.clipboard.readText()fallback for paste flows (including the AI assist dialog).MoneyString(src/lib/money.ts) anddecimal.json the frontend; Rust usesrust_decimalwith amounts serialized as decimal strings over IPC.- One-time money migration (
migration:money_decimal_v1) converts legacyf64postcard snapshots to precise decimals on startup.
Changed
- Dashboard client refactored to use the shared IPC types and leaner invoke helpers.
commandClient/invokeCommandextended for dashboard payloads andapp_readylifecycle wiring inApp.vue.- All money-bearing IPC (subscriptions, expenses, dashboard, rates, notifications, AI DTOs, iOS widget snapshot) now uses decimal strings end-to-end; Zod schemas,
useCurrencyFormat, and dashboard / form components updated accordingly. - Frontend secrets now use
tauri-plugin-keyring-store-apionly; legacysecure_storage_*Tauri commands were removed from Rust while keeping the same logical key prefix, so existing keychain entries remain valid. tauri-plugin-keyring-store0.2 —keyring_storemapsKeychainLockedtokeyring_locked; iOS background sync credential reads use the plugin’s background-safe API when the keychain is unavailable.- Cloud sync — global
tokio::sync::MutexserializesCheckRemote,PullRemote,PushLocal,SyncNow, andFlushBeforeExit; the background scheduler usestry_lockand skips a tick while sync is already running (replacesredb_write_flagpolling). - AI extraction — balanced-brace JSON parser prefers the last valid object/array; OpenAI-compatible providers request
response_format: json_objectwith automatic fallback when unsupported; vision shares the same chat-completions path. - Catalog delete — removing a category, currency, payment method, household member, or tag reassigns or blocks defaults safely and emits
app:data-changedso open screens refresh. - AI Smart dialog, subscriptions / expenses pages,
aiClient,aiConfigStore, andSubscriptionFormupdated for the new IPC and clipboard behavior. - Tauri capabilities updated where clipboard read is required; iOS and widget
Info.plistmetadata aligned with the current app build. - Locale files refreshed for new or adjusted copy across all supported languages.
pnpm-lock.yamlandCargo.lockupdated with dependency refreshes tied to the release.
Fixed
- iOS widget —
upcoming.jsonis written atomically (.tmp+rename) so the extension never reads a truncated file. - Calendar — when the calendar day rolls over while the app stays open, stores refetch so
daysLeft, overdue flags, and payment dates stay correct without leaving the page.
Removed
src-tauri/src/commands/secure_storage.rs— redundant now that the UI talks to the keyring plugin directly.src-tauri/src/commands/sync/redb_write_flag.rs— superseded by the global sync mutex.
v1.0.3
Added
- AI-powered assistance (opt-in, off by default), exposed through a single
ai_smart_inputbackend command and unifiedAiSmartDialogon the frontend.
One entry point handles all four capabilities — the model decides whether
the input is one item or a list based on the picture / file / text:- Quick-add subscription — natural-language input ("Telegram premium 300₽ monthly card") that extracts price, currency, billing cycle, category, payment method and tags using the existing catalog as context.
- Quick-add expense — same flow for one-off transactions with description, amount, date and tag parsing.
- Receipt OCR — point a vision-capable model at a photo / scan of a receipt; line items, totals, currency, merchant and date come back as a draft expense.
- Statement import — bulk import of bank/card statements (CSV, XLSX, PDF, JSON) with heuristic pre-parsing and per-row preview before saving.
Backend uses theaisdkcrate for text generation, directreqwestfor vision endpoints, prompted-JSON output (instead ofaisdkschema validation) and a robust JSON parser that tolerates LLM markdown fences.
- Seven AI providers out of the box: OpenRouter, OpenAI, OpenAI-compatible (Ollama / custom), Groq, DeepSeek, Google Gemini (OpenAI-compatible endpoint), Mistral — each with curated recommended-model presets and vision-capability flags.
- New AI Settings panel with master toggle, per-feature switches, provider/model picker, custom model input, test-connection button, and an auto-enable shortcut on first successful save.
- Single AI header button on subscriptions and expenses pages: opens the AI assist dialog when AI is ready, or shortcuts to settings when no provider/key is configured.
- Drag-and-drop, paste-from-clipboard and image preview in the AI assist dialog. Drops are wired through Tauri's
onDragDropEvent(the WebView's HTML5dropevent is suppressed in Tauri 2); files are read via@tauri-apps/plugin-fsand capped at 64 MiB. Images get a checkerboard-backed preview with a one-tap clear button; non-image files render as a rich row with icon + size + filename. - Responsive AI dialog layout: larger touch targets, full-width dropzone on mobile, wrap-friendly result header with thumbnail, and a 44×44 cancel target during parsing.
SecretInputcomponent — reusable password-style field that displays a saved-value mask, clears on focus, and disables Save until the user actually edits.- Backend signal for saved secrets:
ProviderField.has_saved_valueis now returned by CloudSync provider descriptors so the UI can show a masked field with "API key saved" instead of an empty input. - GitHub Actions workflow + setup doc for publishing Android AAB to Google Play (signed release, internal/alpha/beta/production tracks, release notes auto-extracted from this changelog, native debug symbols, mapping upload).
- Splash screens on every platform: dedicated splash window on desktop with theme-aware background, native iOS
LaunchScreen.storyboardwith brand logo, and Androidandroidx.core.splashscreenflow with light/dark color variants. - Skeleton loaders for the subscriptions list while data is loading.
LazyVChartwrapper forvue-echartsthat delays mount until the container has a real size, eliminating ECharts "Can't get DOM width or height" race.- Animated mobile tab bar: sliding pill indicator for the active item, scale-on-tap feedback, haptic vibration on navigation, translucent blurred background, and stronger shadow.
- Backdrop-blur overlay for the mobile sidebar drawer and safe-area-aware insets for header / drawer / main content (
--sat,--sabCSS variables). - iOS WKWebView full-screen fix (
ios_webview_fix.rs) so the web view extends acrosssafeAreaLayoutGuideand the bottom navigation actually sits on the bottom of the screen. - Improved empty state on Calendar page with illustration and new i18n strings.
- New shared
brand-logo.svgand regenerated desktop icons (macOS.icns, Windows.ico, Linux PNGs) with transparent rounded corners; iOS app icons kept square per Apple guidelines.
Changed
- Keyring migration: replaced the legacy v3
keyringcrate (which silently fell back to an in-memory mock without explicit platform features — causing API keys and OAuth tokens to "disappear" after each restart) withkeyring-core1.x and platform-specific backends —apple-native-keyring-store,windows-native-keyring-store,dbus-secret-service-keyring-store,android-native-keyring-store. AI keys, sync credentials and Telegram tokens now reliably persist on every platform without the previous redb fallback layer. - Telegram bot token and exchange-rate provider API keys migrated to the new
SecretInputflow — fields no longer pre-fill the actual secret into the input, just show a masked placeholder. - CloudSync secret fields (Dropbox app secret, WebDAV password, ...) use
SecretInputwithhas_saved_valuefrom the backend, so the form correctly reflects "credentials already stored" without leaking the values. - Android release builds are now signed via
keystore.properties(local dev) or env vars (CI). Unsigned release stays as fallback for dev machines without a keystore. - Android release bundle ships with native debug symbols (
ndk.debugSymbolLevel = "SYMBOL_TABLE") so Play Console can deobfuscate Rust crash traces. - Major Rust error-handling refactor: all
commands/*migrated to a unifiedAppErrorenum (newsrc/errors.rs) withFromimpls forredb/ IO / serde / keyring errors, enabling clean?propagation across the backend. - AI prompts now ship the user's real taxonomy — categories, enabled payment methods and a tag pool (favourites first, then by
sortOrder, capped at 30) are rendered directly into everyai_smart_inputsystem prompt. The model is told to reuse existing names instead of inventing new ones, which cuts duplicate categories/tags on save and improves payment-method matching. commands/ai/extract/smart.rssplit into asmart/module (mod.rsdispatch,expense.rs,subscription.rs) so each surface owns its parsing/mapping/heuristics in isolation. The prior single-file extractor is gone along with the obsolete per-feature commands (ai_quick_add_*,ai_extract_receipt,ai_import_statement_file,ai_import_subscriptions_file).- Prompt fragments gained reusable
payment_method_rulesandtag_ruleshelpers incommands/ai/prompts/fragments.rs; thecategory_ruleshelper now gracefully degrades to "omit the field" when the catalog is empty. - iOS widget snapshot export moved to a background thread with a shared
ureqagent and deduplication flags. The HTTP fetch for subscription logos no longer blocks app startup, sync-pull, or subscription save. - Settings → Appearance: color-theme swatches now render as perfect circles on all platforms (iOS Safari previously squashed them into ovals) and the row is centered so the trailing gap on the right no longer looks crooked.
- Dashboard Stats cards redesigned into a clean tile layout.
- Subscriptions list: compact view collapses metadata progressively on small screens to keep everything on a single line; default view leaves more room for long names on mobile.
- ECharts is now tree-shaken with
LegacyGridContainLabel(silences the ECharts 6+ deprecation warning) and lazy-loaded from the dashboard page instead of the global bundle, so the splash screen disappears noticeably faster on cold start. - Chart theme is transparent by default, so widgets blend with both light and dark surfaces.
- Tauri / npm / cargo dependencies refreshed to current versions (Tauri 2.11.1, plugins, Vite 8, Vue 3.5, Tailwind 4, etc.).
Fixed
- API keys / OAuth tokens no longer "disappear" after app restart — see Keyring migration above. Affects AI provider keys, sync credentials and Telegram bot tokens on all platforms.
- AI feature visibility race on subscriptions/expenses pages: the Pinia
featuresslot was areactive()object instead of aref(), sostoreToRefs()couldn't track it and the header button stayed grey-disabled until a full reload. Now correct everywhere. - AI prompts speak the user's language: prompts are kept English (more reliable LLM behaviour) but explicitly instruct the model to write free-form fields (descriptions, tag names, notes) in the user's UI locale.
- Android crash on launch ("Unable to resolve local data directory"). On Android the
dirscrate has no$HOME/ XDG paths and always returnedNone, panicking the setup hook. The DB path is now resolved via Tauri's path API on Android (Context.getFilesDir()→/data/user/0/<package>/files/); desktop + iOS keep the existing~/Library/Application Support/Subly/location so no data migration is required. - Android Gradle build "Cannot find module '...src-tauri/tauri'" fixed by resolving the Tauri CLI through the project's package manager (
TAURI_CLI_PACKAGE_MANAGER→ pnpm/npm/yarn/node) instead of invokingnode tauridirectly. - iOS bottom tab bar no longer floats with a gap below it — WKWebView is now stretched to
view.boundsandcontentInsetAdjustmentBehavior = .never, soposition: fixed; bottom: 0reaches the actual bottom of the screen. - Mobile sidebar no longer overlaps the status bar / Dynamic Island; header and main content respect
safe-area-inset-*. - ECharts "Expenses by Tags" widget rendered empty:
itemStyle.borderColorreferenced a CSS variable that ECharts cannot parse — the value is now resolved withgetComputedStyleand re-resolved on theme change. - ECharts "Expenses by Day of Week" widget no longer overflows below its card on mobile (conflicting
min-h-*constraints removed). - Charts no longer paint a solid black background in dark mode.
- Content S...
v1.0.2
v1.0.1
v1.0.0
Subly 1.0.0 – Release Notes
⚠️ Before you update
Make a backup first:
Go to Settings → Data Management, export a .subly file, install the new version, and if anything is broken, restore from that file.
This update completely replaces the old sync/storage stack; the backup is your safety net.
If you skipped backup/restore:
You can temporarily return to the previous app version, export your .subly backup there, then update to 1.0.0 again and restore from that backup.
What actually changed (short)
-
Cloud sync is new
- Sync file format and merge logic are fully rewritten.
- Old
subly-sync-v1.jsonis no longer used; the first device must use Upload to Cloud to create a new sync file.
-
iCloud behaves the same on macOS and iOS
- Both platforms use the same app container
iCloud.com.s00d.subly. - No more “file is in iCloud Drive but the app can’t see it”.
- Both platforms use the same app container
-
Secrets moved out of the DB
- Cloud tokens, API keys, subscription credentials, Telegram token, etc. are now stored in the OS keychain, not in the local database.
-
macOS build is production-grade
- Signed with Developer ID Application: Pavel Kuzmin (R6VC7JTLE8) and notarized.
- iCloud entitlements and the provisioning profile are embedded in the
.app, so iCloud works on end-user machines without extra manual steps.
If anything goes wrong after updating, restore from your .subly backup and open an issue with the sync provider (iCloud/Drive/…) and platform (iOS/macOS/Windows/Linux).