Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 222 additions & 0 deletions docs/design/logging-redesign/PLAN.md

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions docs/design/logging-redesign/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Logging & category-system redesign

This folder holds the design handover for GoFlo's logging/category redesign and a phased plan to implement it across multiple sessions **without losing existing features**.

## Start here

1. **[`PLAN.md`](PLAN.md)** — the master plan. Golden rules, feature-preservation inventory, gap analysis, the 8 phases with acceptance criteria, a progress log, and open decisions. A fresh session should read this first.
2. **[`phases/`](phases/)** — one detailed build guide per phase (exact files, current line refs, code sketches, migration/data details, acceptance + preservation checklists, gotchas). Read the guide for your phase after the master plan.
3. **[`subsystem-maps/`](subsystem-maps/)** — stamped groundwork so you don't have to re-explore the codebase:
- [`01-logging-screens.md`](subsystem-maps/01-logging-screens.md) — the two current log screens, routes, input controls, save flows.
- [`02-category-data-model.md`](subsystem-maps/02-category-data-model.md) — entities, DB version + migration chain, DAOs, repository API.
- [`03-theme-color-machinery.md`](subsystem-maps/03-theme-color-machinery.md) — `colorSchemeFor`, palettes, colour-role resolution hook.
Each map is stamped with the **commit + build** it was verified against (`d07d947`, versionCode 116, DB v23) and a staleness check. If HEAD has moved, run the staleness check before trusting line numbers.
4. **[`handover/`](handover/)** — the original design bundle (verbatim):
- `README.md` — design intent + the reusable-component-library requirement.
- `GoFlo Theme Redesign.md` — the 12-palette colour spec (largely already applied; reconciliation is a separate task).
- `Log Screens Rethought.html` — the pannable mockup canvas (open in a browser).
- `screens/01-06-row.png` — rendered reference of each design row.

## The one-paragraph version

A category stores a colour **role** and an optional **group**; the group owns the role and a default input type. From that: three extra in-theme colour roles (quaternary/quinary/senary) plus a fixed off-theme colour; a small library of reusable Compose primitives that every screen is built from; and one unified log screen where a running period is a *state of the day*, not a separate screen. Most of the per-category model (icons, input types, allow-multiple, timeline, show-in-period) **already exists** in the code — see the gap analysis in `PLAN.md` §4. The work is unification and a few genuinely new pieces (the group model, the extra roles, the unified screen, the component library, Yes/No + Time input types), done additively so nothing currently shipping is lost.
504 changes: 504 additions & 0 deletions docs/design/logging-redesign/handover/GoFlo Theme Redesign.md

Large diffs are not rendered by default.

676 changes: 676 additions & 0 deletions docs/design/logging-redesign/handover/Log Screens Rethought.html

Large diffs are not rendered by default.

156 changes: 156 additions & 0 deletions docs/design/logging-redesign/handover/README.md

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions docs/design/logging-redesign/phases/KICKOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Phase kickoff prompts

Paste one of these into a **fresh session** to implement a phase. Replace `<N>` and the phase name. Each phase is its own branch + PR off `main`.

---

## Reusable template

```
You are implementing ONE phase of the GoFlo logging & category-system redesign.

Repo: mapgie/GoFlo. Do all work on a new branch off main named
`claude/logging-redesign-phase-<N>`. Open a draft PR when done.

READ FIRST, IN THIS ORDER (do not skip):
1. docs/design/logging-redesign/PLAN.md — golden rules (§1), feature-preservation
inventory (§3), gap analysis (§4). These are binding.
2. docs/design/logging-redesign/phases/phase-<N>-*.md — your build guide: exact
files, code sketches, migration/data details, acceptance + preservation checklists.
3. The one subsystem map your phase touches (docs/design/logging-redesign/subsystem-maps/).
Run its staleness check first: the maps were stamped at commit d07d947 / DB v23 —
if HEAD or the DB version has moved, re-verify the files it describes before trusting
line numbers.

HARD RULES:
- Additive-first. Do NOT delete or rewire any existing screen/feature unless your phase
guide's removal section explicitly says to (only Phase 8 removes). Every item in
PLAN.md §3 must still work when you finish.
- DB changes: additive, numbered migration only (check GoFloDatabase.kt's current
`version`), never fallbackToDestructiveMigration. Add a migration test.
- This environment can't build (no Android SDK) — CI is the build check. Verify by
inspection + run `python3 a11y_check.py` and `python3 wcag_check.py` before pushing.
Don't add "couldn't compile" disclaimers.
- Every app-code PR needs a changelog fragment at changelog/unreleased/<slug>.json
(the guide suggests one). No en/em dashes in user-facing text. Bind colour to roles,
never literal hex. Respect the a11y rules in .claude/CLAUDE.md (Role on every clickable).
- If your phase hits one of the "open decisions" in PLAN.md §8, STOP and ask me — don't guess.

WHEN DONE:
- Tick your guide's acceptance + preservation checklists in the PR description.
- Update PLAN.md §7 progress log (status, PR/commit, DB version after, any deviations).
- If a file a subsystem map describes changed materially, update that map or note the drift.
- Open a draft PR; do not merge.

Start by reading the three docs above, then tell me your plan for Phase <N> before writing code.
```

---

## Phase 1 (ready to paste)

```
You are implementing Phase 1 (Extended colour roles) of the GoFlo logging redesign.

Repo: mapgie/GoFlo. New branch off main: `claude/logging-redesign-phase-1`. Draft PR when done.

READ FIRST: docs/design/logging-redesign/PLAN.md (§1 golden rules, §3 preservation, §4 gap
analysis), then docs/design/logging-redesign/phases/phase-1-color-roles.md (your build guide),
then docs/design/logging-redesign/subsystem-maps/03-theme-color-machinery.md (run its staleness
check first).

Goal: add quaternary/quinary/senary in-theme colour roles (derived from the active ColorScheme
via HSL, provided through a CompositionLocal) plus a Fixed off-theme colour, and extend the
category colour picker. Fully additive, no DB migration.

HARD RULES: additive only (don't touch existing primary/secondary/tertiary/hex behaviour);
don't change colorSchemeFor's signature (extend around it); derived on-colours must pass WCAG
contrast in all 12 palettes — run python3 wcag_check.py and add a spot-check for the new roles;
run python3 a11y_check.py (picker chips need Role.RadioButton, ≥48dp, not colour-only). Add a
changelog fragment. No en/em dashes in user-facing copy.

When done: tick the guide's acceptance + preservation checklists in the PR, update PLAN.md §7,
open a draft PR (don't merge).

Start by reading the three docs, then give me your plan before writing code.
```

> For later phases, copy the template and point it at the matching `phase-<N>-*.md`. Phase 5 is large — its guide suggests splitting into sub-PRs (5a-5d); you can run those as separate sessions too.
121 changes: 121 additions & 0 deletions docs/design/logging-redesign/phases/phase-1-color-roles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Phase 1 — Extended colour roles

**Goal:** categories can use three extra in-theme colour roles (quaternary/quinary/senary) that re-theme with the palette, plus a fixed off-theme colour. Fully additive, no DB migration.

**Prerequisites:** none (first build phase).
**Read first:** `PLAN.md` §1, §5; `subsystem-maps/03-theme-color-machinery.md` (the whole thing); `handover/screens/06-row.png` ("Pick a colour" mock).

---

## Why

The whole redesign rests on "a category stores a colour *role*, never a hex." Today only `primary`/`secondary`/`tertiary` (+ raw hex) exist. The mock offers six in-theme roles plus a Fixed track. Material3's `ColorScheme` has no 4th-6th accent slot, so we derive them and expose them through a `CompositionLocal`.

## Files to touch

| File | Change |
|---|---|
| `ui/theme/Color.kt` | Add a derivation function producing 3 extra roles + on-colours from a `ColorScheme`. Add a small holder data class. |
| `ui/theme/Theme.kt` | Derive roles once per theme; provide via a new `CompositionLocal` wrapping `MaterialTheme`. |
| `ui/util/CategoryAppearance.kt` | Add enum entries + resolver branches for the 3 new tokens. |
| `ui/screens/categories/ManageCategoriesScreen.kt` | Colour picker becomes a role row of 6 + a Fixed track (factor a `RolePicker` here or inline; it is extracted into a shared component in Phase 3). |

## Step-by-step

### 1. Derivation (`Color.kt`)
Add near `buildCustomColorScheme` (mirror its HSL house style — do **not** add an HCT/TonalPalette dependency):

```kotlin
/** Three extra in-theme accent roles derived from a ColorScheme, + WCAG-safe on-colours. */
data class ExtendedRoles(
val quaternary: Color, val onQuaternary: Color,
val quinary: Color, val onQuinary: Color,
val senary: Color, val onSenary: Color,
)

fun deriveExtendedRoles(scheme: ColorScheme): ExtendedRoles {
// Rotate hue off the three existing accents so the extras are harmonious but distinct.
// Pin S/L into the scheme's own accent band so contrast stays in range.
fun rotate(base: Color, degrees: Float): Color {
val hsl = FloatArray(3)
ColorUtils.colorToHSL(base.toArgb(), hsl)
hsl[0] = (hsl[0] + degrees + 360f) % 360f
return Color(ColorUtils.HSLToColor(hsl))
}
val q = rotate(scheme.primary, 30f)
val qi = rotate(scheme.secondary, -30f)
val s = rotate(scheme.tertiary, 45f)
fun on(c: Color): Color = if (c.luminance() > 0.35f) Color(0xFF1C1B1F) else Color.White
return ExtendedRoles(q, on(q), qi, on(qi), s, on(s))
}
```
> The exact rotation is a judgement call — tune it so each role is visually separated from the base three in every palette. An alternative is interpolating between existing accents (`lerp`). Whatever you pick, verify contrast (step 5).

### 2. Provide it (`Theme.kt`)
Add a CompositionLocal and wrap the existing `MaterialTheme(...)`:

```kotlin
val LocalExtendedRoles = staticCompositionLocalOf<ExtendedRoles> { error("ExtendedRoles not provided") }
```
Inside `GoFloTheme`, after `colorScheme` is chosen:
```kotlin
CompositionLocalProvider(LocalExtendedRoles provides deriveExtendedRoles(colorScheme)) {
MaterialTheme(colorScheme = colorScheme, typography = GoFloTypography, content = content)
}
```
Keep the `SideEffect` status-bar logic as-is.

### 3. Resolver (`CategoryAppearance.kt`)
Extend the enum:
```kotlin
enum class CategoryColor(val key: String, val displayName: String) {
PRIMARY("primary","Primary"), SECONDARY("secondary","Secondary"), TERTIARY("tertiary","Accent"),
QUATERNARY("quaternary","Quaternary"), QUINARY("quinary","Quinary"), SENARY("senary","Senary"),
}
```
Extend both resolvers (they are `@Composable`, so they can read the local):
```kotlin
@Composable fun String.toCategoryColor(): Color {
val s = MaterialTheme.colorScheme
val ext = LocalExtendedRoles.current
return when (this) {
"primary" -> s.primary; "secondary" -> s.secondary; "tertiary" -> s.tertiary
"quaternary" -> ext.quaternary; "quinary" -> ext.quinary; "senary" -> ext.senary
else -> runCatching { Color(toLong(16)) }.getOrDefault(s.secondary) // FIXED = hex
}
}
```
Mirror in `toCategoryOnColor()` with `onQuaternary`/`onQuinary`/`onSenary`.

### 4. Picker UI (`ManageCategoriesScreen.kt`, existing "More colours" area ~line 1141)
Render a row of 6 role chips (selected = filled with the role colour + a check/ring) and, below, the existing fixed hex track labelled so the user knows it "stays put on theme change" (mock row 6). Each role chip: `Role.RadioButton` in `.semantics`, min 48dp, `contentDescription` naming the role. Selected state must not be colour-only (add a ring/check).

### 5. Verify contrast
- Run `python3 wcag_check.py` (base palettes unaffected, must stay green).
- Add a spot-check: for each of the 12 light+dark schemes, derive the 3 roles and assert on-colour contrast ≥ 3:1 (icon) against the role. If a derived role fails in some palette, adjust the rotation/L clamp until all pass. Consider adding this as a small script (`wcag_check_roles.py`) so it is repeatable.

## Data changes
None. `colorToken` is a free-form string; new tokens need no migration.

## Acceptance criteria
- [ ] A category set to each of quaternary/quinary/senary renders that colour and **re-themes** when the palette changes (verify in one light + one dark palette).
- [ ] A category set to a Fixed colour does **not** change on theme switch.
- [ ] Derived on-colours pass contrast in all 12 palettes (spot-check script green).
- [ ] `wcag_check.py` and `a11y_check.py` both green.
- [ ] Picker chips carry `Role.RadioButton`, are ≥48dp, and selection is not colour-only.

## Feature-preservation checklist
- [ ] Existing `primary`/`secondary`/`tertiary`/hex tokens resolve exactly as before.
- [ ] Calendar dots, `DayLogSheet` bubbles, Stats colour usage unchanged.
- [ ] The `CUSTOM` theme path (`buildCustomColorScheme`) still works — `deriveExtendedRoles` runs on the custom scheme too.

## Gotchas
- `LocalExtendedRoles` is read inside `@Composable` resolvers only. Anything resolving a category colour **outside** composition (rare) needs the scheme passed explicitly.
- `staticCompositionLocalOf` won't recompose on change; theme changes swap the whole `GoFloTheme` subtree so that's fine. If you see stale colours, switch to `compositionLocalOf`.
- Comfortaa/typography untouched here.

## Changelog fragment
`changelog/unreleased/extended-color-roles.json`:
```json
{ "bump": "minor", "added": ["Three extra in-theme colour roles for categories, plus a fixed off-theme colour option"] }
```
Loading
Loading