Commit Graph

73 Commits

Author SHA1 Message Date
Cosmo
56844a539d feat(voice/events): full CRUD — POST/PUT/DELETE with owner routing
All checks were successful
Deploy / deploy (push) Successful in 2m57s
Голосовой ассистент теперь может создавать, изменять и удалять события
в календарях Даниила и Светы.

- POST  /api/voice/tools/events — create (title, date, start_time, end_time,
  all_day, owner). Маппит owner (daniil/sveta) в calendar_id и проксирует
  в /api/calendar POST.
- PUT   — update (event_id, owner, fields). Передаёт только изменённые
  поля + нужный calendarId.
- DELETE ?event_id=X&owner=Y — удаление.
- GET   — теперь возвращает id события и owner (daniil/sveta), чтобы
  скрипт мог их передать в update/delete.
- range=month поддержан с year/month query params.

Все три метода под bearer auth (VOICE_API_KEY), как остальные voice tools.
Loopback к /api/calendar идёт через internalHeaders() x-voice-internal.
2026-04-23 14:34:32 +00:00
Cosmo
fa583cd279 fix(timer): dismiss actually cancels on server + shorter retention
All checks were successful
Deploy / deploy (push) Successful in 3m10s
Bug: после перезагрузки страницы оверлей «Таймер прозвенел» открывался
снова и снова. Две причины:

- dismissTimer в TimerWidget удалял таймер только из локального
  useState, но /data/tablet-timers.json оставался нетронутым. После
  reload таймер возвращался в список и firedRef (которая пустая после
  reload) снова триггерила alarm.
- lib/timers.ts держал просроченные таймеры 30 минут, давая им шанс
  повторно сработать при каждом reload в этом окне.

Фикс:
- dismissTimer теперь POST /api/voice/timer {action:cancel, id} через
  cookie auth (endpoint с прошлого коммита принимает и cookie, и bearer).
- Retention в listActive снижена до 30 секунд — этого хватает чтобы
  клиент увидел свежий звонок; старше = самоудаление.
- TimerWidget клиентский фильтр тоже 30 секунд.
2026-04-23 13:58:53 +00:00
Cosmo
e2b2a5d82f fix(voice): dismiss overlay after TTS ends, show listening state for followups
All checks were successful
Deploy / deploy (push) Successful in 3m11s
Two fixes:

1) Overlay was hiding mid-TTS because dismiss timer used
   text.length * 80ms — ElevenLabs speaks slower, so the audio got
   cut off. Now scheduleDismiss is only called from playTTS's
   onEnded callback (plus 4s lingering after audio finishes).

2) After response, the Python script silently re-entered record()
   for follow-ups but the overlay disappeared, so the user had to
   re-wake every turn. Added a new 'listening' event — Python
   emits it just before each followup record(), tablet shows the
   orb pulsing at medium intensity with 'жду' status and the last
   response text preserved below.

Safety: any state now arms a 60s auto-close in case Python dies
and never emits idle.
2026-04-23 13:55:25 +00:00
Cosmo
0c677df558 feat(voice): hero TimerHomeWidget + timer cancel/adjust by label
All checks were successful
Deploy / deploy (push) Successful in 3m25s
UI:
- Replace Notes column on Home bento with TimerHomeWidget. Shows all
  active timers as stacked cards with big 30px countdowns, per-timer
  +1/-1 minute buttons and cancel. Colors: indigo default, amber in
  last 10s, red when expired. Empty state suggests voice command.
- Existing chip TimerWidget (bottom-right) kept for ambient view on
  other tabs — redundant on Home, but harmless.

API:
- /api/voice/timer accepts cookie OR bearer (browser widget cancel
  works with user's auth_token cookie; Python script uses bearer).
- New action 'adjust' — shifts endsAt by delta_seconds. Clamps so
  endsAt never goes into the past.
- Cancel now supports {label} in addition to {id} (fuzzy substring
  match, most-recently-started wins). Emits timer_cancel with id+label
  so clients can refresh.
- findByLabel / adjustTimer helpers in lib/timers.ts.
2026-04-23 13:51:25 +00:00
Cosmo
7fb05181e6 fix(voice/tools): use x-voice-internal header for loopback fetches
All checks were successful
Deploy / deploy (push) Successful in 3m10s
Tool endpoints (events, notes, transport, weather) call other /api/*
routes via loopback (http://localhost:3000). Those routes are
middleware-protected — cookie-less loopbacks were getting 401, which
surfaced to the voice agent as get_today_events → tool_http_502.

Add internal header bypass: middleware lets the request through when
x-voice-internal matches VOICE_API_KEY. Only our own tool endpoints
use this header, from inside the same container, so the blast radius
is limited to loopback traffic.

- middleware.ts: check x-voice-internal before cookie
- lib/voice-tools.ts: internalHeaders() helper
- app/api/voice/tools/{weather,transport,events,notes}: use it
2026-04-23 13:41:57 +00:00
Cosmo
e96e7a1342 feat(voice): tool endpoints, timer widget, clean Siri-style overlay
All checks were successful
Deploy / deploy (push) Successful in 3m18s
Adds the infrastructure for Claude tool use + visual timer.

Tablet API surface (all bearer-authed with VOICE_API_KEY, middleware bypassed):
- /api/voice/tools/weather    — current + short forecast via Open-Meteo
- /api/voice/tools/transport  — tram arrivals by direction / route filter
- /api/voice/tools/events     — Google Calendar today/week
- /api/voice/tools/notes      — notes + shopping lists
- /api/voice/timer            — start (with seconds+label), cancel; GET list (cookie ok)
  Active timers persisted at /data/tablet-timers.json

UI:
- VoiceOverlay stripped to minimal Siri look: no agent emoji/name, just the
  pulsing orb (3-layer radial gradient, independent breath animations),
  subtle status label on wake only, transcription/response text centered.
  Agents distinguished by orb color (Cosmo indigo/violet, Люся pink).
- TimerWidget: bottom-right chip stack with countdown, progress bar, turns
  amber in last 10s. On expiry, fires fullscreen alarm overlay with beep
  (WebAudio osc) + Остановить button.

Other:
- lib/timers.ts — persistent timer store in /data
- lib/voice-tools.ts — shared bearer-auth helper
- middleware — bypass list now covers /api/voice/tools/* and /api/voice/timer
2026-04-23 13:33:31 +00:00
Cosmo
c29da75c19 feat(voice/tts): route ElevenLabs through HTTP proxy for non-RU egress
All checks were successful
Deploy / deploy (push) Successful in 4m3s
ElevenLabs Cloudflare returns 302 to a region-restricted help page
when requested from a Russian IP. Tablet host (.60) is in RU, so the
Stage 2 call was failing with 502 upstream.

Fix: use https-proxy-agent when ELEVENLABS_PROXY (or generic HTTPS_PROXY
/ HTTP_PROXY) env var is set. Tinyproxy on .103 (non-RU egress host)
acts as the tunnel.

- package.json: add https-proxy-agent ^7.0.6
- app/api/voice/tts: switch from global fetch to node:https with
  explicit Agent (either direct or HttpsProxyAgent). Still streams
  MP3 back via Readable.toWeb so Next.js Response pipes it to the
  browser as audio arrives.

Operational: set ELEVENLABS_PROXY=http://192.168.31.103:8888 in
tablet.env after bringing tinyproxy up on .103.
2026-04-23 13:00:55 +00:00
Cosmo
a780fc7bd5 feat(voice): play TTS through tablet speakers via ElevenLabs proxy
All checks were successful
Deploy / deploy (push) Successful in 2m58s
Stage 2 of voice integration — centralizes TTS on the tablet so the
Python satellite no longer needs ElevenLabs credentials or mpv.

- app/api/voice/tts — POST {text, agent}, proxies to ElevenLabs
  streaming endpoint with flash_v2_5 default, returns audio/mpeg.
  Per-agent voice id via COSMO_TTS_VOICE / LUSYA_TTS_VOICE env.
- VoiceOverlay — on response/error events fetches TTS and plays via
  HTMLAudioElement; on wake event stops playback (barge-in). Dismiss
  timer extended by text length so long responses do not cut off.
- Autoplay caveat: browser may block first playback until user taps
  anywhere on the page (FKB: enable Force Autoplay to bypass).
2026-04-23 12:52:26 +00:00
Cosmo
51c3d6016a feat(voice): SSE bridge + Siri-blob overlay for wake-word script
All checks were successful
Deploy / deploy (push) Successful in 3m12s
Adds the tablet side of voice assistant integration. External Python
script (openWakeWord + Groq STT + OpenClaw) will POST state transitions
to /api/voice/event with a bearer token, and the tablet shows a
fullscreen overlay with Siri-style animated blob + current agent +
recognized text / response text.

- lib/voice-bus.ts — in-process EventEmitter singleton, preserved
  across hot reloads via globalThis
- app/api/voice/event — POST, bearer-auth via VOICE_API_KEY env,
  validates event kind, broadcasts on voiceBus
- app/api/voice/stream — GET, SSE endpoint, per-connection listener
  with 15s keep-alive ping and abort-signal cleanup
- components/VoiceOverlay — full-screen overlay, 3-layer pulsing
  Siri blob, per-agent palette (cosmo indigo/violet, lusya pink/rose),
  auto-dismiss timeouts (wake=20s safety, response=6s, error=4s),
  auto-reconnect on SSE drop
- middleware bypasses /api/voice/event so the script does not need
  a user auth cookie
- VoiceOverlay mounted in HomePageInner outside tab routing so it
  appears on every view
2026-04-23 12:36:26 +00:00
Cosmo
9fec9bca99 fix(calendar): all-day end.date must be next day, clear opposite field
All checks were successful
Deploy / deploy (push) Successful in 2m50s
Google Calendar API rejects all-day events where start.date == end.date
(end is exclusive). POST/PUT were sending the same date for both,
producing Invalid start time when toggling Весь день on edit.

- Added nextDayISO helper (UTC-safe +1d arithmetic)
- all-day: start = { date }, end = { date: nextDay, dateTime: null }
- timed: also explicitly nulls start.date/end.date so patching a
  timed-only event over a previously all-day one doesnt leave stale
  date fields that also trigger Invalid start time
2026-04-23 09:29:21 +00:00
Cosmo
dc5c9b3673 fix(home): remove black flash between tab switches
All checks were successful
Deploy / deploy (push) Successful in 2m40s
- AnimatePresence mode=wait keeps the DOM empty while the outgoing tab
  finishes its exit transition (200ms) before mounting the incoming
  tab. On touch devices this shows as a brief black frame — reported
  as экран остается черным.
- Switch to mode=sync: outgoing fades out while incoming fades in,
  no gap. initial=false suppresses the enter animation on first render.
- Drop the y: 12 → -8 slide (cheap jank on hydration), keep just
  opacity. Duration 200ms → 150ms for snappier feel.
2026-04-23 09:22:55 +00:00
Cosmo
8d32e7ebb0 feat: forecast swipe nav, note swipe-to-delete, night-shift tint
All checks were successful
Deploy / deploy (push) Successful in 2m45s
- WeatherDayModal now accepts the full forecast array and an onChange
  callback; supports horizontal drag (framer-motion) plus prev/next
  chevrons and a dot-indicator. Drag > 60px switches day; style uses
  semantic tokens (shadow-xl, surface-1).
- NotesTab list items wrap each note in a motion.button with drag=x,
  constrained to -80px. Below it a gradient+trash reveal layer. Drag
  past 60px opens the existing confirmDelete modal.
- HomePageInner adds a night-shift overlay (fixed, mixBlendMode multiply,
  rgba(255,120,40,0.12)) active 22:00-06:00, auto-checked each minute,
  fades in/out over 800ms. No user toggle yet — fully automatic.
2026-04-23 09:17:22 +00:00
Cosmo
0908ad93de fix(home): prevent bento grid overflow on narrow viewports
All checks were successful
Deploy / deploy (push) Successful in 2m40s
CSS grid items default min-width to min-content; the tram widget 3-col
subgrid plus its баbadges and long затем text forced its cell wider
than 1.1fr, collapsing the outer layout on tablet. Fixes:

- outer bento row → gridTemplateColumns: minmax(0, 1fr) minmax(0, 1.1fr)
- events+notes row same treatment
- TransportWidget inner subgrids: 58px → 52px badge, 1fr → minmax(0, 1fr)
- Cell: minWidth: 0, overflow: hidden, затем text trimmed with ellipsis
  and short м suffix (5м instead of 5 мин)
- big number 32→28px, badge 22→20px to fit in denser columns
2026-04-23 08:46:00 +00:00
Cosmo
e967924f1f ui(topbar): move greeting from Home body to TopBar center
All checks were successful
Deploy / deploy (push) Successful in 2m43s
The big Доброе утро line on Home ate one row for marginal value.
Moved it into TopBar as a center column via 3-col grid (1fr auto 1fr),
so it appears on all tabs and leaves the Home body denser.
2026-04-23 08:38:41 +00:00
Cosmo
09185c2a2a fix(home): drop greeting date; fix touch scroll; tokenize TopBar icons
Some checks failed
Deploy / deploy (push) Has been cancelled
- Removes the date label from the greeting row (user request — тoo dense
  on tablet and redundant with TopBar clock).
- Scrolling: drops overflowY: auto on the inner Events/Notes cards and
  removes flex: 1 / minHeight: 0 from their grid. On iPad-class touch
  nested scroll containers fought the root scroll; now only the root
  scrolls, which the browser handles natively.
- TopBar: replaces hardcoded rgba(255,255,255,X) on sensor icons, chip
  background, chip border and header bottom-border with semantic tokens
  (--text-tertiary, --surface-2, --border-subtle, --hairline) so the
  thermometer/humidity/wind icons are visible in light theme.
2026-04-23 08:36:56 +00:00
Cosmo
121bf30ab1 redesign: bento home + semantic tokens + solid cards
All checks were successful
Deploy / deploy (push) Successful in 2m43s
- introduces semantic CSS tokens (--surface-1/2/3, --border-subtle/strong,
  --hairline, --shadow-sm/md/lg/xl) with distinct dark and light values;
  fixes broken light theme caused by hardcoded rgba(255,255,255,X)
- drops glassmorphism on cards — solid var(--surface-1) with 1px border
  and layered shadows; glass kept only for aurora page background
- introduces .card/.card-raised/.card-hero utility classes
- Home page restructured into a bento grid:
  * greeting row with inline day/date
  * hero weather (64px number, large icon, ощущается/влажность/ветер)
    next to the tram widget (1fr 1.1fr)
  * forecast as a single hairline-separated band (no per-day cards)
  * events+notes in a 2-column grid; events card combines today and
    tomorrow with a divider; notes card styled via surface tokens
- TransportWidget repainted to use tokens, larger numbers (32px for the
  next arrival), imminent highlight uses color-mix against surface-2
2026-04-23 08:30:03 +00:00
Cosmo
9ad758174d style(home): drop weather-hint block; recolor trams 23 green, 27 blue, 39 red
All checks were successful
Deploy / deploy (push) Successful in 2m47s
Weather hint (оденьтесь потеплее / не забудьте зонт) was pushing the
home screen past one viewport on the tablet — removed the block and its
helper fn. New tram color palette per user preference.
2026-04-23 08:21:09 +00:00
Cosmo
43dff776f5 fix(transport): use node:https instead of undici (module not exported)
All checks were successful
Deploy / deploy (push) Successful in 2m47s
Next.js could not resolve undici as a top-level import even though it
ships internally. Drop that path and call the ORGP endpoint via the
built-in node:https with a per-request Agent(rejectUnauthorized: false).
Adds runtime = nodejs on the route so Node APIs are guaranteed.
2026-04-23 08:13:52 +00:00
Cosmo
c25e15e697 fix(transport): accept ORGP self-signed cert via undici Agent
Some checks failed
Deploy / deploy (push) Failing after 1m20s
ORGP SPb uses a TLS chain Node rejects by default (curl works with -k
but Node fetch doesnt). Use an undici Agent with rejectUnauthorized
false for this one hop. Also drop the conflicting next.revalidate: 0
option — cache: no-store already covers it.
2026-04-23 08:11:16 +00:00
Cosmo
95352356b7 refactor(transport): group by route with two direction columns
Some checks failed
Deploy / deploy (push) Has been cancelled
Restructures the tram widget: instead of one card per stop (showing all
routes at that stop), now one row per route (23, 27, 39) with two
columns — → Лента (в центр) and → Дыбенко (от центра). Each cell shows
the next arrival prominently plus the following 1-2 pickups inline.
2026-04-23 08:09:44 +00:00
Cosmo
0523482aa1 feat(home): tram arrival widget for Ул. Антонова-Овсеенко
All checks were successful
Deploy / deploy (push) Successful in 3m10s
Adds a live transit widget on the home screen showing upcoming trams
at both directions of the stop: toward Новочеркасская (stopID 16226)
and toward пр. Большевиков (stopID 16354).

- /api/transport proxies the СПб ORGP endpoint /stop/{id}/arriving
  (DataTables POST format, JSON response with route number + minutes).
  No auth required, free.
- TransportWidget renders two glassmorphism cards with route badges,
  minutes-to-arrival, wheelchair indicator; imminent (<=2 min) arrivals
  get a colored highlight. Filters to trams 23/27/39; refreshes every 30s.
- Route colors: 23 blue, 27 amber, 39 purple.
2026-04-23 08:05:15 +00:00
Cosmo
54287af7d0 feat(notes): replace native confirm with styled delete modal
All checks were successful
Deploy / deploy (push) Successful in 3m16s
Native browser confirm() looked out of place on the dashboard. Replaced
with a glassmorphism modal matching the rest of the UI — trash icon,
note title preview, Cancel/Delete buttons with proper styling.
2026-04-23 06:32:35 +00:00
Cosmo
b0fb9d0c54 fix: 4 bugs — MSK today events, settings scroll, note dates, persistent notes volume
All checks were successful
Deploy / deploy (push) Successful in 4m35s
- calendar API: today/week ranges use Moscow time (UTC+3) instead of UTC — previously today events did not appear until 03:00 MSK
- settings tab: add -webkit-overflow-scrolling: touch + touchAction pan-y for tablet scroll
- NotesTab: add date picker (pinDate) in editor header + date badge in list
- home: pinnedNotes now filters by pinDate (today or future), falls back to latest
- notes/auth: storage moved from /tmp to /data (falls back to /tmp if /data missing)
- deploy workflow: mount /opt/digital-home/smart-home-tablet-data:/data so notes survive redeploys
2026-04-23 06:13:16 +00:00
Cosmo
3a93d5bbea feat: remove weather from TopBar, clickable forecast days with detail modal (feels like, humidity, wind, precip)
All checks were successful
Deploy / deploy (push) Successful in 3m8s
2026-04-22 21:00:37 +00:00
Cosmo
bce9578fa1 feat: redesigned Home (weather+forecast bar, today+tomorrow, pinned notes), fix snow animation, scrollable weather modal, weather hints
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-04-22 20:58:05 +00:00
Cosmo
4bcfff775c fix: remove slice(0,3) in weather API — return all 7 forecast days
All checks were successful
Deploy / deploy (push) Successful in 2m52s
2026-04-22 20:36:40 +00:00
Cosmo
bc01443f03 feat: Notes tab (notes + shopping lists), fix 7-day forecast layout, fix screensaver dismiss
All checks were successful
Deploy / deploy (push) Successful in 2m54s
2026-04-22 20:29:33 +00:00
Cosmo
a7611b46c4 fix: date input overflow, 7-day forecast on Home, screensaver button in settings
All checks were successful
Deploy / deploy (push) Successful in 2m45s
2026-04-22 20:19:04 +00:00
Cosmo
08739ba2f5 fix: add missing updateEvent function to CalendarTab
All checks were successful
Deploy / deploy (push) Successful in 2m47s
2026-04-22 20:12:12 +00:00
Cosmo
494126c7d4 feat: animated SVG weather icons + dynamic gradient background by weather/time
Some checks failed
Deploy / deploy (push) Failing after 2m6s
2026-04-22 20:09:13 +00:00
Cosmo
408be1d0c4 fix: restore template literals in POST handler dateTime fields
Some checks failed
Deploy / deploy (push) Failing after 2m6s
2026-04-22 20:03:31 +00:00
Cosmo
8c60590ed0 fix: restore template literals in calendar PUT handler
Some checks failed
Deploy / deploy (push) Failing after 1m1s
2026-04-22 20:00:22 +00:00
Cosmo
89382bef95 fix: PIN -> getPin() in auth check endpoint
Some checks failed
Deploy / deploy (push) Failing after 1m5s
2026-04-22 19:56:58 +00:00
Cosmo
690db4c6cf feat: event editing, light/dark theme, device animations, 7-day forecast
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-04-22 19:56:38 +00:00
Cosmo
b797d0d660 fix: redesign add-event modal — vertical layout, toggle switch, unified time picker
Some checks failed
Deploy / deploy (push) Failing after 2m9s
2026-04-22 19:50:35 +00:00
Cosmo
1d330f0f41 feat: settings (PIN change, city selector, logout), greeting, screensaver, tab animations, HA status
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-04-22 19:48:53 +00:00
Cosmo
eed8db5865 fix: wind speed in m/s, redesigned weather modal with hero section and forecast cards
All checks were successful
Deploy / deploy (push) Successful in 2m55s
2026-04-22 19:35:46 +00:00
Cosmo
868d35ba3e feat: redesigned add-event modal with calendar selector (Даниил/Света)
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-04-22 19:33:38 +00:00
Cosmo
d29deedc90 fix: upcoming panel as slide-out overlay to prevent layout overflow on tablets
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-04-22 19:30:03 +00:00
Cosmo
2c732d435b fix: move env file outside repo to /opt/digital-home/tablet.env, untrack .tablet.env
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-04-22 19:28:14 +00:00
Cosmo
4e4d434c0b fix: client-side auth check instead of middleware rewrite
All checks were successful
Deploy / deploy (push) Successful in 2m38s
2026-04-22 19:19:33 +00:00
Cosmo
c7fc4d6e8e fix: use Web Crypto API in middleware (Edge Runtime compat)
All checks were successful
Deploy / deploy (push) Successful in 2m34s
2026-04-22 19:12:19 +00:00
Cosmo
bdbf0f363e feat: wider calendar event strips, richer detail/add modals, day popover for multi-event days
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-04-22 19:11:13 +00:00
Cosmo
1a529fc23e feat: add PIN lock screen auth + calendar owner filter toggles
All checks were successful
Deploy / deploy (push) Successful in 2m49s
2026-04-22 18:50:56 +00:00
Cosmo
eb644ff341 feat: premium UI redesign — glassmorphism, gradient accents, ambient background
All checks were successful
Deploy / deploy (push) Successful in 2m40s
2026-04-22 18:38:31 +00:00
Cosmo
4874466985 feat: add calendar event deletion with confirmation
All checks were successful
Deploy / deploy (push) Successful in 4m37s
2026-04-22 18:28:13 +00:00
Cosmo
000e25ddb1 ci: fix - apk add docker-cli git in runner (alpine)
All checks were successful
Deploy / deploy (push) Successful in 4m25s
2026-04-22 13:36:29 +00:00
Cosmo
1852664f6a ci: debug run
Some checks failed
Deploy / deploy (push) Failing after 1s
2026-04-22 13:35:51 +00:00
Cosmo
72b38c631e ci: remove container block, run directly on runner host
Some checks failed
Deploy / deploy (push) Failing after 1s
2026-04-22 13:34:44 +00:00
Cosmo
b2ad005f44 ci: fix valid_volumes for docker.sock
Some checks failed
Deploy / deploy (push) Failing after 0s
2026-04-22 13:33:37 +00:00