Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2f2e9f7
build(deps): zod 4.6.7 (GHSA-mrhx-c7xc-m3w5), @fastify/cors 11 and mi…
Consterna Oct 24, 2026
ccaca0e
fix(server): transaction safety, session integrity, restart recovery …
Consterna Oct 24, 2026
90b4415
fix(shared): DaVinci Resolve marker EDL layout and drop-frame base ti…
Consterna Oct 24, 2026
fbef228
fix(client): per-server credentials, reconnect loops, room handling, …
Consterna Oct 24, 2026
96ded2e
test: component tests in Chromium, axe WCAG 2.1 AA scans, coverage th…
Consterna Oct 24, 2026
ff83ddb
docs, test: honest accessibility statement and test claims; mutation …
Consterna Oct 24, 2026
ecf3d8a
fix: rejoining a room created after an unknown-code error, per-server…
Consterna Oct 24, 2026
f667cfe
fix(client): retry a room only when not in it; e2e waits for JOINED b…
Consterna Oct 24, 2026
57664a3
build(docker): drop npm from the runtime image
Consterna Oct 24, 2026
a071098
fix(shared): one Resolve marker per frame in the EDL export
Consterna Oct 24, 2026
14146f0
fix(server): log client WebSocket protocol errors as warnings
Consterna Oct 24, 2026
8a54a62
fix(client): keep credentials in memory when browser storage is blocked
Consterna Oct 24, 2026
0d47ab9
build(deps): declare ws where it is imported
Consterna Oct 24, 2026
e358ae0
refactor(client): drop the unused React default import in App
Consterna Oct 24, 2026
6ccfa32
build: mark the workspace packages private and MIT
Consterna Oct 24, 2026
049796c
fix(server): an Auto Session starts at the key press that opens it
Consterna Oct 24, 2026
21e69e0
fix: unbiased room codes, unpredictable member ids; security policy f…
Consterna Oct 24, 2026
9c0b699
docs(changelog): room code bias and member id randomness
Consterna Oct 24, 2026
262c0dc
fix(client): category colors for Dock markers; Dock clock follows the…
Consterna Oct 24, 2026
c7d38c9
docs: per-POV exports start at the session's base timecode
Consterna Oct 24, 2026
4339129
fix(client): Director Panel reflows at 320 px; axe covers the live HU…
Consterna Oct 24, 2026
f10a32d
fix(client): keep keyboard focus when Start and Stop Session swap
Consterna Oct 24, 2026
ad7172e
docs: current test counts and files; accessibility statement lists re…
Consterna Oct 24, 2026
7040f97
chore: drop a local tool folder from the ignore files (kept in .git/i…
Consterna Oct 24, 2026
99f3931
fix(client): storage stays the source of truth for credentials it cou…
Consterna Oct 25, 2026
0761c1f
fix(server): an Auto Session never starts before the previous session…
Consterna Oct 25, 2026
5b4ced2
fix(client): focus follows the Start / Stop swap from any tab; docs o…
Consterna Oct 25, 2026
df819b9
fix(server): the latest session breaks start-time ties by creation order
Consterna Oct 25, 2026
aa35e17
fix(client): forget the session button focus once the user moves to t…
Consterna Oct 25, 2026
e909d6d
test(client): a Dock refused with RATE_LIMITED rejoins by itself once…
Consterna Oct 25, 2026
da072d2
test: give Stryker a 60 s timeout so its score no longer depends on m…
Consterna Oct 25, 2026
58e9742
test(shared): kill the surviving mutants that were real gaps; list th…
Consterna Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
.env.*
!.env.example
.git
.claude
.vscode
.idea
*.log
Expand Down
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ ALLOWED_ORIGIN=
ADMIN_TOKEN=
# Rooms one IP may create per minute
ROOM_CREATE_LIMIT_PER_MINUTE=10
# Set to true behind a reverse proxy (Caddy / Nginx) so rate limits see the real client IP
# Simultaneous WebSocket connections from one IP (raise it when many streamers share one IP)
MAX_SOCKETS_PER_IP=64
# Set to true behind a reverse proxy (Caddy / Nginx) so rate limits see the real client IP.
# Left false behind a proxy, every client shares its IP and the per-IP limits (room creation,
# unknown room codes, 64 WebSocket connections) apply to all of them together.
TRUST_PROXY=false

# Note: the OBS WebSocket address / password are entered in each streamer's Dock
Expand Down
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ updates:
directory: /
schedule:
interval: weekly
ignore:
# @types/node follows the Node.js runtime (engines.node and the Docker image), not the latest major
- dependency-name: '@types/node'
update-types: ['version-update:semver-major']
groups:
# @vitest/coverage-v8 pins vitest exactly (peer dependency), so they must move together
vitest:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ jobs:
- run: npm ci
- run: npm run build
- run: npx playwright install --with-deps chromium
# Unit + component tests in Chromium, with the coverage thresholds of vitest.config.mts
- run: npm run test:coverage
- run: npm run e2e

- uses: actions/upload-artifact@v7
Expand All @@ -59,6 +61,7 @@ jobs:
path: |
playwright-report/
test-results/
coverage/
retention-days: 14

docker:
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Mutation testing

# On demand only (Actions -> Mutation testing -> Run workflow): it takes minutes and its score is a
# review aid, not a gate. Stryker is installed for the run, not added to the project's dependencies.
on:
workflow_dispatch:

permissions:
contents: read

jobs:
stryker:
name: Stryker (timecode and exporters)
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm

- run: npm ci
- run: npm install --no-save @stryker-mutator/core@10.1.1 @stryker-mutator/vitest-runner@10.1.1
- run: npx stryker run

- uses: actions/upload-artifact@v7
if: ${{ !cancelled() }}
with:
name: mutation-report
path: reports/mutation/
retention-days: 14
10 changes: 7 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ node_modules/
# Production & Build outputs
dist/

# Playwright E2E artifacts
# Playwright E2E and Vitest browser-mode artifacts
e2e/.data/
.vitest/
.preview-data/
test-results/
playwright-report/
blob-report/

# Local Claude Code launch configs
.claude/
# Local launch configs
build/
out/
.cache/
Expand Down Expand Up @@ -51,6 +52,9 @@ pnpm-debug.log*

# Testing & Coverage
coverage/
# Mutation testing (stryker.config.mjs)
reports/mutation/
.stryker-tmp/

# OS generated files
.DS_Store
Expand Down
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ coverage
node_modules
package-lock.json
*.sqlite*
.claude
CHANGELOG.md
53 changes: 53 additions & 0 deletions ACCESSIBILITY.es.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Declaración de accesibilidad de StreamCollab

[English](ACCESSIBILITY.md) | **Español**

StreamCollab busca cumplir las **Pautas de Accesibilidad para el Contenido Web (WCAG) 2.1, nivel AA** en la Dock, el Panel del director y el HUD. Es un objetivo, no una declaración de conformidad: el proyecto no ha pasado una auditoría de accesibilidad independiente. Esta página explica qué se comprueba automáticamente, qué no se ha probado todavía y cómo informar de una barrera.

---

## Qué se comprueba en cada cambio

- **axe-core (reglas WCAG 2.1 A y AA)** se ejecuta en la suite end-to-end ([`e2e/a11y.spec.ts`](e2e/a11y.spec.ts)), en inglés y en español, sobre:
- la Dock, vacía y con un marcador y los ajustes de OBS abiertos,
- el Panel del director, su diálogo de inicio de sesión y su diálogo de exportación,
- el Panel del director con un miembro, marcadores y un marcador en edición,
- la vista previa del HUD y el HUD en vivo mostrando el aviso de un marcador.

Cualquier infracción hace fallar el build. Las herramientas automáticas solo detectan una parte de los problemas WCAG (contraste, nombres, roles, estructura); no pueden juzgar, por ejemplo, si una etiqueta tiene sentido.
- **Diálogos** (Exportar, Iniciar sesión): se prueban en Chromium ([`packages/client/test/components.browser.test.tsx`](packages/client/test/components.browser.test.tsx)). Al abrirse, el foco entra en el diálogo; `Tab` / `Mayús+Tab` no salen de él; `Escape` lo cierra y el foco vuelve al botón que lo abrió.
- **Docks estrechas:** la suite end-to-end comprueba que todos los controles de la Dock siguen en pantalla con 300, 360 y 420 px de ancho.
- **Reflow (WCAG 1.4.10):** la suite end-to-end comprueba que el Panel del director, con una sesión en marcha, no necesita desplazamiento horizontal con 320 px de ancho.
- **Foco tras iniciar / detener la sesión:** cuando se sustituye el botón Iniciar o Detener que tenía el foco, lo recibe el nuevo en vez de la página, también si la sesión se inició o detuvo desde otra pestaña (tests end-to-end).

## Qué hace StreamCollab

- **Teclado:** todos los controles son botones, enlaces, campos o desplegables nativos. En la Dock, `1`–`5` añaden un marcador y `0` o `Ctrl+Z` / `Cmd+Z` deshacen el último tuyo. Los atajos se ignoran mientras escribes en un campo, con un diálogo abierto, al mantener la tecla pulsada y con `Ctrl` / `Alt` / `Cmd` (salvo el atajo de deshacer).
- **Diálogos** con `role="dialog"`, `aria-modal="true"` y una etiqueta tomada de su título.
- **Mensajes de estado:** "Servidor desconectado" en la Dock es un elemento `role="status"`. "Marcador no enviado" y la pregunta de confianza en otro servidor son `role="alert"`.
- **No solo color:** las categorías de marcador siempre se muestran con su nombre (y un icono en los botones de la Dock), nunca solo con un color.
- **Movimiento reducido:** con la preferencia del sistema `prefers-reduced-motion: reduce` se suprimen animaciones y transiciones.
- **Sonidos opcionales del HUD:** una fuente HUD abierta con `?sound=1` reproduce un tono corto y distinto para cada categoría, con `&volume=0..1`, para que el streamer oiga que el marcador se registró sin apartar la vista del juego.
- **Idiomas:** la interfaz está en inglés y en español, y `<html lang>` sigue el idioma elegido para los lectores de pantalla.

## Qué no se ha probado todavía

- **Lectores de pantalla:** StreamCollab no se ha probado con NVDA, JAWS, VoiceOver ni el Narrador de Windows.
- **Navegadores:** los tests automáticos se ejecutan en Chromium (el motor de las docks y fuentes de navegador de OBS Studio). Firefox y Safari no se han probado.
- **OBS Studio:** el uso con teclado dentro de una dock de OBS depende de su navegador integrado y solo se ha probado a mano.

## Limitaciones conocidas

- **HUD:** la superposición que se usa como fuente de navegador en OBS no tiene controles interactivos a propósito; es una capa visual (y opcionalmente sonora) de tu emisión.
- **Contraste de color:** axe lo comprueba en las pantallas indicadas arriba. Otros estados (por ejemplo, un aviso del HUD sobre el vídeo de tu juego) dependen de lo que haya detrás.

---

## Comentarios e informes

Si algo de StreamCollab te resulta difícil o imposible de usar con tu configuración, cuéntanoslo:

- **Correo:** [cristianguerrero1958@outlook.com](mailto:cristianguerrero1958@outlook.com)
- **GitHub Issues:** abre un [informe de error](https://github.com/Consterna/streamcollab/issues/new?template=bug_report.yml) y añade la etiqueta `accessibility`.

Indica la vista (Dock, HUD o Panel del director), tu navegador o versión de OBS y la tecnología de apoyo que uses.
77 changes: 32 additions & 45 deletions ACCESSIBILITY.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,53 @@
# Accessibility Statement for StreamCollab

**English** | [Español](docs/ACCESSIBILITY.es.md)
**English** | [Español](ACCESSIBILITY.es.md)

StreamCollab is committed to ensuring digital accessibility for all content creators, streamers, editors, and viewers, regardless of physical, sensory, or cognitive abilities. We continually improve the user experience for everyone and apply relevant accessibility standards, targeting conformance with the **Web Content Accessibility Guidelines (WCAG) 2.1 Level AA**.
StreamCollab aims for the **Web Content Accessibility Guidelines (WCAG) 2.1 Level AA** in the Dock, the Director Panel and the HUD. This is a goal, not a conformance claim: the project has not had an independent accessibility audit. This page says what is checked automatically, what has not been tested yet, and how to report a barrier.

---

## Accessibility Features in StreamCollab
## What is checked on every change

### 1. High-Contrast Studio Dock & HUD Overlay
- **Contrast Ratios:** The StreamCollab OBS Dock and Dashboard feature an ergonomic dark theme with contrast ratios meeting or exceeding 4.5:1 for normal body text and 3:1 for large typography, borders, and interactive UI controls.
- **Color-Blind Accessible Markers:** Marker categories (Clip, Funny, Death, Chapter) are differentiated by distinct geometric iconography, typography, and shape semantics, never relying solely on color hue to convey critical operational status (WCAG 1.4.1 Use of Color).
- **Reduced Motion:** Interactive animations and HUD toast transitions respect the user's operating system preferences (`prefers-reduced-motion: reduce`).
- **axe-core (WCAG 2.1 A and AA rules)** runs in the end-to-end suite ([`e2e/a11y.spec.ts`](e2e/a11y.spec.ts)) in English and Spanish on:
- the Dock, empty and with a marker and the OBS settings open,
- the Director Panel, its Start Session dialog and its Export dialog,
- the Director Panel with a member, markers and a marker being edited,
- the HUD preview and the live HUD showing a marker toast.

### 2. Comprehensive Keyboard Navigation & Hotkeys
- **Logical Tab Order:** All actionable buttons, inputs, toggles, and session history items are accessible via sequential keyboard navigation (`Tab` / `Shift+Tab`).
- **Visible Focus States:** Clear, high-contrast focus rings (`:focus-visible`) indicate current interactive element placement across all views.
- **Keyboard Shortcuts:** Quick-marking hotkeys and instant undo (`Ctrl+Z` / `Cmd+Z`) allow fast-paced live operation without mouse reliance.
- **Focus Traps & Escape Handlers:** Modals and dialogs trap keyboard focus to prevent background traversal and dismiss cleanly with the `Escape` key, restoring focus to the triggering element.
The build fails on any violation. Automated tools only find part of the WCAG issues (contrast, names, roles, structure); they cannot judge, for example, whether a label is meaningful.
- **Dialogs** (Export, Start Session) are tested in Chromium ([`packages/client/test/components.browser.test.tsx`](packages/client/test/components.browser.test.tsx)): focus moves into the dialog when it opens, `Tab` / `Shift+Tab` stay inside it, `Escape` closes it and focus returns to the button that opened it.
- **Narrow docks:** the end-to-end suite checks that every Dock control stays on screen at 300, 360 and 420 px wide.
- **Reflow (WCAG 1.4.10):** the end-to-end suite checks that the Director Panel, with a session running, needs no horizontal scrolling at 320 px wide.
- **Focus after Start / Stop Session:** when the Start or Stop button that had the focus is replaced, the new one receives it instead of the page, also when the session was started or stopped from another tab (end-to-end tests).

### 3. Semantic Markup & WAI-ARIA
- **HTML5 Landmarks:** User interfaces are structured with native landmarks (`<header>`, `<nav>`, `<main>`, `<section>`, `<dialog>`) for swift screen-reader navigation.
- **Live Regions:** Dynamic status changes — such as live SNTP clock synchronization offsets, room connection states, and quick-marker confirmations — are announced using non-intrusive live regions (`aria-live="polite"` and `role="status"`).
- **Critical Alerts:** Connection loss, rate-limiting warnings, or session errors utilize `role="alert"` for immediate screen-reader feedback.
- **Descriptive Labels:** All icon buttons, form fields, and dropdown controls include explicit `aria-label`, `aria-labelledby`, or `aria-describedby` attributes.
## What StreamCollab does

### 4. Auditory Feedback & Assistive Sound Cues
- **HUD Sound Synthesizer:** StreamCollab includes configurable audio tones with unique frequencies for each marker category. Streamers can audit marker captures without looking away from their game or broadcast.
- **Independent Controls:** Complete volume adjustment and one-click mute toggles enable fine-grained auditory customization.
- **Keyboard:** every control is a native button, link, input or select. In the Dock, `1`–`5` add a marker and `0` or `Ctrl+Z` / `Cmd+Z` undoes your last one; the shortcuts are ignored while you type in a field, while a dialog is open, when the key is held down, and with `Ctrl` / `Alt` / `Cmd` (other than the undo shortcut).
- **Dialogs** use `role="dialog"`, `aria-modal="true"` and a label taken from their title.
- **Status messages:** "Server disconnected" in the Dock is a `role="status"` element. "Marker not sent" and the trust question for another server are `role="alert"`.
- **Not only color:** marker categories are always shown with their name (and an icon on the Dock buttons), not by color alone.
- **Reduced motion:** with the system setting `prefers-reduced-motion: reduce`, animations and transitions are suppressed.
- **Optional HUD sounds:** a HUD source opened with `?sound=1` plays a short, different tone for each marker category, with `&volume=0..1`, so streamers can hear that a marker landed without looking away from the game.
- **Languages:** the interface is in English and Spanish, and `<html lang>` follows the chosen language for screen readers.

### 5. Responsive Studio Scaling
- **Compact Dock Adaptation:** The OBS Dock scales cleanly down to narrow widths (300px to 360px) in OBS Studio without clipping text, truncation of critical buttons, or horizontal viewport overflow.
## Not tested yet

---

## Technical Specifications & Conformance

StreamCollab relies on the following technologies to work with assistive tools:
- HTML5 & WAI-ARIA
- CSS3 (with Flexbox, CSS Grid, and high-contrast color variables)
- Modern ECMAScript / React 19
- Modern Chromium and Firefox web engines, including OBS Studio 30+ CEF (Chromium Embedded Framework) docks

Tested assistive technologies include:
- Screen readers: NVDA, JAWS, VoiceOver (macOS), and Windows Narrator.
- Keyboard-only navigation across Chromium and Firefox.

---
- **Screen readers:** StreamCollab has not been tested with NVDA, JAWS, VoiceOver or Windows Narrator.
- **Browsers:** the automated tests run in Chromium (the engine of OBS Studio's docks and browser sources). Firefox and Safari have not been tested.
- **OBS Studio:** keyboard use inside an OBS dock depends on OBS's embedded browser and has only been tried by hand.

## Known Limitations & Workarounds
## Known limitations

While StreamCollab strives to adhere to WCAG 2.1 AA across all interfaces, some platform constraints exist:
- **OBS Studio Dock Focus Capture:** In certain older OBS Studio versions on Windows/Linux, internal CEF browser docks may occasionally require an initial mouse click inside the dock panel before receiving full keyboard input focus.
- **HUD Browser Source Transparency:** The transparent HUD overlay intended as an OBS browser source does not contain focusable interactive elements by design, as it operates as a visual/audio broadcast layer.
- **HUD:** the overlay used as an OBS browser source has no interactive controls by design; it is a visual (and optionally audio) layer of your stream.
- **Color contrast** is checked by axe on the screens listed above. Other states (for example a HUD toast over your game video) depend on what is behind them.

---

## Feedback and Reporting Process
## Feedback and reporting

We welcome feedback on the accessibility of StreamCollab. If you encounter accessibility barriers, discover a keyboard trap, or need assistance:
If something in StreamCollab is hard or impossible to use with your setup, please tell us:

- **Email:** [cristianguerrero1958@outlook.com](mailto:cristianguerrero1958@outlook.com)
- **GitHub Issues:** Submit an issue using the [Bug Report template](https://github.com/Consterna/streamcollab/issues/new?template=bug_report.yml) and include the `accessibility` label.
- **GitHub Issues:** open a [bug report](https://github.com/Consterna/streamcollab/issues/new?template=bug_report.yml) and add the `accessibility` label.

We aim to acknowledge and address reported accessibility concerns promptly.
Please mention the view (Dock, HUD or Director Panel), your browser or OBS version and any assistive technology you use.
Loading
Loading