Commit graph

11 commits

Author SHA1 Message Date
342b56af35 ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.

## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)

## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
  styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)

## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
  width on progress bar). Live DOM was 487 inline-styled elements due
  to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
  job-row-actions, job-row-status-* utility classes in styles-screens.css

## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
  Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
  not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor

## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
  beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)

## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing

## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
  live recorders, 'Last 24 hours' tiles for newly created assets, plus
  an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen

## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title

## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
  badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 23:50:07 +00:00
f54c49d2dc fix(web-ui): fix duplicate DeckLink groups in new-recorder modal; refactor device pickers
The /cluster/devices/blackmagic endpoint returns one entry per port (flat
array). The old SDI picker iterated over each entry and synthesised
port_count buttons per entry — 4 entries × 4 synthesised ports = 16 buttons
rendered as 4 identical duplicate groups.

Fix: extract DevicePortPicker component that groups the flat per-port
response by node_id (Map keyed on node_id, one group per physical node,
ports sorted by index). One button rendered per actual API entry.

Also extract ManualDevicePicker for the fallback empty-state dropdowns.
Both components shared between SDI and Deltacast pickers.

Visual improvements:
- Port label shows device node (io0, io1…) from device path instead of
  redundant index number
- Node header only shows model+hostname, not repeated per port
- TEST CARD badge styled inline for Deltacast test-card ports
2026-05-28 23:18:55 +00:00
888ca65045 feat(capture): Deltacast SDI framework — test-card capture, cluster detection, UI
## capture service
- capture-manager.js: add 'deltacast' source_type to _buildInputArgs.
  Uses 'deltacast://<index>' with ffmpeg deltacast demuxer when
  /dev/deltacast<N> exists; falls back to lavfi testsrc2 + sine test card
  (matching deltacast-sdi-recorder standalone app) when hardware absent.
- routes/capture.js: add GET /devices/deltacast endpoint (enumerates
  /dev/deltacast* + DELTACAST_PORT_COUNT env fallback). Extend /probe to
  handle source_type=deltacast.

## node-agent
- detectHardware(): add 'deltacast' array to capabilities payload.
  Enumerates /dev/deltacast* nodes; falls back to DELTACAST_PORT_COUNT env.
  Adds DELTACAST_MODEL env support. Logs dc= count in heartbeat line.
- sidecar /start: bind /dev/deltacast* device nodes into capture containers
  when sourceType='deltacast'.

## mam-api
- cluster.js: add GET /cluster/devices/deltacast and
  GET /cluster/devices/deltacast/signal endpoints — same shape as
  blackmagic equivalents for UI parity.
- recorders.js /start: pass DELTACAST_PORT_COUNT env to capture container;
  bind /dev/deltacast* device nodes on local spawn.
- migration 024: ALTER TYPE source_type ADD VALUE 'deltacast' (idempotent).
- schema.sql: add 'deltacast' to source_type ENUM for fresh installs.

## web-ui
- modal-new-recorder.jsx: add 'Deltacast' source type card; fetch
  /cluster/devices/deltacast on selection; port picker with TEST CARD
  badge when hardware absent; falls through to manual index entry if
  no devices detected.
2026-05-28 23:12:40 +00:00
opencode
04ce096e67 chore: 1.2 ship-prep sweep — close 38 issues
Frontend / UX / a11y
- Sidebar collapse/expand toggle with localStorage persistence (#142)
- Settings sections wrap inputs in <form> with Enter-to-submit + native
  validation; password autocomplete=new-password (#141, #138)
- Asset thumbnails get descriptive alt text (#140)
- Production deploy now precompiles JSX via esbuild and loads the
  production React UMD instead of dev builds + in-browser Babel (#139,
  #122)
- Search wrapper gets role=search; global search input gets aria-label,
  role=combobox, aria-controls/aria-expanded/aria-activedescendant
  wiring (#137, #135)
- Dashboard and Library no longer share the same nav icon (#136)
- Sidebar collapses off-canvas with a topbar menu button below 768 px;
  mobile default is collapsed (#134)
- --text-3 bumped to #8B92A0 for WCAG AA contrast on --bg-0 (#133)
- Schedule and Library routes were rendering empty inside the .main
  flex container — switched to flex:1 + min-height:0 (#131, #132,
  editor + asset detail get the same fix)
- Jobs nav badge now polls /jobs?status=active every 10 s and reflects
  the live count (#130, #113)
- aria-label sweep on every icon-only button (#126)
- Premiere panel release list moved to window.PREMIERE_RELEASES in
  data.jsx; Editor + Settings read from the same source (#125)
- Typo setPgMclips → setPgmClips (#124)
- Stray console.error / console.warn calls gated behind
  window.DF_LOG.{warn,error} (#123)
- Hardcoded /api/v1 paths route through window.ZAMPP_API_PREFIX (#115)
- Schedule rows no longer crash on null recorder_id (#117)
- EditorKeyboard guards against document.activeElement === null (#116)
- Unmount-safe timers for PasswordResetModal, Containers, Editor (#111)
- Player seek clamps below totalMs, server-side range clamping +
  uncached 416 on EOF, client-side EOF-stall watchdog (#143)
- Duration badge overlap fix on narrow asset cards (#52)

Backend / security / reliability
- GET /recorders fixed N+1: single LATERAL JOIN for live_asset_id;
  Docker inspects bounded to actually-recording rows (#121)
- Upload disk-storage (multer.diskStorage) streams parts to S3 instead
  of buffering 500 MB in RAM (#120)
- /assets list clamps limit to MAX_LIMIT=500 to prevent OOM (#119)
- SDK upload archive listing + post-extract sanitize block zip-slip /
  tar-slip and symlink escapes (#118)
- Migrations track applied state in schema_migrations, run in a
  transaction, and exit non-zero on failure (#107)
- node-agent BMD_COUNT override uses BMD_DEVICE_PREFIX; filesystem
  detection wins (#109, #127)
- GPU_COUNT override now merges with nvidia-smi enrichment (#108)
- /cluster/heartbeat requires a node-bound token or admin user;
  tokens carry bound_hostname (#106)
- /recorders/:id/start error responses no longer echo the Docker
  create payload — env vars stay out of client responses (#105)
- /recorders/probe restricts schemes (srt/rtmp/rtsp/udp/rtp), blocks
  private + loopback hosts for non-admins, denies common service
  ports (#104)
- Scheduler tick guarded by a Postgres advisory lock; pending/running
  rows claimed via UPDATE...RETURNING + FOR UPDATE SKIP LOCKED to
  survive multi-node deploys (#103)
- UUID validateUuid('id') param middleware on every /:id route (#102)
- Error handler scrubs Postgres error messages and 5xx detail (#101)
- Graceful SIGTERM/SIGINT shutdown — stops scheduler, drains the HTTP
  server, ends the pool, 25 s force-exit watchdog (#100)
- AMPP sync moved from fire-and-forget to a persisted retry queue
  (ampp_sync_status / attempts / next_attempt_at + scheduler retry
  loop with exponential backoff) (#77)

Migrations
- 019: api_tokens.bound_hostname (#106)
- 020: assets.ampp_sync_status + retry bookkeeping (#77)

Other
- Defer #92 Growing-files per-upload toggle, #80 Audio tab, #57
  Dashboard redesign, #56 Editor SPA polish phase 3, #114 S3
  migration tool to v1.3
2026-05-27 02:06:14 +00:00
6a1d271576 feat(ui): polish round 2 — live refresh, schedule calendar, jobs times, real sidebar user
- recorders: dispatch df:recorders-changed on create/start/stop/delete so the
  list updates immediately instead of waiting for the 10s poll tick
- library: poll every 4s while any asset is live/processing (15s otherwise) and
  listen for df:assets-changed so a stopped recorder's LIVE badge drops and
  the thumbnail appears without a manual refresh
- auth: synthetic /auth/me (AUTH_ENABLED=false) now uses LOCAL_OPERATOR / USER /
  USERNAME instead of hardcoding "Admin", and flags synthetic:true
- shell: Sidebar takes `me` as a prop, drops the misleading "Admin" fallback,
  and surfaces an "auth off" hint when the response is synthetic
- jobs: replace the always-empty ETA column with a Time column that shows
  queued/started/done/failed N ago (full timestamp on hover); widen column
- schedule: new month-calendar view (default) with events plotted on day cells
  by status; clicking a day pre-fills the new-schedule modal with a 30-min
  window on that day; List view kept behind a toggle

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 14:52:04 -04:00
claude
7700548dee test: deploy/api-smoke.sh — exercises every API surface
Walks GET endpoints for auth, projects, assets, recorders, jobs, bins,
users, groups, cluster, settings, metrics, schedules, sdk, and the
freshly added comments routes. Deep-links one asset + one recorder by
ID so per-asset endpoints (stream, thumbnail, comments) get coverage.

Prints HTTP codes inline and exits non-zero on any failure. Treats
2xx/3xx as pass; 400/401 also pass since they indicate the route
exists and auth/validation is working as designed.

Usage:
  deploy/api-smoke.sh                      # localhost:47432
  API=http://10.0.0.25:47432 deploy/api-smoke.sh

NewRecorderModal: hardened ZAMPP_DATA hydration with defensive
defaults so first-load timing doesn't blow up the modal.
2026-05-23 04:24:10 +00:00
8b57a9a35a expand codec list, add MXF container, remove proxy settings (fixed profile) 2026-05-22 17:20:01 -04:00
bb508d3256 feat: add probe button to SRT/RTMP sources, fix node labels 2026-05-22 11:33:45 -04:00
6510871448 fix: implement real upload (XHR + S3 multipart) and fix SDI recorder device_index + manual fallback: modal-new-recorder.jsx 2026-05-22 11:10:01 -04:00
529d14cb6b fix: SDI crash, monitors polling, home RAM fields, editor IN DEV splash, timecode, create recorder API: modal-new-recorder.jsx 2026-05-22 10:55:22 -04:00
bd9dfd2cce Add Z-AMPP UI: screens-jobs + screens-editor + modal-new-recorder: modal-new-recorder.jsx 2026-05-22 08:19:03 -04:00