From ea3577a45e2136d4b1ba137e0e84e561bd5e7b94 Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Fri, 15 May 2026 19:10:56 -0700 Subject: [PATCH] Items-list sparklines, retro CSS, pinned tooling, deploy docs - Bulk-load recent price points per item and render a sparkline in the items list (new LoadRecentPriceHistory query avoids N+1). - Add retro.css visual layer and refreshed login/items/layout styling. - Swap the logo from webp to avif. - Pin htmx/Chart.js/Tailwind/templ versions in the Makefile with vendor / tools / update-deps targets; README documents the dependency-bump flow and the hardened systemd deploy. Co-Authored-By: Claude Opus 4.7 (1M context) --- Makefile | 59 +++- README.md | 111 +++++- Veola.webp | Bin 57936 -> 0 bytes internal/db/queries.go | 43 +++ internal/handlers/items.go | 14 +- static/css/app.css | 163 +++++++++ static/css/retro.css | 118 +++++++ static/css/tailwind.css | 2 +- static/img/veola.avif | Bin 0 -> 19354 bytes static/img/veola.webp | Bin 57936 -> 0 bytes static/js/price-chart.js | 56 ++- templates/items.templ | 119 ++++++- templates/items_templ.go | 693 ++++++++++++++++++++++++------------- templates/layout.templ | 7 +- templates/layout_templ.go | 10 +- templates/login.templ | 106 +++--- templates/login_templ.go | 16 +- 17 files changed, 1174 insertions(+), 343 deletions(-) delete mode 100644 Veola.webp create mode 100644 static/css/retro.css create mode 100644 static/img/veola.avif delete mode 100644 static/img/veola.webp diff --git a/Makefile b/Makefile index 9a1bf32..c00b87f 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,28 @@ # Veola build. # -# Requires the `templ` CLI (go install github.com/a-h/templ/cmd/templ@latest). -# The Tailwind standalone CLI is fetched on demand into bin/ (gitignored) — no -# node toolchain required. static/css/tailwind.css is a committed build -# artifact so a plain `go build` deploy still has styles; run `make css` -# (or `make build`) after touching templates or static/css/input.css. +# Tool dependencies — the `*_VERSION` variables below are the single source of +# truth for every non-Go thing Veola pulls in at build time. To upgrade one, +# bump its version and run `make vendor` (vendored JS) or just `make css` +# (Tailwind) on a clean `bin/` to refetch. +# +# Go module dependencies live in go.mod and are bumped via `make update-deps`. TAILWIND_VERSION := v3.4.17 -TAILWIND_BIN := bin/tailwindcss -# linux-x64 only; change the asset name for other platforms. -TAILWIND_URL := https://github.com/tailwindlabs/tailwindcss/releases/download/$(TAILWIND_VERSION)/tailwindcss-linux-x64 -TEMPL := $(shell go env GOPATH)/bin/templ +HTMX_VERSION := 2.0.4 +CHARTJS_VERSION := 4.4.6 +TEMPL_VERSION := v0.3.1020 -.PHONY: all generate css build run test clean +TAILWIND_BIN := bin/tailwindcss +# Override TAILWIND_URL for non-linux-x64 platforms — see README "Build-time +# tools" for the matching asset names on the Tailwind releases page. +TAILWIND_URL ?= https://github.com/tailwindlabs/tailwindcss/releases/download/$(TAILWIND_VERSION)/tailwindcss-linux-x64 + +HTMX_URL := https://unpkg.com/htmx.org@$(HTMX_VERSION)/dist/htmx.min.js +CHARTJS_URL := https://cdn.jsdelivr.net/npm/chart.js@$(CHARTJS_VERSION)/dist/chart.umd.min.js + +TEMPL := $(shell go env GOPATH)/bin/templ + +.PHONY: all generate css build run test clean tools vendor update-deps all: build @@ -41,3 +51,32 @@ test: clean: rm -f veola-bin + +# Install the pinned templ CLI into $(go env GOPATH)/bin. Pinning matters +# because two contributors on different templ versions can produce +# differently-formatted generated Go for the same .templ source. +tools: + go install github.com/a-h/templ/cmd/templ@$(TEMPL_VERSION) + +# Re-fetch vendored third-party JS at the currently pinned versions, +# overwriting whatever is on disk. Also clears bin/tailwindcss so the next +# `make css` pulls the pinned Tailwind binary. Use after bumping any of +# HTMX_VERSION / CHARTJS_VERSION / TAILWIND_VERSION. +vendor: + curl -sL --fail $(HTMX_URL) -o static/vendor/htmx.min.js + curl -sL --fail $(CHARTJS_URL) -o static/vendor/chart.umd.min.js + rm -f $(TAILWIND_BIN) + $(MAKE) $(TAILWIND_BIN) + +# Bump Go module dependencies to their newest compatible versions, then +# print upstream-release URLs for the non-Go tools so you can decide +# whether to bump those pins too. Does not modify the pinned versions. +update-deps: + go get -u ./... + go mod tidy + @echo + @echo "Pinned tool versions (current → upstream releases):" + @printf " Tailwind %s → https://github.com/tailwindlabs/tailwindcss/releases/latest\n" "$(TAILWIND_VERSION)" + @printf " htmx %s → https://github.com/bigskysoftware/htmx/releases/latest\n" "$(HTMX_VERSION)" + @printf " Chart.js %s → https://github.com/chartjs/Chart.js/releases/latest\n" "$(CHARTJS_VERSION)" + @printf " templ %s → https://github.com/a-h/templ/releases/latest\n" "$(TEMPL_VERSION)" diff --git a/README.md b/README.md index d1ae0cd..921f7f2 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,68 @@ See [`veola-spec.md`](veola-spec.md) for the full specification. ## Requirements - Go 1.22+ (developed against 1.25) +- `make`, `curl`, and a POSIX shell (for the Makefile) - A reachable [ntfy](https://ntfy.sh) instance (self-hosted or ntfy.sh) - An [eBay developer](https://developer.ebay.com) keyset (App ID + Cert ID) — for eBay marketplaces - An [Apify](https://apify.com) account + API key — for the non-eBay marketplaces -- To build from source: the [`templ`](https://templ.guide) CLI. The Tailwind standalone CLI is fetched automatically by the Makefile — no Node toolchain required. + +### Build-time tools + +All non-Go tool versions are pinned in the Makefile at the top: + +```make +TAILWIND_VERSION := v3.4.17 +HTMX_VERSION := 2.0.4 +CHARTJS_VERSION := 4.4.6 +TEMPL_VERSION := v0.3.1020 +``` + +Install `templ` yourself at the pinned version; Tailwind is downloaded for you on first `make css`. htmx and Chart.js are already vendored in `static/vendor/` at the pinned versions above and committed to the repo. + +```sh +# Install the pinned templ CLI. +make tools + +# (or, equivalently) +go install github.com/a-h/templ/cmd/templ@v0.3.1020 + +# Confirm it's on PATH (typically $(go env GOPATH)/bin). +templ --version +``` + +You **do not need Node, npm, or Yarn**. Tailwind ships as a self-contained binary — the Makefile fetches the [standalone Tailwind CLI](https://github.com/tailwindlabs/tailwindcss/releases) into `./bin/tailwindcss` on first `make css`. The `bin/` directory is gitignored. + +By default the Makefile downloads the **linux-x64** build. For other platforms, override `TAILWIND_URL` on the command line — pick the matching asset from the [Tailwind releases page](https://github.com/tailwindlabs/tailwindcss/releases/tag/v3.4.17): + +```sh +# macOS Apple Silicon +make css TAILWIND_URL=https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.17/tailwindcss-macos-arm64 + +# macOS Intel +make css TAILWIND_URL=https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.17/tailwindcss-macos-x64 + +# linux-arm64 +make css TAILWIND_URL=https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.17/tailwindcss-linux-arm64 +``` + +Or drop a binary at `./bin/tailwindcss` yourself and the Makefile will use it. ## Build +First time: + +```sh +go install github.com/a-h/templ/cmd/templ@latest # if not already +make build # fetches Tailwind, runs templ, compiles +``` + +Subsequent builds: + ```sh make build ``` -This runs `templ generate`, compiles Tailwind, and produces `veola-bin`. Makefile targets: +This runs `templ generate`, compiles Tailwind utilities into `static/css/tailwind.css`, and produces `veola-bin`. Makefile targets: | Target | What it does | | --- | --- | @@ -38,10 +88,11 @@ This runs `templ generate`, compiles Tailwind, and produces `veola-bin`. Makefil | `make build` | `generate` + `css` + `go build -o veola-bin .` | | `make run` | `build`, then run against `config.toml` | | `make test` | `go test ./...` | +| `make clean` | Remove `veola-bin` | The binary is named `veola-bin` rather than `veola` because the module is also `veola` — `go build` cannot write a binary with the same name as the module dir. -`static/css/tailwind.css` is committed, so a deploy box can `go build -o veola-bin .` without the Tailwind CLI. Re-run `make css` whenever you change templates or `static/css/input.css`. The hand-written component layer in `static/css/app.css` is loaded separately and needs no rebuild. +`static/css/tailwind.css` is committed, so a deploy box can `go build -o veola-bin .` without the Tailwind CLI or even `make`. Re-run `make css` whenever you change templates or `static/css/input.css`. The hand-written component layer in `static/css/app.css` is loaded separately and needs no rebuild. ## Configure @@ -70,13 +121,22 @@ Other notable config: ./veola-bin -config config.toml ``` +CLI flags: + +| Flag | Default | Notes | +| --- | --- | --- | +| `-config ` | `config.toml` | Path to the TOML config file | +| `-debug` | off | Verbose `log/slog` at `LevelDebug`. Logs raw external API payloads (eBay / ZenMarket / etc.) — useful when diagnosing parse failures. Not for production. | + First-run flow: 1. Visit `http://localhost:8080/`. With no users, you are redirected to `/setup`. -2. Create the admin account. +2. Create the admin account. The first user is always an admin. 3. Log in at `/login`. 4. Add items at `/items/new`. Optionally fill in your eBay/Apify credentials and ntfy URL via `/settings` if you didn't put them in `config.toml`. The Settings page also shows the running eBay API call count for the day. +Account registration is admin-only — there is no public signup. Once at least one user exists, `/setup` returns 404. New users are created from the Settings page by an admin. + The scheduler starts with the server and polls each active item on its configured interval. The bottom-of-hour global poll runs every `scheduler.global_poll_interval_minutes`. ## Layout @@ -108,6 +168,30 @@ go test ./... Unit tests cover crypto round-trip, db round-trip and dedup, scheduler alert/badge logic, and eBay marketplace/filter mapping. No handler-level tests yet. +## Keeping dependencies current + +Veola pulls from four sources, all version-pinned for reproducibility. + +```sh +# Bump Go module deps to their newest compatible versions, then prints +# upstream-release URLs for the four pinned tools so you can spot bumps. +make update-deps + +# After bumping any of TAILWIND_VERSION / HTMX_VERSION / CHARTJS_VERSION +# in the Makefile, refetch the vendored assets at the new pins: +make vendor + +# After bumping TEMPL_VERSION: +make tools + +# Then rebuild and run the test suite: +make build && make test +``` + +The pinned tool versions live at the top of the Makefile. The vendored JS at `static/vendor/htmx.min.js` and `static/vendor/chart.umd.min.js` is committed and updated only by `make vendor`. Tailwind v3.4.17 is intentionally pinned — v4 is a breaking release that drops the `tailwind.config.js` format Veola uses. + +For automated tracking, point [Dependabot](https://docs.github.com/en/code-security/dependabot) or [Renovate](https://docs.renovatebot.com/) at the repo. Both can watch `go.mod` natively; Renovate's custom-regex managers can also track the `*_VERSION` lines in the Makefile. + ## Operate - The SQLite file lives at `server.db_path` (default `./veola.db`). Back this up — it holds your watched items, history, encrypted secrets, and user accounts. @@ -122,6 +206,25 @@ Veola speaks plain HTTP and is meant to sit behind a TLS-terminating reverse pro - Keep `server.secure_cookies = true` (the default). - Terminate TLS at the proxy and set HSTS there — Veola does not emit HSTS itself. - Veola sets `Content-Security-Policy`, `X-Frame-Options`, `X-Content-Type-Options`, and `Referrer-Policy` on every response, and trusts `X-Forwarded-For` for client IPs — configure the proxy to strip client-supplied `X-Forwarded-*` headers so they cannot be spoofed. +- `GET /healthz` returns 200 `ok` with no auth — wire it up to your proxy/uptime probe. + +A hardened systemd unit template lives at [`deploy/veola.service`](deploy/veola.service). It assumes: + +- Binary at `/usr/local/bin/veola-bin` +- Config at `/etc/veola/config.toml` +- A `veola` system user with `/var/lib/veola` as its working / data directory (the only writable path under `ReadWritePaths`) + +Install sketch: + +```sh +sudo useradd --system --home /var/lib/veola --shell /usr/sbin/nologin veola +sudo install -d -o veola -g veola -m 0750 /var/lib/veola /etc/veola +sudo install -m 0755 veola-bin /usr/local/bin/veola-bin +sudo install -m 0640 -o veola -g veola config.toml /etc/veola/config.toml +sudo install -m 0644 deploy/veola.service /etc/systemd/system/veola.service +sudo systemctl daemon-reload +sudo systemctl enable --now veola +``` ## Aesthetic diff --git a/Veola.webp b/Veola.webp deleted file mode 100644 index 4bae7683af2faf53b7b05bf48f8ced6f20de88c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57936 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 YHrgtZU{T-hsVz8rFU_y-YhC~V0GAR$3;+NC diff --git a/internal/db/queries.go b/internal/db/queries.go index 999902c..fc4949f 100644 --- a/internal/db/queries.go +++ b/internal/db/queries.go @@ -798,6 +798,49 @@ func (s *Store) InsertPricePoint(ctx context.Context, p *models.PricePoint) erro return err } +// LoadRecentPriceHistory bulk-loads the last `perItem` price points (oldest → +// newest) for each item id. Returns a map keyed by item id; items with no +// history are absent from the map. Used by the items list page to render a +// sparkline per row without N+1 queries. +func (s *Store) LoadRecentPriceHistory(ctx context.Context, ids []int64, perItem int) (map[int64][]models.PricePoint, error) { + out := make(map[int64][]models.PricePoint, len(ids)) + if len(ids) == 0 || perItem <= 0 { + return out, nil + } + placeholders := make([]string, len(ids)) + args := make([]any, 0, len(ids)+1) + for i, id := range ids { + placeholders[i] = "?" + args = append(args, id) + } + args = append(args, perItem) + q := ` + WITH ranked AS ( + SELECT item_id, price, store, polled_at, + ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY polled_at DESC) AS rn + FROM price_history + WHERE item_id IN (` + strings.Join(placeholders, ",") + `) + ) + SELECT item_id, price, store, polled_at FROM ranked WHERE rn <= ? + ORDER BY item_id, polled_at ASC + ` + rows, err := s.DB.QueryContext(ctx, q, args...) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + var p models.PricePoint + var store sql.NullString + if err := rows.Scan(&p.ItemID, &p.Price, &store, &p.PolledAt); err != nil { + return nil, err + } + p.Store = s.dec(store.String) + out[p.ItemID] = append(out[p.ItemID], p) + } + return out, rows.Err() +} + func (s *Store) ListPriceHistory(ctx context.Context, itemID int64) ([]models.PricePoint, error) { rows, err := s.DB.QueryContext(ctx, `SELECT id, item_id, price, store, polled_at FROM price_history WHERE item_id = ? ORDER BY polled_at ASC`, diff --git a/internal/handlers/items.go b/internal/handlers/items.go index dcfdbde..79ef021 100644 --- a/internal/handlers/items.go +++ b/internal/handlers/items.go @@ -30,11 +30,22 @@ func (a *App) GetItems(w http.ResponseWriter, r *http.Request) { } } cats, _ := a.Store.ListCategories(r.Context()) + + // Bulk-load recent price history so each row can render a sparkline + // without N+1 queries. 20 points is enough for a meaningful trend line + // at 80px wide and stays cheap on the largest realistic watchlists. + ids := make([]int64, 0, len(items)) + for _, it := range items { + ids = append(ids, it.ID) + } + history, _ := a.Store.LoadRecentPriceHistory(r.Context(), ids, 20) + render(w, r, templates.Items(templates.ItemsData{ Page: a.page(r, "Items", "items"), Items: items, Categories: cats, SelectedCategory: cat, + PriceHistory: history, })) } @@ -387,7 +398,8 @@ func (a *App) PostToggleItem(w http.ResponseWriter, r *http.Request) { return } a.Scheduler.SyncItem(*it) - render(w, r, templates.ItemRow(*it, a.Auth.CSRFToken(r.Context()))) + hist, _ := a.Store.LoadRecentPriceHistory(r.Context(), []int64{id}, 20) + render(w, r, templates.ItemRow(*it, a.Auth.CSRFToken(r.Context()), hist[id])) } func (a *App) PostDeleteItem(w http.ResponseWriter, r *http.Request) { diff --git a/static/css/app.css b/static/css/app.css index 235c548..f001fcf 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -176,6 +176,19 @@ a:hover { text-decoration: underline; } } .v-side-nav a:hover { color: white; } +/* The brand wordmark at the top of the sidebar is also an anchor (→ /), but + shouldn't pick up the active-item border-left / padding treatment that + the nav links get. Higher specificity overrides .v-side-nav a defaults. */ +.v-side-nav a.v-side-brand { + border-left: 0; + color: var(--text); + text-decoration: none; +} +.v-side-nav a.v-side-brand:hover { + text-decoration: none; + filter: brightness(1.15); +} + .v-veola-portrait { background: #f3ead8; border-radius: 12px; @@ -304,6 +317,156 @@ table.v-table tbody tr:hover td { background: rgba(0, 164, 228, 0.08); } 50% { opacity: 0.55; } } +/* Sparkline cells in the items list. Color follows trend: green when the + latest price is meaningfully below the running average (good news for a + watchlist), red when it's risen, neutral otherwise. */ +.v-sparkline { display: block; overflow: visible; } +.v-spark-down { color: var(--success); filter: drop-shadow(0 0 4px rgba(0, 228, 164, 0.45)); } +.v-spark-up { color: var(--danger); filter: drop-shadow(0 0 4px rgba(232, 64, 64, 0.40)); } +.v-spark-flat { color: var(--text-2); } + +/* Trend arrow rendered next to Best Price. Same palette as the sparkline, + so a glance at the column reads consistently. */ +.v-trend { font-size: 0.95rem; font-weight: 700; } +.v-trend-down { color: var(--success); } +.v-trend-up { color: var(--danger); } +.v-trend-flat { color: var(--text-2); } + +/* Mascot "deal" moment: Veola appears next to an item's name only when the + current best price is at or below target. Small, animated, decorative — + purely a delight hit on top of the existing "Deal" badge. */ +.v-deal-mascot { + width: 24px; + height: 24px; + border-radius: 6px; + object-fit: cover; + background: #f3ead8; + padding: 1px; + box-shadow: 0 0 0 1px rgba(0, 228, 164, 0.6), 0 0 12px rgba(0, 228, 164, 0.45); + animation: v-deal-bob 2.4s ease-in-out infinite; +} +@keyframes v-deal-bob { + 0%, 100% { transform: translateY(0) rotate(-2deg); } + 50% { transform: translateY(-2px) rotate(2deg); } +} + +@media (prefers-reduced-motion: reduce) { + .v-deal-mascot { animation: none; } +} + +/* --- Login / Setup chrome ----------------------------------------------- + The auth pages use the Bare layout (no sidebar) so the form has to carry + its own visual weight. The v-auth-* classes give it that: a glassy card + for the form, a softly-rotating conic halo behind the mascot, a + typeset wordmark + tagline, and the right column is left translucent so + the global aurora reaches edge-to-edge instead of stopping at a flat + blue block. */ + +.v-auth-wordmark { + font-family: 'Outfit', system-ui, sans-serif; + text-shadow: 0 2px 14px rgba(0, 164, 228, 0.30); +} + +.v-auth-card { + /* Slightly heavier shadow + inner glow ring since the card stands + alone on the page with no sidebar context. */ + box-shadow: + 0 16px 44px rgba(0, 0, 80, 0.55), + 0 0 0 1px rgba(0, 164, 228, 0.18), + inset 0 1px 0 rgba(255, 255, 255, 0.06); +} + +.v-auth-tagline { + margin-top: 1.5rem; + text-align: center; + letter-spacing: 0.34em; + font-size: 0.72rem; + text-transform: uppercase; + color: var(--text-2); + text-shadow: 0 0 12px rgba(0, 164, 228, 0.35); +} + +.v-auth-portrait-col { + /* Translucent overlay instead of a solid block: a soft inward vignette + plus a faint diagonal sheen, keeping the column distinct from the + form side without hiding the aurora behind it. */ + background: + radial-gradient(ellipse at center, rgba(0, 0, 0, 0.0) 30%, rgba(0, 0, 0, 0.25) 100%), + linear-gradient(135deg, rgba(0, 164, 228, 0.05) 0%, rgba(245, 196, 0, 0.04) 100%); + position: relative; + overflow: hidden; +} + +.v-auth-portrait-halo { + position: relative; + isolation: isolate; + width: min(420px, 80%); + display: flex; + align-items: center; + justify-content: center; + animation: v-auth-portrait-float 7s ease-in-out infinite; + will-change: transform; +} + +/* Rotating conic-gradient halo. Sits behind the portrait via z-index -1 + inside the isolated stacking context, blurred so it reads as a glow + rather than a hard rainbow. */ +.v-auth-portrait-halo::before { + content: ""; + position: absolute; + inset: -15%; + border-radius: 50%; + background: conic-gradient( + from 180deg, + rgba(0, 164, 228, 0.55), + rgba(245, 196, 0, 0.40), + rgba(232, 64, 64, 0.30), + rgba(0, 228, 164, 0.40), + rgba(0, 164, 228, 0.55) + ); + filter: blur(50px); + z-index: -1; + animation: v-auth-portrait-spin 22s linear infinite; + will-change: transform; +} + +/* A second, slower halo gives the impression of depth: two layers of + light rotating at different speeds keep the eye from locking onto a + single repeating pattern. */ +.v-auth-portrait-halo::after { + content: ""; + position: absolute; + inset: -25%; + border-radius: 50%; + background: conic-gradient( + from 0deg, + rgba(0, 228, 164, 0.18), + rgba(0, 164, 228, 0.10), + rgba(245, 196, 0, 0.14), + rgba(0, 228, 164, 0.18) + ); + filter: blur(70px); + z-index: -1; + animation: v-auth-portrait-spin 42s linear infinite reverse; + will-change: transform; + opacity: 0.85; +} + +@keyframes v-auth-portrait-spin { + to { transform: rotate(360deg); } +} + +@keyframes v-auth-portrait-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-6px); } +} + +@media (prefers-reduced-motion: reduce) { + .v-auth-portrait-halo, + .v-auth-portrait-halo::before, + .v-auth-portrait-halo::after { animation: none; } +} + @media (prefers-reduced-motion: reduce) { .v-card { transition: none; } .v-card:hover { transform: none; } diff --git a/static/css/retro.css b/static/css/retro.css new file mode 100644 index 0000000..1f9b344 --- /dev/null +++ b/static/css/retro.css @@ -0,0 +1,118 @@ +/* retro.css — optional "retro-arcade identity" layer. + * + * Entirely additive. To revert, delete this file and remove its tag + * from templates/layout.templ. Nothing else depends on it. + * + * What this adds: + * 1. Two independently-animated aurora blobs that drift across the + * viewport on different paths — the real "shifting light" feel, + * not a synchronized slide. + * 2. Dot-grid overlay over the whole viewport. + * 3. Heavier display headings with a soft accent glow. + * + * Both blobs live on html pseudo-elements at z-index -2 / -1, sitting + * behind everything else so card glassiness still reads on top. + */ + +body { + /* Let the html-level aurora show through. app.css set a solid blue here; + dropping it here unlocks the gradient. Removing retro.css restores the + original solid background automatically. */ + background: transparent; +} + +html { + background-color: var(--bg); + /* Static dot-grid baked into the root background so the two pseudo-element + slots stay free for the animated blobs. */ + background-image: radial-gradient( + circle, + rgba(255, 255, 255, 0.045) 1px, + transparent 1px + ); + background-size: 26px 26px; + background-attachment: fixed; +} + +/* Two aurora blobs animated with seven waypoints each (no `alternate`, so + the second half doesn't mirror the first) on incommensurate periods — + the two cycles drift in and out of phase, so the eye never locks onto a + repeating pattern even though each blob is technically on a loop. + Opacity also wobbles so the lights "breathe" instead of just sliding. */ + +html::before { + content: ""; + position: fixed; + inset: 0; + z-index: -2; + pointer-events: none; + background: radial-gradient( + closest-side, + rgba(0, 164, 228, 0.60), + rgba(0, 164, 228, 0.0) 70% + ); + width: 70vmax; + height: 70vmax; + border-radius: 50%; + filter: blur(60px); + /* Centered base; keyframes drive all motion from here. */ + top: 50%; left: 50%; + margin-top: -35vmax; margin-left: -35vmax; + animation: v-retro-drift-a 41s cubic-bezier(.6,.1,.4,.9) infinite; + will-change: transform, opacity; +} + +html::after { + content: ""; + position: fixed; + inset: 0; + z-index: -2; + pointer-events: none; + background: radial-gradient( + closest-side, + rgba(245, 196, 0, 0.50), + rgba(245, 196, 0, 0.0) 70% + ); + width: 55vmax; + height: 55vmax; + border-radius: 50%; + filter: blur(70px); + top: 50%; left: 50%; + margin-top: -27.5vmax; margin-left: -27.5vmax; + animation: v-retro-drift-b 29s cubic-bezier(.5,.2,.3,.8) infinite; + will-change: transform, opacity; +} + +@keyframes v-retro-drift-a { + 0% { transform: translate3d(-30vw, -20vh, 0) scale(1.00); opacity: 0.85; } + 14% { transform: translate3d( 5vw, -28vh, 0) scale(1.18); opacity: 1.00; } + 29% { transform: translate3d( 32vw, -10vh, 0) scale(0.92); opacity: 0.75; } + 43% { transform: translate3d( 18vw, 22vh, 0) scale(1.10); opacity: 0.95; } + 58% { transform: translate3d(-15vw, 30vh, 0) scale(1.05); opacity: 0.80; } + 72% { transform: translate3d(-38vw, 8vh, 0) scale(0.95); opacity: 1.00; } + 86% { transform: translate3d(-20vw, -12vh, 0) scale(1.12); opacity: 0.90; } + 100% { transform: translate3d(-30vw, -20vh, 0) scale(1.00); opacity: 0.85; } +} + +@keyframes v-retro-drift-b { + 0% { transform: translate3d( 28vw, -25vh, 0) scale(1.00); opacity: 0.70; } + 17% { transform: translate3d( 0vw, -10vh, 0) scale(1.20); opacity: 0.95; } + 33% { transform: translate3d(-30vw, 8vh, 0) scale(0.90); opacity: 0.80; } + 48% { transform: translate3d(-12vw, 28vh, 0) scale(1.10); opacity: 1.00; } + 64% { transform: translate3d( 22vw, 18vh, 0) scale(0.95); opacity: 0.75; } + 80% { transform: translate3d( 36vw, -2vh, 0) scale(1.15); opacity: 0.90; } + 100% { transform: translate3d( 28vw, -25vh, 0) scale(1.00); opacity: 0.70; } +} + +/* Display headings: heavier, slightly tighter, with an accent glow that ties + into the card border gradient. Class-free selectors so existing Tailwind + utilities (text-3xl etc.) stack on top untouched. */ +h1, h2 { + font-weight: 800; + letter-spacing: -0.02em; + text-shadow: 0 2px 14px rgba(0, 164, 228, 0.30); +} + +@media (prefers-reduced-motion: reduce) { + html::before, html::after { animation: none; } +} diff --git a/static/css/tailwind.css b/static/css/tailwind.css index 336aa96..e45a086 100644 --- a/static/css/tailwind.css +++ b/static/css/tailwind.css @@ -1,2 +1,2 @@ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: } -/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.my-4{margin-bottom:1rem;margin-top:1rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-40{height:10rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-40{width:10rem}.w-48{width:12rem}.w-full{width:100%}.max-w-3xl{max-width:48rem}.max-w-6xl{max-width:72rem}.max-w-\[140px\]{max-width:140px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.cursor-pointer{cursor:pointer}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2rem*var(--tw-space-y-reverse));margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)))}.overflow-hidden,.truncate{overflow:hidden}.truncate{text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.bg-\[\#152560\]{--tw-bg-opacity:1;background-color:rgb(21 37 96/var(--tw-bg-opacity,1))}.bg-black\/30{background-color:rgba(0,0,0,.3)}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-4{padding-left:1rem;padding-right:1rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.pb-2{padding-bottom:.5rem}.pl-5{padding-left:1.25rem}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-wide{letter-spacing:.025em}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@media (min-width:768px){.md\:flex{display:flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-\[160px_1fr\]{grid-template-columns:160px 1fr}.md\:flex-row{flex-direction:row}} \ No newline at end of file +/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.my-4{margin-bottom:1rem;margin-top:1rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-40{height:10rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-40{width:10rem}.w-48{width:12rem}.w-full{width:100%}.max-w-3xl{max-width:48rem}.max-w-6xl{max-width:72rem}.max-w-\[140px\]{max-width:140px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.cursor-pointer{cursor:pointer}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2rem*var(--tw-space-y-reverse));margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)))}.overflow-hidden,.truncate{overflow:hidden}.truncate{text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.bg-\[\#152560\]{--tw-bg-opacity:1;background-color:rgb(21 37 96/var(--tw-bg-opacity,1))}.bg-black\/30{background-color:rgba(0,0,0,.3)}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-4{padding-left:1rem;padding-right:1rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.pb-2{padding-bottom:.5rem}.pl-5{padding-left:1.25rem}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-wide{letter-spacing:.025em}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@media (min-width:768px){.md\:flex{display:flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-\[160px_1fr\]{grid-template-columns:160px 1fr}.md\:flex-row{flex-direction:row}} \ No newline at end of file diff --git a/static/img/veola.avif b/static/img/veola.avif new file mode 100644 index 0000000000000000000000000000000000000000..64dec6764f5fc5566c539db8b8e14405e0154db3 GIT binary patch literal 19354 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 YHrgtZU{T-hsVz8rFU_y-YhC~V0GAR$3;+NC diff --git a/static/js/price-chart.js b/static/js/price-chart.js index ecc8f6d..780dfac 100644 --- a/static/js/price-chart.js +++ b/static/js/price-chart.js @@ -9,7 +9,29 @@ return; } var data = JSON.parse(canvas.dataset.chart); - new Chart(canvas.getContext('2d'), { + var ctx = canvas.getContext('2d'); + + // Vertical gradient fill — bright accent at the top, transparent at the + // baseline — makes the chart read at a glance instead of as a thin line. + var fill = ctx.createLinearGradient(0, 0, 0, canvas.clientHeight || 200); + fill.addColorStop(0, 'rgba(0, 228, 164, 0.45)'); + fill.addColorStop(1, 'rgba(0, 228, 164, 0.00)'); + + // Chart.js plugin that paints a soft glow under the line stroke before + // the dataset draws. Cheap enough to keep on by default; respects + // prefers-reduced-motion only insofar as nothing animates here. + var glowPlugin = { + id: 'priceLineGlow', + beforeDatasetDraw: function (chart) { + var c = chart.ctx; + c.save(); + c.shadowColor = 'rgba(0, 228, 164, 0.55)'; + c.shadowBlur = 12; + }, + afterDatasetDraw: function (chart) { chart.ctx.restore(); } + }; + + new Chart(ctx, { type: 'line', data: { labels: data.labels, @@ -17,19 +39,37 @@ label: 'Best price', data: data.points, borderColor: '#00e4a4', - backgroundColor: 'rgba(0,228,164,0.15)', - pointBackgroundColor: '#e84040', + borderWidth: 2.5, + backgroundColor: fill, + pointBackgroundColor: '#ffffff', + pointBorderColor: '#00e4a4', + pointBorderWidth: 1.5, pointRadius: 3, - tension: 0.25, + pointHoverRadius: 5, + tension: 0.3, fill: true }] }, options: { + responsive: true, + maintainAspectRatio: false, + interaction: { mode: 'index', intersect: false }, scales: { - x: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.07)' } }, - y: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.07)' } } + x: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.06)' } }, + y: { ticks: { color: '#a8c0f0' }, grid: { color: 'rgba(255,255,255,0.06)' } } }, - plugins: { legend: { labels: { color: '#ffffff' } } } - } + plugins: { + legend: { labels: { color: '#ffffff' } }, + tooltip: { + backgroundColor: 'rgba(20, 32, 80, 0.95)', + borderColor: 'rgba(0, 164, 228, 0.6)', + borderWidth: 1, + titleColor: '#ffffff', + bodyColor: '#a8c0f0', + padding: 10 + } + } + }, + plugins: [glowPlugin] }); })(); diff --git a/templates/items.templ b/templates/items.templ index a31a005..889a850 100644 --- a/templates/items.templ +++ b/templates/items.templ @@ -2,6 +2,8 @@ package templates import ( "fmt" + "math" + "strings" "veola/internal/models" ) @@ -11,6 +13,85 @@ type ItemsData struct { Items []models.Item Categories []string SelectedCategory string + // PriceHistory holds the last ~20 points per item id, ascending in time. + // Nil/missing entries render an empty sparkline cell. + PriceHistory map[int64][]models.PricePoint +} + +// sparklinePoints turns a price-history slice into an SVG polyline "points" +// attribute, normalized to a 80x24 viewBox with 2px padding so endpoints +// don't clip the stroke. Returns "" when there isn't enough history to draw. +func sparklinePoints(history []models.PricePoint) string { + if len(history) < 2 { + return "" + } + const w, h, pad = 80.0, 24.0, 2.0 + minP, maxP := math.Inf(1), math.Inf(-1) + for _, p := range history { + if p.Price < minP { + minP = p.Price + } + if p.Price > maxP { + maxP = p.Price + } + } + span := maxP - minP + if span == 0 { + // All-equal series: draw a flat line through the middle. + span = 1 + } + step := (w - 2*pad) / float64(len(history)-1) + parts := make([]string, len(history)) + for i, p := range history { + x := pad + float64(i)*step + y := h - pad - ((p.Price-minP)/span)*(h-2*pad) + parts[i] = fmt.Sprintf("%.1f,%.1f", x, y) + } + return strings.Join(parts, " ") +} + +// sparklineTrendClass compares the last point to the average of the prior +// points and returns a CSS class so the sparkline tints green (price down), +// red (up), or neutral. Threshold is ±3% to ignore tiny wobbles. +func sparklineTrendClass(history []models.PricePoint) string { + if len(history) < 2 { + return "" + } + last := history[len(history)-1].Price + var sum float64 + for _, p := range history[:len(history)-1] { + sum += p.Price + } + avg := sum / float64(len(history)-1) + if avg == 0 { + return "v-spark-flat" + } + switch { + case last <= avg*0.97: + return "v-spark-down" + case last >= avg*1.03: + return "v-spark-up" + } + return "v-spark-flat" +} + +// trendArrow returns the unicode arrow + a CSS class describing the direction. +// Same threshold logic as the sparkline. Empty when there's no trend signal. +func trendArrow(history []models.PricePoint) (glyph, class string) { + switch sparklineTrendClass(history) { + case "v-spark-down": + return "↓", "v-trend-down" + case "v-spark-up": + return "↑", "v-trend-up" + case "v-spark-flat": + return "→", "v-trend-flat" + } + return "", "" +} + +// isDeal is the gate for the mascot "deal" moment on a row. +func isDeal(it models.Item) bool { + return it.BestPrice != nil && it.TargetPrice != nil && *it.BestPrice <= *it.TargetPrice } templ itemsBody(d ItemsData) { @@ -41,6 +122,7 @@ templ itemsBody(d ItemsData) { Category Target Best Price + Trend Last Polled Status @@ -48,7 +130,7 @@ templ itemsBody(d ItemsData) { for _, it := range d.Items { - @itemRow(it, d.CSRFToken) + @itemRow(it, d.CSRFToken, d.PriceHistory[it.ID]) } @@ -60,7 +142,7 @@ templ itemsBody(d ItemsData) { templ itemsEmpty() {
- Veola + Veola

Nothing on the watchlist.

@@ -70,10 +152,15 @@ templ itemsEmpty() {
} -templ itemRow(it models.Item, csrf string) { +templ itemRow(it models.Item, csrf string, history []models.PricePoint) { - { it.Name } +
+ if isDeal(it) { + + } + { it.Name } +
if it.LastPollError != "" {
@@ -89,7 +176,12 @@ templ itemRow(it models.Item, csrf string) { if it.BestPrice != nil { -
{ fmtPrice(it.BestPrice, it.BestPriceCurrency) }
+
+ { fmtPrice(it.BestPrice, it.BestPriceCurrency) } + if glyph, cls := trendArrow(history); glyph != "" { + { glyph } + } +
if it.BestPriceURL != "" { { it.BestPriceStore } } else if it.BestPriceStore != "" { @@ -99,6 +191,15 @@ templ itemRow(it models.Item, csrf string) { not yet } + + if pts := sparklinePoints(history); pts != "" { + + } else { + + } + if it.LastPolledAt != nil { { humanTime(*it.LastPolledAt) } @@ -154,9 +255,11 @@ templ Items(d ItemsData) { @Layout(d.Page, itemsBody(d)) } -// ItemRow renders a single row partial, used by HTMX endpoints. -templ ItemRow(it models.Item, csrf string) { - @itemRow(it, csrf) +// ItemRow renders a single row partial, used by HTMX endpoints. Callers +// that don't have history cheaply on hand pass nil; the sparkline cell +// degrades to an em-dash placeholder. +templ ItemRow(it models.Item, csrf string, history []models.PricePoint) { + @itemRow(it, csrf, history) } // EmptyRow lets a delete handler return a row replacement that vanishes. diff --git a/templates/items_templ.go b/templates/items_templ.go index 46310cf..0d5c610 100644 --- a/templates/items_templ.go +++ b/templates/items_templ.go @@ -10,6 +10,8 @@ import templruntime "github.com/a-h/templ/runtime" import ( "fmt" + "math" + "strings" "veola/internal/models" ) @@ -19,6 +21,85 @@ type ItemsData struct { Items []models.Item Categories []string SelectedCategory string + // PriceHistory holds the last ~20 points per item id, ascending in time. + // Nil/missing entries render an empty sparkline cell. + PriceHistory map[int64][]models.PricePoint +} + +// sparklinePoints turns a price-history slice into an SVG polyline "points" +// attribute, normalized to a 80x24 viewBox with 2px padding so endpoints +// don't clip the stroke. Returns "" when there isn't enough history to draw. +func sparklinePoints(history []models.PricePoint) string { + if len(history) < 2 { + return "" + } + const w, h, pad = 80.0, 24.0, 2.0 + minP, maxP := math.Inf(1), math.Inf(-1) + for _, p := range history { + if p.Price < minP { + minP = p.Price + } + if p.Price > maxP { + maxP = p.Price + } + } + span := maxP - minP + if span == 0 { + // All-equal series: draw a flat line through the middle. + span = 1 + } + step := (w - 2*pad) / float64(len(history)-1) + parts := make([]string, len(history)) + for i, p := range history { + x := pad + float64(i)*step + y := h - pad - ((p.Price-minP)/span)*(h-2*pad) + parts[i] = fmt.Sprintf("%.1f,%.1f", x, y) + } + return strings.Join(parts, " ") +} + +// sparklineTrendClass compares the last point to the average of the prior +// points and returns a CSS class so the sparkline tints green (price down), +// red (up), or neutral. Threshold is ±3% to ignore tiny wobbles. +func sparklineTrendClass(history []models.PricePoint) string { + if len(history) < 2 { + return "" + } + last := history[len(history)-1].Price + var sum float64 + for _, p := range history[:len(history)-1] { + sum += p.Price + } + avg := sum / float64(len(history)-1) + if avg == 0 { + return "v-spark-flat" + } + switch { + case last <= avg*0.97: + return "v-spark-down" + case last >= avg*1.03: + return "v-spark-up" + } + return "v-spark-flat" +} + +// trendArrow returns the unicode arrow + a CSS class describing the direction. +// Same threshold logic as the sparkline. Empty when there's no trend signal. +func trendArrow(history []models.PricePoint) (glyph, class string) { + switch sparklineTrendClass(history) { + case "v-spark-down": + return "↓", "v-trend-down" + case "v-spark-up": + return "↑", "v-trend-up" + case "v-spark-flat": + return "→", "v-trend-flat" + } + return "", "" +} + +// isDeal is the gate for the mascot "deal" moment on a row. +func isDeal(it models.Item) bool { + return it.BestPrice != nil && it.TargetPrice != nil && *it.BestPrice <= *it.TargetPrice } func itemsBody(d ItemsData) templ.Component { @@ -59,7 +140,7 @@ func itemsBody(d ItemsData) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(c) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 28, Col: 23} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 109, Col: 23} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2) if templ_7745c5c3_Err != nil { @@ -82,7 +163,7 @@ func itemsBody(d ItemsData) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(c) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 28, Col: 67} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 109, Col: 67} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -104,12 +185,12 @@ func itemsBody(d ItemsData) templ.Component { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
NameCategoryTargetBest PriceLast PolledStatus
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } for _, it := range d.Items { - templ_7745c5c3_Err = itemRow(it, d.CSRFToken).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = itemRow(it, d.CSRFToken, d.PriceHistory[it.ID]).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -148,7 +229,7 @@ func itemsEmpty() templ.Component { templ_7745c5c3_Var4 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
\"Veola\"

Nothing on the watchlist.

Add an item and Veola will keep an eye on it.

Add the first item
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
\"Veola\"

Nothing on the watchlist.

Add an item and Veola will keep an eye on it.

Add the first item
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -156,7 +237,7 @@ func itemsEmpty() templ.Component { }) } -func itemRow(it models.Item, csrf string) templ.Component { +func itemRow(it models.Item, csrf string, history []models.PricePoint) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -184,418 +265,532 @@ func itemRow(it models.Item, csrf string) templ.Component { var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("item-row-%d", it.ID)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 74, Col: 43} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 156, Col: 43} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\"> Edit") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -629,9 +824,9 @@ func Items(d ItemsData) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var32 := templ.GetChildren(ctx) - if templ_7745c5c3_Var32 == nil { - templ_7745c5c3_Var32 = templ.NopComponent + templ_7745c5c3_Var39 := templ.GetChildren(ctx) + if templ_7745c5c3_Var39 == nil { + templ_7745c5c3_Var39 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = Layout(d.Page, itemsBody(d)).Render(ctx, templ_7745c5c3_Buffer) @@ -642,8 +837,10 @@ func Items(d ItemsData) templ.Component { }) } -// ItemRow renders a single row partial, used by HTMX endpoints. -func ItemRow(it models.Item, csrf string) templ.Component { +// ItemRow renders a single row partial, used by HTMX endpoints. Callers +// that don't have history cheaply on hand pass nil; the sparkline cell +// degrades to an em-dash placeholder. +func ItemRow(it models.Item, csrf string, history []models.PricePoint) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -659,12 +856,12 @@ func ItemRow(it models.Item, csrf string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var33 := templ.GetChildren(ctx) - if templ_7745c5c3_Var33 == nil { - templ_7745c5c3_Var33 = templ.NopComponent + templ_7745c5c3_Var40 := templ.GetChildren(ctx) + if templ_7745c5c3_Var40 == nil { + templ_7745c5c3_Var40 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = itemRow(it, csrf).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = itemRow(it, csrf, history).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -689,12 +886,12 @@ func EmptyRow() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var34 := templ.GetChildren(ctx) - if templ_7745c5c3_Var34 == nil { - templ_7745c5c3_Var34 = templ.NopComponent + templ_7745c5c3_Var41 := templ.GetChildren(ctx) + if templ_7745c5c3_Var41 == nil { + templ_7745c5c3_Var41 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/templates/layout.templ b/templates/layout.templ index c5475d3..df554eb 100644 --- a/templates/layout.templ +++ b/templates/layout.templ @@ -23,6 +23,9 @@ templ head(title string) { + + @@ -30,10 +33,10 @@ templ head(title string) { templ Sidebar(active string) {
NameCategoryTargetBest PriceTrendLast PolledStatus
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var7 templ.SafeURL - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/items/%d/results", it.ID))) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 76, Col: 67} + if isDeal(it) { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"\" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "") + var templ_7745c5c3_Var8 templ.SafeURL + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/items/%d/results", it.ID))) if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var8 string - templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(it.Name) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 76, Col: 79} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 162, Col: 68} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(it.Name) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 162, Col: 80} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if it.LastPollError != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" hx-swap=\"innerHTML\">!
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(it.Category) + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(it.Category) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 82, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 169, Col: 35} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if it.TargetPrice != nil { - var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmtPrice(it.TargetPrice, "USD")) + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmtPrice(it.TargetPrice, "USD")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 85, Col: 37} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 172, Col: 37} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if it.BestPrice != nil { - var templ_7745c5c3_Var14 = []any{"font-mono text-lg", priceClass(it.BestPrice, it.TargetPrice)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - if it.BestPriceURL != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if glyph, cls := trendArrow(history); glyph != "" { + var templ_7745c5c3_Var18 = []any{"v-trend", cls} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var17 templ.SafeURL - templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(it.BestPriceURL)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 94, Col: 61} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" target=\"_blank\" rel=\"noopener\">") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var18 string - templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(it.BestPriceStore) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 94, Col: 114} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else if it.BestPriceStore != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var20 string + templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(glyph) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 182, Col: 44} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if it.BestPriceURL != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var22 string + templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(it.BestPriceStore) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 186, Col: 114} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else if it.BestPriceStore != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var23 string + templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(it.BestPriceStore) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 188, Col: 54} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "not yet") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "not yet") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if pts := sparklinePoints(history); pts != "" { + var templ_7745c5c3_Var24 = []any{"v-sparkline", sparklineTrendClass(history)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var24...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if it.LastPolledAt != nil { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var21 string - templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(humanTime(*it.LastPolledAt)) + var templ_7745c5c3_Var28 string + templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(humanTime(*it.LastPolledAt)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 104, Col: 95} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/items.templ`, Line: 205, Col: 95} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "—") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "—") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if it.Active { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "active") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "active") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "paused") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "paused") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "
Edit