Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,102 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Phase 3.5 — Dashboard rewrite (multi-plan ranked view)

Throws away the Amber-vs-current-plan two-comparator dashboard
(2447 LOC) and rebuilds it as a multi-plan ranked-alternatives view
keyed off the Phase 3.2 / 3.3 / 3.4 sensors. Visual seed lifted from
`assets/dashboard-v3-apple.html` — dark default, Outfit + IBM Plex
Mono, ambient radial bg, semantic accent tokens (no per-provider
colours).

#### Added

- **Full rewrite of `custom_components/pricehawk/www/dashboard.html`**
(~1250 LOC, down from 2447). New card hierarchy per plan section 5.1:
- NAV bar (brand + connection status pill + clock + theme toggle).
- HERO row: current-cost card + savings-vs-best-alt card (with
projected-annual extrapolation).
- PERIOD TABS: `[Today][Week][Month][3 Month][Year]` — clicking a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Consider changing the tab label from "3 Month" to "3 Months" for grammatical consistency.

If this is the user-visible tab label (not just an internal identifier), consider pluralizing it to “3 Months” to better match the other period names and read more naturally.

Suggested change
- PERIOD TABS: `[Today][Week][Month][3 Month][Year]` — clicking a
- PERIOD TABS: `[Today][Week][Month][3 Months][Year]` — clicking a

tab swaps the entity binding for every rollup card to the matching
`_today` / `_week` / `_month` / `_3month` / `_year` sensor in
one tick. Active tab persists to `localStorage['pricehawk-window']`
so re-opens land on the user's last view.
- RANKED ALTERNATIVES table rendered from
`sensor.pricehawk_ranked_alternatives.attributes.alternatives[]`
(already sorted by cheap-rank score in `summarize_for_sensor`).
Click a row → drill-in card slides up below.
- DRILL-IN CARD: peak rate / daily supply / customer type / plan ID
/ cheap-rank score, plus a "Pin as Named Comparator" button that
deep-links to `/config/integrations/integration/pricehawk` (HA
doesn't support per-step deep-linking; locked in plan section 9
REVISIT 4).
- DATA HEALTH FOOTER: `sensor.pricehawk_backfill_status` state
(state-coloured: green=complete, amber=running, red=failed) +
`days_loaded` + `ranked_alternatives.last_run` as relative +
absolute time + alternatives count.
- **Empty-state UI for first-run users** (plan section 5.3 surprise #3):
when `backfill_status.days_loaded < 7`, hero rollup values are
replaced with an "Accruing… [n/365]" pill instead of showing a
misleading `$0.00`. Surfaces clearly that we don't have enough
history yet.
- **CSP `connect-src` extended** to include `ws://*.local:*` +

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.lang.security.detect-insecure-websocket): Insecure WebSocket Detected. WebSocket Secure (wss) should be used for all WebSocket connections.

Source: opengrep

`wss://*.local:*` so the dashboard works on Ryan's HA Green at
`homeassistant.local` (plan section 5.3 surprise #1). Existing
`localhost` + `*.ui.nabu.casa` entries preserved.
- **`assets/DESIGN.claude.md` — new PriceHawk Dashboard section**
noting divergence: PriceHawk is a dark data-dashboard inside HA's
sidebar, not a warm-canvas editorial site. Inherits typographic
rationale (humanist sans + mono numerics) and the card-as-surface
model + accent-discipline rule, but uses its own token palette.
The rest of the Claude marketing-site spec stays intact.

#### Changed

- **WebSocket auth + URL detection preserved verbatim** from the prior
dashboard:
- `location.protocol === 'https:' ? 'wss://' : 'ws://'` for the WS

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.lang.security.detect-insecure-websocket): Insecure WebSocket Detected. WebSocket Secure (wss) should be used for all WebSocket connections.

Source: opengrep

URL (AEGIS rule: never hardcode `ws://`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.lang.security.detect-insecure-websocket): Insecure WebSocket Detected. WebSocket Secure (wss) should be used for all WebSocket connections.

Source: opengrep

- Token sourced from URL params first, then `window.parent
.hassConnection`, then `localStorage.hassTokens`, then
`window.parent.localStorage.hassTokens` (AEGIS rule: never
hardcode the token).
- **Per-provider colour tokens deleted** (`--amber-primary`,
`--globird-primary`). Replaced with `--accent-positive` /
`--accent-negative` / `--accent-neutral` / `--accent-warn` — matches
the Phase 3.0 pivot away from provider-specific branding.
- **`dashboard_config.setup_panel_iframe` cache-busting unchanged** —
the existing `?v=<version>.<epoch>` query param survives the
rewrite (it's appended to the URL, doesn't touch dashboard.html
itself). Verified by smoke test; no code change.

#### Removed

- CSV import card, backfill-trigger button, Amber-API winner card,
GloBird TOU strip, Amber forecast strip, sparkline chart, grid-power
gauge, two-provider rate chart, ZeroHero status card — all replaced
by the ranked-alternatives + rollup-sensor model.

#### Notes

- **No new JS framework, no build step.** Vanilla JS only, same
constraint as the prior dashboard. All CSS + JS inlined; no CDN
fetches beyond the Google Fonts stylesheet that the prior dashboard
already used.
- **30s setInterval re-render** for the ranked + footer cards so
relative timestamps ("ran 27s ago / 3h ago") tick forward without
waiting on a state_changed event. Cheap (<1ms per tick on HA Green).
- **XSS hardening**: all CDR-sourced strings (plan_id, display_name,
brand, customer_type) pass through `escapeHtml()` before innerHTML
insertion. Defensive — current registry payloads don't contain
HTML-ish characters, but future ones might.
- **Manual UAT only** for this commit (per plan section 6.3 table —
`3.5 | none | manual on Ryan's HA + JS console`). Local smoke test:
HTML parses cleanly via `html.parser`; JS extracted + run under
Node `--check` + mock-DOM render harness exercising all 5 period
windows + accruing branch + empty-ranked branch + drill render
without throwing.

### Phase 3.4 — Named comparator drill-in

Lets the user pin ONE CDR plan from the ranked alternatives list as a
Expand Down
85 changes: 85 additions & 0 deletions assets/DESIGN.claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -587,3 +587,88 @@ When photography is used (rare — mostly testimonials), avatars crop to perfect
- Form validation states beyond `{component.text-input-focused}` are not extracted — error / success states would need a sign-up or feedback flow to confirm.
- The actual Claude product surface (claude.ai chat interface) shares some tokens with the marketing site but adds many product-specific components (chat bubbles, message tools, file upload chips, conversation history sidebar) that are out of scope for this marketing-surface document.
- The "agent" / "computer use" demo cards on certain pages display animated Claude controlling a browser — the static screenshot doesn't fully capture the animation chrome.

---

## PriceHawk Dashboard (divergence from this spec)

The PriceHawk HA integration dashboard at
`custom_components/pricehawk/www/dashboard.html` deliberately does NOT
follow the Claude marketing-site spec above. PriceHawk is a dark
**data-dashboard** surfaced inside the Home Assistant sidebar iframe,
not a warm-canvas editorial site, and its visual language is
incompatible with the cream/coral/dark-navy trinity documented in this
file.

### Why divergence

- **Surface context**: PriceHawk renders inside HA's chrome alongside
other dark dashboards (Lovelace, Energy, Logbook). A warm-cream canvas
would look broken next to those panels. Most HA users run dark mode
by default; cream-on-cream would be uncomfortable late at night
during high-tariff windows when the dashboard is actually consulted.
- **Information density**: a data-dashboard with 16+ live entity reads
+ a ranked alternatives table needs tabular numerals, mono digits,
and high-contrast accent colours. The editorial typography stack
(serif display + humanist sans) doesn't suit dense tabular layouts.
- **Brand independence**: PriceHawk is an open-source HACS integration,
not an Anthropic product. The Anthropic coral / radial-spike mark
doesn't apply here. PriceHawk has its own logo (orange hawk-head
glyph at `custom_components/pricehawk/icon.png`).

### What PriceHawk DOES inherit from this spec

- **Typographic rationale**: humanist sans (Outfit, here, vs StyreneB)
for UI text; mono with tabular numerics (IBM Plex Mono, here, vs no
mono in the Claude spec) for all money + rate values. The "use mono
for numbers users compare against each other" rule is unbreakable.
- **Card-as-surface model**: rounded `border-radius` (12-16px),
subtle border, optional 1px gradient top-stroke on hover for
affordance. PriceHawk uses `--card-radius: 16px` matching the Claude
spec's `r-lg: 18px` ballpark.
- **Accent-colour discipline**: ONE positive, ONE negative, ONE
neutral. Don't introduce a fourth accent. Claude uses
primary-coral as its single accent; PriceHawk uses
`--accent-positive` (savings green) + `--accent-negative` (loss red)
+ `--accent-neutral` (info blue) + `--accent-warn` (accruing amber)
— four because the dashboard surfaces four distinct semantic states,
not as decoration.

### PriceHawk token map (for reference)

```css
--bg-base: #070B14 // OLED-friendly true black
--bg-surface: #0C1220
--bg-card: rgba(15,23,42,0.6)
--text-primary: #F1F5F9
--text-secondary: #94A3B8
--text-muted: #64748B
--accent-positive: #10B981 // savings, "you save"
--accent-negative: #EF4444 // loss, "you lose"
--accent-neutral: #38BDF8 // info, current plan / pinned baseline
--accent-warn: #F59E0B // accruing, < 7 days of backfill history
--card-radius: 16px
--card-blur: 20px
```

Light theme inverts via `[data-theme="light"]` selector overriding the
same tokens (canvas: `#F5F6FA`, card: `rgba(255,255,255,0.78)`, accents
shift one stop darker for contrast). Theme persists to
`localStorage['pricehawk-theme']`; first-visit defaults to OS
`prefers-color-scheme`.

### Where to look for PriceHawk's full visual treatment

- `assets/dashboard-v3-apple.html` — the v3 visual seed (1478 LOC
dark-theme mockup; ambient radial bg, noise overlay, Outfit + IBM
Plex Mono). Not the deployed dashboard, but the design-language
source-of-truth.
- `custom_components/pricehawk/www/dashboard.html` — the actual
deployed dashboard at `/local/pricehawk/dashboard.html`. Hierarchy:
nav / hero row (current cost + savings) / period tabs
(today|week|month|3month|year) / ranked alternatives table /
drill-in card / data-health footer.

Don't try to reconcile PriceHawk back into the Claude spec.
They are different products and the visual languages are
deliberately separate.
Loading
Loading