Repository navigation
iPhone: the widgets and the Live Activity in the app's material (WP14) - #54
Conversation
…e Activity ReceiptLog (FTWShared) is the car command's receipt in the terminal layer: SENT ▸ ACCEPTED ▸ MEASURED ▸ CONFIRMED. Every slot is always on the line and lights only on its own evidence: the local send, the box's cmd.ack, lane 0 showing the car above 42 W while live once the box has the command, and cmd.result. The box often confirms before the car draws, so the slots light in the order their evidence comes. A refusal takes the last slot in alarm red, padded to the same width, so the line never moves. ReceiptRow (FTWCharts) draws it for the Live Activity and, next, the car view. The Charge now Live Activity shows the receipt in place of its three capsules. LoadpointsModel lights MEASURED from lane 0 when the home has one Live Activity up; lane 0 has one car figure for every charger. The card goes stale two minutes after the box last confirmed it (it was fifteen): the app confirms it once a minute while someone looks, and nothing does while it sleeps. The Control still trusts its record for fifteen. The card sits on FTW's ground with SF Pro figures and never glows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… its body Grid today drew the day as bars for what it bought and a thin curve below zero for what it sold. It now draws what the grid's Today draws in the app (research decision D): each quarter's net power as one signed line, buying above zero and selling below, magenta both ways. The geometry is the app's own ChartSeries.history: one point per slot of at least 6 pt (30 minutes on the medium widget, 60 on the small), each the exact mean of its quarters, through a Steffen curve that never overshoots, with its body, and on the medium the band of the quarters in each slot. A hole the box has no record for breaks the line and shows a dotted baseline; nothing is drawn after the read. Still paths, a 2 pt line, no glow. The arrow beside net becomes a word in the grid's colour, "köpt netto" or "sålt netto", next to both sides. The chart says ↑ KÖPER and ↓ SÄLJER beside the scale's ends, in the corner the hours still to come leave empty, where that side has room. The kWh, the cost from the box and "kl hh:mm" stay. Figures are SF Pro with even digits; the kicker and the time are the terminal layer. Tinted and clear draw the line in primary at 0.7, marked for the accent, and the body fainter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…OF on every one One palette: the widgets' own Tone table is gone, and every widget, the Live Activity and the Control read the roles in FTWCharts/Theme.xcassets through Theme, as the app does. Backgrounds are the ground, grey (hairline) when there is nothing for the moment. The price: the small widget, StandBy's, adds the next twelve hours as the block-glyph scale, one bar an hour in eight levels by thirds of ink, the hour that holds now in full ink. The lock screen's rectangle writes the same hours in SF Mono, ▁▂▃▄▅▆▇█, under the price, with CHEAPEST and AS OF on one mono line. PriceBlocks (FTWShared) works the hours out for both, from the household's clock. The medium keeps the app's price chart as still paths. Figures are SF Pro with even digits; the kicker, CHEAPEST and AS OF are the terminal layer. The plan: the medium draws the next twelve hours as Now's strip does, in still paths in place of Swift Charts: the car and the battery as 6 pt lanes of runs (violet with a tick at the car's goal, cyan storing, gold giving), an idle lane as a faint hairline, the price as bars in eight levels by thirds, mono hours and a now rule. Every plan widget ends on PLANNED 17:21, or PLAN FROM 14:05 once old. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 97639e5414
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let ids = host.loadpointIDs(home: activityHome) | ||
| guard ids.count == 1 else { return } | ||
| showChargeNow(ids[0], .measured(atMs: site.scheduler.nowMs)) |
There was a problem hiding this comment.
Don't attribute aggregate EV power to one charger
When a home has multiple chargers but only one active Live Activity, Contract.FID.evW is the site-wide car load, while ids.count == 1 only proves that one activity is open. If another charger is already drawing under its plan, any subsequent site observation will therefore mark the active command's MEASURED slot even when its own charger has not started; use per-loadpoint power or require a single charger, not merely a single activity.
Useful? React with 👍 / 👎.
| current: current, | ||
| cheapest: cheapest, | ||
| curve: curve, | ||
| hours: PriceBlocks.hours(curve, nowMs: t, count: stripHours, clock: ZoneClock.of(calendar.timeZone)), |
There was a problem hiding this comment.
Preserve the final available price block
When exactly one price slot remains active, PriceStrip.summary still supplies current but intentionally returns an empty bars array; passing that empty curve here makes both the small widget and lock-screen widget show no block or glyph for a price that is still valid. Build the block input from the remaining slots independently, or ensure the singleton current slot is retained.
Useful? React with 👍 / 👎.
| if e.kind == .car, e.targetPct != nil { | ||
| path.addRect(CGRect(x: max(x0, x1 - 2), y: y - 2, width: 2, height: Self.lane + 4)) |
There was a problem hiding this comment.
Hide goal ticks beyond the displayed horizon
When a car charge window continues past the 12-hour horizon, x(e.endMs) is clamped to the chart's right edge, but this unconditional target tick is still added there. The widget consequently depicts the goal as occurring at the end of the visible horizon even though PlanGlance deliberately preserves the later real end time; add the tick only when the event end is inside the displayed interval.
Useful? React with 👍 / 👎.
The car view extends WP14's receipt log with the slots a command has and MEASURED for the car view; inside each conflict this branch's side is the superset, and everything else comes from main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WP14 of the iPhone polish release (Phosphor): the widgets and the Charge now Live Activity in the app's material, so the home screen, the lock screen and the app read as one. Design:
design/ftw-ios-polish-2026-10/ftw-ios-redesign.md§15 WP14, §10, §12;charts-research.md§4.6 and §5.7; Fredrik's note 17.Paired with the car view PR (WP12, #55): both carry the first commit, the receipt log. Whichever merges first, the other's copy drops out on rebase.
What changes
One palette. The widgets' own
Tonetable is gone. Every widget, the Live Activity and the Control read the roles inFTWCharts/Theme.xcassetsthroughTheme, as the app does. Backgrounds are the ground, and grey (hairline) when there is nothing for the moment. Figures are SF Pro with even digits. Kickers,AS OF,CHEAPESTand the receipt are the terminal layer in SF Mono.Grid today, in the app's grammar (research decision D). One signed line with its body, buying above zero and selling below, magenta both ways. It is the app's own
ChartSeries.history: one point per slot of at least 6 pt (30 min on the medium widget, 60 on the small), each the exact mean of its quarters, through a Steffen curve that never overshoots. The medium adds the band of the quarters in each slot. A hole the box has no record for breaks the line and shows a dotted baseline; nothing is drawn after the read. The arrow beside net becomes a word in magenta,köpt nettoorsålt netto. The chart says↑ KÖPERand↓ SÄLJERbeside the scale's ends, in the corner the hours still to come leave empty, and only where that side has room. The kWh, the cost from the box andkl 16:55stay. No bars.Price. The small widget, also StandBy's, adds the next twelve hours as the block-glyph scale: one bar an hour in eight levels, ink by thirds, the hour that holds now in full ink. The lock screen's rectangle writes the same hours in SF Mono,
▁▂▃▄▅▆▇█, under the price, withCHEAPEST 00–02 · AS OF 17:21on one mono line.PriceBlocks(FTWShared) works the hours out for both, on the household's clock, and ends the strip where the market has not priced. The medium keeps the app's price chart as still paths.Plan. The medium draws the next twelve hours as Now's strip does, in still paths in place of Swift Charts: the car and the battery as 6 pt lanes (violet with a tick at the car's goal, cyan storing, gold giving), an idle lane as a faint hairline, the price as bars in eight levels by thirds, mono hours and a now rule. Every plan widget ends on
PLANERAD 17:21, orPLAN FRÅN 14:05 · ÖPPNA FTWonce old.Tinted and clear. Every widget reads
widgetRenderingMode: lines, bars and runs in.primary, with the line, the now bar or the runs marked.widgetAccentable().The Live Activity. The three capsules become the receipt log,
SKICKAT ▸ MOTTAGET ▸ UPPMÄTT ▸ BEKRÄFTAT, each slot lit on its own evidence (below). It goes stale two minutes after the box last confirmed Charge now, not fifteen: the app confirms it once a minute while someone looks, and nothing does while it sleeps. The Control still trusts its record for fifteen. The card sits on FTW's ground, the time is SF Pro with even digits, and nothing glows.The receipt log (
ReceiptLogin FTWShared, drawn byReceiptRowin FTWCharts). Four slots, always on the line, so lighting one moves nothing: SENT on the local send, ACCEPTED oncmd.ack, MEASURED when lane 0 shows the car above 42 W while live, once the box has the command, and CONFIRMED (lime) oncmd.result. The box often confirms before the car draws, so the slots light in the order their evidence comes. A refusal takes the last slot as REFUSED in alarm red, padded to the same width. Lane 0 has one car figure for every charger, soLoadpointsModellights MEASURED on the Live Activity only when the home has one up.CLAUDE.mdgains the widgets' material and the receipt log under "The look".FTW Cloud's widget push: not wired
WP14 changes how the widgets look, not what they know. The app already writes the App Group cache whenever it reads the box and reloads the timelines, and it refreshes prices in the background.
/v1/nowcarries the live readings, prices and the heartbeat, but not the plan or today's grid totals, so it could feed only the price widget. Wiring it would put a token in the App Group and make the widget extension a network client, against the rule inCLAUDE.mdthat widgets read the App Group cache and nothing else. The gain would be tomorrow's prices reaching the price widget around 13:00 while the app sleeps. That is worth its own change, with the rule rewritten on purpose.Proof
swift test: FTWShared 190 tests, FTWKit 342 (the live suites skip, as always), all green. New:ReceiptLogTests, the Live Activity's MEASURED and two-minute stale date (ChargeNowTests), lane 0 lighting MEASURED through the demo box (RemoteTests), the grid's signed line, its slots by width, holes and sides (GridGlanceTests,GridTodayShareTests), the block strip andAS OF(PriceGlanceTests).Home screen, both pages, demo records in the App Group (17:21), light / dark / tinted / clear:
Lock screen: the price as block glyphs beside Grid today, then beside the plan; and the whole lock screen with a Live Activity that got no answer.
Live Activity, lock screen: sent, accepted (the car already drawing, so MEASURED is lit before CONFIRMED), on with every slot lit, and stale two minutes after the last confirmation (time stopped, all ink):
Light: on and stale. Dynamic Island: compact, then expanded while accepted, on and stale.
Not proven here: StandBy at night (the simulator cannot enter StandBy) and a run on Fredrik's iPhone. Widgets have no motion, so there is no Animation Hitches run. The inline price widget did not change and is not pictured. The commits carry no DCO sign-off.
🤖 Generated with Claude Code