36 Commits
Author SHA1 Message Date
valknarandClaude Sonnet 5 b58640beb2 Unify card title size to text-sm
Several CardTitle usages were text-base while others were already
text-sm, giving section headers inconsistent sizes across pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BuWtppPBizG2NQRa31FCRa
2026-09-05 11:29:41 +02:00
valknarandClaude Sonnet 5 140ba10df6 Bump to 0.9.4
CI / Static checks (push) Successful in 1m40s
CI / Build and push image (push) Successful in 1m7s
- SessionTimelineChart: adds explicit gap between the tooltip's device
  label and its value via a custom formatter, instead of relying on
  justify-between's leftover-space spacing, which collapsed to zero in
  the narrow single-item tooltip

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BuWtppPBizG2NQRa31FCRa
2026-09-04 23:46:45 +02:00
valknarandClaude Sonnet 5 494e963fa3 Streamline the desktop nav with the mobile drawer's tab style
Desktop tabs drop the uppercase/underline treatment for plain-case
labels with a per-icon primary tint, and the active tab lights up
with a bg-primary/10 fill (no border) instead of an underline -
matching the mobile drawer's active-row treatment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:36:02 +02:00
valknarandClaude Sonnet 5 f8f687af4e Polish the mobile menu
- Header: replaces the plain "Menu" title with the brand mark
  (animated, matching the dashboard hero) + wordmark and a short
  description line
- Nav rows get icons and an active-state background/icon tint instead
  of reusing the desktop tab strip's underline
- Desktop tabs also gain matching icons
- Theme toggle and Log out move from the cramped mobile header into
  labeled rows at the bottom of the drawer; desktop keeps them in the
  header
- The header hamburger button is now a self-contained, controlled
  toggle whose bars morph into an X on open/close, sized to match the
  drawer's built-in close button

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:23:38 +02:00
valknarandClaude Sonnet 5 69e4fb286c Give the "no devices connected" alerts a title
Splits the placeholder text into an AlertTitle ("No devices connected
yet") and an AlertDescription for the rest, in both ButtplugConsole
and ReplayPlayer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:06:28 +02:00
valknarandClaude Sonnet 5 ea320548b6 Use shadcn Alert for the "no devices connected" hints
Replaces the ad-hoc bp-glass Card used for the "No devices connected
yet." placeholder in ButtplugConsole and ReplayPlayer with the shadcn
Alert component (newly installed).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:02:32 +02:00
valknarandClaude Sonnet 5 e436ced95b Move Devices before Stats in the nav order
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-31 19:43:05 +02:00
valknarandClaude Sonnet 5 fe017497a2 Unify inline-editable field styling app-wide
Device display name, session title, and session description editors
now share one style: underline is invisible until hover/focus (then
primary), the caret is primary while editing, and text keeps its own
default color at all times instead of shifting on focus.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-31 19:41:13 +02:00
valknarandClaude Sonnet 5 8c3d4e4e33 Add a delete button on the session detail view
Same confirm-dialog delete UX as the sessions table, next to Replay in
the header. Deleting redirects back to /sessions since the session no
longer exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-31 19:31:16 +02:00
valknar 581c3c0e6c chore: cleanup 2026-08-31 19:25:51 +02:00
valknarandClaude Sonnet 5 1bad1f4e33 Streamline the Replay view with the Control view
Page title is now "{session name} - Replay" (matching generateMetadata)
instead of a bare "Replay" heading with the name duplicated inside a
Card title. Dropped that redundant outer Card - the pre-connection
scan/match buttons now sit in a flat row below the header, same as
Control's scan/record row, with the same "no devices connected" hint
card shown underneath when nothing's connected yet. Playback controls
once a replay starts still live in a card, matching DeviceCard's style.
Also gave "Match & replay" a Play icon.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-31 17:31:57 +02:00
valknarandClaude Sonnet 5 c06f119dcf Fix Control view button layout and responsiveness
The scan/record button row used justify-between, spreading the two
button groups to opposite ends instead of sitting next to each other.
Also made the record-controls row and each device card's header wrap
instead of overflowing on narrow screens.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-31 17:31:51 +02:00
valknar 20433a6749 chore: cleanup 2026-08-31 17:20:22 +02:00
valknarandClaude Sonnet 5 1d88ab96c8 Move STATUS_VARIANT out of the "use client" SessionsTable module
The session detail page (a server component) was importing STATUS_VARIANT
from SessionsTable.tsx, a "use client" file. Next.js compiles client
modules separately for the server and client bundles, and pulling a plain
value (not a component) across that boundary let the two compiler caches
drift out of sync in dev, so the same session status could render a
different Badge color depending on which page rendered it. Moved the
constant into its own plain module that both files import.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-31 17:19:24 +02:00
valknarandClaude Sonnet 5 b0394d5c9d Size the description input to its content, like the title
Dropping flex-1 stops the textarea (and its underline) from stretching
to fill the row width regardless of text length - now it hugs the
actual content the same way the title input does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 21:21:40 +02:00
valknarandClaude Sonnet 5 5c92b416c0 Tone down the focus underline to just a step past hover
focus:border-b-muted-foreground was a big jump from the faint hover
border tone. Use foreground at 40% opacity instead - a bit more
present than hover, not a solid gray line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 21:19:07 +02:00
valknarandClaude Sonnet 5 2a52b5db9b Use a gray focus underline for the title/description inputs
focus:border-b-muted-foreground instead of the primary accent color -
matches the existing hover state's neutral tone rather than switching
to a bright accent on focus.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 21:17:23 +02:00
valknarandClaude Sonnet 5 a0211f90e4 Cap title and description editor width
Both used field-sizing:content with only max-w-full, which doesn't
reliably clamp a long single-line value and could push the header row
wider than its container. Add an explicit sm:max-w-xl ceiling on both.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 21:11:27 +02:00
valknarandClaude Sonnet 5 ffc63f0b03 Make the session description always visible and inline-editable
New SessionDescriptionEditor renders even when empty (with an "Add a
description..." placeholder) and autosaves on typing - same debounced,
no-Save-button pattern as the title and device name editors. Unlike
the name field, an empty description is a valid saved state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 21:08:45 +02:00
valknarandClaude Sonnet 5 e9d5bb37d2 Show session status as a badge on the detail page
Reuses SessionsTable's STATUS_VARIANT mapping (now exported) so the
detail page's status matches the table's badge instead of plain text.
Also drops the now-stale px-1.5 alignment padding on the meta lines
now that the title input carries no horizontal padding of its own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 21:03:00 +02:00
valknarandClaude Sonnet 5 354c30d641 Underline the session title input instead of a full border
Drops the boxed border-b-2/rounded treatment for just a bottom
border - transparent at rest, muted on hover, primary-colored on
focus - which reads as inline editing rather than a form field.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:55:07 +02:00
valknarandClaude Sonnet 5 7bf4b3d4bf Fix session title input's border offset and width
The -mx-2 trick pushed the hover/focus border noticeably left of the
text, and flex-1 stretched the input across the whole header row.
Tighten the offset to px-1.5/-ml-1.5 and size the input to its content
via field-sizing:content instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:52:44 +02:00
valknarandClaude Sonnet 5 884ced7a47 Make the session title editable inline on the detail page
New SessionTitleEditor swaps the static <h1> for a heading-styled input
that autosaves on typing (600ms debounce, spinner-then-checkmark) - the
same click-in-place, no-Save-button pattern as the device display name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:49:34 +02:00
valknarandClaude Sonnet 5 005d729119 Autosave device display name instead of an explicit Save button
Typing in the name field now debounces (600ms) and saves in the
background, with an inline spinner-then-checkmark instead of a button
click - one less step, and no risk of leaving an edited name unsaved.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:46:53 +02:00
valknarandClaude Sonnet 5 fa04cb32b4 Streamline card titles to a single color
Every CardTitle now uses the default foreground color; only size marks
the hierarchy (text-sm for stat-tile labels, text-base for section
titles, text-2xl for the login wordmark) instead of some titles being
muted-gray and others white.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:42:41 +02:00
valknarandClaude Sonnet 5 c778ab3cf2 Add a title to the device usage table card on Stats
Matches the "History"/"Known devices" card-title pattern used on the
Sessions and Devices pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:41:05 +02:00
valknarandClaude Sonnet 5 d5a8cb81cf Highlight nav link as active for sub-routes too
/sessions/16 (and any other nested route) now underlines its parent
nav link instead of only an exact pathname match, which previously
left every detail/replay page with no active tab at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:37:25 +02:00
valknarandClaude Sonnet 5 c4ed849d37 Remove stats tab switcher, drop duration-per-device, show devices inline
Sessions and Devices no longer live behind a Tabs switcher on the stats
page - both sections render directly, one after the other. Also drops
the "Duration per device" list from the sessions summary cards since
DeviceUsageTable already shows per-device active time in more detail.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:33:47 +02:00
valknarandClaude Sonnet 5 e459ccd0ed Polish replays stat into the summary card grid
The replay count was a lone plain-text bar below the three stat cards,
and read oddly as "Replayed 0x" when nothing had been replayed yet.
Fold it into the same 4-tile grid with the same styling, and show "-"
at zero instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd
2026-08-30 20:31:31 +02:00
valknarandClaude Sonnet 5 401b9b5033 Remove recordings feature, replay sessions directly, bump to 0.6.0
CI / Build and push image (push) Successful in 1m41s
CI / Static checks (push) Successful in 2m12s
Recordings were just a thin named pointer over an already-captured
session's events, so the whole separate feature (recordings table, API
routes, pages, UI) is gone: any completed session can now be named and
replayed directly. Replaying no longer creates a session or duplicates
events of its own - it just bumps the source session's playCount/lastPlayedAt.

Also renames play_sessions/playSession(s) to sessions/session(s) throughout
the schema, queries, API routes, and UI for consistency, and updates the
README to match the new flow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 21:38:51 +02:00
valknarandClaude Sonnet 5 2a3c4ff1f2 Add site footer and switch display font to Sora, bump to 0.5.0
CI / Static checks (push) Successful in 1m7s
CI / Build and push image (push) Successful in 1m6s
Adds a Footer component (credit line, repo/buttplug.io links) below main
content on all app pages, with the layout shell now flex-column so it
sticks to the bottom on short pages. Also swaps the display font from
Space Grotesk to Sora (after trying Fraunces, a serif, which didn't fit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 19:02:09 +02:00
valknarandClaude Sonnet 5 c46adf788f Add animated glass background, breadcrumbs, and brand mark polish
Cards now sit on an animated signature-gradient background and use a
translucent, blurred bp-glass surface so it shines through; extended
bp-glass to every stat surface on the Stats page and to its tab switcher.
Adds a Breadcrumbs component below the header on all app pages.

Also: drop the gradient text fill from the wordmark/heading, animate the
BrandMark's heart and curve on the dashboard hero instead of a plain pulse
ring, recenter the heart/curve artwork in icon.svg and BrandMark, close the
mobile nav menu on link click, remove the redundant "back to recordings"
button, and match the "Start session"/"Scan for devices" button sizes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 18:47:30 +02:00
valknarandClaude Sonnet 5 5484d3cefe Fix device/replay/session UX issues, add pagination, bump to 0.3.0
CI / Build and push image (push) Successful in 1m2s
CI / Static checks (push) Successful in 1m7s
- Fix device rename input collapsing on mobile (fixed width vs w-full
  inside an auto-layout table column).
- Add cascade-delete confirmation for sessions with a saved recording.
- Fix header connection LED: derive state from scanning/device-count/
  recording instead of the Buttplug client's raw connected flag; drop
  the label text and hide the indicator entirely when idle.
- Add a per-device disconnect button (stop + remove from store, the
  closest equivalent Buttplug's protocol allows per device).
- Fix replay ending early: duration now comes from the recording's
  actual durationMs, not the last event's timestamp.
- Replay robustness: show which devices are being replayed to, reset
  actuators to zero on start/play/pause, fully disconnect devices and
  the whole client on stop/unmount, surface command failures via toast.
- Stop flagging the header LED red during replay - recording is only
  for live sessions.
- Add page-number pagination to recordings/sessions/devices lists.
- Wordmark SEXY -> Sexy; add proper per-page <title>s including
  dynamic titles for recording/session detail and replay pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 21:26:18 +02:00
valknarandClaude Sonnet 5 0bd36b08c9 Upgrade to buttplug v5, add battery level display, bump to 0.2.0
CI / Static checks (push) Successful in 1m0s
CI / Build and push image (push) Successful in 1m3s
buttplug-wasm@3.0.0 still declares buttplug@^4.0.2 as its dependency,
and v5 changed the OutputCmd wire shape (Value: number[] -> number) -
a real protocol difference, not just a type mismatch. Confirmed by
testing against real Lovense hardware that device control still works
in practice, so the bump stands; the risk is documented at the
client.connect() cast in case a future device/build doesn't fare as
well.

Also fixes a Map vs ReadonlyMap mismatch in the version-agnostic
feature-type extraction (v4 returns Map, v5 returns ReadonlyMap) that
surfaced while making this change, and adds a battery indicator to each
connected device's card - reads once on connect, refreshes every 60s,
using device.hasInput("Battery") to detect support.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8WkFF5ppURBAB918593Eb
2026-08-25 17:33:48 +02:00
valknarandClaude Sonnet 5 f7503d03d0 Restyle UI with a channel-strip console look and brand icon
CI / Static checks (push) Successful in 1m9s
CI / Build and push image (push) Failing after 1m2s
Adds a pink/purple/blue SVG brand mark (favicon + header) and reworks
the visual language around the app's actual interaction - fader-driven
device control - via LED-style indicators, mono instrument readouts,
hairline-framed cards, console-tab nav, and an oscilloscope-style
session timeline. Leaves components/ui/* untouched, restyling only via
design tokens and the app's own composed components. Also fixes a
proxy.ts matcher gap that was gating /icon.svg behind auth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8WkFF5ppURBAB918593Eb
2026-08-25 16:43:28 +02:00
valknarandClaude Sonnet 5 1119c8eea0 Initial implementation of Bluetooth toy control app
CI / Static checks (push) Successful in 1m27s
CI / Build and push image (push) Skipped
Next.js app with a browser-side buttplug/buttplug-wasm control layer
(server never touches real-time device commands), SQLite storage via
Drizzle, single-secret auth, recordings/replay with device remapping,
a usage stats dashboard, Docker deployment, and Gitea CI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8WkFF5ppURBAB918593Eb
2026-08-25 07:51:38 +02:00