Files
arr-tui/webos/src/ui/dom.ts
T
nilsandClaude Opus 5.5 7f6d4d07b9 webOS: a TV app with the views of arr-tui
A TypeScript web app for LG webOS TVs (webOS 5 / Chromium 68 and newer),
driven by the remote: Movies and Series search with the Trakt lists,
Library with status, title and file-property filters and download marks,
seasons and episodes, release search and grab, Downloads, Calendar,
Settings and Log. It talks to Radarr, Sonarr, SABnzbd and Trakt directly
(they answer cross-origin requests); the arr-tui config is baked in at
build time and can be changed on the TV.

dev/screenshots/server.py answers cross-origin requests and accepts writes
for it; webos/dev/drive.mjs drives the app in Chromium for screenshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 16:51:48 +01:00

43 lines
1.7 KiB
TypeScript

// Tiny DOM helpers: h("div.card", {onclick}, child, "text").
type Child = Node | string | number | null | undefined | false;
type Attrs = Record<string, unknown>;
export function h<K extends keyof HTMLElementTagNameMap>(spec: K | `${K}.${string}` | `${K}#${string}`, attrs?: Attrs | Child, ...children: Child[]): HTMLElementTagNameMap[K] {
const [tagAndId, ...classes] = spec.split(".");
const [tag, id] = tagAndId.split("#");
const el = document.createElement(tag) as HTMLElementTagNameMap[K];
if (id) el.id = id;
if (classes.length) el.className = classes.join(" ");
if (attrs && typeof attrs === "object" && !(attrs instanceof Node)) {
for (const [k, v] of Object.entries(attrs)) {
if (v === undefined || v === null || v === false) continue;
if (k.startsWith("on") && typeof v === "function") el.addEventListener(k.slice(2), v as EventListener);
else if (k === "class") el.className += (el.className ? " " : "") + v;
else if (k === "style") el.setAttribute("style", String(v));
else if (k in el && typeof v !== "string") (el as unknown as Record<string, unknown>)[k] = v;
else el.setAttribute(k, v === true ? "" : String(v));
}
} else if (attrs !== undefined) {
children.unshift(attrs as Child);
}
append(el, ...children);
return el;
}
export function append(el: Element, ...children: Child[]): void {
for (const c of children) {
if (c === null || c === undefined || c === false) continue;
el.appendChild(c instanceof Node ? c : document.createTextNode(String(c)));
}
}
export function clear(el: Element): void {
while (el.firstChild) el.removeChild(el.firstChild);
}
export function replace(el: Element, ...children: Child[]): void {
clear(el);
append(el, ...children);
}