Files
Tobias GesellchenandClaude Sonnet 4.6 6723515f54 feat(soundtouch-web): migrate to pkg/service/soundtouchweb with Preact UI
Move soundtouch-web from Bootstrap+vanilla JS to an embedded-asset Go service
using Preact+htm (no build step). Implements Stockholm UI parity: device list,
now playing, transport controls, presets, sources, and TuneIn browser.

- Relocate handlers/websocket/webtypes to pkg/service/soundtouchweb/
- Replace old static/ with CSS-custom-property design system (dark mode)
- Add Preact component tree: DeviceList, NowPlaying, Controls, Presets, Sources, TuneInBrowser
- Wire WebSocket for real-time device status updates
- Slim cmd/soundtouch-web/main.go to a thin CLI wrapper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 21:22:57 +02:00

54 lines
2.0 KiB
JavaScript

import { h } from 'preact';
import htm from 'htm';
const html = htm.bind(h);
function DeviceCard({ id, device, onSelect }) {
const { info, status } = device;
const np = status?.nowPlaying;
const isPlaying = np?.PlayStatus === 'PLAY_STATE';
const isStandby = !np || np.Source === 'STANDBY';
return html`
<div class="device-card" onClick=${() => onSelect(id)}>
<div class="device-header">
<span class="device-name">${info?.Name || id}</span>
<span class="device-indicator ${status?.isConnected ? 'online' : 'offline'}"></span>
</div>
<div class="device-type">${info?.Type || ''}</div>
${!isStandby && html`
<div class="now-playing-mini">
<span class="play-status">${isPlaying ? '▶' : '⏸'}</span>
<span class="track-mini">${np.Track || np.StationName || np.Source}</span>
${np.Artist && html`<span class="artist-mini"> — ${np.Artist}</span>`}
</div>
`}
${isStandby && html`<div class="standby-label">Standby</div>`}
</div>
`;
}
export function DeviceList({ devices, onSelect, onDiscover }) {
const entries = Object.entries(devices);
return html`
<div class="page-header">
<h2>Devices</h2>
<button class="btn-secondary" onClick=${onDiscover}>Discover</button>
</div>
${entries.length === 0
? html`
<div class="empty-state">
<div class="empty-icon">◉</div>
<p>No devices found on your network.</p>
<button class="btn-primary" onClick=${onDiscover}>Start Discovery</button>
</div>`
: html`
<div class="device-grid">
${entries.map(([id, device]) => html`
<${DeviceCard} key=${id} id=${id} device=${device} onSelect=${onSelect} />
`)}
</div>`
}
`;
}