fix: remaining dependabot security issues (#890)

* Upgrade docs deps

* Upgrade frontend to latest deps

* Update package overrides

* Remove redundant deps

* Add tailwind postcss plugin

* Replace language select with chakra

* Fix main nav

* Patch gray matter

* Fix webpack override

* Replace python-jose with pyjwt

* Override kv url for frontend in compose

* Upgrade hatchet sdk

* Update docs

* Supress pydantic warnings
This commit is contained in:
Sergey Mankovsky
2026-03-02 17:17:40 +01:00
committed by GitHub
parent 4d915e2a9f
commit 0931095f49
34 changed files with 20117 additions and 26696 deletions

1
.gitignore vendored
View File

@@ -21,7 +21,6 @@ CLAUDE.local.md
www/.env.development
www/.env.production
.playwright-mcp
docs/pnpm-lock.yaml
.secrets
opencode.json

View File

@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
Reflector is an AI-powered audio transcription and meeting analysis platform with real-time processing capabilities. The system consists of:
- **Frontend**: Next.js 14 React application (`www/`) with Chakra UI, real-time WebSocket integration
- **Frontend**: Next.js 16 React application (`www/`) with Chakra UI, real-time WebSocket integration
- **Backend**: Python FastAPI server (`server/`) with async database operations and background processing
- **Processing**: GPU-accelerated ML pipeline for transcription, diarization, summarization via Modal.com
- **Infrastructure**: Redis, PostgreSQL/SQLite, Celery workers, WebRTC streaming

View File

@@ -93,6 +93,7 @@ services:
environment:
NODE_ENV: development
SERVER_API_URL: http://host.docker.internal:1250
KV_URL: redis://redis:6379
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:

7
docs/.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
node_modules
build
.git
.gitignore
*.log
.DS_Store
.env*

View File

@@ -1,14 +1,17 @@
FROM node:18-alpine AS builder
FROM node:20-alpine AS builder
WORKDIR /app
# Install curl for fetching OpenAPI spec
RUN apk add --no-cache curl
# Copy package files
COPY package*.json ./
# Enable pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy package files and lockfile
COPY package.json pnpm-lock.yaml* ./
# Install dependencies
RUN npm ci
RUN pnpm install --frozen-lockfile
# Copy source
COPY . .
@@ -21,7 +24,7 @@ RUN mkdir -p ./static && curl -sf "${OPENAPI_URL}" -o ./static/openapi.json || e
RUN sed -i "s/onBrokenLinks: 'throw'/onBrokenLinks: 'warn'/g" docusaurus.config.ts
# Build static site (skip prebuild hook by calling docusaurus directly)
RUN npx docusaurus build
RUN pnpm exec docusaurus build
# Production image
FROM nginx:alpine

View File

@@ -5,13 +5,13 @@ This website is built using [Docusaurus](https://docusaurus.io/), a modern stati
### Installation
```
$ yarn
$ pnpm install
```
### Local Development
```
$ yarn start
$ pnpm start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
@@ -19,7 +19,7 @@ This command starts a local development server and opens up a browser window. Mo
### Build
```
$ yarn build
$ pnpm build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
@@ -29,13 +29,13 @@ This command generates static content into the `build` directory and can be serv
Using SSH:
```
$ USE_SSH=true yarn deploy
$ USE_SSH=true pnpm deploy
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> yarn deploy
$ GIT_USER=<Your GitHub username> pnpm deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

View File

@@ -11,7 +11,7 @@ Reflector is built as a modern, scalable, microservices-based application design
### Frontend Application
The user interface is built with **Next.js 15** using the App Router pattern, providing:
The user interface is built with **Next.js 16** using the App Router pattern, providing:
- Server-side rendering for optimal performance
- Real-time WebSocket connections for live transcription

View File

@@ -36,14 +36,15 @@ This creates `docs/static/openapi.json` (should be ~70KB) which will be copied d
The Dockerfile is already in `docs/Dockerfile`:
```dockerfile
FROM node:18-alpine AS builder
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Enable pnpm and copy package files + lockfile
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY package.json pnpm-lock.yaml* ./
# Inshall dependencies
RUN npm ci
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source (includes static/openapi.json if pre-fetched)
COPY . .
@@ -52,7 +53,7 @@ COPY . .
RUN sed -i "s/onBrokenLinks: 'throw'/onBrokenLinks: 'warn'/g" docusaurus.config.ts
# Build static site
RUN npx docusaurus build
RUN pnpm exec docusaurus build
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html

View File

@@ -46,7 +46,7 @@ Reflector consists of three main components:
Ready to deploy Reflector? Head over to our [Installation Guide](./installation/overview) to set up your own instance.
For a quick overview of how Reflector processes audio, check out our [Pipeline Documentation](./pipelines/overview).
For a quick overview of how Reflector processes audio, check out our [Pipeline Documentation](./concepts/pipeline).
## Open Source

View File

@@ -124,11 +124,11 @@ const config: Config = {
items: [
{
label: 'Architecture',
to: '/docs/reference/architecture/overview',
to: '/docs/concepts/overview',
},
{
label: 'Pipelines',
to: '/docs/pipelines/overview',
to: '/docs/concepts/pipeline',
},
{
label: 'Roadmap',

23526
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,26 +14,26 @@
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"fetch-openapi": "./scripts/fetch-openapi.sh",
"gen-api-docs": "npm run fetch-openapi && docusaurus gen-api-docs reflector",
"prebuild": "npm run fetch-openapi"
"gen-api-docs": "pnpm run fetch-openapi && docusaurus gen-api-docs reflector",
"prebuild": "pnpm run fetch-openapi"
},
"dependencies": {
"@docusaurus/core": "3.6.3",
"@docusaurus/preset-classic": "3.6.3",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"docusaurus-plugin-openapi-docs": "^4.5.1",
"docusaurus-theme-openapi-docs": "^4.5.1",
"@docusaurus/theme-mermaid": "3.6.3",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"@docusaurus/core": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@docusaurus/theme-mermaid": "3.9.2",
"@mdx-js/react": "^3.1.1",
"clsx": "^2.1.1",
"docusaurus-plugin-openapi-docs": "^4.7.1",
"docusaurus-theme-openapi-docs": "^4.7.1",
"prism-react-renderer": "^2.4.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.6.3",
"@docusaurus/tsconfig": "3.6.3",
"@docusaurus/types": "3.6.3",
"typescript": "~5.6.2"
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/tsconfig": "3.9.2",
"@docusaurus/types": "3.9.2",
"typescript": "~5.9.3"
},
"browserslist": {
"production": [
@@ -49,5 +49,15 @@
},
"engines": {
"node": ">=18.0"
},
"pnpm": {
"overrides": {
"minimatch@<3.1.4": "3.1.5",
"minimatch@>=5.0.0 <5.1.8": "5.1.8",
"minimatch@>=9.0.0 <9.0.7": "9.0.7",
"lodash@<4.17.23": "4.17.23",
"js-yaml@<4.1.1": "4.1.1",
"gray-matter": "github:jonschlinkert/gray-matter#234163e"
}
}
}

13976
docs/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

4129
docs/static/openapi.json vendored

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,7 @@ dependencies = [
"protobuf>=4.24.3",
"celery>=5.3.4",
"redis>=5.0.1",
"python-jose[cryptography]>=3.3.0",
"pyjwt[crypto]>=2.8.0",
"python-multipart>=0.0.6",
"transformers>=4.36.2",
"jsonschema>=4.23.0",

View File

@@ -0,0 +1,13 @@
"""
Suppress known dependency warnings. Import this before any reflector/hatchet_sdk
imports that pull in pydantic (e.g. llama_index) to hide UnsupportedFieldAttributeWarning
about validate_default.
"""
import warnings
warnings.filterwarnings(
"ignore",
message=".*validate_default.*",
category=UserWarning,
)

View File

@@ -4,8 +4,8 @@ from fastapi import Depends, HTTPException
if TYPE_CHECKING:
from fastapi import WebSocket
import jwt
from fastapi.security import APIKeyHeader, OAuth2PasswordBearer
from jose import JWTError, jwt
from pydantic import BaseModel
from reflector.db.user_api_keys import user_api_keys_controller
@@ -54,7 +54,7 @@ class JWTAuth:
audience=jwt_audience,
)
return payload
except JWTError as e:
except jwt.PyJWTError as e:
logger.error(f"JWT error: {e}")
raise
@@ -94,7 +94,7 @@ async def _authenticate_user(
)
user_infos.append(UserInfo(sub=user.id, email=email))
except JWTError as e:
except jwt.PyJWTError as e:
logger.error(f"JWT error: {e}")
raise HTTPException(status_code=401, detail="Invalid authentication")

View File

@@ -9,9 +9,9 @@ from collections import defaultdict
from datetime import datetime, timedelta, timezone
from typing import TYPE_CHECKING, Annotated, Optional
import jwt
from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.security import APIKeyHeader, OAuth2PasswordBearer
from jose import JWTError, jwt
from pydantic import BaseModel
from reflector.auth.password_utils import verify_password
@@ -110,7 +110,7 @@ async def _authenticate_user(
user_id = payload["sub"]
email = payload.get("email")
user_infos.append(UserInfo(sub=user_id, email=email))
except JWTError as e:
except jwt.PyJWTError as e:
logger.error(f"JWT error: {e}")
raise HTTPException(status_code=401, detail="Invalid authentication")

View File

@@ -7,6 +7,7 @@ Configuration:
- Worker affinity: pool=cpu-heavy
"""
import reflector._warnings_filter # noqa: F401 -- side effect: suppress pydantic validate_default warning
from reflector.hatchet.client import HatchetClientManager
from reflector.hatchet.workflows.daily_multitrack_pipeline import (
daily_multitrack_pipeline,

View File

@@ -5,6 +5,7 @@ Handles: all tasks except mixdown_tracks (transcription, LLM inference, orchestr
import asyncio
import reflector._warnings_filter # noqa: F401 -- side effect: suppress pydantic validate_default warning
from reflector.hatchet.client import HatchetClientManager
from reflector.hatchet.workflows.daily_multitrack_pipeline import (
daily_multitrack_pipeline,

View File

@@ -17,6 +17,7 @@ from typing import Callable
from celery.result import AsyncResult
from hatchet_sdk.clients.rest.models import V1TaskStatus
import reflector._warnings_filter # noqa: F401 -- side effect: suppress pydantic validate_default warning
from reflector.db import get_database
from reflector.db.transcripts import Transcript, transcripts_controller
from reflector.hatchet.client import HatchetClientManager

View File

@@ -1,10 +1,10 @@
from datetime import datetime, timedelta, timezone
from typing import Annotated, Literal, Optional, assert_never
import jwt
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi_pagination import Page
from fastapi_pagination.ext.databases import apaginate
from jose import jwt
from pydantic import (
AwareDatetime,
BaseModel,

View File

@@ -7,8 +7,8 @@ Transcripts audio related endpoints
from typing import Annotated, Optional
import httpx
import jwt
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from jose import jwt
import reflector.auth as auth
from reflector.db.transcripts import AudioWaveform, transcripts_controller
@@ -44,7 +44,7 @@ async def transcript_get_audio_mp3(
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
except jwt.JWTError:
except jwt.PyJWTError:
raise unauthorized_exception
transcript = await transcripts_controller.get_by_id_for_http(

View File

@@ -1,8 +1,8 @@
"""Tests for the password auth backend."""
import jwt
import pytest
from httpx import AsyncClient
from jose import jwt
from reflector.auth.password_utils import hash_password
from reflector.settings import settings

View File

@@ -67,7 +67,7 @@ def appserver_ws_user(setup_database):
@pytest.fixture(autouse=True)
def patch_jwt_verification(monkeypatch):
"""Patch JWT verification to accept HS256 tokens signed with SECRET_KEY for tests."""
from jose import jwt
import jwt
from reflector.settings import settings
@@ -84,7 +84,7 @@ def _make_dummy_jwt(sub: str = "user123") -> str:
# Create a short HS256 JWT using the app secret to pass verification in tests
from datetime import datetime, timedelta, timezone
from jose import jwt
import jwt
from reflector.settings import settings

77
server/uv.lock generated
View File

@@ -861,18 +861,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" },
]
[[package]]
name = "ecdsa"
version = "0.19.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" },
]
[[package]]
name = "email-validator"
version = "2.2.0"
@@ -1195,7 +1183,7 @@ wheels = [
[[package]]
name = "hatchet-sdk"
version = "1.21.6"
version = "1.27.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
@@ -1207,11 +1195,12 @@ dependencies = [
{ name = "pydantic-settings" },
{ name = "python-dateutil" },
{ name = "tenacity" },
{ name = "typing-inspection" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7c/df/75dd02e1dc6b99f7151a57f084876c50f739ad4d643b060078f65d51d717/hatchet_sdk-1.21.6.tar.gz", hash = "sha256:b65741324ad721ce57f5fe3f960e2942c4ac2ceec6ca483dd35f84137ff7c46c", size = 219345, upload-time = "2025-12-11T15:04:24.899Z" }
sdist = { url = "https://files.pythonhosted.org/packages/5b/02/e8bcc42654f03af3a39f9319d21fc42ab36abca9514cee275c04b2810186/hatchet_sdk-1.27.0.tar.gz", hash = "sha256:c312a83c8e6c13040cc2512a6ed7e60085af2496587a2dbd5c18a62d84217cb8", size = 246838, upload-time = "2026-02-27T18:21:40.236Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/86/e4cd7928bcabd33c634c33d4e878e2454e03f97c87b72947c7ff5762d813/hatchet_sdk-1.21.6-py3-none-any.whl", hash = "sha256:589fba9104a6517e1ba677b9865fa0a20e221863a8c2a2724051198994c11399", size = 529167, upload-time = "2025-12-11T15:04:23.697Z" },
{ url = "https://files.pythonhosted.org/packages/ef/5b/3c2a8b6908a68d42489d903c41fa460cd6d61e07a27252737fcec8d97b31/hatchet_sdk-1.27.0-py3-none-any.whl", hash = "sha256:3cea10e68d3551881588ec941b50f0e383855b191eb79905ee57ee806b08430b", size = 574642, upload-time = "2026-02-27T18:21:37.611Z" },
]
[[package]]
@@ -2240,15 +2229,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031, upload-time = "2024-10-16T11:21:34.211Z" },
]
[[package]]
name = "pyasn1"
version = "0.6.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" },
]
[[package]]
name = "pycparser"
version = "2.22"
@@ -2405,6 +2385,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/7f/113b16d55e8d2dd9143628eec39b138fd6c52f72dcd11b4dae4a3845da4d/pyinstrument-5.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:88df7e3ab11604ae7cef1f576c097a08752bf8fc13c5755803bd3cd92f15aba3", size = 124314, upload-time = "2025-07-02T14:13:26.708Z" },
]
[[package]]
name = "pyjwt"
version = "2.11.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" },
]
[package.optional-dependencies]
crypto = [
{ name = "cryptography" },
]
[[package]]
name = "pylibsrtp"
version = "0.12.0"
@@ -2619,25 +2613,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" },
]
[[package]]
name = "python-jose"
version = "3.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ecdsa" },
{ name = "pyasn1" },
{ name = "rsa" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" },
]
[package.optional-dependencies]
cryptography = [
{ name = "cryptography" },
]
[[package]]
name = "python-multipart"
version = "0.0.22"
@@ -2791,8 +2766,8 @@ dependencies = [
{ name = "psycopg2-binary" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "pyjwt", extra = ["crypto"] },
{ name = "pytest-env" },
{ name = "python-jose", extra = ["cryptography"] },
{ name = "python-multipart" },
{ name = "redis" },
{ name = "requests" },
@@ -2867,8 +2842,8 @@ requires-dist = [
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
{ name = "pydantic", specifier = ">=2.12.5" },
{ name = "pydantic-settings", specifier = ">=2.0.2" },
{ name = "pyjwt", extras = ["crypto"], specifier = ">=2.8.0" },
{ name = "pytest-env", specifier = ">=1.1.5" },
{ name = "python-jose", extras = ["cryptography"], specifier = ">=3.3.0" },
{ name = "python-multipart", specifier = ">=0.0.6" },
{ name = "redis", specifier = ">=5.0.1" },
{ name = "requests", specifier = ">=2.31.0" },
@@ -3087,18 +3062,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c", size = 555334, upload-time = "2025-07-01T15:56:51.703Z" },
]
[[package]]
name = "rsa"
version = "4.9.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyasn1" },
]
sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
]
[[package]]
name = "s3transfer"
version = "0.13.0"

View File

@@ -1,10 +1,8 @@
import { Container, Flex } from "@chakra-ui/react";
import { featureEnabled } from "../lib/features";
import NextLink from "next/link";
import Image from "next/image";
import UserInfo from "../(auth)/userInfo";
import AuthWrapper from "./AuthWrapper";
import { RECORD_A_MEETING_URL } from "../api/urls";
import MainNav from "../components/MainNav";
export default async function AppLayout({
children,
@@ -47,44 +45,7 @@ export default async function AppLayout({
</p>
</div>
</NextLink>
<div>
{/* Text link on the right */}
<NextLink href={RECORD_A_MEETING_URL} className="font-light px-2">
Create
</NextLink>
{featureEnabled("browse") ? (
<>
&nbsp;·&nbsp;
<NextLink href="/browse" className="font-light px-2">
Browse
</NextLink>
</>
) : (
<></>
)}
{featureEnabled("rooms") ? (
<>
&nbsp;·&nbsp;
<NextLink href="/rooms" className="font-light px-2">
Rooms
</NextLink>
</>
) : (
<></>
)}
{featureEnabled("requireLogin") ? (
<>
&nbsp;·&nbsp;
<NextLink href="/settings/api-keys" className="font-light px-2">
Settings
</NextLink>
&nbsp;·&nbsp;
<UserInfo />
</>
) : (
<></>
)}
</div>
<MainNav />
</Flex>
<AuthWrapper>{children}</AuthWrapper>

View File

@@ -1,13 +1,11 @@
"use client";
import React, { useEffect, useState } from "react";
import useAudioDevice from "../useAudioDevice";
import "react-select-search/style.css";
import "../../../styles/form.scss";
import About from "../../../(aboutAndPrivacy)/about";
import Privacy from "../../../(aboutAndPrivacy)/privacy";
import { useRouter } from "next/navigation";
import useCreateTranscript from "../createTranscript";
import SelectSearch from "react-select-search";
import { supportedLanguages } from "../../../supportedLanguages";
import {
Flex,
@@ -21,6 +19,7 @@ import {
} from "@chakra-ui/react";
import { useAuth } from "../../../lib/AuthProvider";
import { featureEnabled } from "../../../lib/features";
import { SearchableLanguageSelect } from "../../../components/SearchableLanguageSelect";
const TranscriptCreate = () => {
const router = useRouter();
@@ -147,31 +146,27 @@ const TranscriptCreate = () => {
p={8}
flexDir="column"
my={4}
className="form-on-primary"
>
<Heading size="xl" mb={4}>
Try Reflector
</Heading>
<Box mb={4}>
<Text>Recording name</Text>
<div className="select-search-container">
<Text mb={1}>Recording name</Text>
<input
className="select-search-input"
className="form-field-input"
type="text"
onChange={nameChange}
placeholder="Optional"
/>
</div>
</Box>
<Box mb={4}>
<Text>Do you want to enable live translation?</Text>
<SelectSearch
search
<Text mb={1}>Do you want to enable live translation?</Text>
<SearchableLanguageSelect
options={supportedLanguages}
value={targetLanguage}
onChange={onLanguageChange}
onBlur={() => {}}
onFocus={() => {}}
placeholder="Choose your language"
placeholder="No translation"
/>
</Box>
{!loading ? (

View File

@@ -0,0 +1,46 @@
import NextLink from "next/link";
import { featureEnabled } from "../lib/features";
import UserInfo from "../(auth)/userInfo";
import { RECORD_A_MEETING_URL } from "../api/urls";
function NavLink({
href,
children,
}: {
href: string;
children: React.ReactNode;
}) {
return (
<NextLink href={href} className="font-light px-10">
{children}
</NextLink>
);
}
export default function MainNav() {
return (
<nav>
<NavLink href={RECORD_A_MEETING_URL}>Create</NavLink>
{featureEnabled("browse") && (
<>
&nbsp;·&nbsp;
<NavLink href="/browse">Browse</NavLink>
</>
)}
{featureEnabled("rooms") && (
<>
&nbsp;·&nbsp;
<NavLink href="/rooms">Rooms</NavLink>
</>
)}
{featureEnabled("requireLogin") && (
<>
&nbsp;·&nbsp;
<NavLink href="/settings/api-keys">Settings</NavLink>
&nbsp;·&nbsp;
<UserInfo />
</>
)}
</nav>
);
}

View File

@@ -0,0 +1,98 @@
"use client";
import React, { useMemo } from "react";
import {
Combobox,
createListCollection,
useComboboxContext,
} from "@chakra-ui/react";
export type LangOption = { value: string | undefined; name: string };
type Item = { label: string; value: string };
function FilteredComboboxItems({ items }: { items: Item[] }) {
const ctx = useComboboxContext();
const inputValue = (ctx as { inputValue?: string }).inputValue ?? "";
const filtered = useMemo(() => {
const q = inputValue.trim().toLowerCase();
if (!q) return items;
return items.filter((item) => item.label.toLowerCase().includes(q));
}, [items, inputValue]);
return (
<>
<Combobox.Empty>No matches</Combobox.Empty>
{filtered.map((item) => (
<Combobox.Item key={item.value} item={item}>
{item.label}
</Combobox.Item>
))}
</>
);
}
type Props = {
options: LangOption[];
value: string;
onChange: (value: string) => void;
placeholder: string;
};
export function SearchableLanguageSelect({
options,
value,
onChange,
placeholder,
}: Props) {
const items = useMemo(() => {
const result: Item[] = [];
let addedNone = false;
for (const opt of options) {
const val = opt.value ?? "NOTRANSLATION";
if (val === "NOTRANSLATION" || val === "") {
if (addedNone) continue;
addedNone = true;
result.push({ label: "No translation", value: "NOTRANSLATION" });
} else {
result.push({ label: opt.name, value: val });
}
}
return result.sort((a, b) => {
if (a.value === "NOTRANSLATION") return -1;
if (b.value === "NOTRANSLATION") return 1;
return a.label.localeCompare(b.label);
});
}, [options]);
const collection = useMemo(() => createListCollection({ items }), [items]);
const selectedValues = value && value !== "NOTRANSLATION" ? [value] : [];
return (
<Combobox.Root
collection={collection}
value={selectedValues}
onValueChange={(e) => onChange(e.value[0] ?? "NOTRANSLATION")}
openOnClick
closeOnSelect
selectionBehavior="replace"
placeholder={placeholder}
className="form-combobox"
size="md"
positioning={{ strategy: "fixed", hideWhenDetached: true }}
>
<Combobox.Control>
<Combobox.Input />
<Combobox.IndicatorGroup>
<Combobox.Trigger />
</Combobox.IndicatorGroup>
</Combobox.Control>
<Combobox.Positioner>
<Combobox.Content>
<FilteredComboboxItems items={items} />
</Combobox.Content>
</Combobox.Positioner>
</Combobox.Root>
);
}

View File

@@ -1,42 +1,74 @@
@media (prefers-color-scheme: dark) {
.select-search-container,
.input-container {
--select-search-background: #fff;
--select-search-border: #dce0e8;
--select-search-selected: #1e66f5;
--select-search-text: #000;
--select-search-subtle-text: #6c6f85;
--select-search-highlight: #eff1f5;
/* Form fields on primary (blue) card white inputs like previous react-select */
.form-on-primary {
--form-bg: #fff;
--form-border: #dce0e8;
--form-focus-border: #1e66f5;
--form-text: #000;
--form-placeholder: #6c6f85;
--form-option-bg: #fff;
--form-option-hover: #eff1f5;
--form-dropdown-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
--form-radius: 0.5rem; /* 8px, matches rounded-lg elsewhere */
}
.form-on-primary .form-field-input,
.form-on-primary .form-field-select,
.form-on-primary .form-field-search-input {
box-sizing: border-box;
width: 100%;
padding: 0.5rem 0.75rem;
border-radius: var(--form-radius);
border: 1px solid var(--form-border);
background-color: var(--form-bg);
color: var(--form-text);
font-size: 0.9375rem;
outline: none;
cursor: pointer;
&::placeholder {
color: var(--form-placeholder);
}
&:focus {
border-color: var(--form-focus-border);
}
}
body.is-dark-mode .select-search-container,
body.is-dark-mode .input-container {
--select-search-background: #fff;
--select-search-border: #dce0e8;
--select-search-selected: #1e66f5;
--select-search-text: #000;
--select-search-subtle-text: #6c6f85;
--select-search-highlight: #eff1f5;
.form-on-primary .form-field-select option {
background: var(--form-option-bg);
color: var(--form-text);
}
body.is-light-mode .select-search-container,
body.is-light-mode .input-container {
--select-search-background: #fff;
--select-search-border: #dce0e8;
--select-search-selected: #1e66f5;
--select-search-text: #000;
--select-search-subtle-text: #6c6f85;
--select-search-highlight: #eff1f5;
}
/* Chakra Combobox inside form-on-primary: white input + dropdown, dark text */
.form-on-primary .form-combobox {
width: 100%;
.input-container,
.select-search-container {
max-width: 100%;
width: auto;
}
[data-part="control"],
& input {
border-radius: var(--form-radius);
border-color: var(--form-border);
background-color: var(--form-bg);
color: var(--form-text);
body .select-search-container .select-search--top.select-search-select {
top: auto;
bottom: 46px;
&:focus,
&[data-focus] {
border-color: var(--form-focus-border);
}
}
[data-part="content"] {
border-radius: var(--form-radius);
border: 1px solid var(--form-border);
background: var(--form-bg);
box-shadow: var(--form-dropdown-shadow);
color: var(--form-text);
}
[data-part="item"] {
color: var(--form-text);
&:hover {
background: var(--form-option-hover);
}
}
}

View File

@@ -13,61 +13,66 @@
"test": "jest"
},
"dependencies": {
"@chakra-ui/react": "^3.24.2",
"@daily-co/daily-js": "^0.84.0",
"@chakra-ui/react": "^3.33.0",
"@daily-co/daily-js": "^0.87.0",
"@emotion/react": "^11.14.0",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-solid-svg-icons": "^7.2.0",
"@fortawesome/react-fontawesome": "^3.2.0",
"@sentry/nextjs": "^10.40.0",
"@tanstack/react-query": "^5.85.9",
"@whereby.com/browser-sdk": "^3.3.4",
"autoprefixer": "10.4.20",
"axios": "^1.13.5",
"eslint": "^9.33.0",
"@tanstack/react-query": "^5.90.21",
"@whereby.com/browser-sdk": "^3.18.21",
"autoprefixer": "10.4.27",
"axios": "^1.13.6",
"eslint": "^10.0.2",
"eslint-config-next": "^16.1.6",
"fontawesome": "^5.6.3",
"ioredis": "^5.7.0",
"jest-worker": "^29.6.2",
"lucide-react": "^0.525.0",
"ioredis": "^5.10.0",
"jest-worker": "^30.2.0",
"lucide-react": "^0.575.0",
"next": "^16.1.6",
"next-auth": "^4.24.12",
"next-auth": "^4.24.13",
"next-themes": "^0.4.6",
"nuqs": "^2.4.3",
"openapi-fetch": "^0.14.0",
"openapi-react-query": "^0.5.0",
"postcss": "8.4.31",
"nuqs": "^2.8.9",
"openapi-fetch": "^0.17.0",
"openapi-react-query": "^0.5.4",
"postcss": "8.5.6",
"prop-types": "^15.8.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-dropdown": "^1.11.0",
"react-icons": "^5.0.1",
"react-markdown": "^9.0.0",
"react-qr-code": "^2.0.12",
"react-select-search": "^4.1.7",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"react-qr-code": "^2.0.18",
"react-uuid-hook": "^0.0.6",
"redlock": "5.0.0-beta.2",
"remeda": "^2.31.1",
"sass": "^1.63.6",
"remeda": "^2.33.6",
"sass": "^1.97.3",
"simple-peer": "^9.11.1",
"tailwindcss": "^3.3.2",
"typescript": "^5.1.6",
"wavesurfer.js": "^7.4.2",
"zod": "^4.1.5"
"tailwindcss": "^4.2.1",
"typescript": "^5.9.3",
"wavesurfer.js": "^7.12.1",
"zod": "^4.3.6"
},
"main": "index.js",
"repository": "https://github.com/Monadical-SAS/reflector-ui.git",
"author": "Andreas <andreas@monadical.com>",
"license": "All Rights Reserved",
"devDependencies": {
"@types/ioredis": "^5.0.0",
"@tailwindcss/postcss": "^4.2.1",
"@types/jest": "^30.0.0",
"@types/react": "19.2.14",
"@types/react-dom": "^19.2.3",
"jest": "^30.2.0",
"openapi-typescript": "^7.9.1",
"prettier": "^3.0.0",
"openapi-typescript": "^7.13.0",
"prettier": "^3.8.1",
"ts-jest": "^29.4.6"
},
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
"pnpm": {
"overrides": {
"minimatch@>=5.0.0 <5.1.8": "5.1.8",
"js-yaml@<4.1.1": "4.1.1",
"webpack": "5.105.3"
}
}
}

4559
www/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
"@tailwindcss/postcss": {},
autoprefixer: {},
},
};