Files
floret-kit/CHANGELOG.md
T
makiolaj 05c79f2afc release: floret-kit 0.3.0
Two new components since 0.2.1 — the onboarding shell (OnboardingScaffold,
OnboardingProgress, BenefitRow) that Calendula's first-run wizard is built
on, and SnackChip, the compact confirmation converged from Agendula's and
Calendula's undo chips — plus grouped-list and picker richness (long-press
rows, styled row text, exported GroupedListInset, per-option summaries,
public SelectedCheck, AboutCard in a group) and two fixes: all-day values
rendering a day early west of UTC, and full-screen picker bar icons
following the XML theme rather than the running one.

The changelog had gone unwritten since 0.2.1; this backfills every entry.
2026-08-10 17:20:57 +02:00

13 KiB

Changelog

All notable changes to floret-kit are documented here. The format follows Keep a Changelog.

floret-kit is consumed from source — each app embeds it as a git submodule and builds it through a Gradle composite build, pinning a specific commit. There are no binary releases; "version" tracks the shared version in the root build (currently 0.3.0). Entries are grouped by module.

[Unreleased]

[0.3.0] - 2026-08-10

Added

  • components — OnboardingScaffold, the shared shell for a first-run flow: hero, body and a call-to-action stack, so the family's onboarding screens read as one family while keeping their own mark. A multi-step flow passes progress, which OnboardingProgress draws as one capsule per step with the caller's own localised label, and navigationIcon for a step that can be stepped back from — the navigation slot's width is reserved whether or not it is filled and mirrored on the other side, so the progress stays centred as steps come and go. topSpacing gives a step whose hero is a large picture rather than a mark room to breathe; contentPadding is the scrolling column's horizontal inset, which a step built from grouped rows passes 0 for since those carry their own; and scrollingActions moves the actions off the bottom bar onto the end of the content, for a step taller than the screen where a pinned bar eats the room the content needs and hides that there is more below. BenefitRow is the icon/title/body line those screens list their points with.

  • components — SnackChip, the family's compact confirmation: a content-sized pill sliding up from the bottom with an optional trailing action, for the case where the surface behind the confirmation is the confirmation (a list that just lost a row, a calendar that just moved an event) and a full-width snackbar would cover it. Placement and timing stay with the caller — the bottom inset to clear differs per screen, and what an expiring undo window commits is the app's business. Converged from Agendula's swipe-delete undo chip and Calendula's drag-to-reschedule confirmation; the shared version keeps the polite live region so the chip is announced, takes a maxWidth so a message can't run into a bottom-end FAB, and takes color/contentColor so an app can put it on the FAB's own fill. A message replaced in place crossfades and the pill resizes with it, so an answered confirmation ("Moved" → "Move undone") reads as one chip changing rather than two chips swapping.

  • components — GroupedRow takes an AnnotatedString title and summary as well as plain Strings, so a row can carry its own emphasis: marking the run a search matched, without every caller having to rebuild the row. The String overload delegates to it, so existing callers are unchanged.

  • components — GroupedSurface/GroupedRow take an onLongClick (with the platform long-press haptic), for a list that enters a selection mode from a row.

  • components — GroupedListInset, the 16dp a grouped list keeps from the screen edge, exported. It was a private literal, so everything sitting outside the cards but belonging to them — section headers, hints, picker descriptions — repeated the number and had drifted to 24dp in places, leaving pages with two ragged left margins.

  • components — OptionPicker takes a per-option summary, put under the label, for a row that names something it doesn't spell out (a range's dates, a reminder's firing time, what "follow the system" resolves to today). Every option then shows its effect at once and stays comparable, without the cost of a live preview. The KDoc states the three rungs of picker richness — plain rows / per-option summary / live preview — and the rule that a preview picker must not close on selection, since closing hides the thing it exists to show.

  • components — SelectedCheck, the trailing check that marks a chosen row in a full-screen picker, made public. It was private to OptionPicker, so every consumer hand-rolled a copy — Calendula alone carried five, all drawing a different check than the kit's.

  • components — AboutCard takes a Position and renders through GroupedSurface, so it can be the top of a group with rows continuing underneath rather than only standing alone. Defaults to Position.Alone.

Fixed

  • core-time — all-day values rendered a day early west of UTC. All-day items are date-only in iCalendar and stored as UTC midnight of their calendar date, but formatDate/formatDateTime/formatDateTimeCompact resolved every instant against ZoneId.systemDefault(). formatDate now takes an allDay flag (default false, so existing call sites are unaffected) and resolves those against UTC; callers must pass it at the call site.

  • components — full-screen picker status/navigation-bar icons matched the XML theme rather than the running one. A Compose Dialog owns its own window, seeded from the XML theme, so a picker drawing under the bars showed dark icons on a dark surface. FloretExpressiveTheme now exposes its resolved light/dark decision as LocalFloretDarkTheme and the dialog window's bar appearance follows it, tracking the in-app choice even when it diverges from the system night qualifier.

[0.2.1] - 2026-07-17

Added

  • components — CollapsingScaffold takes a scrollable flag (default true, so every existing caller is unchanged). Set it to false when the content brings its own scrolling container: the scaffold's own verticalScroll composes each row up front and can't host a LazyColumn (nesting a same-axis scrollable throws), which ruled out any surface with more options than fit an eager pass. Opting out hands the content a plain column and leaves the insets, IME padding and top-bar collapse as they were. FullScreenPicker forwards the flag. Consumed by Calendula's ~600-entry time-zone picker from 2.16.0.

[0.2.0] - 2026-07-17

Added

  • components — CustomAmountEditor, the amount + unit editor behind an "Custom…" picker option (Calendula's custom snooze duration). Consumed by Calendula since 2.15.0.
  • core-locale — the locale-aware formatting half of the module, so apps stop hand-rolling "$day. $month $year" templates that bake one language's field order (and its separators, and its ordinal dot) into every language they ship:
    • localizedDateFormatter(locale, skeleton) — a DateTimeFormatter for a field skeleton ("LLLLy", "dMMMy", "EEEdMMM") laid out in the locale's own order, via Android's best-pattern matching. Drop a field from the skeleton to drop it from the output — the locale still supplies the arrangement.
    • currentLocale() — the display locale, observed from LocalConfiguration. Since AppLanguage.apply() changes the language inside a running process, a locale captured once goes stale; this keys recomposition to the change.

Changed

  • core-locale now carries Compose (runtime + ui only) for currentLocale(). The module previously declared itself Compose-free; applying a language mid-process is its own API, so the hook that makes the UI notice belongs beside it rather than in a Compose module depending back on this one. core-crash is the precedent for a core-* module carrying Compose.

Known gaps

  • core-time's Instant.formatDate() family now overlaps core-locale. Its private dateNoYearFormatter strips the year by regex-ing the pattern string, which localizedDateFormatter(locale, "dMMM") does properly; and its formatters resolve Locale.getDefault() once at class-init ("locale changes within a running process are rare enough to ignore"), which AppLanguage.apply() makes untrue. Left alone for now — it is unused by Calendula but Agendula-visible. Reconcile when Agendula next re-pins.

[0.1.0] - 2026-07-07

Added

  • core-locale (Android library) — per-app language plumbing (AppLanguage): read the shipped languages from the app's res/xml/locales_config.xml, get/set the applied language via AppCompatDelegate, render each language's autonym. The app passes its own locales_config resource id; the system-default choice is null. (Shipped in 0.1.0 but omitted from this changelog at the time; recorded here retroactively.)

  • core-time (JVM library) — pure-Kotlin date/time helpers, no Android and no third-party dependencies:

    • DayWindow.today() — local-day boundaries for smart-list logic.
    • Instant.formatDate() / formatTime() / formatDateTime() — locale- and zone-aware display formatting over kotlin.time.Instant.
    • TimeBridge — Long epoch-millis ↔ Instant, for content-provider I/O.
    • LocalDate.isoWeekNumber() — ISO-8601 week-of-week-based-year, the calendar-week number for week/month grids (one scheme, shared).
  • core-crash (Android library) — privacy-respecting, on-device crash capture and reporting. Writes an allowlist-only report (app/Android/device version, locale, time, stack trace — nothing else) to private storage and chains to the platform handler; uploads nothing. Includes startup crash-loop detection, a report dialog that shows the full text before it leaves the device, and an issue-tracker hand-off. Per-app specifics (display name, issue URLs) come from a CrashConfig supplied at install(); each app keeps its own thin themed CrashReportActivity.

  • identity (Android library) — the family's Material 3 Expressive theme factory FloretExpressiveTheme(lightScheme, darkScheme, …) (dynamic colour on API 31+, system light/dark fallback, the standard motion scheme) plus the shared motion helpers — rememberNavSlideSpec(), the content transitions, Modifier.predictiveBack() and Modifier.animateContentSizeMotion() (eases a block's own height changes, e.g. a text field wrapping to a new line), each reduced-motion-aware. Each app passes its own seed-derived colour schemes and typography — the mechanics are shared, the look is not.

  • components (Android library) — the shared Compose component vocabulary:

    • GroupedSurface / GroupedRow + Position / positionOf — the canonical press-morphing tonal grouped-row primitive (the Android-15 settings pattern).
    • InlineTextField — the borderless tonal text input (capitalization is a parameter).
    • OptionCard — the sanctioned selection-dialog pick.
    • CollapsingScaffold — the collapsing settings/sub-screen scaffold; set largeTopBar = false for a pinned single-line bar (the picker variant).
    • FullScreenPicker / OptionPicker — the full-screen single-select picker, now over a pinned single-line bar (a picker is a short list, so no tall collapsing header to scroll past). FullScreenPicker takes an optional actions slot (app-bar trailing items) for pickers that carry a commit or extra action — a custom-value Add/OK, a Reset.
    • ReorderableColumn — dependency-free drag-to-reorder for the short, fixed grouped-card Settings lists; pairs with GroupedRow(gapBelow = false), which hands the column uniform control of row spacing.
    • DebugRibbon — a stark, un-themed "DEBUG" corner ribbon; gate on BuildConfig.DEBUG at the call site.
    • InlineTextField gained enabled (a disabled field dims to a locked value).
    • pastelize() — softens a raw provider colour to a theme-fitting pastel.

    App-agnostic by design: apps compose their own screens and keep their own identity chips/icons. String resources here (e.g. back) are fallbacks an app can override.

  • core-reminders (JVM library) — the app-agnostic reminder-override model: ReminderOverride (Inherit / None / Minutes), where Minutes carries a list of lead-times so a target can hold several reminders at once (an app that offers a single reminder just uses a one-element list). Map helpers (reminderOverrideFor, reminderLeadsFor, applyReminderOverride, reminderOverrideForMinutes, normalizeReminders) plus ReminderOverrideCodec (three configurable separators; single legacy values round-trip byte-identically) and the lead-time unit model (ReminderUnit, decomposeReminderMinutes). The labels/presets and the alarm scheduler stay per-app.

Conventions

  • No foojay toolchain resolver anywhere — it can fetch a JDK at build time, which reproducible / official F-Droid builds reject. Modules set jvmTarget directly. See docs/ARCHITECTURE.md.
  • AGP 9.x provides built-in Kotlin compilation, so Android modules apply only the Android library + Compose plugins (no separate kotlin.android).