cmux-next New Tab: Weather and Machines widgets, off by default

Merged
TE
15d ago
+2773-46

Stacked on #22674 (lane newtab-minimal, which owns tabs.newTabWidgets.*). The base is its branch, so this diff is only the new widgets. Merge it after #22674.

Summary

Adds two optional widgets to the New Tab page, both off by default. They sit in one quiet row under the composer, drawn in the greeting's dim color. Because the row is below the field, turning a widget on or getting a new reading never moves the composer. When a widget is on, every layout shows it; otherwise the other layouts are unchanged.

  • Weather (tabs.newTabWidgets.weather, plus the text setting tabs.newTabWidgets.weatherCity): shows the current conditions as a glyph and a temperature, today's high and low, and the city. The app asks Open-Meteo, which needs no key. The city comes from weatherCity; if that is empty, it is the city of this Mac's time zone, so no location permission is needed. A reading is cached for 30 minutes in memory and in UserDefaults, and each page context carries the latest one. The page dims a reading older than 30 minutes, which happens when the refresh failed (for example offline). Nothing is fetched while the widget is off. Agents may not set the two weather keys; they are .network in the agent policy.
  • Machines (tabs.newTabWidgets.machines): lists this Mac, then acpmux's peers (online), then the hosts that older chats ran on and acpmux no longer lists (offline). Each has an icon by kind (Mac, SSH, Cloud) and a color derived from its name, so a machine keeps the same color everywhere.

gen-settings-sources.py learns the text row kind, which weatherCity needs.

Not in this PR (follow-ups from the lane brief): project tiles with real icons, which will share the project-picker lane's icon resolver. Music is the existing Now Playing card behind tabs.newTabNowPlaying, so it gets no new key.

Testing

  • bun test test/new-tab-ambient-widgets.test.ts test/new-tab-minimal-default.test.tsx: 12 pass. They cover parsing the weather reading, dimming a stale reading, the degree and glyph mapping, the machine order and online state, stable colors, and which widgets each layout shows.
  • webviews checks: layers, type-scale, status-glyphs, system-blue, text-focus, dialog-keys, switch-fetches, browser-lanes and acpmux-protocol pass. vp lint and vp fmt are clean on the changed files.
  • prepush-gate.py: no new violations.
  • Swift: added NewTabWeatherMachinesSettingsTests (defaults, parsing, trimming the city, schema rows, the agent policy). It has not run yet; the fleet or CI will run it.
  • Gallery: added two states, minimal-ambient (fresh reading; this Mac and an SSH peer online, an old Cloud host offline) and minimal-ambient-offline (dimmed reading).

No capture: the widgets are webview UI, shown by the gallery states above (minimal-ambient, minimal-ambient-offline); the native change is only the new settings rows and the weather fetch, both off by default.

Observed / Not run

Observed: the bun tests and the web checks above. Not run: the Swift build and tests (they compile on CI), a live Open-Meteo fetch in a tagged build, and gallery captures. The vp check formatting findings in src/pages/settings/* and the new-tab-cards-template.test.tsx type error come from the base or #22674, not from this diff.

Changelog

Added: New Tab can show today's weather and your machines with their online state (Settings > Tabs, off by default).

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Adds two optional New Tab widgets, both off by default, drawn in one quiet row under the composer so the field never moves. The edit mode does not frame or move them; Settings turns them on and off.

  • Weather (tabs.newTabWidgets.weather, plus tabs.newTabWidgets.weatherCity): shows current conditions, today's high and low, and the city, fetched from Open-Meteo which needs no key. The city falls back to this Mac's time zone city, taken only from IANA zones that end in a city so "US/Pacific" finds no place; no location permission is required. Readings are cached for 30 minutes in memory and UserDefaults; a stale reading (refresh failed, e.g. offline) is drawn dimmed. A finished refresh goes to every open New Tab page in place via an acpmux-newtab-weather event, so a page opened before the first reading still shows it. A late answer for a city settings no longer asks for doesn't replace the reading. Nothing is fetched while the widget is off, and a failed refresh holds back only that city. Agents may not set the two weather keys, which are .network in the agent policy; the page's show edit is refused too.
  • Machines (tabs.newTabWidgets.machines): lists this Mac, then acpmux's online peers, then hosts older chats ran on that acpmux no longer lists (offline). Each machine gets an icon by kind (Mac, SSH, Cloud) and the composer's identity color by name (machineColor.ts); this Mac stays quiet. The composer's computers and this widget share one machine walk (knownMachines) that skips "local" host ids, so no list names this Mac twice. With the stage layout the row sits in the stage over the composer.
  • gen-settings-sources.py learns the text row kind, which weatherCity needs.