Phase C of the program viewer. Replaces the "panel coming soon" stub with a real Lit-based side panel that consumes the Phase-B websocket commands. Layout (top to bottom): * Header — title + total/filtered count * Filter bar — search box (substring match), trigger-type chips (TIMED / EVENT / YEARLY / WHEN / AT / EVERY / REMARK), a clearable "filtering on <ref>" pill when an entity filter is active * Two-column body: program list on the left, slide-in detail panel on the right. Collapses to single-column on `narrow` view. The program list renders one row per program (or per chain head, for clausal multi-record programs). Each row carries the slot number, the rendered one-line summary token stream, and meta pills for trigger type / condition count / multi-action count. The detail panel renders the full structured-English token stream inside a styled <pre>. A "Fire now" button calls ``omni_pca/programs/fire`` over the wire — the panel actually runs the program. For chain detail the spanned slot range is shown underneath. REF tokens are rendered as `<button>` elements that click to filter the list to "programs that mention this entity" — the most useful navigational affordance for the "why is this happening?" use case. Live-state badges (SECURE / NOT READY / ON 60% / Away / 72°F / …) are appended to REF tokens via the Phase-B coordinator-backed StateResolver. The panel polls ``programs/list`` every 5 seconds to refresh badges; switching to push-event subscriptions is a follow-up when polling overhead becomes visible. Theming uses HA's standard CSS variables (--primary-color, --card-background-color, --divider-color, etc.) so the panel inherits the user's HA theme automatically. Build pipeline: * TypeScript source under ``custom_components/omni_pca/frontend/src/`` * esbuild bundles entry → ESM in one self-contained file * Output at ``custom_components/omni_pca/www/panel.js`` (~34 KB minified) is committed so end-users don't need Node installed * ``npm run watch`` for HA-dev-time iteration * tsconfig has strict mode + noUnusedLocals; bundle currently type-checks clean Manifest declares deps on ``http`` and ``websocket_api``; ``frontend`` and ``panel_custom`` are loaded opportunistically (they require ``hass_frontend`` which the test harness doesn't ship — keeping them out of the manifest deps keeps tests green). Full suite: 634 passed, 1 skipped (no test changes; the integration side hasn't moved since Phase B).
53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
// Token-stream → DOM. Each TokenKind gets distinctive styling so the
|
|
// structured-English programs read cleanly even at a glance.
|
|
//
|
|
// REF tokens are rendered as <button> nodes so they can dispatch a
|
|
// "ref-click" event for the parent component to act on (filter the
|
|
// list, jump to that entity's HA page, etc.).
|
|
|
|
import { html, TemplateResult } from "lit";
|
|
import { Token } from "./types.js";
|
|
|
|
export function renderTokens(
|
|
tokens: Token[],
|
|
onRefClick?: (kind: string, id: number) => void,
|
|
): TemplateResult {
|
|
return html`${tokens.map((t) => renderToken(t, onRefClick))}`;
|
|
}
|
|
|
|
function renderToken(
|
|
t: Token,
|
|
onRefClick?: (kind: string, id: number) => void,
|
|
): TemplateResult {
|
|
switch (t.k) {
|
|
case "newline":
|
|
return html`<br />`;
|
|
case "indent":
|
|
// Convert leading spaces to a CSS class so the panel can switch
|
|
// indent styling (e.g. left border) without re-rendering tokens.
|
|
return html`<span class="indent">${t.t}</span>`;
|
|
case "keyword":
|
|
return html`<span class="keyword">${t.t}</span>`;
|
|
case "operator":
|
|
return html`<span class="operator">${t.t}</span>`;
|
|
case "value":
|
|
return html`<span class="value">${t.t}</span>`;
|
|
case "ref": {
|
|
const handler = onRefClick && t.ek && typeof t.ei === "number"
|
|
? () => onRefClick(t.ek!, t.ei!)
|
|
: undefined;
|
|
return html`<button
|
|
type="button"
|
|
class="ref ref-${t.ek}"
|
|
title=${t.ek ?? ""}
|
|
@click=${handler}
|
|
>
|
|
<span class="ref-name">${t.t}</span>
|
|
${t.s ? html`<span class="ref-state">${t.s}</span>` : ""}
|
|
</button>`;
|
|
}
|
|
default:
|
|
return html`<span>${t.t}</span>`;
|
|
}
|
|
}
|