Skip to content

Releases: TomSchmidtDev/visualizer-lite

v1.17.0

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 07 Jun 12:51

What's new

Added

  • Pre-commit i18n validation: scripts/check-i18n.mjs + husky v9 pre-commit hook validates all static t('...') calls against both de.json and en.json, and checks that both files have identical key sets. Dynamic keys (template literals) emit a warning but do not block the commit. Calls with extra arguments (t('key', { count })) are also validated.

Fixed

  • Missing translation keys: shots.profileTitle and shots.grinderModel were used in SearchBar.tsx filter chips but were absent from both de.json and en.json (discovered by the new validation script).

See CHANGELOG.md for the full history.

v1.16.0

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 07 Jun 11:13

What's new

Fixed

  • i18n — complete DE/EN separation: All remaining hardcoded German/English strings in UI components replaced with t() calls: context window labels in AnalysisPanel (7 T, 30 T, Alle → translated), Roaster tab label, theme toggle, filter chips (Röster:, Profil:, Mühle:), shot title and error fallbacks in ShotCard, ShotDetail, ShotCompare, Stats
  • Password change form: New password must now be entered twice for confirmation; mismatch is caught client-side before the API request

Added

Changed

  • README screenshot gallery: Updated with new high-quality screenshots covering shot list, extraction curves, statistics dashboard, shot comparison (overlaid + split), AI analysis (Barista + Roaster perspectives), and all settings tabs
  • Architecture section: Added link to AI analysis documentation (docs/ai-analysis.md)

v1.15.0 — AI Analysis: Context Transparency, Timing, i18n

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 05 Jun 11:44

What's new in v1.15.0

New Features

  • Historical context visible: New first metadata row shows how many shots were passed to the AI as a comparison baseline (avg pressure, avg flow, avg temperature, matching tier)
  • Collapsible details: Timing + tokens/cost behind a ▸ toggle; row 1 (timestamp + context) always visible
  • Configurable context window: 7d / 30d / 90d / All in Settings → AI Analysis
  • Configurable matching thresholds: Tier-1 minimum and minimum shot count are adjustable
  • Full i18n: All AI analysis strings translated in DE + EN

Fixes

  • AI now responds in the configured language (explicit language instruction added to all system prompts)
  • Accept-Language header is now parsed correctly (including German as secondary language)
  • Timing fields now correctly returned from cache path
  • Tier-1 threshold raised to ≥ 10 shots (prevents misleading small-sample results)
  • Stale "Analyst" instruction removed from default prompt

Upgrade

Standard Docker Compose update — no manual DB migrations required (run automatically on startup).

v1.13.2

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 05 Jun 00:40

Bug Fix

AI analysis crash: React Error #31 when model returns objects instead of strings

The optimised analysis mode occasionally made the model return structured objects like {"phase": "Extraction", "finding": "...", "action": "..."} instead of plain strings in the barista/roaster arrays. React then crashed with Error #31 ("Objects are not valid as a React child").

Root cause: The optimised system prompt showed {"barista":[...],...} without an explicit string example — the model interpreted the instruction to "reference phase names" as a reason to build structured objects.

Fix (three layers):

  1. All four system prompts (standard/optimised × DE/EN) now show string examples and explicitly state: "Each entry must be a plain text string — NOT an object, NOT nested JSON."
  2. New normalizeAnalysisArray() function converts any object entries to readable strings (phase — finding — action) at the API boundary — runs on both fresh responses and cached results, so already-stored bad analyses are fixed on next view without regenerating.
  3. AnalysisPanel renders defensively as a last-resort fallback.

v1.13.1

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 04 Jun 23:46

Bug Fix

AI analysis: profile- and bean-scoped historical baseline

Previously the historical context (avg pressure, flow, temperature shown at the bottom of the analysis prompt) was computed from all shots in the time window — mixing completely different profiles. A Turbo shot and a Blooming Flow shot have fundamentally different extraction characteristics, making cross-profile averages meaningless.

New behaviour (tiered matching):

  1. Same profile & beanprofileTitle + beanBrand + beanType must all match (most accurate baseline)
  2. Fallback: same profile — if fewer than 2 shots match on profile+bean, match by profile only
  3. No context — if no profileTitle is set, or fewer than 2 matching shots exist

The prompt now labels which tier matched, e.g.:

History (14 shots, same profile & bean): pres=9.1bar · flow=1.9ml/s · temp=93.2°C

v1.13.0

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 04 Jun 21:50

What's New

Settings tabbed navigation — The Settings page is now organized into 4 labeled tabs with icons:

  • 🎨 Ansicht — Language, Theme, Statistics
  • 💾 Daten — DE1 Direct Import, Export, Database Info
  • 🔒 Sicherheit — Password
  • 🤖 KI Analyse — AI Analysis settings

The active tab is remembered across sessions.

v1.11.0 — AI Analysis Cost Tracking

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 04 Jun 15:12

What's new

AI analysis cost tracking

Each shot analysis now records the USD cost of input and output tokens at the time the analysis runs. Costs are displayed inline alongside the existing metadata:

04.06.2026, 14:23 • claude-haiku-4-5-20251001 • ↑ 1.234 / ↓ 567 Tokens • ↑ $0.000309 / ↓ $0.000071 = $0.000380

Why store costs explicitly? Prices change over time — storing costs at analysis time means the historical record stays accurate.

How pricing works: Live prices are fetched from the OpenRouter API and cached in-memory for 24 hours. If the API is unreachable, a hardcoded fallback table is used. If pricing is completely unavailable, the analysis runs normally and costs are stored as null — the cost display is simply omitted.

Supported models: claude-haiku-4-5, claude-sonnet-4-6, claude-opus-4-8, gpt-4o-mini, gpt-4o (and any OpenRouter-listed model via direct ID).


Full Changelog: https://github.com/TomSchmidtDev/visualizer-lite/blob/main/CHANGELOG.md

v1.10.0

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 04 Jun 12:41

What's new

AI analysis metadata

Creation date, model name, and token counts (input / output) are now shown above the "Regenerate" button on the shot detail page — so you can see when an analysis was generated and what it cost.

README overhaul (EN + DE)

Both READMEs have been significantly improved:

  • New "Why Visualizer Lite?" section explaining the motivation behind the project
  • Expanded Quick Start with plain-language explanations of key parameters (data path, session secret, password, certificates)
  • New "Importing Shots" section describing all three import methods (auto-push, direct import, manual upload) — including a note on running alongside the official Visualizer in parallel
  • Expanded DE1 Plugins section describing both plugins and why the Upload to Visualizer plugin was extended to support custom targets over HTTP and HTTPS
  • New Related Links section
  • Disclaimer clarifying the hobby-project nature and BUSL-1.1 terms
  • Architecture diagrams updated to reflect the AI analysis route, external AI API, and ShotAnalysis data model

v1.9.2 — Security fix (Docker CVEs)

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 02 Jun 19:28

Fixed

Note: The busybox CVEs (CVE-2025-60876) have no Alpine fix available yet and remain open.

v1.9.1 — Gitleaks fix

Choose a tag to compare

@TomSchmidtDev TomSchmidtDev released this 02 Jun 18:02

Fixed

  • Suppressed gitleaks false positive on test fixture API key (sk-invalid-key-12345 in analysisService.test.ts) via .gitleaksignore and inline # gitleaks:allow comment — no real credentials were ever committed