diff --git a/CHANGELOG.md b/CHANGELOG.md index fd5f8a7e3..37a4cc709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to Bambuddy will be documented in this file. ### Fixed - **External Camera Not Used for Snapshot + Stream Dropping** ([#325](https://github.com/maziggy/bambuddy/issues/325)) — The snapshot endpoint (`/camera/snapshot`) always used the internal printer camera even when an external camera was configured. Now checks for external camera first, matching the existing stream endpoint behavior. Also fixed external MJPEG and RTSP streams silently dropping every ~60 seconds due to missing reconnect logic — the underlying stream generators exit on read timeout, and the caller now retries up to 3 times with a 2-second delay instead of ending the stream. +- **H2C Nozzle Rack Text Unreadable on Light Filament Colors** ([#300](https://github.com/maziggy/bambuddy/issues/300)) — Nozzle rack slots use the loaded filament color as background, but white/light filaments made the white "0.4" text nearly invisible. Now uses a luminance check to switch to dark text on light backgrounds. ## [0.1.9] - 2026-02-10 diff --git a/frontend/src/pages/PrintersPage.tsx b/frontend/src/pages/PrintersPage.tsx index 76184c069..7a0e9c873 100644 --- a/frontend/src/pages/PrintersPage.tsx +++ b/frontend/src/pages/PrintersPage.tsx @@ -421,6 +421,14 @@ function parseFilamentColor(rgba: string): string | null { return `rgba(${parseInt(r, 16)}, ${parseInt(g, 16)}, ${parseInt(b, 16)}, ${a})`; } +function isLightFilamentColor(rgba: string): boolean { + if (!rgba || rgba.length < 6) return false; + const r = parseInt(rgba.slice(0, 2), 16); + const g = parseInt(rgba.slice(2, 4), 16); + const b = parseInt(rgba.slice(4, 6), 16); + return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.6; +} + // Expand nozzle type codes to material names // Handles full text ("hardened_steel"), 2-char codes ("HS"/"HH"), and 4-char codes ("HS01") // Material mapping: 00=stainless steel, 01=hardened steel, 05=tungsten carbide @@ -798,6 +806,7 @@ function NozzleRackCard({ slots, filamentInfo }: { slots: import('../api/client' {rackSlots.map((slot, i) => { const isEmpty = !slot.nozzle_diameter && !slot.nozzle_type; const filamentBg = !isEmpty ? parseFilamentColor(slot.filament_color) : null; + const lightBg = filamentBg ? isLightFilamentColor(slot.filament_color) : false; return ( = 0 ? slot.id : `empty-${i}`} slot={slot} index={i} filamentName={slot.filament_id ? filamentInfo?.[slot.filament_id]?.name : undefined}> @@ -809,8 +818,8 @@ function NozzleRackCard({ slots, filamentInfo }: { slots: import('../api/client' }`} style={filamentBg ? { backgroundColor: filamentBg } : undefined} > - {isEmpty ? '—' : (slot.nozzle_diameter || '?')}