Skip to content

Releases: gavindi/lookout

Lookout 0.9.241

Choose a tag to compare

@github-actions github-actions released this 17 Oct 01:10

[v0.9.241] - 2026-10-17

Reference: Calendar — event status and the Outlook event-chip look (Track U, item U.7 / UX-57)
Summary: Touches crates/core/src/{calendar,lib}.rs, crates/dav/src/{ical,recurrence,birthdays,cache}.rs, crates/graph/src/calendar/{mod,client,session}.rs, crates/app/src/{calendar_view,calendar_colors,calendar_invites,event_editor,chat_links,invite_conflicts,lookout_view,profile_card,reminders,assistant_tools}.rs, crates/app/src/window/{calendar_glue,tests}.rs and OUTLOOK_PARITY.md; adds a serde-defaulted status field to CalendarEvent/EventOccurrence (an older cached month still deserializes) and no other data-format change. Calendar entries adopt the look and feel of the mailcraft reference: a soft calendar-colour tint over the pane, a colour stripe down the left edge whose style carries the "Show as" state, a title with a dim time, and the event's status marks - replacing the old flat, saturated block. Event status: RFC 5545 §3.8.1.11 STATUS becomes first-class - a new lookout_core::EventStatus (Confirmed the default, Tentative, Cancelled) rides on both CalendarEvent and EventOccurrence; lookout-dav parses STATUS and writes it only when non-default (a confirmed event grows no noise, and an iMIP CANCEL still always says CANCELLED), the recurrence expansion and the birthdays calendar carry it into occurrences, and Microsoft Graph's isCancelled reads into Cancelled (added to the calendar event $select). Every event literal (mostly tests) gained the field, and the editor carries it through a save so an edit can't silently revive a cancelled event. The look: one pure entry_look derives each entry's colours and style from its calendar colour, "Show as" and status - a soft alpha fill (0.30, brightened on hover), a 5px left stripe whose style encodes the state (solid for busy/away, dotted for working elsewhere, hatched for tentative, a hollow outline for free, and Outlook's OOF purple for away), and theme-foreground text. The cairo views - the Day/Week/Work week chips, the all-day band and the Schedule lanes - draw through a shared paint_entry: 3px rounded corners, the stripe, a bold title with a dim time (stacked onto its own line on a tall block) and the status marks (recurring, private, meeting, online meeting, reminder) drawn as small vectors at the right; a cancelled event fades with its title struck through, and a multi-day bar rounds only the ends the event actually begins and finishes on (new starts/ends on the internal chip, so the middle joins read as one continuous bar). The month/agenda chips: the widget paths are rebuilt the same way - event_label now builds [dim time][title][marks] with themed symbolic icons, and the generated .calendar-event-<hex> rules carry the tint (alpha(color, 0.30)), the 5px left stripe and a 3px radius, with per-"Show as" variant classes (dashed tentative, dotted working-elsewhere, hollow free, purple away) and a cancelled fade; the now-unused luminance-contrast helpers in calendar_colors are removed, since text follows the theme foreground again. Testing: new lookout-app tests pin entry_look's per-state fill/stripe/outline and cancelled fade, entry_corners' bar-end rounding and entry_marks; the calendar-colour CSS-parse test now asserts the show-as variants; lookout-dav covers STATUS parse/write round-trips (Confirmed writing nothing) and lookout-graph the isCancelled mapping - the whole workspace suite passes with cargo fmt --check and cargo clippy --workspace --all-targets -- -D warnings clean. Not yet done (noted in UX-57): the chip's location line and category-colour dots, and per-event importance, which the model doesn't carry.

Lookout 0.9.231

Choose a tag to compare

@github-actions github-actions released this 16 Oct 03:03

[v0.9.231] - 2026-10-16

Reference: Tasks — fix startup RefCell panic in the My Day reload (4.9 / T-02)
Summary: Touches crates/app/src/window.rs and the packaging metadata (source/snapcraft.yaml, source/data/io.github.gavindi.Lookout.metainfo.xml, .github/workflows/release.yml, all bumped to 0.9.231); no data-format change. build_window's My Day pin reload took a RefCell borrow on calendar_state inside an if let scrutinee, which under Rust 2021's temporary-lifetime rules stays alive for the whole body - the borrow_mut below it therefore panicked (already borrowed) on every launch, and because the block runs inside a GLib signal handler (where unwinding is impossible) the panic aborted the process. The database handle is now copied out into a local before the if let, the same idiom calendar_view's gesture state already documents. Verified by cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, the workspace suite (1,410 passing) and a launch of the release binary; the build_window path has no unit coverage (it needs a GTK display), which is how the borrowed-scrutinee regression slipped past 1,389 green tests.

Lookout 0.9.190

Choose a tag to compare

@github-actions github-actions released this 14 Oct 06:27

[v0.9.190] - 2026-10-14

Reference: Mail — instant folder unread counts on move
Summary: Touches crates/mail/src/session.rs; no data-format change. Deleting, archiving, moving or snoozing a message updated the folder pane's unread badge only once the move path's STATUS round trips had returned, so the count visibly lagged the message list (which already updates from the cache instantly). Every move path now applies a best-effort local patch the moment the server-side move succeeds, the same contract as the existing mark-read path: the moved messages' cached summaries are read before the cache drop (the new cached_move_info, which also supplies the bare Message-IDs the move ledger needs), and the new apply_move_unread_delta saturating-subtracts the unread ones from the source folder and adds them to the destination, so Inbox's badge drops and Trash's rises with the row. Undo, unsnooze and both wake paths reverse the same way, with a wake counting every message as arriving unread (it strips \Seen). The STATUS refresh that follows still overwrites the count with the server's number, so any miscount is corrected a round trip later. Covered by new apply_move_unread_delta (both ends, read-only moves, saturating, wake semantics) and cached_move_info (unread tally from cached summaries) tests.

Lookout 0.9.172

Choose a tag to compare

@github-actions github-actions released this 13 Oct 01:06

[v0.9.172] - 2026-10-13

Reference: Mail — conversation options (Phase 1.7 / M-04, UX-35, UX-22)
Summary: Touches crates/mail/src/cache.rs, crates/app/src/{window,message_list,message_header,settings,config_view,main}.rs and the gschema, and adds crates/app/src/conversation_view.{rs,js}. Adds the mail-conversation-order and mail-conversation-include-sent GSettings keys. No data-format change. Stacked conversations: selecting a conversation header, or a message inside a conversation, now shows the whole conversation in the reading pane, Outlook-style. The newest message, every unread message and the selected one start open; the rest collapse to sender and a one-line preview, and a click opens them. Each card has Reply, Reply all, Forward and ⋯ (the message context menu for that message), plus attachment tiles (Open, Open With…, Save…). Expand all / Collapse all sit at the top. A lone message, or any message while conversations are off, still opens on its own as before. Order and reach: View ▸ Messages ▾ replaces the conversations icon toggle. It switches between "Show as conversations" and "Show as individual messages" and puts the "Newest message on top" (the default) or "on bottom". Settings → Appearance mirrors both and adds "Include messages from Sent Items" (on by default). With it on, your replies from Sent Items join the conversation with a "Sent Items" chip, found by Message-ID, In-Reply-To and References through the new Cache::conversation_members_in. Changes apply live. Selecting a header: conversation headers are now selectable. Ribbon and keyboard verbs act on every message in the conversation; Reply, Forward, Open (Enter / double-click) and Print use its newest message, as the context menu already did. Rendering and safety: the conversation is one document in a second reading-pane WebView, beside the untouched single-message view. Each body sits in a sandboxed srcdoc iframe (no allow-scripts) carrying its own sender's CSP, so remote content stays blocked or allowed per sender, and one sender's styles can't leak into another card. Markup JavaScript is off, and the only script is a trusted user script in its own world. It loads and sizes the frames, toggles cards, retargets links to the browser and posts clicks to the app. With markup JavaScript off, WebKit drops a parsed srcdoc attribute, so bodies travel as data-srcdoc. Links are retargeted because a script-less sandboxed frame runs no click listener. Inline cid: images resolve per card (cid:lkc<card>~…), and the pending cid: map is now keyed by message and part. "Show blocked content" on a card loads that card's remote content once. A card with a meeting invitation or a read-receipt request offers "Show details", which opens it on the single-message page with all its banners and a "Back to conversation" button. Mark-as-read follows its setting for each card you open. The message theme toggle applies to every card. Print (Ctrl+P, More ▸ Print…) prints the selected card's message, or the newest. Known limits: other folders means Sent Items only, and read-receipt and unsubscribe prompts appear only under "Show details". Covered by new conversation builder tests (member merge and order, open-card rule, per-card CSP, a hostile body that can't escape its frame, cid: qualification, script messages), conversation_members_in, conversation-order settings and reading_target tests, and selectable-header and menu-target GTK tests. The real document and script were also checked in WebKitGTK 2.52 on a scratch harness. The app suite passes with TZ set to Pacific/Kiritimati, Pacific/Pago_Pago and UTC.

Lookout 0.9.156

Choose a tag to compare

@github-actions github-actions released this 09 Oct 01:46
0.9.156

Lookout 0.9.155

Choose a tag to compare

@github-actions github-actions released this 08 Oct 22:13
0.9.155

Lookout 0.9.116

Choose a tag to compare

@github-actions github-actions released this 28 Aug 17:35
0.9.116

Lookout 0.9.108

Choose a tag to compare

@github-actions github-actions released this 24 Aug 20:09

0.9.108 (2026-08-25)

Changed

  • Mail: a brand-new email's body is now fetched and cached the instant the sync learns it exists, instead of whenever the background prefetch pass happens to reach its folder (with the default cooperative pacing, that could effectively be never). sync_mailbox follows its envelope emit with an inline body fetch for the sync's genuinely-new UIDs - one UID FETCH of the text/calendar parts through the same cache-first fetch_bodies_batch the on-demand path uses, since the envelope pass already fetched each new message's BODYSTRUCTURE. The fetched body doubles as the message's list-row preview (preview_from_body, the same whitespace-collapse/500-char normalization as the raw-prefix preview), so the separate preview round trip folds into the body fetch for exactly the messages that matter, and each cached body is emitted as BodyFetched so the reading pane's 25-entry in-memory cache warms before the user can even click the row. The pass is bounded so the sync that makes new mail visible can't balloon: only the newest 25 new UIDs, only messages under 2 MB (RFC822.SIZE), skipped entirely when any interactive command is queued (the click wins; that message fetches on demand), and best-effort on failure like previews - a new email is openable from the local cache one round trip after it appears.

  • App: opening a message no longer pays a round trip through the account session to learn what the on-disk cache already knows. The selection handler previously checked only the 25-entry in-memory body cache on the UI thread and dispatched FetchBody on every miss, after which the session re-read the same SQLite row it was about to serve from the network. The handler now also kicks an off-thread load_bodies read (spawn_cache_read, uidvalidity-guarded via the account handle's folder list, with UidValidity(0) as a deliberate cache-miss sentinel while the cache handle or folder list is still opening) in parallel with the FetchBody dispatch: whichever lands first renders, and the loser is deduped by pending_body_request - the session's own fetch serves the same row from its own cache check with no network round trip, and the disk-path render clears the pending request so the late BodyFetched is treated as a stale duplicate rather than re-rendering. Between fetch-on-arrival and this fast path, opening mail is instant from the local cache, and a true miss still dispatches the interactive fetch immediately, so the network case pays no added latency.

  • Mail: the session's command queue is now two channels - interactive and background - so a user action never queues behind a pile of background work. FetchBody/FetchAttachment/FetchRawMessage (the user-facing fetches: opening a message, an inline cid: image, an attachment download, a .eml export) travel an interactive channel the main loop drains ahead of everything else: the pre-IDLE check races it first, the IDLE wait races both channels against the server wait, and the command drain empties the interactive channel before touching the background queue (syncs, searches, moves, flag stores, refreshes) or the carried-over wake command. The cooperative background work that already yielded before every round trip (the STATUS drain, the prefetch batches) now checks both channels (no_commands_pending), so a click waits at most the single in-flight round trip - never a queue of queued commands. search_mailbox also yields between its envelope-fetch chunks, the one long command a user can genuinely click through (the local FTS results are already on screen): it stops fetching further chunks when an interactive command arrives and returns the partial match set, which the UI merges into its accumulated results. Reordering is safe by construction - the fetch commands depend on nothing in the background queue, and FetchBody coalescing stays within the interactive channel.

  • Mail: the background body prefetch's worst-case footprint shrinks: the aggressive-off default is one body per round trip instead of three (PREFETCH_BATCH_SIZE, mirrored in the prefetch-batch-size schema default and the settings tests), so an in-flight batch can hold the connection behind at most a single body download, and prefetched messages over 4 MB (via RFC822.SIZE, now requested in the prefetch's envelope pass) are skipped entirely so one pathological message can't hold the connection hostage. Aggressive users can still raise the batch size in Config → Advanced.

Testing

  • lookout-app: settings tests pin the new one-body default (prefetch_policy_assembly, untouched_keys_report_the_schema_defaults); lookout-mail's full suite passes against the two-channel run_account_session signature. The whole workspace suite (645 tests) passes.

Lookout 0.9.107

Choose a tag to compare

@github-actions github-actions released this 24 Aug 03:51

0.9.107 (2026-08-24)

Fixed

  • App: the dock badge's unread count could get stuck at a stale value - on the report it sat at "1" (via count-visible) after the last message was read, while the tray icon's count had correctly dropped to nothing, even though both indicators are fed from the same refresh_unread_indicators computation. launcher_entry::set_unread_count spawned a fresh async task per update onto the Worker's multi-threaded tokio runtime, and each task independently did its dedup check and its emit_signal calls with awaits in between - so two near-simultaneous updates (a burst of FoldersUpdated events while reading) could interleave, and the older count's signal could land on the bus after the newer one. The dock paints the badge from the last signal it saw, so it ended up showing the stale count; the tray wasn't affected because it dedupes synchronously on the UI thread before spawning. launcher_entry now routes every count through a single unbounded FIFO channel drained by one consumer task (badge_loop, spawned once by init), which owns the session-bus connection and every emission - counts are processed strictly in call order, so the badge always lands on the last count the window published, and a failed emission leaves the dedup slot unset so a later update retries instead of being swallowed. set_unread_count just queues the count (a silent no-op before init, exactly as before for the unit tests); the persistent-connection, count-visible-at-zero, and multi-desktop-file-id behavior are unchanged.

Testing

  • lookout-app: the launcher_entry tests (candidate desktop-file ids, the count/count-visible vardict at non-zero and zero) are unchanged and pass. The full suite (255 tests) passes.

Lookout 0.9.106

Choose a tag to compare

@github-actions github-actions released this 24 Aug 01:22

0.9.106 (2026-08-24)

Fixed

  • App: installing the .deb on Ubuntu 24.04+/26.04 failed with lookout depends on libwebkitgtk-6.0-0; however: Package libwebkitgtk-6.0-0 is not installed - the [package.metadata.deb] depends string in crates/app/Cargo.toml hardcoded a runtime package name that doesn't exist on those archives, the same wrong name already fixed for the snap build in 0.9.102. The actual runtime package is libwebkitgtk-6.0-4 (matching the soname libwebkitgtk-6.0-dev pulls in), so the deb now depends on that instead.