release: v2.4.0 — live webcams, mobile detection fix, sentry triage

- Live Webcams Panel with region filters and grid/single view (#111)
- Mobile detection: width-only, touch notebooks get desktop layout (#113)
- Le Monde RSS URL fix, workbox precache HTML, panel ordering migration
- Sentry: ML timeout catch, YT player guards, maplibre noise filters
- Changelog for v2.4.0
This commit is contained in:
Elie Habib
2026-02-19 01:25:05 +04:00
parent aa36426293
commit 572f380856
7 changed files with 71 additions and 4 deletions

View File

@@ -0,0 +1,32 @@
# Sentry Triage — 2026-02-19
Commit: `09174fd` on `main`
## Issues Triaged (5)
### ACTIONABLE — Fixed in Code
| ID | Title | Events | Users | Fix |
|---|---|---|---|---|
| WORLDMONITOR-1G | `Error: ML request unload-model timed out after 120000ms` | 30 | 27 | Wrapped `unloadModel()` in try/catch; timeout no longer leaks as unhandled rejection. Cleans up `loadedModels` set on failure. |
| WORLDMONITOR-1F | `Error: ML request unload-model timed out after 120000ms` | 9 | 9 | Same root cause as 1G (different release build hash). |
| WORLDMONITOR-1K | `TypeError: this.player.playVideo is not a function` | 1 | 1 | Added optional chaining (`playVideo?.()`, `pauseVideo?.()`) in `LiveNewsPanel.ts`. YT IFrame API player object may not have methods ready during initialization race. |
### NOISE — Filtered
| ID | Title | Events | Users | Filter |
|---|---|---|---|---|
| WORLDMONITOR-1J | `InternalError: too much recursion` | 1 | 1 | i18next internal `translate -> extractFromKey` cycle on Firefox 147. Added `/too much recursion/` to `ignoreErrors`. |
| WORLDMONITOR-1H | `TypeError: Cannot read properties of null (reading 'id')` | 1 | 1 | maplibre-gl internal render crash (`_drawLayers -> renderLayers`). Extended `beforeSend` regex to suppress null `id`/`type` when stack is in map chunk. |
## Files Modified
| File | Change |
|---|---|
| `src/services/ml-worker.ts` | `unloadModel()`: try/catch around `this.request()`, clean `loadedModels` on failure |
| `src/components/LiveNewsPanel.ts` | Optional chaining on `playVideo?.()` and `pauseVideo?.()` |
| `src/main.ts` | Added `/too much recursion/` to `ignoreErrors`; extended maplibre `beforeSend` filter for null `id`/`type` |
## Sentry Status
All 5 issues marked **resolved (in next release)** via API. They will auto-reopen if errors recur after deployment.

View File

@@ -2,6 +2,33 @@
All notable changes to World Monitor are documented here.
## [2.4.0] - 2026-02-19
### Added
- **Live Webcams Panel**: 2x2 grid of live YouTube webcam feeds from global hotspots with region filters (Middle East, Europe, Asia-Pacific, Americas), grid/single view toggle, idle detection, and full i18n support (#111)
- **Linux download**: added `.AppImage` option to download banner
### Changed
- **Mobile detection**: use viewport width only for mobile detection; touch-capable notebooks (e.g. ROG Flow X13) now get desktop layout (#113)
- **Webcam feeds**: curated Tel Aviv, Mecca, LA, Miami; replaced dead Tokyo feed; diverse ALL grid with Jerusalem, Tehran, Kyiv, Washington
### Fixed
- **Le Monde RSS**: English feed URL updated (`/en/rss/full.xml``/en/rss/une.xml`) to fix 404
- **Workbox precache**: added `html` to `globPatterns` so `navigateFallback` works for offline PWA
- **Panel ordering**: one-time migration ensures Live Webcams follows Live News for existing users
- **Mobile popups**: improved sheet/touch/controls layout (#109)
- **Intelligence alerts**: disabled on mobile to reduce noise (#110)
- **RSS proxy**: added 8 missing domains to allowlist
- **HTML tags**: repaired malformed tags in panel template literals
- **ML worker**: wrapped `unloadModel()` in try/catch to prevent unhandled timeout rejections
- **YouTube player**: optional chaining on `playVideo?.()` / `pauseVideo?.()` for initialization race
- **Panel drag**: guarded `.closest()` on non-Element event targets
- **Beta mode**: resolved race condition and timeout failures
- **Sentry noise**: added filters for Firefox `too much recursion`, maplibre `_layers`/`id`/`type` null crashes
## [2.3.9] - 2026-02-18
### Added

View File

@@ -1,7 +1,7 @@
{
"name": "world-monitor",
"private": true,
"version": "2.3.9",
"version": "2.4.0",
"type": "module",
"scripts": {
"lint:md": "markdownlint-cli2 '**/*.md'",

View File

@@ -2253,6 +2253,14 @@ export class App {
panelOrder.unshift('live-news');
}
// live-webcams MUST follow live-news (one-time migration for existing users)
const webcamsIdx = panelOrder.indexOf('live-webcams');
if (webcamsIdx !== -1 && webcamsIdx !== panelOrder.indexOf('live-news') + 1) {
panelOrder.splice(webcamsIdx, 1);
const afterNews = panelOrder.indexOf('live-news') + 1;
panelOrder.splice(afterNews, 0, 'live-webcams');
}
// Desktop configuration should stay easy to reach in Tauri builds.
if (this.isDesktopApp) {
const runtimeIdx = panelOrder.indexOf('runtime-config');

View File

@@ -411,7 +411,7 @@ const FULL_FEEDS: Record<string, Feed[]> = {
{
name: 'Le Monde',
url: {
en: rss('https://www.lemonde.fr/en/rss/full.xml'),
en: rss('https://www.lemonde.fr/en/rss/une.xml'),
fr: rss('https://www.lemonde.fr/rss/une.xml')
}
},

View File

@@ -49,7 +49,7 @@ Sentry.init({
if (frames.some(f => /^(chrome|moz)-extension:/.test(f.filename ?? ''))) return null;
}
// Suppress maplibre internal null-access crashes (light, placement) only when stack is in map chunk
if (/this\.light is null|can't access property "type", \w+ is undefined|Cannot read properties of null \(reading '(id|type)'\)/.test(msg)) {
if (/this\.style\._layers|this\.light is null|can't access property "type", \w+ is undefined|Cannot read properties of null \(reading '(id|type)'\)/.test(msg)) {
if (frames.some(f => /\/map-[A-Za-z0-9]+\.js/.test(f.filename ?? ''))) return null;
}
return event;

View File

@@ -196,7 +196,7 @@ export default defineConfig({
},
workbox: {
globPatterns: ['**/*.{js,css,ico,png,svg,woff2}'],
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
globIgnores: ['**/ml-*.js', '**/onnx*.wasm'],
navigateFallback: '/index.html',
navigateFallbackDenylist: [/^\/api\//, /^\/settings/],