Clicking deployed/testing/degraded sets the state filter to that
value. Clicking total clears all filters. Hover style added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces ad-hoc Tailscale config tracking with a proper jobs system.
Jobs get their own nav page (master/detail layout), a dedicated DB
table, and full run history persisted forever. Tailscale connection
settings move from the Settings modal into the Jobs page. Registry
pattern makes adding future jobs straightforward.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Separate vmid / id / created with a subtle vertical border so they
don't run together. Bump font to 13px. Labels drop to 11px muted,
values use full --text colour so the actual data stands out clearly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each event is now one row: label · old → new on the left, timestamp
right-aligned. Nothing is far from anything else. State changes use the
existing badge component for immediate visual recognition. The created
event reads 'instance created' in accent. Middle-dot separator keeps
field label and change value clearly associated without forced spacing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Timestamp now sits on its own line above each event so it's visually
separate from the change description. Field names use a friendly label
map (hardware_acceleration → hw acceleration, tailscale_ip → tailscale ip,
etc.). The created event reads "instance created" in accent colour instead
of a raw "created / —". Padding between rows increased.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a Display section to the settings modal with a timezone dropdown.
Selection is persisted to localStorage and applied to all timestamps via
fmtDate (date-only) and fmtDateFull (date + time + TZ abbreviation, e.g.
"Mar 28, 2026, 2:48 PM EDT"). Changing the timezone live-re-renders the
current page. Defaults to UTC.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an instance_history table that records every field change:
- createInstance logs a 'created' event
- updateInstance diffs old vs new and logs one row per changed field
(name, state, stack, vmid, tailscale_ip, all service flags)
- History is stored under the new vmid when vmid changes
New endpoint: GET /api/instances/:vmid/history
The 'timestamps' section on the detail page is replaced with a
grid timeline showing timestamp | field | old → new for each event.
State changes are colour-coded (deployed=green, testing=amber,
degraded=red). Boolean service flags display as on/off.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removing the :first-child { padding-top: 0 } override lets every
section use the same padding: 16px 0, so the gap above Export matches
the gap above Import (and any future sections).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The modal-body's 22px padding-top created a visible gap between the
header divider and the Export section title.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
padding-top on the first .settings-section created a visible gap
above the Export title. Fixed with :first-child { padding-top: 0 }.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a gear button to the nav that opens a settings modal with:
- Export: GET /api/export returns all instances as a JSON backup file
with a Content-Disposition attachment header
- Import: POST /api/import validates and bulk-replaces all instances;
client uses FileReader to POST the parsed JSON, with a confirm dialog
before destructive replace
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.badge lacked text-align: center. Inside the card's flex-end right
column, badge text was left-justified within each pill, making state
labels (deployed / testing / degraded) appear skewed to the left.
TDD: CSS regression test added to tests/helpers.test.js — reads
css/app.css directly and asserts the rule is present, so this
cannot regress silently in future.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>