Skip to content

Add a redesigned interface with Now, Trends and Logbook tabs (next-dev) - #2621

Open
Nutcasey wants to merge 13 commits into
LoopKit:next-devfrom
Nutcasey:loop-ui-redesign-next-dev
Open

Nutcasey wants to merge 13 commits into
LoopKit:next-devfrom
Nutcasey:loop-ui-redesign-next-dev

Conversation

@Nutcasey

@Nutcasey Nutcasey commented Oct 28, 2026 •

Copy link
Copy Markdown

This is the redesigned interface from #2615 (Now, Trends, Logbook and Settings tabs), rebuilt on next-dev. #2615 is now closed; its description covers the design, the error states, the appearance and icon options, the localisation and the widget, and all of that applies here. This description covers what changed to fit next-dev.

The first six commits follow #2615's commits one for one. The later commits are the review, parity and fix updates listed below. The branch is rebased on next-dev e0eb9856 (30 October 2026).

What changed for next-dev

  • Home screen. next-dev hosts StatusTableViewController inside the SwiftUI StatusTableView with its own action bar. In the new interface the tabs wrap that hosting controller and the action bar is hidden (StatusTableView.showsActionTabs(_:)), because Now has its own Add carbs, Bolus and Presets buttons. The classic interface is next-dev's screen, unchanged.
  • Data. The Now screen reads LoopDataManager.algorithmDisplayState, activeInsulin and activeCarbs, plus SettingsManager and TemporaryPresetsManager, instead of getLoopState and LoopSettings. The day pages' insulin and carbs on board come from two small LoopDataManager helpers. They use next-dev's own maths: doses annotated with basal history, and the carb absorption review.
  • Presets. On next-dev, pre-meal is one of the presets on the Presets screen, so the Now screen has one Presets button that opens it, rather than Add a redesigned interface with Now, Trends and Logbook tabs #2615's pre-meal menu. The button shows the active preset's name. next-dev's active-preset banner stays above the screen.
  • Closed loop paused uses DeviceDataManager.closedLoopDisallowedLocalizedDescription (no sensor session, or a manual temp basal).
  • Status rows. next-dev already draws No Recent Glucose, Tap to Resume and bolus progress as rounded cards, so Add a redesigned interface with Now, Trends and Logbook tabs #2615's card styling for them is left out.
  • Deeplinks. DeeplinkManager is gone on next-dev. LoopAppManager.handle(_:) switches to the Now tab before opening the carb, bolus or presets screen. The widget's buttons use the shared Deeplink type.
  • Settings tab. It selects Now before presenting Settings. Device and service setup screens open from Settings through StatusTableViewController, which is only in the window while Now is selected. Without this, choosing a service in Add Service from Trends or Logbook showed nothing.
  • Closed-loop history for Trends. This is recorded from SettingsManager.dosingEnabled. LoopDataManager.automationHistory keeps only 36 hours, too short for the 90-day view.
  • Strings. The catalogue keeps next-dev's entries and comments and adds the redesign's keys and translations. next-dev's Statistics screen gains translations for the labels it shares with Trends: Average, GMI, High and Low.
  • Names. next-dev has its own AGPChartView (Statistics), so the Trends chart is TrendsAGPChart.

Screenshots (iOS 26.3 simulator, Pump and CGM Simulators)

Now while a bolus is delivered Trends, 14 days Day page with insulin and carbs on board A service set up from Settings New Interface off: next-dev's classic screen

Testing

On the iOS 26.3 simulator, with the Pump and CGM Simulators and data carried over from a dev install:

  • Now: carbs and a bolus entered and delivered; IOB, COB, prediction, Today and the loop, pump and CGM pills each open their screen; a preset started; delivery suspended and resumed.
  • Trends and Logbook: Day, 7 d and 14 d checked against the day totals. A carb entry edited and deleted from Logbook.
  • Settings: a service added from Add Service with Logbook selected; New Interface switched off and on; Night appearance and the alternate icons.
  • Other: German; the redesigned Lock Screen Live Activity.

LoopTests: 389 of 391 pass. The two failures, BolusEntryViewModelTests.testCarbEntryDateAndAbsorptionTimeString and …String2, fail the same way on plain next-dev on the same simulator ("12:00 PM" against "12:00").

Known limitations

  • Upgrading from dev: loop runs that dev stored before the upgrade didn't come back from DosingDecisionStore.executeDosingDecisionQuery, which drops records it can't decode. So "Loop active" on Trends starts at the upgrade. I haven't confirmed which field fails to decode.
  • Negative predictions: with a large IOB the prediction can go below zero, and "Eventually" shows that value. The classic screen does the same, as neither LoopAlgorithm nor dev's LoopMath sets a floor.
  • Runtime coverage: the medium Home Screen widget was verified on the iOS 27.0 simulator in the 31 October comprehensive review below. Other widget sizes and Lock Screen/expanded Live Activity layouts were not re-exercised in that review. A real iPhone install was checked in the 28 October rebase update.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QHnVPACb9uRhdQPgF5h2mj

Review update — 28 October 2026

A comprehensive code-reviewer-cx review of the full PR found four issues, fixed in 42b476ad:

  • Historical IOB now applies the preset multipliers to the stored basal history before calculating net active insulin.
  • Day and Logbook history use stored basal profiles and dated presets, avoiding repetition of recent presets across older days.
  • Weekly insulin bars use the same eligible-day rule as the summary, excluding missing days and a partial current day when complete days exist in that week.
  • The status error alert reads the manager's last completion time, so the hidden classic HUD cannot cause an older device error to mask the current error.

Both added regression tests failed on the original code and pass after the fixes. The two affected suites pass all 33 tests. Simulator app and test builds succeed.

With an isolated XCTest runner using the compiled production and test objects, 409 of 411 executed tests pass. The other two are the existing BolusEntryViewModelTests.testCarbEntryDateAndAbsorptionTimeString / …String2 locale-format failures, reproduced on the original PR head. Four DeviceDataManagerTests remain unverified because the isolated runner lacks HealthKit's registered app-host context. The normal Xcode-hosted run remains blocked during test-host launch; this is not a passing full hosted-suite result.

The loop simulator was driven through preset activation, carb entry and saving, Logbook editing, CSV sharing, day navigation and Settings presentation. After the fixes, Now, the day pages, Logbook, Settings and the 90-day Trends view were checked again. Widget and Live Activity changes were reviewed and built; their runtime rendering remains unverified in this review.

Parity update — 28 October 2026

Checked the new interface screen by screen against next-dev's own screens and totals, on the loop simulator (Xcode 27.1, iOS 27.0, Pump and CGM Simulators, 90 days of generated history). Fixed in 41e9535a:

Parity

  • IOB tile opens next-dev's Insulin Delivery Log, which has dose details and manual dose entry. The classic IOB chart uses the same showInsulinDelivery, so the two can't drift apart.
  • Loop pill opens next-dev's LoopStatusModalView. A newer device or loop error still opens the error first. With no classic HUD to keep the modal's "ago" current, it showed "Automation was unsuccessful" while Loop was running; the value is now set before the modal is presented.
  • Settings hides next-dev's Statistics row in the new interface, because Trends covers glucose metrics, time in range, AGP and daily insulin. Current Setup stays, as it has no counterpart.
  • Day page hides the tab bar when pushed from Now, as next-dev's Insulin, Carbohydrates and Predicted Glucose screens do.

Accuracy

  • Basal now follows next-dev's InsulinDeliveryHistory rule. From the first dose record, time without a basal record counts at the pump's programmed rate, rather than as no insulin. On the same data, Trends and next-dev's Statistics now agree on basal (25.6 and 25.3 U/day). The remaining gap in total insulin comes from the period, not the maths:
    • Trends' 7 d runs to today and averages the complete days ("6 of 7 days").
    • Statistics uses the seven complete days before today.
  • Totals match across screens: today's insulin is the same on Now, the Day page, Logbook and the Insulin screen (28.8 U, rising as the day went on).
  • Pump alarms in Logbook show the pump's own alarm text, or "Pump alarm", instead of the event's debug title. The simulator's events were showing a UUID.

Performance (debug build, simulator)

  • Trends loaded in 6.6–16.4 s, almost all of it in loopRuns(), which paged and fully decoded every stored dosing decision. It now decodes only date, reason and errors for the local window through DosingDecisionStore.getDosingDecisions, and loads in 0.5–1.8 s.
  • Logbook took 12.3 s, because basalSegments scanned every record for every cut. A binary search bounded by the running furthest end brings it to 1.7 s. The 90-day view spends about 9 s reading Apple Health for the 83 older days.

Noticed, outside the redesign (not changed here)

  • next-dev's simulated history writes pump events without reconciling them, so getNormalizedDoseEntries ignores them, and Trends, Statistics and the Insulin screen show no generated insulin. The generator also adds a bolus about every 20 minutes, although its comment says eight a day. For these screenshots I adjusted it locally (not committed).
  • Its alert-history step never returns, so the "Generating" spinner stays up after the rest of the data is saved.
  • next-dev's Carbohydrates screen showed "40 g absorbed" for a 10 g entry.

The "Alarms are turned OFF" banner in the screenshots is next-dev's warning: the simulator can't grant AlarmKit or Critical Alerts.

Screenshots (iOS 27.0 simulator)

Now Now, Today so far Loop pill: LoopStatusModalView IOB: Insulin Delivery Log

COB: Carbohydrates Next 3 hours: Predicted Glucose Day page Presets

Trends, 14 days Trends, daily pattern and insulin Trends, insulin and Loop status Trends, 90 days

Logbook Logbook, edit or delete carbs Settings: New Interface, no Statistics row Appearance and App Icon

Now, dark Trends, dark Logbook, dark Insulin, dark

Tests: LoopHistoryMathTests gains a regression for the programmed-rate rule. As noted above, the Xcode-hosted LoopTests run still hangs at test-host launch on this machine, so the new test has been compiled but not run there.

Basal and bolus split — 29 October 2026

Trends, the Day page and the Logbook now show basal and bolus explicitly, with the total, instead of one insulin number.

  • Trends has a "Basal and bolus" card above Insulin per day. It shows a ratio bar, basal and bolus in units and percent, manual and automatic bolus, and the total.
    • For a period it's the average per day, over the same days as the Insulin per day figure. For a single day it's that day, and today is "so far".
    • The Insulin per day legend now only names its marks, because the figures are in the card above it.
  • Day page: the same card replaces the Bolus and Basal tiles.
  • Logbook: each day header has a hairline ratio bar and "Basal x · Bolus y · z U" under the date.
  • Colour: bolus gets its own token, a deeper orange than basal's pale fill. Automatic bolus is the same colour hatched, on the timeline, daily bars and legends; in the hairline it is a tint. We looked at Glooko (purple: basal #6C4E87, bolus #BD9CBB) and Tidepool (blue: basal #19A0D7, bolus #7CD0F0, automated #00B2C3). Both keep insulin in one colour family, tell basal from bolus by lightness, and reserve red for warnings. Loop does the same in its own orange: blue is Loop's glucose colour, and the IOB, classic screens, watch and widgets all stay orange.
  • Unclassified doses: a dose in Apple Health without MetadataKeyAutomaticallyIssued is no longer counted as manual. While any bolus in range is unclassified, the manual and automatic detail is hidden rather than guessed.
  • CSV export adds manual_bolus_u and auto_bolus_u columns, which are blank for a day with an unclassified bolus.
  • Translations: the 15 new strings are translated into the redesign's 23 languages. Native-speaker review is welcome.

Tests

  • New: testInsulinSplitSeparatesManualAutomaticAndUnclassifiedBolus. Changed: the CSV test now checks the ten columns, including a day with an unclassified bolus.
  • The hosted run still hangs at launch on this machine, so the tests were run with the simulator's xctest agent on the built test bundle.
    • LoopHistoryMathTests: 22 tests, 0 failures.
    • All classes except DeviceDataManagerTests, which needs a HealthKit host: 413 tests, with only the 2 known BolusEntryViewModelTests 12-hour-clock failures. These also fail without this change.

Checked on the simulator: Trends for one day (today), 14 days and 90 days, the weekly bars, the Logbook, and the Day page, in light and dark and at Accessibility Extra Large text. The card, the Summary tile and the Logbook totals agree.

Trends, today Trends, 14 days Logbook Day page

Trends, today, dark Trends, 14 days, dark Logbook, dark Day page, dark

Basal and bolus contrast — 30 October 2026

In use, basal and bolus were still too alike. The part of basal you notice is its edge line, and it was nearly the same orange as bolus: 1.04:1 in dark mode and 1.43:1 in light. The pale basal fill barely shows against the card (1.34:1).

  • Colours. Everything stays orange.
    • Basal is paler: #FCE3C2 light, #43301B dark.
    • Basal's edge is a quiet tan: #E3B983 light, #6E5232 dark.
    • Bolus is deeper in light mode (#A9480A, ink #8A3A06). Dark mode bolus is unchanged.
    • The edge and bolus are now at least 3:1 apart (3.2:1 light, 3.6:1 dark), and the fill and bolus are 4.7:1 and 6.3:1.
  • Gaps. Bolus parts now get a thin card-coloured gap wherever they touch basal or each other: in the Insulin per day bars, the ratio bar and the day timeline. This follows WCAG 1.4.11 and technique G209 for touching chart parts.
  • Options considered. Violet basal with orange bolus, and the reverse. Both were clearer still, but we kept Loop's insulin orange.
  • Tests. No logic changed. Checked on the simulator on Trends (14 days and Day), the Logbook and Now, in light and dark.

Basal and bolus card Insulin per day Logbook Today so far

Basal and bolus card, dark Insulin per day, dark Logbook, dark Today so far, dark

Add carbs, Trends Day and Logbook insulin kinds — 31 October 2026

Add carbs is redesigned. CarbEntryScreen drives the existing CarbEntryViewModel unchanged, so validation, warnings, favourite foods and the hand-off to the bolus screen all behave as before.

  • The amount is the main element on the screen.
  • "How fast it absorbs" offers Fast, Medium, Slow and Other. Each card draws Loop's own absorption curve (the piecewise-linear model in CarbMath) on a shared time axis, so a slow meal draws as a long, low ramp. Other shows "Custom" until it is chosen, then offers a food type and an absorption time.
  • "Eaten" has quick choices (Now, 15m, 30m, 1h ago) and a time picker.
  • Continue stays pinned at the bottom of the screen.
  • Scrolling or tapping Done puts the keyboard away. At accessibility text sizes the cards switch to a 2 × 2 grid.
  • The classic interface still uses CarbEntryView.

Trends → Day now shows the same timeline card as Now's "Today so far", instead of an AGP with one day of data. The tiles use whole counts and say "Insulin", "Carbs" and "Boluses". Carb entries close together share one label showing their total, so labels no longer print over each other.

Logbook:

  • Manual bolus, automatic bolus and basal are drawn the way the charts draw them: solid, hatched and pale basal.
  • Runs of automatic boluses fold into one row, which opens a list of them.
  • At accessibility sizes, the amount moves under the title and the day totals move under the date.

Strings: new strings are added in all 23 languages. These translations are mine and haven't been reviewed by native speakers.

Testing:

  • On the iOS 27.0 simulator with the Pump and CGM Simulators: entered 60 g with Other (🍝, 6 h, eaten 30 minutes ago), checked the hand-off to Meal Bolus and delivered 5.5 U. Then edited that entry in the Logbook to Medium and saved it without a bolus. Checked Trends → Day, the Logbook and the folded run list in light, dark and AX XXXL.
  • LoopTests: 429 tests, 2 failures. Both are the existing BolusEntryViewModelTests.testCarbEntryDateAndAbsorptionTimeString{,2} assertions that expect a 12-hour clock (they fail the same way without this PR).
  • New tests cover DayHistory.clipped(to:) and the folding of automatic boluses.
  • Installed 3fa82de3 on a physical iPhone 18 Pro (iOS 27.2) running Omnipod 5 and Dexcom G7 in closed loop. Loop relaunched in closed loop, and Now, Trends and the Logbook load with real pump history. Nothing was delivered or changed during this check.

A simulator issue found along the way, not caused by this PR: the Pump Simulator can lose a bolus if Loop is killed before the bolus is finalised. MockKit/UnfinalizedDose.rawValue stores decisionId as a UUID, which isn't a property-list type, so saving PumpManagerState fails ("Error saving PumpManagerState") whenever a dose with a decision ID is in progress. After a restart, the pump state has no bolus, and the next sync's replacePendingEvents purges the mutable event. Storing decisionId.uuidString would fix it. This only affects the simulator pump.

Add carbs Other food type and absorption time Hand-off to Meal Bolus Editing a carb entry

Trends, one day Trends day timeline Logbook with manual, automatic and basal Folded automatic boluses

Add carbs, dark Trends day, dark Logbook, dark Add carbs at AX XXXL Logbook at AX XXXL

Comprehensive review — 31 October 2026

Reviewed the complete PR against next-dev e0eb9856 using code-reviewer-cx. Fixes are in 088e6bbd.

  • Favourite meals: choosing an absorption speed after a favourite no longer clears the amount or resets the absorption time. Favourite selection now applies synchronously on the main actor.
  • Historical targets: day charts read stored target profiles and dated presets, including their actual end times. They no longer repeat today's profile or omit ended presets.
  • Midnight boluses: the initial history query retains instantaneous boluses at the start of the day.
  • Carb entry: the exact picker includes the date; a prefilled older time does not select Now. The amount field has an explicit accessibility label. The favourite editor receives the selected preset emoji.
  • Accessibility: the date/time picker stacks beneath its label at accessibility text sizes.

Simulator verification: Xcode 27.1, iOS 27.0, Device Hub loop, Pump and CGM Simulators. Built from an isolated PR checkout and clean LoopKit checkout, excluding the unrelated local Glooko integration changes.

Exercised favourite selection and speed changes, Meal Bolus hand-off, saving and editing carbs through Logbook, every Logbook filter, folded automatic-dose details, day navigation, all Trends periods through 90 days, previous/next periods, CSV/PDF sharing, classic/redesigned interface switching, dark mode, AX XXXL and the maximum-meal warning. Checked the final accessibility layout after rebuilding. The medium Home Screen widget now has runtime verification, including glucose, COB, forecast and its four actions. Lock Screen and expanded Live Activity layouts were not re-exercised in this review.

Tests: the two bug regressions fail with the pre-fix scheduling and target-selection behaviour restored; all three new tests pass with the fixes. The isolated XCTest run executes 432 tests: 430 pass, two fail. Both failures are the existing BolusEntryViewModelTests.testCarbEntryDateAndAbsorptionTimeString{,2} 12/24-hour formatting assertions, also reproduced with the pre-fix behaviour.

Coverage limit: the normal Xcode-hosted run stalled at launch and was stopped after 150 seconds without a completed test result. Four DeviceDataManagerTests remain unverified: HealthKit reports a nil client bundle identifier, including in a separately registered disposable test app. This is not a clean full hosted-suite pass.

Changing a favourite to Fast retains 35 g Trends Day The four doses inside a folded automatic-bolus row Medium Home Screen widget running in the simulator Date and time at AX XXXL in dark mode

@Nutcasey
Nutcasey force-pushed the loop-ui-redesign-next-dev branch from 056eab5 to 54c9d85 Compare October 28, 2026 19:37
@Nutcasey

Copy link
Copy Markdown
Author

Rebased onto the current next-dev (3c4f8b0e, 11 commits newer than the previous base 1640e148). The rebase was clean, with no code changes to the redesign.

Checked after the rebase:

  • Simulator (Xcode 27.1, iOS 27.0, Pump and CGM Simulators): Now, Trends and Logbook load, with a single tab bar.
  • Installed on a real iPhone 18 Pro (iOS 27.2) with the Pump Simulator and a Dexcom G7 read through the Dexcom app.

LoopTests weren't run for this update. On this build machine the test host hangs before XCTest connects, with both ad-hoc and automatic simulator signing, so that's still to do.

This replaces #2615, which targeted dev and is now closed.

🤖 Generated with Claude Code

@Nutcasey

Copy link
Copy Markdown
Author

Reviewed the full 79-file diff at 54c9d857 against next-dev 3c4f8b0e using code-reviewer-cx, including the surrounding stores and controllers. Four high-confidence findings are fixed in 42b476ad.

Critical (confidence 90–100)

  • Historical IOB ignored preset-adjusted basal — confidence 95/100. Delivery matching a preset's basal could appear as positive or negative net IOB. The history calculation now applies the dated preset multipliers before annotating doses, as Loop's main calculation does.

Important (confidence 80–89)

  • Historical schedules could repeat recent presets on older days — confidence 89/100. Day and Logbook history expanded a repeating daily schedule based on the current profile. It now reads stored profiles and applies each preset over its actual dates.
  • Weekly insulin averages disagreed with the summary — confidence 89/100. Weekly aggregation included missing days and today's partial total. For two complete 30 U days, one missing day and a partial 3 U day, the bar showed 15.75 U/day. It now shows 30 U/day and retains the existing fallback when only a partial day is available.
  • An older device error could mask the current error — confidence 86/100. Error selection read the completion time from the classic HUD, which the redesign does not instantiate. It now reads loopManager.lastLoopCompleted directly.

Verification

  • Both added regression cases fail before the fixes and pass afterwards; all 33 tests in the two affected suites pass.
  • Simulator app and test builds succeed. The simulator was driven through presets, carb entry/saving, Logbook editing, CSV sharing and navigation. After rebuilding, Now, day navigation, Logbook, Settings and 90-day Trends were checked again.
  • The isolated XCTest run completed 411 tests: 409 passed, two existing locale-format failures. Both failures were reproduced using the original PR code. Four HealthKit-dependent DeviceDataManagerTests remain unverified: the normal hosted run fails during launch, and the isolated runner lacks HealthKit's registered app-host context. This is not a clean full hosted-suite pass.
  • Both string catalogues retain all pre-existing keys and translations. Added format placeholders and asset JSON validate. Widget/Live Activity code was reviewed and built; runtime rendering was not exercised during this review.

The PR description now records the fixes, current validation and limitations, and corrects the stale statement that #2615 is still open. No further findings met the skill's confidence threshold in this pass.

Nutcasey added a commit to Nutcasey/Loop-1 that referenced this pull request Oct 29, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVP5sTMyrVG4zz5Wp4KM
Nutcasey and others added 10 commits October 30, 2026 18:26
…dling

Build Now, Trends and Logbook around Loop's existing controllers and stores.
Keep delivered quantities and history partitions accurate, preserve editing
drafts across updates, propagate load failures and share chart averages.
Per-day Trends figures leave out the day in progress and share insulin's
days, the day chart names a day without readings, and the Logbook carb
editor receives Loop updates on the main thread.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Driving the error states in the simulator (pump fault, comms issue,
suspension, sensor warmup, signal loss, sensor failed) showed that a
device problem could be off-screen: the status pills scrolled sideways,
so during warmup the CGM pill was past the edge and nothing visible said
so.

- Status pills wrap instead of scrolling.
- A device highlight, or a loop that hasn't completed for 15 minutes,
  gets a notice under the pills with the device's message and icon, what
  it means for Loop, and a tap through to the device; most serious first.
- Two-line plugin highlights ("Signal\nLoss") are joined.
- When Loop pauses closed loop (no CGM session or data), the pill says
  "Closed loop paused" instead of "Open loop".
- Loop's status rows (No Recent Glucose, Tap to Resume, bolus progress,
  presets) take the card shape in the new interface; the non-critical
  pump notice is left out while the Resume row shows.

LoopTests: 323 pass on the iPhone 17 Pro simulator (iOS 26.3).
On next-dev, Loop's status rows are already drawn as rounded cards (LOOP-5437), so the
new interface keeps them as they are rather than adding a second card behind them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVPACb9uRhdQPgF5h2mj
Settings gets an "Appearance and App Icon" screen:

- Appearance: System, Day or Night. It sets the window's interface
  style, so it applies to the whole app (new and classic interface,
  sheets included) and takes effect without a relaunch.
- App Icon: Classic (the primary icon, unchanged), Day, Night, and Day
  and Night, whose dark appearance variant follows the home screen's
  light or dark icons. Day, Night and Day and Night are alternate icon
  sets in DerivedAssetsBase, listed in the Loop target's
  ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES; small preview image
  sets show them in the picker because iOS doesn't expose alternate
  icons as images.

The new icons keep Loop's ring and add the blue reading dot from the
Now screen, on a light ground for Day and a midnight-blue one for
Night. The artwork was generated with GPT image generation and chosen
from two takes each.

iOS can report an error from its "icon changed" alert even though the
icon changed (the iOS 26.3 simulator does this every time), so the
picker trusts the icon iOS reports afterwards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVPAQn8LyVAJrZEA3f2T
Every string the new interface shows or speaks now goes through
NSLocalizedString with a translator comment, and Localizable.xcstrings
gains 213 keys with translations in all 23 of Loop's other languages
(ar cs da de es fi fr he hu it ja nb nl pl pt-BR ro ru sk sv tr uk vi
zh-Hans). Fourteen existing keys the redesign reuses ("Bolus", "%1$@ U",
"Settings", ...) keep their translations and gain the languages they
were missing.

- Sentences with values are whole keys with positional placeholders,
  and counts ("%lld days", "%lld shorter gaps", "Loop · %lld changes")
  use plural variations, so no text is glued together in code.
- Numbers, dates, ranges and durations follow the user's locale:
  LoopFormat.number replaces String(format:), date ranges use
  DateIntervalFormatter and durations DateComponentsFormatter.
- The Trends one-day period has its own key, "Day (Trends period)",
  because "Day" is also the light appearance and icon name and some
  languages need different words for the two.
- The Now action buttons wrap to two lines and tile labels scale down,
  so longer languages don't truncate.
- The CSV export stays English with '.' decimals on purpose; it is
  data for spreadsheets, not display text.

Translations follow Loop's existing terms per language and were checked
for placeholder and plural-category integrity. Each language was run
through the simulator with NSShowNonLocalizedStrings. They have not
been reviewed by native speakers.
On next-dev the catalogue merge keeps next-dev's own entries and comments and adds the
redesign's keys and translations. The pre-meal menu strings are left out because next-dev
opens pre-meal from the Presets screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVPACb9uRhdQPgF5h2mj
…esign

The home screen widget and the Live Activity now follow the redesign's
rules from the Now screen: a white SF Pro Rounded glucose number with a
blue trend arrow, low and high said in words rather than by tinting the
number, Loop's state as a dot with words, sunken tiles, and device
problems written out.

- Widget: the Now hero with COB and Forecast tiles; the medium size adds
  Add carbs, Bolus, Pre-meal and Presets in the Now screen's styles.
- Lock Screen: hero, the Now chart's style (blue readings, dashed
  prediction, target band) and the configured bottom row as one-line
  tiles, within the 160 pt limit.
- Dynamic Island: white number and blue arrow, the change or "Low"/"High"
  in words with a loop dot, a keyline tinted for low and high, and an
  expanded view with a full-width status line and the chart.

They follow the New Interface setting: the app mirrors it into the app
group, reloads widget timelines and redraws the Live Activity when it
changes, so classic users keep the classic widget and Live Activity.
LoopDesign.swift and the redesign's colour sets are shared with the
widget target. The widget entry gains the CGM highlight and COB from the
existing extension context.

The widget catalogue gains the redesign's strings in all 24 languages,
reusing Loop's translations, plus a short "Forecast" tile label. The
Arabic "in 5 min" now uses an Arabic-Indic digit in both catalogues.
On next-dev the widget buttons use next-dev's shared Deeplink type, which replaced
SystemActionLink, and glucose units are LoopUnit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVPACb9uRhdQPgF5h2mj
Parity with next-dev:
- The IOB tile opens next-dev's Insulin Delivery Log (dose details, manual
  dose entry), shared with the classic IOB chart through showInsulinDelivery.
- The Loop pill opens next-dev's LoopStatusModalView. Device and loop errors
  still open the last error first. The new interface has no HUD to keep the
  modal's "ago" current, so it is set before presenting; without it the modal
  reported "Automation was unsuccessful" while Loop was running.
- Settings hides next-dev's Statistics row in the new interface, which the
  Trends tab covers. Current Setup stays.
- The Day page hides the tab bar when pushed from Now, like the Insulin,
  Carbohydrates and Predicted Glucose screens.

Accuracy:
- Basal totals follow next-dev's InsulinDeliveryHistory: from the first dose
  record, time without a basal record counts at the pump's programmed rate
  instead of as no insulin. Trends now agrees with next-dev's Statistics on
  basal (25.6 vs 25.3 U/day on the same data). Remaining differences are the
  period: Trends' 7 d includes today and averages complete days.
- Pump alarms in the Logbook show the pump's own alarm text, or "Pump alarm",
  rather than the event's debug title.

Performance:
- Loop runs decode only date, reason and errors for the local window via
  DosingDecisionStore.getDosingDecisions, instead of paging every stored
  decision: Trends loads in 0.5-1.8 s instead of 6.6-16.4 s.
- basalSegments finds the covering record by binary search bounded by the
  running furthest end, instead of a linear scan per cut: a week of
  five-minute records builds in 0.4 s instead of 10.8 s.
- The start of the local insulin records is cached for five minutes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVP5sTMyrVG4zz5Wp4KM
- Trends has a "Basal and bolus" card above Insulin per day. It shows a
  ratio bar, basal and bolus in units and percent, the manual and automatic
  bolus, and the total. For a period it's the average per day, over the
  same days as the Insulin per day figure; for today it's "so far".
- The day page shows the same card in place of the bolus and basal tiles.
- Each Logbook day header has a hairline ratio bar and
  "Basal x · Bolus y · z U" under the date.
- Bolus gets its own colour, a deeper orange than basal's pale fill, so the
  two read apart by lightness as in Glooko and Tidepool, with no new hue.
  Automatic bolus is the same colour hatched (tinted in hairline bars), on
  the timeline, the daily bars and the legends.
- A dose in Apple Health that doesn't say whether it was automatic is no
  longer counted as manual. While any bolus in range is unclassified, the
  manual and automatic detail is hidden rather than guessed.
- The CSV export adds manual_bolus_u and auto_bolus_u columns, which are
  blank for days with an unclassified bolus.
- The new strings are translated into the redesign's 23 languages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVP5sTMyrVG4zz5Wp4KM
Pushed inside the Logbook tab, the carb editor and its bolus screen kept
the floating tab bar over their bottom button (Save Carbs & Deliver /
Save without Bolusing). The editor now opens as a sheet with Cancel,
the way Add carbs does on Now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVP5sTMyrVG4zz5Wp4KM
@Nutcasey
Nutcasey force-pushed the loop-ui-redesign-next-dev branch from 415926d to 1b6efc9 Compare October 30, 2026 19:25
@Nutcasey

Copy link
Copy Markdown
Author

Rebased onto the current next-dev (e0eb9856), adding #2625, #2626, #2628 and #2629. The PR now merges cleanly.

Conflicts

Fix found in testing (1b6efc9a)

  • When a carb entry was edited from Logbook, the floating tab bar covered the bottom button of the carb and meal-bolus screens. Logbook now opens the editor as a sheet with Cancel, the way Add carbs works on Now. Hiding the tab bar with SwiftUI's .toolbar(.hidden, for: .tabBar) was tried first and had no effect on this UIKit tab bar.

Checked on the loop simulator (Xcode 27.1, iOS 27.0, Pump and CGM Simulators):

  • Now: 30 g of carbs → 3.7 U recommended → delivered. IOB went from −0.72 to 3.58 U and COB from 0 to 30 g.
  • Now: the IOB tile and the loop, pump, CGM and Presets pills each open their screen. Walking was started (the Presets button shows "Walking", target 8.3–9.4) and ended.
  • Logbook: the carb entry was edited from 30 to 35 g with a 0.6 U top-up, then the editor was opened and cancelled, then the entry was deleted. Day totals updated each time (55 → 60 → 25 g).
  • Trends: Day, 7, 14, 30 and 90 days, the previous day, and a PDF export ("Loop trends 29 Oct 2026", 48 KB).
  • The Day page from "Today so far" hides the tab bar. Its 34.0 U (20.3 basal, 13.8 bolus) matches the Logbook and Trends.
  • Deep links from Trends and Logbook (carb entry, manual bolus, custom presets) switch to Now and open the right screen.
  • New Interface off and on. The classic screen and the new interface agree: IOB 3.94 U, "Eventually" −4.4 mmol/L.

LoopTests were not run on this simulator. The hosted test runner hangs before connecting ("The test runner hung before establishing connection"). It hangs the same way on plain next-dev e0eb9856, so the cause is the iOS 27.0 simulator environment, not this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QHnVP5sTMyrVG4zz5Wp4KM

Nutcasey and others added 2 commits October 30, 2026 20:38
Basal's edge was the same orange as bolus (1.04:1 in dark mode), and the
pale basal fill barely showed against the card, so the two read as one.
Basal is now paler with a quiet tan edge, bolus is deeper in light mode,
and the edge and bolus are at least 3:1 apart in both modes. Bolus marks
also get a thin card-coloured gap wherever they touch basal or each other:
in the daily bars, the split bar and the day timeline.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVP8UwbiZYLRjsYUYXhE
…kinds apart in the Logbook

Add carbs
- New CarbEntryScreen on top of the unchanged CarbEntryViewModel: a large
  amount, "How fast it absorbs" cards that draw Loop's own absorption curve
  (Fast, Medium, Slow, Other), "Eaten" quick times plus a time picker, and a
  pinned Continue that hands over to the existing bolus screen.
- Other shows "Custom" and no curve until it is chosen.
- Any scroll or the Done button puts the keyboard away, so the form stays
  reachable at accessibility text sizes; the cards and times go 2 x 2 there.
- Used from the Now screen and for editing carbs in the Logbook when the
  redesign is on; the classic interface keeps CarbEntryView.

Trends, one day
- Shows the same timeline card as Now's "Today so far" (shared as
  DayTimelineCard) instead of the AGP and the single Insulin per day bar.
- Tiles read Insulin, Carbs and Boluses with whole counts, and "3 entries"
  is a proper plural.
- Carb markers closer than the label spacing share one label with their
  total, placed after the last marker, so labels no longer overprint.

Logbook
- Manual bolus, automatic bolus and basal use the charts' encoding: solid,
  hatched and pale basal tiles; the day key and hairline bar match.
- Two or more automatic boluses in a row fold into one "Automatic boluses"
  row that opens the list.
- At accessibility sizes the amount sits under the title and the day
  totals move under the date.

Strings added in all 23 languages (unreviewed translations).
Tests: DayHistory.clipped(to:) and the automatic-bolus folding.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVP8UWK6Li65s9wma3FF
Nutcasey added a commit to Nutcasey/Loop-1 that referenced this pull request Oct 31, 2026
…s for PR LoopKit#2621

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHnVP8UWK6Li65s9wma3FF
@Nutcasey

Copy link
Copy Markdown
Author

Reviewed the complete PR #2621 diff (85 changed files) against next-dev e0eb9856, starting at 3fa82de3, using code-reviewer-cx. All findings below are fixed in 088e6bbd.

Critical

  • 96/100 confidence — Loop/View Models/CarbEntryViewModel.swift:265: Selecting a speed after a favourite queues a reset that clears the carbohydrate amount and overwrites the absorption time. Fixed: Apply favourite selection synchronously on the main actor, before the replacement meal is entered.
  • 94/100 confidence — Loop/Redesign/LoopHistoryProvider.swift:479: Historical day charts repeat the current target profile and omit presets that have ended. Fixed: Read stored target history and apply dated presets, respecting their actual end times.

Important

  • 89/100 confidence — Loop/Redesign/LoopHistoryProvider.swift:305: The cold-cache dose filter drops an instantaneous bolus at the interval start because its end equals its start. Fixed: Use the bolus start date for the half-open window, retaining overlap checks for basal delivery.
  • 88/100 confidence — Loop/Redesign/CarbEntryScreen.swift:254: The time-only picker cannot select the previous date across midnight; prefilled historical entries can also have Now selected. Fixed: Restore the date component, honour the prefilled time, and stack the picker at accessibility text sizes.
  • 85/100 confidence — Loop/Redesign/CarbEntryScreen.swift:157: The new amount field has no accessible label in the simulator hierarchy. Fixed: Restore the Amount Consumed label and the existing carbohydrate amount accessibility identifier.

The favourite editor also receives the effective food emoji. The exact date/time controls stack at accessibility sizes; the final AX XXXL capture confirms the label stays readable. Final diff review found no further issues at the skill's ≥80 confidence threshold.

Verification

  • Simulator app and test builds succeed; bundle signature verification and git diff --check pass.
  • The favourite reset was reproduced through the original UI: 35 g became blank and Continue was disabled. The fixed UI retains 35 g and hands the chosen absorption time to Meal Bolus.
  • Both bug regressions fail with the old scheduling/target-selection behaviour restored. All three added tests pass after the fixes.
  • 432 tests executed in the isolated XCTest agent: 430 passed, two existing locale-format assertions failed. The same two failures reproduce with the pre-fix behaviour.
  • Four DeviceDataManagerTests remain unverified. HealthKit rejects the isolated agent's client identity; installing and launching a separately registered disposable app did not resolve it.
  • The normal Xcode-hosted attempt stalled at launch and was stopped after 150 seconds. No completed hosted-suite result was obtained.
  • Hands-on checks covered favourite/speed changes, carb entry and Logbook editing, all filters, folded-dose totals, day navigation, Day/7/14/30/90-day Trends periods, previous/next periods, CSV/PDF sharing, interface switching, dark mode, AX XXXL and the maximum-meal warning.
  • The medium Home Screen widget rendered with glucose, COB, forecast and all four actions. Lock Screen and expanded Live Activity layouts were not re-exercised.

The PR description contains five simulator screenshots and the same coverage limits. The review build uses the PR sources and clean LoopKit dependencies, without the unrelated local Glooko host edits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant