Enforce proper rate limit backoff across cloud sync stack
Details
The cloud sync was hitting 429 rate limits and not properly backing off,
causing repeated error spam. The C# refresh timer kept calling GetQuota()
every 15 seconds even during rate limits, generating cascading 429 errors.
Rust API client (api_client.rs):
- Added sliding window request counter that enforces 75% of the server's
advertised rate limit (450 req/60s instead of 600). Proactively throttles
before hitting the server limit.
- Changed 429 backoff to use 2x the server's Retry-After header with a
60-second floor. Previously used 1x which allowed immediate re-triggering.
- Counter limits are updated from server config on sync engine startup.
Rust FFI layer:
- Added PrivStackError::RateLimited (= 36) to distinguish rate limits from
generic CloudSyncError. Previously all rate limit errors mapped to
CloudSyncError and C# couldn't tell them apart.
- Rate limit errors now log as WARN instead of ERROR (expected transient).
- CloudSyncStatus now includes is_rate_limited and rate_limit_remaining_secs
fields, populated from the API client's gate state.
C# desktop:
- CloudSyncSettingsViewModel.RefreshStatusAsync() checks is_rate_limited
from status and skips all API calls (quota, devices, tokens) when true.
This eliminates the cascading 429 errors from the refresh timer.
- Catches PrivStackError.RateLimited specifically instead of falling through
to the generic exception handler.
- Status refresh timer slows from 15s to 30s during rate limit pause (still
updates the remaining-seconds display but doesn't hit the API).
- Added IsRateLimited and RateLimitDisplay observable properties for UI.
Rust v1.15.2, Desktop v1.66.3
Add reattach button and padding to detached AI tray window
Details
- Add IsDetached property and RequestReattach command to AiSuggestionTrayViewModel
- Show reattach button (dock icon) in tab strip when tray is detached
- Add content margin (8px sides/bottom) to AiTrayWindow for breathing room
- Wire ReattachRequested event in MainWindow detach/reattach lifecycle
- Version bump 1.66.1 → 1.66.2
Desktop v1.66.1: fix floating Duncan DataContext binding failure
Details
When the AiSuggestionTray was reparented into the floating window,
its XAML binding DataContext="{Binding AiTrayVM}" tried to resolve
against the floating window's DataContext (AiSuggestionTrayViewModel),
which doesn't have an AiTrayVM property. This caused the binding to
fail, leaving DataContext null and all internal panel visibility
bindings broken (both Chat and Intents panels visible simultaneously,
link picker showing).
Fix: explicitly set trayControl.DataContext = vm.AiTrayVM after
removing from the inline grid, bypassing the XAML binding path.
Also close the link picker before reparenting for clean state.
SDK v1.66.1: spell check adorner, autocorrect, and Timer ambiguity fixes
Details
Adds SpellCheckAdorner (wavy underlines for misspelled words in TextBox),
SpellCheckBehavior (attached behavior to enable spell check), autocorrect
service, and spell check rendering utilities. Integrates spell check into
RichTextEditor with debounced recheck and dictionary change handling.
Fixes Timer ambiguity (System.Timers vs System.Threading) in SpellCheckAdorner
and RichTextEditor.SpellCheck by fully qualifying System.Timers.Timer. Fixes
IsAttachedToVisualTree → GetVisualRoot() and GetTemplateChildren →
GetVisualDescendants for Avalonia 11.3.x compatibility.
Get notified about new releases