diff --git a/apps/weather/components/CityRail.css b/apps/weather/components/CityRail.css
index 1b8daf5..0b2d569 100644
--- a/apps/weather/components/CityRail.css
+++ b/apps/weather/components/CityRail.css
@@ -31,7 +31,7 @@
gap: 4px;
justify-content: flex-start;
min-width: 0;
- padding: 5px 6px 10px;
+ padding: 5px 6px;
scroll-snap-align: start;
}
diff --git a/apps/weather/components/WeatherBackground.css b/apps/weather/components/WeatherBackground.css
index 092ac6a..029336b 100644
--- a/apps/weather/components/WeatherBackground.css
+++ b/apps/weather/components/WeatherBackground.css
@@ -15,7 +15,11 @@
top: 0;
width: 100%;
height: 100%;
- object-fit: contain;
+ /* The art is the 410x502 panel itself (the shell's exact aspect), so on that
+ shell cover and contain agree. On a taller viewport -- a resizable desktop
+ window -- cover fills it edge to edge instead of leaving solid bands above
+ and below the gradient. */
+ object-fit: cover;
}
.weather-bg-img.is-hidden {
diff --git a/apps/weather/components/WeatherBackground.tsx b/apps/weather/components/WeatherBackground.tsx
index 39b4afb..4383258 100644
--- a/apps/weather/components/WeatherBackground.tsx
+++ b/apps/weather/components/WeatherBackground.tsx
@@ -5,22 +5,25 @@ import { weather } from '../stores/WeatherStore'
// captured from the gea engine itself (WASM simulator), so they are pixel-identical
// to the old CSS `.weather-shell` gradient — same radial glow, 158deg ramp, and
// dither. The win: re-rasterizing this backdrop when content changes is a 1:1
-// decoded-image blit (the images are exactly panel-sized) instead of recomputing a
-// 3-layer linear+radial gradient per pixel (~114ms, the bulk of a city switch).
-// Inactive layers stay decoded (display:none) so switching visuals is instant —
-// same pattern as WeatherVisual's hero art. Visual mapping is clear, cloud/fog,
-// rain and snow; the root shell itself stays static.
+// decoded-image blit (on the 410x502 panel the images are exactly panel-sized)
+// instead of recomputing a 3-layer linear+radial gradient per pixel (~114ms, the
+// bulk of a city switch). Inactive layers stay decoded (display:none) so switching
+// visuals is instant — same pattern as WeatherVisual's hero art. Visual mapping is
+// clear, cloud/fog, rain and snow; the root shell itself stays static.
+//
+// Fit comes from WeatherBackground.css, NOT a `fit` attribute: the attribute is an
+// INLINE style, and inline styles replay after class rules, so it would override the
+// taller-than-the-panel rule the phone/desktop targets need.
export function WeatherBackground() {
return (
)
}
diff --git a/apps/weather/index.tsx b/apps/weather/index.tsx
index 74c178f..7f0ac48 100644
--- a/apps/weather/index.tsx
+++ b/apps/weather/index.tsx
@@ -2,11 +2,13 @@ import { Display, mount } from '@geastack/core'
import { App } from './components/App'
import { weather } from './stores/WeatherStore'
-// Weather needs WiFi for live forecasts. esp_wifi_init requires a chunk of
-// internal DRAM (task stack + driver state) that the default 2-deep flush
-// pipeline (~56 rows) leaves too little of — WiFi init fails ESP_ERR_NO_MEM and
-// fetch() can never connect. A 36-row 2-deep pipeline frees ~32 KB internal so
-// WiFi comes up; the smaller flush chunk is a fine trade for this app.
+// ESP32-only tuning; both calls are inert setters on the phone and desktop
+// targets (they just store the value, no panel to drive). Weather needs WiFi for
+// live forecasts, and esp_wifi_init requires a chunk of internal DRAM (task stack
+// + driver state) that the default 2-deep flush pipeline (~56 rows) leaves too
+// little of — WiFi init fails ESP_ERR_NO_MEM and fetch() can never connect. A
+// 36-row 2-deep pipeline frees ~32 KB internal so WiFi comes up; the smaller
+// flush chunk is a fine trade for this app.
Display.setBrightness(50)
Display.setFlushConfig({ rows: 36, depth: 2 })
diff --git a/apps/weather/macos.json b/apps/weather/macos.json
new file mode 100644
index 0000000..4a44534
--- /dev/null
+++ b/apps/weather/macos.json
@@ -0,0 +1,10 @@
+{
+ "window": {
+ "title": "Weather",
+ "width": 546,
+ "height": 780,
+ "minWidth": 273,
+ "minHeight": 390,
+ "appearance": "dark"
+ }
+}
diff --git a/apps/weather/package.json b/apps/weather/package.json
index 4b07ed4..ef4608f 100644
--- a/apps/weather/package.json
+++ b/apps/weather/package.json
@@ -22,11 +22,15 @@
"name": "Weather",
"entry": "index.tsx",
"runtime": "gea",
+ "designWidth": 273,
"targets": {
"web": true,
"esp32": true,
"geaos": false,
- "android": true
+ "android": true,
+ "ios": true,
+ "macos": true,
+ "windows": true
},
"icons": {
"32": "icons/icon-32.png",
diff --git a/apps/weather/stores/WeatherStore.ts b/apps/weather/stores/WeatherStore.ts
index dbee455..020e45b 100644
--- a/apps/weather/stores/WeatherStore.ts
+++ b/apps/weather/stores/WeatherStore.ts
@@ -67,8 +67,14 @@ function hourLabel(hour: number): string {
return '' + (hour - 12) + ' PM'
}
+// Carries the year, so a forecast that crosses New Year still sorts forward:
+// without it January's key falls below December's and the hourly scan below
+// runs off the end of the array, blanking every slot.
function weatherTimeSortKey(time: string): number {
- return Number(time.substring(5, 7)) * 744 + Number(time.substring(8, 10)) * 24 + Number(time.substring(11, 13))
+ const year = Number(time.substring(0, 4))
+ const month = Number(time.substring(5, 7))
+ const day = Number(time.substring(8, 10))
+ return ((year * 12 + month) * 31 + day) * 24 + Number(time.substring(11, 13))
}
function weekdayFromISO(time: string): string {
diff --git a/docs/EXAMPLE-CATALOG.md b/docs/EXAMPLE-CATALOG.md
index 93fc6e2..55973ef 100644
--- a/docs/EXAMPLE-CATALOG.md
+++ b/docs/EXAMPLE-CATALOG.md
@@ -14,6 +14,7 @@ tests, and marketing proof.
| Games | `sky-hop`, `sky-hop-jsx`, `tic-tac-toe`, `tilt-breakout`, `button-tetris` | Interaction, simple game loops, collision/physics, deterministic logic. |
| Device features | `camera-showcase`, `camera-studio`, `voice-notes`, `hid-clicker`, `weather`, `maps` | Target capabilities such as camera, audio, HID, network, and map assets. |
| Apple/native experiments | `notes-jsx`, `notes-native`, `ios-device-showcase`, `ios-metal-*`, `ios-native-showcase` | Apple target and native renderer experiments. |
+| Cross-platform reference | `weather` | One source across embedded, phone and desktop: `esp32`, `android`, `ios`, `macos`, `web`. Scales its fixed-px layout through `gea.designWidth`. |
| Windows/native experiments | `notes-jsx`, `notes-windows` | Windows target: the JSX notes app through the Win32 renderer, and the same app written against `@geastack/windows/Controls`. |
| Reactive experiments | `reactive-counter`, `reactive-child-probe`, `reactive-nested-probe`, `reactive-tic-tac-toe` | Store/reactivity experiments, often hidden from launchers. |