Claude/cleanup standalone app hvijkh - #4
Open
Yloft17 wants to merge 895 commits into
Open
Conversation
…ge, last gate fired)
…out in Test Centre
…ort (pure night accountant)
…null sink = byte-identical)
…g + readout source
…ate-name and label parity
…HRSample/GravitySample types
…e trace into live path
…ut resolver + readout panel
…ft pretty-print
Swift JSONEncoder .prettyPrinted emits "key" : value (space-colon-space); the
Kotlin emit() used "key": value. Switch the Kotlin object separator to " : "
so the two platforms' meta.json bytes line up, as asserted by the parity test.
Empty-object {} and array branches are unaffected.
…y pass The Android Sleep mode emitted no trace: IntelligenceEngine called AnalyticsEngine.analyzeDay with no traceSink, so detectSleep's gate trace and the Rest sub-score line never reached the bundle. Mirror the iOS wiring: thread an optional sleepTraceSink through analyzeRecent/analyzeRecentOnCpu (Context-free, default null = byte-identical untraced path) and pass it into analyzeDay. The two Context-aware callers (AppViewModel 15-min loop, WhoopBleClient post-backfill pass) build the sink ONLY when TestCentre.active(SLEEP), routing each line to the .sleep-tagged strap log via externalLog/log. Zero-cost when off.
…ding line emitBatteryTrace (iOS) and estimateTrace (Android) had no caller outside tests, so the discharge-run / fitted-slope / gate trace never reached the bundle; only the per-reading soc line emitted. Add a gated caller on each platform, on a natural throttle, never a tight loop. iOS: call emitBatteryTrace from bankBatterySample, right after the per-reading bank line, under the same Battery-mode gate (the strap banks at most one SoC point per ~8 min). Android: emit estimateTrace(...).second through the .battery-tagged externalLog inside the TodayScreen battery LaunchedEffect (the Today-badge refresh that already loads samples + rated, so no extra Room read). Both pure: estimateTrace returns the same Estimate the badge shows, so no displayed number changes. Zero-cost when the mode is off.
The Test Centre gated the experimental 5/MG block on the pref alone (selectedModelName == WHOOP5_MG.name), but the Settings screen it re-hosts uses pref OR live.whoop5Detected. Add the || live.whoop5Detected clause (collect the live snapshot) so a live-detected 5/MG whose pref is not yet written still sees the block, matching SettingsScreen.kt showFiveMGControls.
previewText showed only report.txt, but the assembled bundle also carries meta.json and (Android) last-crash.txt, so the user confirmed a share without seeing those. Concatenate every text-decodable entry, each under a === <name> === header, in bundle order, on both platforms. The bounded raw-capture stream is excluded (binary, already PII-scrubbed by the assembler, not a report surface). report.txt body still renders, so the existing contains() preview tests stay green.
…t Centre The iOS Test Centre Advanced section shows the Continuous-HRV toggle; the Android twin omitted it though Android already stores the key. Add it to the AdvancedCard, bound to the SAME NoopPrefs key (noop.continuousHrv) and the same write path (vm.setContinuousHrv, which persists and re-applies keep-stream-for-data) the Settings card uses, so toggling from either screen is one and the same setting.
Append connection, workouts, display, import, steps, recovery and hrv to TestModeRegistry.all in screen priority order (after sleep; battery keeps its slot). All are plain toggles with the spec section-4 captures, one-line questionnaire and live-readout ids; Display carries includesScreenshot. Update the registry tests to pin all 9 entries: order/ids/count, per-profile captures, questionnaire id+kind, live-readout ids, priorities, toggle kind and the screenshot/5MG flags, plus an em-dash guard on every blurb and prompt.
Mirror the Swift registry entries byte-for-byte (same ids, titles, captures, questionnaire ids+kinds, live-readout ids, priorities, toggle kind). Android icons map to ic_* drawable ids; the dataImport entry uses TestDomain.IMPORT (wire id 'import'). Extend the parity test to all 9 entries with the matching order/ids/count, capture sets, questionnaire id+kind, readouts, priorities, toggle and screenshot/5MG assertions, plus the em-dash guard (escape literal).
…lytics) Add the two Phase-2 Test Centre engine traces, both pure and side-effect-free, both proven to leave the engine numerics byte-identical: - RecoveryScorer.recoveryTrace: the Charge term-breakdown. Rebuilds the four-plus-one weighted terms (hrv/rhr/resp/sleepPerf + skinTempDev) from the SAME inputs recovery() reads, emits each baseline state (mean/spread/nValid/status), each term z*weight, the nil terms that forced the renorm, the composite z, and the final logistic score + band. The returned score IS RecoveryScorer.recovery verbatim, so the trace cannot diverge. - HRVAnalyzer.analyzeTrace: the cleaning trace. Recomputes nInput/nClean/rejected fraction, the range + Malik ectopic counts, the minBeats gate and the spot rejected-fraction gate, with the result read from analyze() verbatim. - TestReadout: pure parsers for the last Charge breakdown / last HRV computation from the tagged log tail, for the iOS live-readout panels. Tests pin the lines a fixture produces and assert traced == untraced output, the nil term naming, the cold-start nil reason, and no em-dashes.
- IntelligenceEngine: read TestCentre.active(.recovery) once before the scoring loop; when on, emit each night's Charge term-breakdown via the new recoveryTraceLines helper (which feeds the SAME inputs recomputeRecovery does into RecoveryScorer.recoveryTrace), tagged .recovery. Zero work when off; the Charge score path is byte-identical. - HRVSnapshotView.finish: when TestCentre.active(.hrv), emit the cleaning trace tagged .hrv and take the result from analyzeTrace (which reuses analyze verbatim). When off, the plain analyze path is untouched. - TestCentreView: add RecoveryReadoutPanel (lastChargeBreakdown) and HrvReadoutPanel (lastHrvComputation) alongside the Sleep/Battery panels, same ReadoutRow tokens, no hardcoded colours.
Kotlin twins of the StrandAnalytics traces, byte-aligned with the Swift line shape: - RecoveryScorerTrace.recoveryTrace + HrvAnalyzerTrace.analyzeTrace: same term-breakdown and cleaning-trace output, each reusing RecoveryScorer.recovery / HrvAnalyzer.analyzeRaw verbatim for the returned value so the engine numerics stay byte-identical. - TestReadout: the lastChargeBreakdown / lastHrvComputation tagged-tail parsers. - IntelligenceEngine: add a recoveryTraceSink param (default null = byte-identical path); when non-null each scored night emits its Charge breakdown via recoveryTraceLines. - AppViewModel + WhoopBleClient: pass recoveryTraceSink only when TestCentre.active(RECOVERY), routing lines to the .recovery-tagged strap log (log()/externalLog PII-scrub). - HrvSnapshotScreen: when TestCentre.active(HRV), emit the cleaning trace tagged HRV and take the result from analyzeTrace; the plain analyzeRaw path is untouched when off. Tests are pure-JVM (no Robolectric/Mockito) and assert traced == untraced output.
…re pinned to sleep+battery)
…panel) The Connection & Sync test mode, wired through the proven Sleep/Battery/Recovery pattern: pure StrandAnalytics formatters/parsers, BLE/offload emitters gated zero-cost behind TestCentre.active(.connection), and an in-app readout panel. - ConnectionReadout.swift (StrandAnalytics): pure ConnectionTrace line formatters (the CLOCK-DRIFT summary = strap-reported [oldest, newest] vs wall clock with a FUTURE-DATE flag, the firmware-layout line, the trim=0xFFFFFFFF no-cursor sentinel) plus the ConnectionReadout tagged-tail parsers for connectionUptime / reconnectCount / lastOffloadResult. No clock, no IO, no PII. - BLEManager: emit connect timing + uptime marker (didConnect), bond-state transitions (both genuine-bond sites), reconnect churn count+reason (didDisconnect / didFailToConnect), the failed-encrypt / other-central hint (bond-write refusal), and the upfront clock-drift summary in the GET_DATA_RANGE handler. Every site reads the .connection gate BEFORE building any string; the connect/bond/offload behaviour is unchanged. - FrameRouter: one frame-timing line per genuine frame-TYPE transition (the existing change-guard throttles the raw flood), gated zero-cost. - Backfiller: a cheap connectionActive() gate + connectionLog sink (default inert) so the offload-progress / firmware-layout / no-cursor lines build only when the mode is on. - TestCentreView: ConnectionReadoutPanel alongside the others, same ReadoutRow tokens, no hardcoded colours. - Tests: pure ConnectionReadoutTests (formatters + parsers) and a StrandTests gating proof that a handled frame emits ZERO .connection lines with the mode off and exactly one with it on (FrameRouter), plus the type-transition throttle.
The whole-day HR view (Deep Timeline) still existed but was buried; make the live HR card a one-tap route into it with a visible 'Full day' affordance. And the new Today only showed strap battery %, never charging state, so add a bolt on the battery ring + '% . Charging' in the data-sources row, matching Mac/Android.
Two data-corruption fixes from pipiche38's real Gen 3 captures, reimplemented clean on both platforms. (1) The 0x6F SpO2 per-sample decoder was adding a scaled base/status nibble to each sample, producing impossible ~223% readings; the samples are direct percentages, so discard the base field. (2) unixSeconds() only guarded ms > 0, so a corrupt ring timestamp on a full history dump could bank a 1970 or far-future sample; gate the result to the same 2020-2035 plausible window used for anchoring. Thanks pipiche38.
…p (#983) An HC-only user (no strap) had no sleep at all: the importer collapsed each SleepSessionRecord to a per-day minute total and never wrote a SleepSession row, so the Sleep screen (which reads repo.sleepSessions) fell to its empty state. Now also build SleepSession rows (bounds + per-stage minutes -> stagesJSON, same shape as the CSV/Xiaomi importers) under WHOOP, only for days the strap does not already cover so a real night is never shadowed. Thanks Retsim.
resolveDayOwner issued a per-day LIMIT-1 HR presence probe per paired device, even on the common single-WHOOP install where the owner is a foregone conclusion. Short- circuit when there is exactly one live device that is the fallback id (byte-identical in that case), saving ~one tiny read per scanned day per analyzeRecent. Thanks MrL0 for the read-efficiency writeup.
The Deep Timeline was clamped to the shown day; older data was only reachable via the day-stepper. Widen the pan/zoom clamp to a rolling 3-day window (the shown day plus the two before it) so a left drag scrolls continuously back, deliberately bounded so one drag can't fling through weeks. The data reload keys on the visible window, so panned-to days load. Both platforms. Thanks ryanbr.
11 fixes on top of the Liquid Metal launch — carry-vitals + Rest honesty, the iOS widget refresh, the Haptic Clock gaps, the Android bond-loop bound, HC-only sleep sessions, the Oura SpO2/timestamp fixes, the full-day HR tap-through, charging on Today, the Deep Timeline 3-day scroll-back, and two perf trims. Version 8.0.1 (iOS build 168 / Android versionCode 257).
Live Sessions: a silence-first strap coach. A recovery-gated HR band (Charge scales a Karvonen %HRR ceiling), two-signal haptic vocabulary (2 light taps = push, 3 heavy = ease off), warm-up/climb grace, dwell + cool-down hysteresis, never-fabricate guards (stale stream = grey ring, coaching pauses). Pure engine + haptics + store in packages with Kotlin twins pinned by identical golden vectors; runner/UI on all 3 platforms behind a beta toggle (default on). Fixes: - #991 Android: HR + Last Workouts cards no longer overlay their own section headers (Box -> spaced Column at the call sites). - #992 iOS: Liquid Today battery row shows the ~X-days-left estimate again (verbatim TodayView.estimateText). - #1000: backups carry a settings.json entry (whitelisted profile + unit prefs, byte-compatible container, legacy zips unchanged); restore re-applies after a successful DB swap only. iOS folder picker starts at a sensible directory + alerts on a nil selection. - Live5RestFrozenTests: banked regression test for the #977 root cause. Verified: 1,382 Swift package tests + 2,258 Android JVM tests green; Release builds green for Strand (macOS), NOOPiOS, and Android.
- Android resolver + habitual-midsleep + Sleep screen join the union model: imported history no longer freezes Sleep tile / Vitality / Fitness age / Rhythm / coach on the import's last day (#1008 ryanbr, #1009, #1014-secondary; + tombstone-first delete parity) - #993: bare sleep aggregates (HC bedtime spans) no longer override a scored night; reconciled with the #547 imports-correct guarantee (bare loses only to a genuinely scored night) - CPS wheel event time 1/2048s + kind-flip baseline reset, both platforms (#1007 ryanbr) - Backup integrity: pre-swap quick_check, post-swap open-verify with auto-rollback, non-deleting Android DatabaseErrorHandler (#1014) - Backfiller: insert-then-archive order parity (#1006 ryanbr) + future-dated ranges stop auto-continue (#1015 ryanbr) - Observability: real Test Centre storage numbers + inactive-mode warning (#1002), cumulative drained-rows counter + clock-drift fix (#990), clock-latched readout + ~1970 RTC warning (#987), SpO2 research dump in Connection mode (#945 ryanbr) - Deep Timeline: sleep band + workout markers + iPhone touch scrub (#979 follow-ups) - Liquid tilt settles level when lying down (#1004); Today HR window selector on Android (#985 ryanbr); hydration card live-updates (#989) - Full German incl. Terms gate (#984 eltomato89); Oura activity/MET decode (#960 pipiche38); perf batch byte-identical w/ pins (#995 #996 #997 #999 ryanbr) - 'reopen Strand' codename leak fixed + codename guard in the artifact script Verified: 1,529 Swift package tests + 2,337 Android JVM tests green; Release builds green for Strand (macOS), NOOPiOS, and Android.
…d history WHOOP exports are onset-to-onset (cycle_start = the sleep onset evening), so keying imported daily metrics / series / stress off cycle_start filed each night under the day before. For an import-only user with no live strap, Today had no row under the current logical day and blanked every score except Stress once the day rolled. Key cycles off the wake (wake_onset, then cycle_end, then start), matching the live engine / mergeSleep wake-day convention. Both platforms; the Android sleep-fold already keyed wake-day so this reunites the two on one row. Also: the timestamp heal purged any computed/imported row below the 2023-11 strap floor, silently deleting multi-year WHOOP import history on any heal. Scope the far-past floor to computed (-noop) rows only; imports (bare my-whoop) keep their real dates. Future-dated rows still purged for all sources. Extract the keying rule into StrandImport.WhoopDayKeying for an executable pin. Tests: WhoopDayKeyingTests, importer-preservation heal test, real onset-to-onset Android regression; repinned fixtures that used the old same-day shortcut. Verified: 1529+ Swift tests, Android unit suite, macOS + iOS Release builds all green.
…crash-loop #1014 (wanxorg): 8.2.0's CorruptionPreservingOpenHelperFactory stopped Android silently deleting a corrupt database (which was wiping non-resendable strap history), but it copied the file to *.corrupt and LEFT the original in place, so the next open re-hit the same corruption and the app crash-looped on startup until a reinstall. onCorruption now MOVES the original aside (rename, or copy+ delete across a storage boundary) and drops the WAL/SHM sidecars, so the next open finds no file and Room rebuilds a clean store: the app always opens, and the corrupt copy is preserved for recovery. iOS unaffected (GRDB has no equivalent preserve-in-place handler; DatabaseIntegrity is a quick_check diagnostic only).
…ecord Replace the single blanket acknowledgment checkbox with FOUR separate, conspicuous attestations that must each be ticked before Accept enables: (1) not a WHOOP employee/contractor/affiliate and not acting on WHOOP's behalf, (2) own device, own data, at own risk, and may breach WHOOP's ToS, (3) unofficial/experimental/ as-is/no-warranty/not-medical, (4) liability waiver to the fullest extent the law allows. Separate knowing consents read stronger than one blanket box. Record the accepted version AND a local timestamp (the on-device consent record). Bump Terms version 1.1 -> 2.0 so every existing user re-accepts. TERMS.md §7 rewritten to the itemised attestations, §8 reconciled. Both platforms in parity (TermsGateView.swift / TermsGate.kt), informational points kept as context above. Non-English locales show the attestations in English (the binding wording); translations are a courtesy follow-up. NOTE: the exact clause phrasing should get a solicitor pass before the next release ships (flagged in code).
…ecord Replace the single blanket acknowledgment checkbox with FOUR separate, conspicuous attestations that must each be ticked before Accept enables: (1) not a WHOOP employee/contractor/affiliate and not acting on WHOOP's behalf, (2) own device, own data, at own risk, and may breach WHOOP's ToS, (3) unofficial/experimental/ as-is/no-warranty/not-medical, (4) liability waiver to the fullest extent the law allows. Separate knowing consents read stronger than one blanket box. Record the accepted version AND a local timestamp (the on-device consent record). Bump Terms version 1.1 -> 2.0 so every existing user re-accepts. TERMS.md §7 rewritten to the itemised attestations, §8 reconciled. Both platforms in parity (TermsGateView.swift / TermsGate.kt), informational points kept as context above. Non-English locales show the attestations in English (the binding wording); translations are a courtesy follow-up. NOTE: the exact clause phrasing should get a solicitor pass before the next release ships (flagged in code).
Removes everything that only existed for public distribution to many users (update checker, donation/crypto UI, in-app changelog history, distribution tooling, community/support docs) and all support for devices other than WHOOP and the Apple Watch (Oura, Huami/Amazfit/ Xiaomi, generic BLE HR straps, FTMS gym equipment), including the Android app entirely. macOS, iOS, and watchOS apps are kept, along with full WHOOP 4.0/5.0 and Apple Watch/HealthKit support. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
The gravity-magnitude expression's nested optional arithmetic was too complex for the Swift type-checker to resolve in reasonable time, failing CI. Pre-existing on main, unrelated to the device-cleanup work in this PR — breaking it into named sub-expressions fixes it without changing behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
…ice support BLEManager's own bandwidth-fallback path (standardHRMode/standardHRFallback) decodes the strap's standard BLE Heart Rate characteristic (0x2A37) via StandardHeartRate.parse — this was wrongly deleted alongside the generic third-party HR-strap support because of the name overlap. Confirmed via `git grep` that WhoopStore's StandardHRMapping (which *was* third-party-only, used solely by the deleted HuamiHRSource/StandardHRSource) is unrelated and stays deleted. Fixes the "cannot find 'StandardHeartRate' in scope" macOS build failure in CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
connectedPeripheralChanged still calls isWhoop(activeId) with a String, but I'd deleted the instance overload when simplifying activeDeviceChanged, leaving only the static isWhoop(_ device: PairedDevice) — which doesn't type-check against a String and can't be called on an instance either way. Restores the instance method exactly as before. Fixes both macOS and iOS app-build failures in CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
Strip app down to a personal WHOOP + Apple Watch companion
…noop All six Xcode targets (Strand, StrandTests, NOOPiOS, NOOPiOSWidgets, NOOPWatch, NOOPWatchComplications) previously used the com.noopapp.* identifier family and group.com.noopapp.noop app group inherited from the project's public-distribution days. Xcode's automatic signing kept reconciling these against the wrong values locally. Updates project.yml's bundleIdPrefix, APP_GROUP_ID, every target's PRODUCT_BUNDLE_IDENTIFIER, WKCompanionAppBundleIdentifier, and the BGTaskSchedulerPermittedIdentifiers entry, plus every hardcoded Swift fallback/constant that mirrored the old values (WidgetSnapshot, WatchScoreSnapshot, ScheduledDebugExport, StorePaths, RawHistoryArchive, NoopLocalAccessCore) and their tests, the two xcodegen-managed Info.plist files, and doc references. Per-target suffixes (.widgets, .watch, .watch.complications, .strandtests) are preserved since iOS requires each embedded extension/companion app to have a distinct bundle id from its parent. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
Personal-only app, so drop identity artifacts left over from when this was a public, community-run project: - Delete LICENSE and NOTICE (a distribution license/copyright notice serves no purpose for a personal, non-distributed build; both also linked to DISCLAIMER.md/ATTRIBUTION.md, already deleted). - Remove ProjectInfo.contactEmail (the old support inbox) and the Support screen's "Get in touch" contact card. - Repoint the in-app bug-report deep link from the old NoopApp/noop GitHub org to this repo. - Delete docs/SAFEGUARDS.md (entirely about the old public repo's GitHub-suspension/donation/mirror concerns; every tool/doc it references was already removed). - Trim docs/IOS.md: drop the AltStore/SideStore sideload+distribution section and the "fold-in" section's community-PR/anonymization history, which no longer apply. - Strip individual former-contributor name credits from code comments (PR# references kept as plain internal history markers) and drop dead org/issue links in docs/WHOOP5_DEEP_DATA.md and tools/linux-capture/README.md. Left untouched: the WHOOP-protocol reverse-engineering attributions (johnmiddleton12/my-whoop, b-nnett/goose) — unrelated third-party research this app's protocol code depends on, not "the previous team." Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
…l edits) Companion to 245f9f8 (which deleted LICENSE/NOTICE/docs/SAFEGUARDS.md): - Remove ProjectInfo.contactEmail (the old support inbox) and the Support screen's "Get in touch" contact card. - Repoint the in-app bug-report deep link from the old NoopApp/noop GitHub org to this repo, and update its test. - Trim docs/IOS.md: drop the AltStore/SideStore sideload+distribution section and the "fold-in" section's community-PR/anonymization history, which no longer apply. - Strip individual former-contributor name credits from code comments (PR# references kept as plain internal history markers) and drop dead org/issue links in docs/WHOOP5_DEEP_DATA.md and tools/linux-capture/README.md. - README: drop the License section and ATTRIBUTION.md link (both pointed at now-deleted files). Left untouched: the WHOOP-protocol reverse-engineering attributions (johnmiddleton12/my-whoop, b-nnett/goose) — unrelated third-party research this app's protocol code depends on, not "the previous team." Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
…dalone-app-hvijkh'
Every token that carried functional color now sits on one continuous violet -> purple -> fuchsia -> pink hue path (cooler/violet reads calm/good, warmer/pink reads hot/urgent), with lightness shifting in step with hue on every ramp since hue alone is unreliable at this end of the wheel for red-green colorblind viewers. Structural chrome (surfaces, text, the titanium ramp) stays strictly black/white/gray with a whisper of cool violet in the undertone. Public API is unchanged (property names frozen per the file's own convention) - only the hex literals and their describing comments moved. Classic mode now mirrors Titanium's values 1:1, since the old red->green throwback scale can't exist inside a pink/purple-only system. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
The app had four separate clock-driven decorative backgrounds layered on top of each other: LiquidSky (a twinkling starfield + hour-cycled sky gradient behind ~20 screens and Liquid Today), SceneScreenBackground (hand-picked day-cycle illustrations behind classic Today), TimeOfDayBackground (a night sky with stars/moon behind the Sleep hero card), plus the already-flat ScenicHeroBackground. All of them repainted themselves as the clock advanced. Replaces all of it with one AppBackground: a static pink/purple/baby-blue gradient mesh, full-bleed edge-to-edge, for light mode only (dark mode keeps the existing flat surfaceBase canvas, untouched). No clock, no TimelineView, no per-frame cost — same look every render, morning or night. ScreenScaffold now paints this as the single background for every screen instead of a 240pt top band that faded into the flat canvas below. Deletes LiquidSky.swift, SceneHeroBackground.swift, and TimeOfDayBackground.swift outright, and the now-meaningless "Day-cycle background" Settings toggle (SceneBackgroundPrefs) along with it, since there's no more day-cycle to toggle. Left untouched: ScenicHeroBackground (DomainTheme.swift) — a local, non-time-based, domain-tinted backdrop behind individual hero cards on 8 detail screens, not the page-wide background this task targeted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019fn4rJ9z1dn5afNREphUFF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.