feat(rhema-code): add the colors setting with four palettes
The stored `colors` key names a palette — rhema (the default, Jadeite), aquamarine, black-and-white, or dracula — and the Appearance page of /settings cycles it live, like the theme and icon toggles beside it. Each palette carries a dark and a light appearance under the existing `theme` key; dracula's light side is derived, the official palette ships only dark. The appearance decision moves off token comparison: Theme now answers is_dark() from the ground/text luminance, so palettes beyond the original two get the right per-profile fallback tones.
This commit is contained in:
commit
81da26b955
159 files changed
+50628
No files matched your search
@@ -0,0 +1,79 @@
|
||||
//! `rhema-calendar-integration` — the calendar integration: one
|
||||
//! normalized occurrence vocabulary and one client contract for the
|
||||
//! calendar feeds a resident agent can remind about, with the adapters
|
||||
//! that read them.
|
||||
//!
|
||||
//! An integration, not an agent: no driver, no loop, and no reminder
|
||||
//! policy lives here. A consumer composes the crate with its own agent
|
||||
//! tier — it picks a [`crate::Window`], polls a [`crate::Calendar`]
|
||||
//! for the occurrences starting inside it, and decides what a start
|
||||
//! time means (the claw daemon today: one reminder slot per lead time,
|
||||
//! dispatched into the resident agent). Every [`crate::CalendarEvent`]
|
||||
//! carries an id that stays the same across polls of the same
|
||||
//! occurrence, so a consumer re-polling on its own cadence never
|
||||
//! re-fires a reminder it already fed; the repetition is the
|
||||
//! consumer's dispatcher's dedup, not this crate's problem.
|
||||
//!
|
||||
//! Recurrence is expanded before a poll answers, so a consumer never
|
||||
//! sees a rule, only starts it can compare against its clock:
|
||||
//! - The Google Calendar adapter asks the server to expand
|
||||
//! (`singleEvents=true`).
|
||||
//! - The ICS and CalDAV adapters expand client-side, covering the
|
||||
//! rule shapes servers actually emit (daily, weekly with `BYDAY`,
|
||||
//! monthly, yearly; `INTERVAL`, `COUNT`, `UNTIL`, `EXDATE`) and
|
||||
//! applying `RECURRENCE-ID` overrides. Anything outside those
|
||||
//! shapes surfaces once, at its `DTSTART`, never silently dropped.
|
||||
//! CalDAV shares the exact expansion: the server returns masters
|
||||
//! plus overrides in range, and the adapter reads them out of the
|
||||
//! multistatus. Where a server exposes both, the feed URL and the
|
||||
//! DAV collection are different identities: their occurrence ids
|
||||
//! carry different provider prefixes.
|
||||
//!
|
||||
//! Times are instants, not wall-clock strings. Every wire format (RFC
|
||||
//! 3339, iCalendar's compact `YYYYMMDDTHHMMSS[Z]`, `VALUE=DATE`
|
||||
//! all-days) normalizes to a `time::OffsetDateTime`; a floating
|
||||
//! (offset-less) time reads as UTC, and a `TZID` parameter rides the
|
||||
//! event's `raw` for a consumer that can resolve zones.
|
||||
//!
|
||||
//! ```
|
||||
//! use rhema_calendar_integration::{Calendar, IcsCalendar, Window};
|
||||
//! use time::format_description::well_known::Rfc3339;
|
||||
//!
|
||||
//! // A feed is one URL (webcal counts as https); the poll is one GET.
|
||||
//! let feed = IcsCalendar::new("https://example.net/nowhere/calendar.ics");
|
||||
//! let from = time::OffsetDateTime::parse("2026-10-20T00:00:00Z", &Rfc3339)
|
||||
//! .expect("parses");
|
||||
//! let window = Window::new(from, from + time::Duration::hours(24));
|
||||
//! // `feed.events(&window).await` answers with the occurrences whose
|
||||
//! // start falls inside the window, oldest first; this doctest only
|
||||
//! // assembles them.
|
||||
//! let _ = (&feed, &window);
|
||||
//! ```
|
||||
//!
|
||||
//! Layout:
|
||||
//! - [`event`] — the normalized vocabulary: `CalendarEvent`, `Window`
|
||||
//! - [`error`] — the crate's single calendar error type
|
||||
//! - [`calendar`] — the `Calendar` client contract
|
||||
//! - [`ics`] — the ICS/webcal adapter (one GET, expanded client-side)
|
||||
//! - [`caldav`] — the CalDAV adapter (one REPORT, the same expansion)
|
||||
//! - [`google`] — the Google Calendar API adapter (expanded server-side)
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
pub mod caldav;
|
||||
pub mod calendar;
|
||||
pub mod error;
|
||||
pub mod event;
|
||||
pub mod google;
|
||||
pub mod ics;
|
||||
|
||||
// Re-exported so implementors of the async seam can apply #[async_trait]
|
||||
// without adding the crate themselves.
|
||||
pub use async_trait::async_trait;
|
||||
pub use caldav::CaldavCalendar;
|
||||
pub use calendar::Calendar;
|
||||
pub use error::CalendarError;
|
||||
pub use event::{CalendarEvent, Window};
|
||||
pub use google::GoogleCalendar;
|
||||
pub use ics::IcsCalendar;
|
||||
Reference in new issue
Block a user