From e236c608f0559af7e87f656708b9c65f156f56ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gadomski?= Date: Fri, 20 Mar 2026 16:54:46 +0100 Subject: [PATCH 1/4] Remove examples --- .../mobile-client/blur-example/.env.example | 2 - .../mobile-client/blur-example/.eslintrc.js | 17 - .../mobile-client/blur-example/.gitignore | 39 -- examples/mobile-client/blur-example/App.tsx | 20 - examples/mobile-client/blur-example/README.md | 93 --- examples/mobile-client/blur-example/app.json | 43 -- .../blur-example/assets/adaptive-icon.png | Bin 17547 -> 0 bytes .../blur-example/assets/favicon.png | Bin 1466 -> 0 bytes .../blur-example/assets/icon.png | Bin 22380 -> 0 bytes .../blur-example/assets/splash-icon.png | Bin 17547 -> 0 bytes .../components/VideosGridItem.tsx | 52 -- .../blur-example/hooks/useConnectFishjam.ts | 59 -- examples/mobile-client/blur-example/index.ts | 8 - .../navigation/RootNavigation.tsx | 32 - .../mobile-client/blur-example/package.json | 33 - .../blur-example/prettier.config.js | 1 - .../blur-example/screens/home/index.tsx | 60 -- .../blur-example/screens/room/index.tsx | 87 --- .../mobile-client/blur-example/tsconfig.json | 7 - .../mobile-client/blur-example/types/index.ts | 7 - .../mobile-client/blur-example/utils/index.ts | 44 -- .../minimal-react-native/.env.example | 1 - .../minimal-react-native/.eslintrc.js | 21 - .../minimal-react-native/.gitignore | 39 -- .../minimal-react-native/App.tsx | 20 - .../minimal-react-native/README.md | 84 --- .../minimal-react-native/app.json | 43 -- .../assets/adaptive-icon.png | Bin 17547 -> 0 bytes .../minimal-react-native/assets/favicon.png | Bin 1466 -> 0 bytes .../minimal-react-native/assets/icon.png | Bin 22380 -> 0 bytes .../assets/splash-icon.png | Bin 17547 -> 0 bytes .../components/VideosGridItem.tsx | 51 -- .../hooks/useConnectFishjam.ts | 59 -- .../minimal-react-native/index.ts | 8 - .../navigation/RootNavigation.tsx | 32 - .../minimal-react-native/package.json | 34 - .../minimal-react-native/prettier.config.js | 1 - .../screens/home/index.tsx | 60 -- .../screens/room/index.tsx | 64 -- .../minimal-react-native/tsconfig.json | 7 - .../minimal-react-native/types/index.ts | 7 - .../minimal-react-native/utils/index.ts | 44 -- examples/mobile-client/text-chat/.env.example | 1 - examples/mobile-client/text-chat/.eslintrc.js | 16 - examples/mobile-client/text-chat/.gitignore | 39 -- examples/mobile-client/text-chat/App.tsx | 20 - examples/mobile-client/text-chat/README.md | 128 ---- examples/mobile-client/text-chat/app.json | 36 -- .../text-chat/hooks/useConnectFishjam.ts | 53 -- examples/mobile-client/text-chat/index.ts | 5 - .../text-chat/navigation/RootNavigation.tsx | 37 -- examples/mobile-client/text-chat/package.json | 34 - .../text-chat/prettier.config.js | 1 - .../text-chat/screens/chat/index.tsx | 295 --------- .../text-chat/screens/home/index.tsx | 91 --- .../mobile-client/text-chat/tsconfig.json | 7 - .../mobile-client/video-player/.env.example | 1 - .../mobile-client/video-player/.eslintrc.js | 16 - .../mobile-client/video-player/.gitignore | 43 -- .../video-player/.vscode/extensions.json | 1 - .../video-player/.vscode/settings.json | 7 - examples/mobile-client/video-player/App.tsx | 154 ----- examples/mobile-client/video-player/README.md | 46 -- examples/mobile-client/video-player/app.json | 47 -- .../assets/images/android-icon-background.png | Bin 17549 -> 0 bytes .../assets/images/android-icon-foreground.png | Bin 78796 -> 0 bytes .../assets/images/android-icon-monochrome.png | Bin 4140 -> 0 bytes .../video-player/assets/images/favicon.png | Bin 1129 -> 0 bytes .../video-player/assets/images/icon.png | Bin 393493 -> 0 bytes .../assets/images/partial-react-logo.png | Bin 5075 -> 0 bytes .../video-player/assets/images/react-logo.png | Bin 6341 -> 0 bytes .../assets/images/react-logo@2x.png | Bin 14225 -> 0 bytes .../assets/images/react-logo@3x.png | Bin 21252 -> 0 bytes .../assets/images/splash-icon.png | Bin 17547 -> 0 bytes .../video-player/babel.config.js | 16 - .../components/FishjamPlayerStreamer.tsx | 230 ------- .../components/FishjamPlayerViewer.tsx | 110 ---- examples/mobile-client/video-player/index.js | 5 - .../mobile-client/video-player/package.json | 28 - .../video-player/prettier.config.js | 1 - .../mobile-client/video-player/tsconfig.json | 11 - examples/react-client/README.md | 29 - examples/react-client/audio-only/.env.example | 1 - examples/react-client/audio-only/.gitignore | 25 - .../react-client/audio-only/.prettierignore | 2 - examples/react-client/audio-only/.prettierrc | 4 - examples/react-client/audio-only/README.md | 3 - .../react-client/audio-only/eslint.config.js | 28 - examples/react-client/audio-only/index.html | 13 - examples/react-client/audio-only/package.json | 42 -- examples/react-client/audio-only/src/App.tsx | 31 - .../audio-only/src/AudioPlayer.tsx | 18 - .../audio-only/src/JoinRoomForm.tsx | 59 -- .../audio-only/src/MicrophoneSettings.tsx | 32 - .../react-client/audio-only/src/PeerList.tsx | 28 - .../react-client/audio-only/src/RoomInfo.tsx | 36 -- examples/react-client/audio-only/src/main.tsx | 12 - .../react-client/audio-only/src/vite-env.d.ts | 1 - .../react-client/audio-only/tsconfig.app.json | 26 - .../react-client/audio-only/tsconfig.json | 7 - .../audio-only/tsconfig.node.json | 24 - .../react-client/audio-only/vite.config.ts | 7 - .../react-client/fishjam-chat/.eslintignore | 2 - .../react-client/fishjam-chat/.eslintrc.cjs | 17 - examples/react-client/fishjam-chat/.gitignore | 24 - .../react-client/fishjam-chat/.prettierrc | 4 - examples/react-client/fishjam-chat/README.md | 5 - .../react-client/fishjam-chat/components.json | 20 - examples/react-client/fishjam-chat/index.html | 35 - .../react-client/fishjam-chat/package.json | 60 -- .../fishjam-chat/postcss.config.cjs | 6 - .../public/shaders/blur/fragment.glsl | 53 -- .../public/shaders/blur/vertex.glsl | 7 - .../react-client/fishjam-chat/src/App.tsx | 20 - .../react-client/fishjam-chat/src/Router.tsx | 28 - .../src/components/AudioPlayer.tsx | 19 - .../src/components/AudioTracks.tsx | 13 - .../src/components/AudioVisualizer.tsx | 73 --- .../src/components/BlurToggle.tsx | 63 -- .../src/components/CallToolbar.tsx | 105 --- .../src/components/DeviceSelect.tsx | 46 -- .../src/components/DeviceSettings.tsx | 73 --- .../src/components/JoinRoomCard.tsx | 207 ------ .../fishjam-chat/src/components/RoomView.tsx | 88 --- .../src/components/SettingsSheet.tsx | 42 -- .../fishjam-chat/src/components/Tile.tsx | 49 -- .../src/components/ToggleButton.tsx | 30 - .../src/components/VideoPlayer.tsx | 31 - .../src/components/ui/accordion.tsx | 55 -- .../src/components/ui/aspect-ratio.tsx | 5 - .../fishjam-chat/src/components/ui/badge.tsx | 33 - .../fishjam-chat/src/components/ui/button.tsx | 56 -- .../fishjam-chat/src/components/ui/card.tsx | 83 --- .../fishjam-chat/src/components/ui/input.tsx | 24 - .../fishjam-chat/src/components/ui/label.tsx | 24 - .../fishjam-chat/src/components/ui/select.tsx | 162 ----- .../src/components/ui/separator.tsx | 29 - .../fishjam-chat/src/components/ui/sheet.tsx | 141 ---- .../fishjam-chat/src/components/ui/switch.tsx | 27 - .../src/components/ui/toaster.tsx | 30 - .../react-client/fishjam-chat/src/index.css | 8 - .../fishjam-chat/src/lib/consts.ts | 3 - .../fishjam-chat/src/lib/roomManager.ts | 26 - .../fishjam-chat/src/lib/utils.ts | 30 - .../react-client/fishjam-chat/src/main.tsx | 14 - .../react-client/fishjam-chat/src/types.ts | 8 - .../src/utils/blur/BlurProcessor.tsx | 261 -------- .../src/utils/blur/BlurProcessorWorker.ts | 17 - .../fishjam-chat/src/vite-env.d.ts | 1 - .../fishjam-chat/tailwind.config.cjs | 38 -- .../fishjam-chat/tsconfig.app.json | 31 - .../react-client/fishjam-chat/tsconfig.json | 17 - .../fishjam-chat/tsconfig.node.json | 13 - .../react-client/fishjam-chat/vite.config.ts | 16 - .../react-client/livestreaming/.env.example | 1 - .../livestreaming/.prettierignore | 2 - .../react-client/livestreaming/.prettierrc | 3 - examples/react-client/livestreaming/README.md | 38 -- .../livestreaming/components.json | 21 - .../livestreaming/eslint.config.mjs | 21 - .../react-client/livestreaming/index.html | 34 - .../react-client/livestreaming/package.json | 60 -- .../livestreaming/src/components/App.tsx | 26 - .../src/components/ConnectForm.tsx | 84 --- .../src/components/FishjamContext.tsx | 15 - .../livestreaming/src/components/Header.tsx | 17 - .../src/components/LivestreamStreamer.tsx | 234 ------- .../src/components/LivestreamViewer.tsx | 115 ---- .../src/components/VideoPlayer.tsx | 20 - .../livestreaming/src/components/ui/alert.tsx | 66 -- .../src/components/ui/button.tsx | 59 -- .../livestreaming/src/components/ui/card.tsx | 92 --- .../livestreaming/src/components/ui/input.tsx | 21 - .../livestreaming/src/components/ui/label.tsx | 22 - .../src/components/ui/select.tsx | 162 ----- .../src/components/ui/toaster.tsx | 25 - .../react-client/livestreaming/src/index.css | 120 ---- .../react-client/livestreaming/src/index.d.ts | 1 - .../livestreaming/src/lib/consts.ts | 3 - .../livestreaming/src/lib/fishjamContext.ts | 14 - .../livestreaming/src/lib/utils.ts | 6 - .../react-client/livestreaming/src/main.tsx | 14 - .../react-client/livestreaming/tsconfig.json | 12 - .../react-client/livestreaming/vite.config.ts | 28 - .../react-client/minimal-smelter/.env.example | 1 - .../minimal-smelter/.eslintignore | 2 - .../react-client/minimal-smelter/.eslintrc | 4 - .../react-client/minimal-smelter/.prettierrc | 3 - .../react-client/minimal-smelter/README.md | 11 - .../react-client/minimal-smelter/index.html | 12 - .../react-client/minimal-smelter/package.json | 43 -- .../minimal-smelter/src/components/App.tsx | 109 ---- .../src/components/TextOverlayStream.tsx | 85 --- .../src/components/VideoPlayer.tsx | 18 - .../minimal-smelter/src/config.ts | 1 - .../minimal-smelter/src/hooks/useSmelter.ts | 26 - .../minimal-smelter/src/index.d.ts | 1 - .../react-client/minimal-smelter/src/main.tsx | 13 - .../minimal-smelter/tsconfig.json | 8 - .../minimal-smelter/vite.config.ts | 46 -- examples/react-client/text-chat/.env.example | 1 - examples/react-client/text-chat/.eslintignore | 2 - examples/react-client/text-chat/.eslintrc | 4 - examples/react-client/text-chat/.prettierrc | 3 - examples/react-client/text-chat/index.html | 12 - examples/react-client/text-chat/package.json | 40 -- examples/react-client/text-chat/src/App.tsx | 338 ---------- examples/react-client/text-chat/src/main.tsx | 13 - .../react-client/text-chat/src/vite-env.d.ts | 1 - examples/react-client/text-chat/tsconfig.json | 8 - .../react-client/text-chat/vite.config.ts | 21 - examples/ts-client/README.md | 19 - examples/ts-client/minimal/.eslintignore | 2 - examples/ts-client/minimal/.eslintrc | 4 - examples/ts-client/minimal/.prettierrc | 3 - examples/ts-client/minimal/index.html | 13 - examples/ts-client/minimal/package.json | 33 - examples/ts-client/minimal/src/main.ts | 82 --- examples/ts-client/minimal/tsconfig.json | 7 - examples/ts-client/minimal/vite.config.ts | 23 - examples/ts-client/simple-app/.eslintignore | 2 - examples/ts-client/simple-app/.eslintrc | 4 - examples/ts-client/simple-app/.prettierrc | 3 - examples/ts-client/simple-app/index.html | 287 --------- examples/ts-client/simple-app/package.json | 36 -- .../ts-client/simple-app/postcss.config.cjs | 6 - .../simple-app/src/createMockStream.ts | 55 -- examples/ts-client/simple-app/src/main.ts | 604 ------------------ examples/ts-client/simple-app/src/style.css | 17 - .../ts-client/simple-app/src/vite-env.d.ts | 1 - .../ts-client/simple-app/tailwind.config.cjs | 11 - examples/ts-client/simple-app/tsconfig.json | 7 - examples/ts-client/simple-app/vite.config.ts | 23 - 233 files changed, 8762 deletions(-) delete mode 100644 examples/mobile-client/blur-example/.env.example delete mode 100644 examples/mobile-client/blur-example/.eslintrc.js delete mode 100644 examples/mobile-client/blur-example/.gitignore delete mode 100644 examples/mobile-client/blur-example/App.tsx delete mode 100644 examples/mobile-client/blur-example/README.md delete mode 100644 examples/mobile-client/blur-example/app.json delete mode 100644 examples/mobile-client/blur-example/assets/adaptive-icon.png delete mode 100644 examples/mobile-client/blur-example/assets/favicon.png delete mode 100644 examples/mobile-client/blur-example/assets/icon.png delete mode 100644 examples/mobile-client/blur-example/assets/splash-icon.png delete mode 100644 examples/mobile-client/blur-example/components/VideosGridItem.tsx delete mode 100644 examples/mobile-client/blur-example/hooks/useConnectFishjam.ts delete mode 100644 examples/mobile-client/blur-example/index.ts delete mode 100644 examples/mobile-client/blur-example/navigation/RootNavigation.tsx delete mode 100644 examples/mobile-client/blur-example/package.json delete mode 100644 examples/mobile-client/blur-example/prettier.config.js delete mode 100644 examples/mobile-client/blur-example/screens/home/index.tsx delete mode 100644 examples/mobile-client/blur-example/screens/room/index.tsx delete mode 100644 examples/mobile-client/blur-example/tsconfig.json delete mode 100644 examples/mobile-client/blur-example/types/index.ts delete mode 100644 examples/mobile-client/blur-example/utils/index.ts delete mode 100644 examples/mobile-client/minimal-react-native/.env.example delete mode 100644 examples/mobile-client/minimal-react-native/.eslintrc.js delete mode 100644 examples/mobile-client/minimal-react-native/.gitignore delete mode 100644 examples/mobile-client/minimal-react-native/App.tsx delete mode 100644 examples/mobile-client/minimal-react-native/README.md delete mode 100644 examples/mobile-client/minimal-react-native/app.json delete mode 100644 examples/mobile-client/minimal-react-native/assets/adaptive-icon.png delete mode 100644 examples/mobile-client/minimal-react-native/assets/favicon.png delete mode 100644 examples/mobile-client/minimal-react-native/assets/icon.png delete mode 100644 examples/mobile-client/minimal-react-native/assets/splash-icon.png delete mode 100644 examples/mobile-client/minimal-react-native/components/VideosGridItem.tsx delete mode 100644 examples/mobile-client/minimal-react-native/hooks/useConnectFishjam.ts delete mode 100644 examples/mobile-client/minimal-react-native/index.ts delete mode 100644 examples/mobile-client/minimal-react-native/navigation/RootNavigation.tsx delete mode 100644 examples/mobile-client/minimal-react-native/package.json delete mode 100644 examples/mobile-client/minimal-react-native/prettier.config.js delete mode 100644 examples/mobile-client/minimal-react-native/screens/home/index.tsx delete mode 100644 examples/mobile-client/minimal-react-native/screens/room/index.tsx delete mode 100644 examples/mobile-client/minimal-react-native/tsconfig.json delete mode 100644 examples/mobile-client/minimal-react-native/types/index.ts delete mode 100644 examples/mobile-client/minimal-react-native/utils/index.ts delete mode 100644 examples/mobile-client/text-chat/.env.example delete mode 100644 examples/mobile-client/text-chat/.eslintrc.js delete mode 100644 examples/mobile-client/text-chat/.gitignore delete mode 100644 examples/mobile-client/text-chat/App.tsx delete mode 100644 examples/mobile-client/text-chat/README.md delete mode 100644 examples/mobile-client/text-chat/app.json delete mode 100644 examples/mobile-client/text-chat/hooks/useConnectFishjam.ts delete mode 100644 examples/mobile-client/text-chat/index.ts delete mode 100644 examples/mobile-client/text-chat/navigation/RootNavigation.tsx delete mode 100644 examples/mobile-client/text-chat/package.json delete mode 100644 examples/mobile-client/text-chat/prettier.config.js delete mode 100644 examples/mobile-client/text-chat/screens/chat/index.tsx delete mode 100644 examples/mobile-client/text-chat/screens/home/index.tsx delete mode 100644 examples/mobile-client/text-chat/tsconfig.json delete mode 100644 examples/mobile-client/video-player/.env.example delete mode 100644 examples/mobile-client/video-player/.eslintrc.js delete mode 100644 examples/mobile-client/video-player/.gitignore delete mode 100644 examples/mobile-client/video-player/.vscode/extensions.json delete mode 100644 examples/mobile-client/video-player/.vscode/settings.json delete mode 100644 examples/mobile-client/video-player/App.tsx delete mode 100644 examples/mobile-client/video-player/README.md delete mode 100644 examples/mobile-client/video-player/app.json delete mode 100644 examples/mobile-client/video-player/assets/images/android-icon-background.png delete mode 100644 examples/mobile-client/video-player/assets/images/android-icon-foreground.png delete mode 100644 examples/mobile-client/video-player/assets/images/android-icon-monochrome.png delete mode 100644 examples/mobile-client/video-player/assets/images/favicon.png delete mode 100644 examples/mobile-client/video-player/assets/images/icon.png delete mode 100644 examples/mobile-client/video-player/assets/images/partial-react-logo.png delete mode 100644 examples/mobile-client/video-player/assets/images/react-logo.png delete mode 100644 examples/mobile-client/video-player/assets/images/react-logo@2x.png delete mode 100644 examples/mobile-client/video-player/assets/images/react-logo@3x.png delete mode 100644 examples/mobile-client/video-player/assets/images/splash-icon.png delete mode 100644 examples/mobile-client/video-player/babel.config.js delete mode 100644 examples/mobile-client/video-player/components/FishjamPlayerStreamer.tsx delete mode 100644 examples/mobile-client/video-player/components/FishjamPlayerViewer.tsx delete mode 100644 examples/mobile-client/video-player/index.js delete mode 100644 examples/mobile-client/video-player/package.json delete mode 100644 examples/mobile-client/video-player/prettier.config.js delete mode 100644 examples/mobile-client/video-player/tsconfig.json delete mode 100644 examples/react-client/README.md delete mode 100644 examples/react-client/audio-only/.env.example delete mode 100644 examples/react-client/audio-only/.gitignore delete mode 100644 examples/react-client/audio-only/.prettierignore delete mode 100644 examples/react-client/audio-only/.prettierrc delete mode 100644 examples/react-client/audio-only/README.md delete mode 100644 examples/react-client/audio-only/eslint.config.js delete mode 100644 examples/react-client/audio-only/index.html delete mode 100644 examples/react-client/audio-only/package.json delete mode 100644 examples/react-client/audio-only/src/App.tsx delete mode 100644 examples/react-client/audio-only/src/AudioPlayer.tsx delete mode 100644 examples/react-client/audio-only/src/JoinRoomForm.tsx delete mode 100644 examples/react-client/audio-only/src/MicrophoneSettings.tsx delete mode 100644 examples/react-client/audio-only/src/PeerList.tsx delete mode 100644 examples/react-client/audio-only/src/RoomInfo.tsx delete mode 100644 examples/react-client/audio-only/src/main.tsx delete mode 100644 examples/react-client/audio-only/src/vite-env.d.ts delete mode 100644 examples/react-client/audio-only/tsconfig.app.json delete mode 100644 examples/react-client/audio-only/tsconfig.json delete mode 100644 examples/react-client/audio-only/tsconfig.node.json delete mode 100644 examples/react-client/audio-only/vite.config.ts delete mode 100644 examples/react-client/fishjam-chat/.eslintignore delete mode 100644 examples/react-client/fishjam-chat/.eslintrc.cjs delete mode 100644 examples/react-client/fishjam-chat/.gitignore delete mode 100644 examples/react-client/fishjam-chat/.prettierrc delete mode 100644 examples/react-client/fishjam-chat/README.md delete mode 100644 examples/react-client/fishjam-chat/components.json delete mode 100644 examples/react-client/fishjam-chat/index.html delete mode 100644 examples/react-client/fishjam-chat/package.json delete mode 100644 examples/react-client/fishjam-chat/postcss.config.cjs delete mode 100644 examples/react-client/fishjam-chat/public/shaders/blur/fragment.glsl delete mode 100644 examples/react-client/fishjam-chat/public/shaders/blur/vertex.glsl delete mode 100644 examples/react-client/fishjam-chat/src/App.tsx delete mode 100644 examples/react-client/fishjam-chat/src/Router.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/AudioPlayer.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/AudioTracks.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/AudioVisualizer.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/BlurToggle.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/CallToolbar.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/DeviceSelect.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/DeviceSettings.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/JoinRoomCard.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/RoomView.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/SettingsSheet.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/Tile.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ToggleButton.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/VideoPlayer.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/accordion.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/aspect-ratio.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/badge.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/button.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/card.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/input.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/label.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/select.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/separator.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/sheet.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/switch.tsx delete mode 100644 examples/react-client/fishjam-chat/src/components/ui/toaster.tsx delete mode 100644 examples/react-client/fishjam-chat/src/index.css delete mode 100644 examples/react-client/fishjam-chat/src/lib/consts.ts delete mode 100644 examples/react-client/fishjam-chat/src/lib/roomManager.ts delete mode 100644 examples/react-client/fishjam-chat/src/lib/utils.ts delete mode 100644 examples/react-client/fishjam-chat/src/main.tsx delete mode 100644 examples/react-client/fishjam-chat/src/types.ts delete mode 100644 examples/react-client/fishjam-chat/src/utils/blur/BlurProcessor.tsx delete mode 100644 examples/react-client/fishjam-chat/src/utils/blur/BlurProcessorWorker.ts delete mode 100644 examples/react-client/fishjam-chat/src/vite-env.d.ts delete mode 100644 examples/react-client/fishjam-chat/tailwind.config.cjs delete mode 100644 examples/react-client/fishjam-chat/tsconfig.app.json delete mode 100644 examples/react-client/fishjam-chat/tsconfig.json delete mode 100644 examples/react-client/fishjam-chat/tsconfig.node.json delete mode 100644 examples/react-client/fishjam-chat/vite.config.ts delete mode 100644 examples/react-client/livestreaming/.env.example delete mode 100644 examples/react-client/livestreaming/.prettierignore delete mode 100644 examples/react-client/livestreaming/.prettierrc delete mode 100644 examples/react-client/livestreaming/README.md delete mode 100644 examples/react-client/livestreaming/components.json delete mode 100644 examples/react-client/livestreaming/eslint.config.mjs delete mode 100644 examples/react-client/livestreaming/index.html delete mode 100644 examples/react-client/livestreaming/package.json delete mode 100644 examples/react-client/livestreaming/src/components/App.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ConnectForm.tsx delete mode 100644 examples/react-client/livestreaming/src/components/FishjamContext.tsx delete mode 100644 examples/react-client/livestreaming/src/components/Header.tsx delete mode 100644 examples/react-client/livestreaming/src/components/LivestreamStreamer.tsx delete mode 100644 examples/react-client/livestreaming/src/components/LivestreamViewer.tsx delete mode 100644 examples/react-client/livestreaming/src/components/VideoPlayer.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ui/alert.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ui/button.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ui/card.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ui/input.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ui/label.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ui/select.tsx delete mode 100644 examples/react-client/livestreaming/src/components/ui/toaster.tsx delete mode 100644 examples/react-client/livestreaming/src/index.css delete mode 100644 examples/react-client/livestreaming/src/index.d.ts delete mode 100644 examples/react-client/livestreaming/src/lib/consts.ts delete mode 100644 examples/react-client/livestreaming/src/lib/fishjamContext.ts delete mode 100644 examples/react-client/livestreaming/src/lib/utils.ts delete mode 100644 examples/react-client/livestreaming/src/main.tsx delete mode 100644 examples/react-client/livestreaming/tsconfig.json delete mode 100644 examples/react-client/livestreaming/vite.config.ts delete mode 100644 examples/react-client/minimal-smelter/.env.example delete mode 100644 examples/react-client/minimal-smelter/.eslintignore delete mode 100644 examples/react-client/minimal-smelter/.eslintrc delete mode 100644 examples/react-client/minimal-smelter/.prettierrc delete mode 100644 examples/react-client/minimal-smelter/README.md delete mode 100644 examples/react-client/minimal-smelter/index.html delete mode 100644 examples/react-client/minimal-smelter/package.json delete mode 100644 examples/react-client/minimal-smelter/src/components/App.tsx delete mode 100644 examples/react-client/minimal-smelter/src/components/TextOverlayStream.tsx delete mode 100644 examples/react-client/minimal-smelter/src/components/VideoPlayer.tsx delete mode 100644 examples/react-client/minimal-smelter/src/config.ts delete mode 100644 examples/react-client/minimal-smelter/src/hooks/useSmelter.ts delete mode 100644 examples/react-client/minimal-smelter/src/index.d.ts delete mode 100644 examples/react-client/minimal-smelter/src/main.tsx delete mode 100644 examples/react-client/minimal-smelter/tsconfig.json delete mode 100644 examples/react-client/minimal-smelter/vite.config.ts delete mode 100644 examples/react-client/text-chat/.env.example delete mode 100644 examples/react-client/text-chat/.eslintignore delete mode 100644 examples/react-client/text-chat/.eslintrc delete mode 100644 examples/react-client/text-chat/.prettierrc delete mode 100644 examples/react-client/text-chat/index.html delete mode 100644 examples/react-client/text-chat/package.json delete mode 100644 examples/react-client/text-chat/src/App.tsx delete mode 100644 examples/react-client/text-chat/src/main.tsx delete mode 100644 examples/react-client/text-chat/src/vite-env.d.ts delete mode 100644 examples/react-client/text-chat/tsconfig.json delete mode 100644 examples/react-client/text-chat/vite.config.ts delete mode 100644 examples/ts-client/README.md delete mode 100644 examples/ts-client/minimal/.eslintignore delete mode 100644 examples/ts-client/minimal/.eslintrc delete mode 100644 examples/ts-client/minimal/.prettierrc delete mode 100644 examples/ts-client/minimal/index.html delete mode 100644 examples/ts-client/minimal/package.json delete mode 100644 examples/ts-client/minimal/src/main.ts delete mode 100644 examples/ts-client/minimal/tsconfig.json delete mode 100644 examples/ts-client/minimal/vite.config.ts delete mode 100644 examples/ts-client/simple-app/.eslintignore delete mode 100644 examples/ts-client/simple-app/.eslintrc delete mode 100644 examples/ts-client/simple-app/.prettierrc delete mode 100644 examples/ts-client/simple-app/index.html delete mode 100644 examples/ts-client/simple-app/package.json delete mode 100644 examples/ts-client/simple-app/postcss.config.cjs delete mode 100644 examples/ts-client/simple-app/src/createMockStream.ts delete mode 100644 examples/ts-client/simple-app/src/main.ts delete mode 100644 examples/ts-client/simple-app/src/style.css delete mode 100644 examples/ts-client/simple-app/src/vite-env.d.ts delete mode 100644 examples/ts-client/simple-app/tailwind.config.cjs delete mode 100644 examples/ts-client/simple-app/tsconfig.json delete mode 100644 examples/ts-client/simple-app/vite.config.ts diff --git a/examples/mobile-client/blur-example/.env.example b/examples/mobile-client/blur-example/.env.example deleted file mode 100644 index 8a2d8c02b..000000000 --- a/examples/mobile-client/blur-example/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -EXPO_PUBLIC_FISHJAM_ID= -EXPO_PUBLIC_FISHJAM_URL= \ No newline at end of file diff --git a/examples/mobile-client/blur-example/.eslintrc.js b/examples/mobile-client/blur-example/.eslintrc.js deleted file mode 100644 index 62156b89c..000000000 --- a/examples/mobile-client/blur-example/.eslintrc.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - extends: ['expo', '../../../.eslintrc.js'], - ignorePatterns: [ - 'dist/*', - 'node_modules/*', - 'coverage/*', - 'build/*', - 'ios/*', - 'android/*', - '.eslintrc.js', - 'prettier.config.js', - 'global.d.ts', - ], - rules: { - 'import/no-unresolved': 'off', - }, -}; diff --git a/examples/mobile-client/blur-example/.gitignore b/examples/mobile-client/blur-example/.gitignore deleted file mode 100644 index ce2ef3cb9..000000000 --- a/examples/mobile-client/blur-example/.gitignore +++ /dev/null @@ -1,39 +0,0 @@ -# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files - -# dependencies -node_modules/ - -# Expo -.expo/ -dist/ -web-build/ - -# Native -*.orig.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision - -# Metro -.metro-health-check* - -# debug -npm-debug.* -yarn-debug.* -yarn-error.* - -# macOS -.DS_Store -*.pem - -# local env files -.env*.local - -# typescript -*.tsbuildinfo -android/* -ios/* -.env -.cursor/ \ No newline at end of file diff --git a/examples/mobile-client/blur-example/App.tsx b/examples/mobile-client/blur-example/App.tsx deleted file mode 100644 index 2d244d1aa..000000000 --- a/examples/mobile-client/blur-example/App.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { FishjamProvider } from '@fishjam-cloud/react-native-client'; -import { NavigationContainer } from '@react-navigation/native'; -import * as React from 'react'; -import { SafeAreaProvider } from 'react-native-safe-area-context'; - -import RootNavigation from './navigation/RootNavigation'; - -const App = () => { - return ( - - - - - - - - ); -}; - -export default App; diff --git a/examples/mobile-client/blur-example/README.md b/examples/mobile-client/blur-example/README.md deleted file mode 100644 index 47abfd27d..000000000 --- a/examples/mobile-client/blur-example/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# Background Blur Example - -A mobile video chat demo showcasing **real-time camera background blur** using the [`@fishjam-cloud/react-native-webrtc-background-blur`](https://www.npmjs.com/package/@fishjam-cloud/react-native-webrtc-background-blur) package. Built with [Fishjam Cloud](https://fishjam.io/), [Expo](https://expo.dev/), and [React Native](https://reactnative.dev/). - -## Features - -- **Background blur toggle** — blur your camera background on/off during a video call using a camera track middleware -- Join a video room with a custom room name and user name -- Real-time video grid with local and remote participants -- Automatic camera and microphone permission handling - -## Getting Started - -### Prerequisites - -- [Node.js](https://nodejs.org/) (v18 or newer recommended) -- [Yarn](https://yarnpkg.com/) or [npm](https://www.npmjs.com/) -- [Expo](https://docs.expo.dev/get-started/installation/): You do **not** need to install Expo CLI globally. Use `npx expo` to run Expo commands. - -### Installation - -1. **Clone the repository:** - ```sh - git clone https://github.com/fishjam-cloud/web-client-sdk.git - cd web-client-sdk - ``` -2. **Install dependencies and build project:** - ```sh - yarn - yarn build - ``` -3. **Set up environment variables:** - - - Create a `.env` file in the `examples/mobile-client/blur-example` directory: - ```sh - cp .env.example .env - ``` - - Fill in your Fishjam ID. _You can find the value for this variable by creating an account on [fishjam.io](https://fishjam.io) and copying it from the sandbox dashboard._ - - There also exists this additional environment variable, which is used for internal testing purposes: - - - `EXPO_PUBLIC_FISHJAM_URL` - Sandbox URL for custom Fishjam environment - -4. **Prebuild native files:** - ```sh - cd examples/mobile-client/blur-example - npx expo prebuild --clean - ``` - > [!NOTE] - > Be sure to run `npx expo prebuild` and not `yarn prebuild` as there's an issue with path generation for the `ios/.xcode.env.local` file - -### Running the App - -- **Run on Android:** - ```sh - yarn android - ``` -- **Run on iOS:** - ```sh - yarn ios - ``` - -## Usage - -1. Enter a room name and your user name on the Home screen. -2. Tap **Connect** to join the video room. -3. See yourself and other participants in a responsive video grid. -4. Tap **Enable Blur** to apply background blur to your camera feed, or **Disable Blur** to turn it off. -5. Leaving the room or closing the app will disconnect you from the session. - -## Architecture Overview - -- **React Native + Expo**: Cross-platform mobile app framework. -- **Fishjam Cloud SDK**: Handles all real-time video, audio, and peer management. -- **`@fishjam-cloud/react-native-webrtc-background-blur`**: Provides a `useBackgroundBlur` hook that returns a camera track middleware. The middleware is applied via `setCameraTrackMiddleware` from the Fishjam `useCamera()` hook. -- **TypeScript**: Provides type safety and better developer experience. - -## Troubleshooting & FAQ - -- **App fails to connect to a room:** - - Ensure your `.env` file is present and `EXPO_PUBLIC_FISHJAM_ID` is set correctly. - - Check your network connection. - - Review logs in the Metro/Expo console for errors. -- **Camera or microphone not working:** - - Make sure you have granted the necessary permissions on your device. - -## License - -This example is provided under the Apache License 2.0. See [LICENSE](../../../LICENSE) for details. - ---- - -_This project is maintained by the Fishjam team. For questions or support, visit [fishjam.io](https://fishjam.io/) or open an issue on GitHub._ diff --git a/examples/mobile-client/blur-example/app.json b/examples/mobile-client/blur-example/app.json deleted file mode 100644 index b3935ef28..000000000 --- a/examples/mobile-client/blur-example/app.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "expo": { - "name": "blur-example", - "slug": "blur-example", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icon.png", - "userInterfaceStyle": "light", - "newArchEnabled": true, - "splash": { - "image": "./assets/splash-icon.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "ios": { - "supportsTablet": true, - "bundleIdentifier": "io.fishjam.mobile.example.blurexample", - "infoPlist": { - "NSCameraUsageDescription": "Allow $(PRODUCT_NAME) to access your camera.", - "NSMicrophoneUsageDescription": "Allow $(PRODUCT_NAME) to access your microphone." - } - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", - "backgroundColor": "#ffffff" - }, - "edgeToEdgeEnabled": true, - "package": "io.fishjam.mobile.example.blurexample", - "permissions": [ - "android.permission.CAMERA", - "android.permission.RECORD_AUDIO", - "android.permission.MODIFY_AUDIO_SETTINGS", - "android.permission.ACCESS_NETWORK_STATE", - "android.permission.ACCESS_WIFI_STATE" - ] - }, - "web": { - "favicon": "./assets/favicon.png" - }, - "plugins": [["@fishjam-cloud/react-native-client"]] - } -} diff --git a/examples/mobile-client/blur-example/assets/adaptive-icon.png b/examples/mobile-client/blur-example/assets/adaptive-icon.png deleted file mode 100644 index 03d6f6b6c6727954aec1d8206222769afd178d8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17547 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 diff --git a/examples/mobile-client/blur-example/assets/icon.png b/examples/mobile-client/blur-example/assets/icon.png deleted file mode 100644 index a0b1526fc7b78680fd8d733dbc6113e1af695487..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22380 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 diff --git a/examples/mobile-client/blur-example/assets/splash-icon.png b/examples/mobile-client/blur-example/assets/splash-icon.png deleted file mode 100644 index 03d6f6b6c6727954aec1d8206222769afd178d8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17547 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 { - const mediaStream = peer.track?.stream ? peer.track.stream : null; - - return ( - - - {mediaStream ? ( - - ) : ( - - No video - - )} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 0.5, - }, - video: { - flex: 1, - aspectRatio: 1, - borderRadius: 8, - overflow: "hidden", - borderColor: "#001A72", - borderWidth: 1, - }, - videoContent: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, -}); diff --git a/examples/mobile-client/blur-example/hooks/useConnectFishjam.ts b/examples/mobile-client/blur-example/hooks/useConnectFishjam.ts deleted file mode 100644 index 5274f535d..000000000 --- a/examples/mobile-client/blur-example/hooks/useConnectFishjam.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - useConnection, - useInitializeDevices, - useSandbox, -} from '@fishjam-cloud/react-native-client'; -import type { NavigationProp } from '@react-navigation/native'; -import { useNavigation } from '@react-navigation/native'; -import { useEffect, useState } from 'react'; - -import type { RootStackParamList } from '../navigation/RootNavigation'; - -export const useConnectFishjam = () => { - const navigation = useNavigation>(); - - const { leaveRoom, joinRoom } = useConnection(); - const { getSandboxPeerToken } = useSandbox({ - configOverride: { - sandboxApiUrl: process.env.EXPO_PUBLIC_FISHJAM_URL, - }, - }); - const { initializeDevices } = useInitializeDevices(); - - const [isLoading, setIsLoading] = useState(false); - - const connect = async (roomName: string, userName: string) => { - try { - setIsLoading(true); - - const peerToken = await getSandboxPeerToken(roomName, userName); - - await initializeDevices({ enableVideo: true, enableAudio: true }); - - await joinRoom({ - peerToken, - peerMetadata: { - displayName: userName, - }, - }); - navigation.navigate('Room', { - userName, - }); - } catch (e) { - console.error('Error connecting to Fishjam', e); - } finally { - setIsLoading(false); - } - }; - - useEffect(() => { - return () => { - leaveRoom(); - }; - }, [leaveRoom]); - - return { - connect, - isLoading, - }; -}; diff --git a/examples/mobile-client/blur-example/index.ts b/examples/mobile-client/blur-example/index.ts deleted file mode 100644 index 1d6e981ef..000000000 --- a/examples/mobile-client/blur-example/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerRootComponent } from 'expo'; - -import App from './App'; - -// registerRootComponent calls AppRegistry.registerComponent('main', () => App); -// It also ensures that whether you load the app in Expo Go or in a native build, -// the environment is set up appropriately -registerRootComponent(App); diff --git a/examples/mobile-client/blur-example/navigation/RootNavigation.tsx b/examples/mobile-client/blur-example/navigation/RootNavigation.tsx deleted file mode 100644 index 8d46d059e..000000000 --- a/examples/mobile-client/blur-example/navigation/RootNavigation.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { createNativeStackNavigator } from '@react-navigation/native-stack'; - -import HomeScreen from '../screens/home'; -import RoomScreen from '../screens/room'; - -export type RootStackParamList = { - Home: undefined; - Room: { - userName: string; - }; -}; - -export type RootScreenProps = - NativeStackScreenProps; - -const RootStack = createNativeStackNavigator(); - -const RootNavigation = () => { - return ( - - - - - ); -}; - -export default RootNavigation; diff --git a/examples/mobile-client/blur-example/package.json b/examples/mobile-client/blur-example/package.json deleted file mode 100644 index 3a709661a..000000000 --- a/examples/mobile-client/blur-example/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "blur-example", - "version": "1.0.0", - "main": "index.ts", - "scripts": { - "start": "expo start", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "lint": "eslint ." - }, - "dependencies": { - "@fishjam-cloud/react-native-client": "workspace:*", - "@fishjam-cloud/react-native-webrtc-background-blur": "^0.1.2", - "@react-navigation/elements": "^2.5.2", - "@react-navigation/native": "^7.1.14", - "@react-navigation/native-stack": "^7.3.21", - "expo": "~54.0.25", - "expo-status-bar": "~3.0.8", - "react": "19.1.0", - "react-native": "0.81.5", - "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.14.0" - }, - "devDependencies": { - "@babel/core": "^7.28.0", - "@types/react": "~19.1.0", - "eslint-config-expo": "~8.0.1", - "prettier": "^3.6.2", - "typescript": "~5.9.2" - }, - "private": true -} diff --git a/examples/mobile-client/blur-example/prettier.config.js b/examples/mobile-client/blur-example/prettier.config.js deleted file mode 100644 index f05c06ee1..000000000 --- a/examples/mobile-client/blur-example/prettier.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../common/prettier.config'); diff --git a/examples/mobile-client/blur-example/screens/home/index.tsx b/examples/mobile-client/blur-example/screens/home/index.tsx deleted file mode 100644 index f4845de06..000000000 --- a/examples/mobile-client/blur-example/screens/home/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useState } from 'react'; -import { Button, StyleSheet, TextInput } from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; - -import { useConnectFishjam } from '../../hooks/useConnectFishjam'; -import type { RootScreenProps } from '../../navigation/RootNavigation'; - -export type HomeScreenProps = RootScreenProps<'Home'>; - -const HomeScreen = () => { - const [roomName, setRoomName] = useState(''); - const [userName, setUserName] = useState(''); - const { connect, isLoading } = useConnectFishjam(); - - return ( - - - - - - ); -}; diff --git a/examples/react-client/audio-only/src/MicrophoneSettings.tsx b/examples/react-client/audio-only/src/MicrophoneSettings.tsx deleted file mode 100644 index 6364156c3..000000000 --- a/examples/react-client/audio-only/src/MicrophoneSettings.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useMicrophone } from "@fishjam-cloud/react-client"; - -export const MicrophoneSettings = () => { - const { - isMicrophoneMuted, - toggleMicrophoneMute, - microphoneDevices, - selectMicrophone, - } = useMicrophone(); - - return ( -

- ); -}; diff --git a/examples/react-client/audio-only/src/PeerList.tsx b/examples/react-client/audio-only/src/PeerList.tsx deleted file mode 100644 index 9b4ba40cc..000000000 --- a/examples/react-client/audio-only/src/PeerList.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { usePeers, useVAD } from "@fishjam-cloud/react-client"; -import { AudioPlayer } from "./AudioPlayer"; - -export const PeerList = () => { - const { remotePeers } = usePeers(); - const peersSpeaking = useVAD({ peerIds: remotePeers.map((p) => p.id) }); - - if (!remotePeers.length) { - return

You're alone in the room.

; - } - - return ( -
    - {remotePeers.map((peer) => ( -
    -

    - {`${peer.metadata?.server?.username}`}{" "} - {peersSpeaking[peer.id] && ( - Speaking now - )} -

    - - -
    - ))} -
- ); -}; diff --git a/examples/react-client/audio-only/src/RoomInfo.tsx b/examples/react-client/audio-only/src/RoomInfo.tsx deleted file mode 100644 index b82a95ce7..000000000 --- a/examples/react-client/audio-only/src/RoomInfo.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useConnection } from "@fishjam-cloud/react-client"; -import { JoinRoomForm, type RoomManagerParams } from "./JoinRoomForm"; -import { useState } from "react"; - -export const RoomInfo = () => { - const [currentParams, setCurrentParams] = useState( - null, - ); - const { leaveRoom, peerStatus } = useConnection(); - - const onDisconnect = () => { - setCurrentParams(null); - leaveRoom(); - }; - - if (peerStatus === "error") { - return

Failed to join the room

; - } - - if (peerStatus === "connecting") { - return

Connecting

; - } - - if (peerStatus === "idle") { - return ; - } - - return ( -
- Connected to "{currentParams?.roomName}" as "{currentParams?.peerName}". - -
- ); -}; diff --git a/examples/react-client/audio-only/src/main.tsx b/examples/react-client/audio-only/src/main.tsx deleted file mode 100644 index 1943102d6..000000000 --- a/examples/react-client/audio-only/src/main.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import App from "./App.tsx"; -import { FishjamProvider } from "@fishjam-cloud/react-client"; - -createRoot(document.getElementById("root")!).render( - - - - - , -); diff --git a/examples/react-client/audio-only/src/vite-env.d.ts b/examples/react-client/audio-only/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2a..000000000 --- a/examples/react-client/audio-only/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/examples/react-client/audio-only/tsconfig.app.json b/examples/react-client/audio-only/tsconfig.app.json deleted file mode 100644 index 358ca9ba9..000000000 --- a/examples/react-client/audio-only/tsconfig.app.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true - }, - "include": ["src"] -} diff --git a/examples/react-client/audio-only/tsconfig.json b/examples/react-client/audio-only/tsconfig.json deleted file mode 100644 index 1ffef600d..000000000 --- a/examples/react-client/audio-only/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] -} diff --git a/examples/react-client/audio-only/tsconfig.node.json b/examples/react-client/audio-only/tsconfig.node.json deleted file mode 100644 index db0becc8b..000000000 --- a/examples/react-client/audio-only/tsconfig.node.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "ES2022", - "lib": ["ES2023"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/examples/react-client/audio-only/vite.config.ts b/examples/react-client/audio-only/vite.config.ts deleted file mode 100644 index 0e43ae8de..000000000 --- a/examples/react-client/audio-only/vite.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [react()], -}); diff --git a/examples/react-client/fishjam-chat/.eslintignore b/examples/react-client/fishjam-chat/.eslintignore deleted file mode 100644 index f06235c46..000000000 --- a/examples/react-client/fishjam-chat/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist diff --git a/examples/react-client/fishjam-chat/.eslintrc.cjs b/examples/react-client/fishjam-chat/.eslintrc.cjs deleted file mode 100644 index b765fa44b..000000000 --- a/examples/react-client/fishjam-chat/.eslintrc.cjs +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - env: { browser: true, es2020: true }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react-hooks/recommended", - ], - ignorePatterns: ["dist", ".eslintrc.cjs"], - parser: "@typescript-eslint/parser", - plugins: ["react-refresh"], - rules: { - "react-refresh/only-export-components": [ - "error", - { allowConstantExport: true }, - ], - }, -}; diff --git a/examples/react-client/fishjam-chat/.gitignore b/examples/react-client/fishjam-chat/.gitignore deleted file mode 100644 index a547bf36d..000000000 --- a/examples/react-client/fishjam-chat/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/examples/react-client/fishjam-chat/.prettierrc b/examples/react-client/fishjam-chat/.prettierrc deleted file mode 100644 index 5120230c2..000000000 --- a/examples/react-client/fishjam-chat/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "printWidth": 80, - "plugins": ["prettier-plugin-tailwindcss"] -} diff --git a/examples/react-client/fishjam-chat/README.md b/examples/react-client/fishjam-chat/README.md deleted file mode 100644 index 79270cf18..000000000 --- a/examples/react-client/fishjam-chat/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Fishjam Chat - -This is an example app demonstrating how to build a simple conferencing react app using Fishjam. - -Install dependencies with `yarn`, run with `yarn dev`. diff --git a/examples/react-client/fishjam-chat/components.json b/examples/react-client/fishjam-chat/components.json deleted file mode 100644 index 16aa538d5..000000000 --- a/examples/react-client/fishjam-chat/components.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "tailwind.config.cjs", - "css": "src/index.css", - "baseColor": "stone", - "cssVariables": false, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - } -} diff --git a/examples/react-client/fishjam-chat/index.html b/examples/react-client/fishjam-chat/index.html deleted file mode 100644 index b03a90b65..000000000 --- a/examples/react-client/fishjam-chat/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - FishjamChat - - - - - -
- - - diff --git a/examples/react-client/fishjam-chat/package.json b/examples/react-client/fishjam-chat/package.json deleted file mode 100644 index 0e45f5427..000000000 --- a/examples/react-client/fishjam-chat/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "@fishjam-example/fishjam-chat", - "private": true, - "version": "0.10.2", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix", - "lint:check": "eslint . --ext .ts,.tsx", - "format": "prettier --write . --ignore-path ./.eslintignore", - "format:check": "prettier --check . --ignore-path ./.eslintignore", - "preview": "vite preview" - }, - "dependencies": { - "@fishjam-cloud/react-client": "workspace:*", - "@mediapipe/tasks-vision": "^0.10.21", - "@radix-ui/react-accordion": "^1.2.3", - "@radix-ui/react-aspect-ratio": "^1.1.1", - "@radix-ui/react-dialog": "^1.1.5", - "@radix-ui/react-icons": "^1.3.2", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-select": "^2.1.6", - "@radix-ui/react-separator": "^1.1.2", - "@radix-ui/react-slot": "^1.1.1", - "@radix-ui/react-switch": "^1.1.3", - "axios": "^1.8.4", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "lucide-react": "^0.482.0", - "next-themes": "^0.4.6", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-hook-form": "^7.55.0", - "sonner": "^2.0.1", - "tailwind-merge": "^2.5.5", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@types/node": "^22.14.0", - "@types/react": "19.1.0", - "@types/react-dom": "19.1.0", - "@vitejs/plugin-react-swc": "^3.8.1", - "autoprefixer": "^10.4.21", - "eslint": "^8.57.1", - "postcss": "^8.5.3", - "prettier": "^3.5.3", - "tailwindcss": "^3.4.17", - "typescript": "^5.8.3", - "vite": "^6.2.6" - }, - "lint-staged": { - "*": [ - "yarn format" - ], - "*.(js|ts|tsx)": [ - "yarn lint" - ] - } -} diff --git a/examples/react-client/fishjam-chat/postcss.config.cjs b/examples/react-client/fishjam-chat/postcss.config.cjs deleted file mode 100644 index 12a703d90..000000000 --- a/examples/react-client/fishjam-chat/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/examples/react-client/fishjam-chat/public/shaders/blur/fragment.glsl b/examples/react-client/fishjam-chat/public/shaders/blur/fragment.glsl deleted file mode 100644 index 3bfed2699..000000000 --- a/examples/react-client/fishjam-chat/public/shaders/blur/fragment.glsl +++ /dev/null @@ -1,53 +0,0 @@ -precision mediump float; -varying vec2 v_pos; - -uniform sampler2D texture; -uniform sampler2D resizedTexture; -uniform sampler2D confidenceTexture; -uniform vec2 u_size; - -const float gamma=1.8; - -const int CONV_SIDE=5; -const float BLUR_RADIUS=float(CONV_SIDE); - -vec4 blur(sampler2D texture, sampler2D confidence, vec2 size, vec2 un) { - float n = 0.0; - vec3 res = vec3(0); - vec2 delta = 1.0 /size; - vec2 confidence_delta = 1.0 / size * 2.; - - for(int x = -CONV_SIDE; x <= CONV_SIDE; x++){ - for(int y = -CONV_SIDE; y <= CONV_SIDE; y++){ - vec2 u_ = un + delta * vec2(x,y); - vec2 cu_=un + confidence_delta * vec2(x,y); - float c = 1.0 - texture2D(confidence, cu_).x; - - if (distance(u_, un) <= BLUR_RADIUS) { - res += texture2D(texture, u_).rgb * c; - n += c; - } - } - } - - if (n > 1.0) { - res /= n; - } - - return vec4(res, 1.0); -} - -void main() { - vec4 blurredValue = blur(resizedTexture, confidenceTexture, vec2(1280, 720) / 4., v_pos); - vec4 sharpValue = texture2D(texture, v_pos); - - vec4 filtered_confidence = smoothstep(0.1, 0.8, texture2D(confidenceTexture, v_pos)); - - if (filtered_confidence.x < 0.5) { - gl_FragColor = blurredValue; - } else { - gl_FragColor = mix(blurredValue, sharpValue, filtered_confidence.x); - } -} - - diff --git a/examples/react-client/fishjam-chat/public/shaders/blur/vertex.glsl b/examples/react-client/fishjam-chat/public/shaders/blur/vertex.glsl deleted file mode 100644 index 7b68eaf0b..000000000 --- a/examples/react-client/fishjam-chat/public/shaders/blur/vertex.glsl +++ /dev/null @@ -1,7 +0,0 @@ -attribute vec4 a_Position; -varying vec2 v_pos; - -void main() { - gl_Position = a_Position; - v_pos = (a_Position.xy + 1.0) / 2.0; -} \ No newline at end of file diff --git a/examples/react-client/fishjam-chat/src/App.tsx b/examples/react-client/fishjam-chat/src/App.tsx deleted file mode 100644 index 5da4c099d..000000000 --- a/examples/react-client/fishjam-chat/src/App.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { FishjamProvider } from "@fishjam-cloud/react-client"; -import { useState } from "react"; - -import { BlurProvider } from "./components/BlurToggle"; -import { DEFAULT_FISHJAM_ID } from "./lib/consts"; -import Router from "./Router"; - -function App() { - const [fishjamId, setFishjamId] = useState(DEFAULT_FISHJAM_ID); - - return ( - - - - - - ); -} - -export default App; diff --git a/examples/react-client/fishjam-chat/src/Router.tsx b/examples/react-client/fishjam-chat/src/Router.tsx deleted file mode 100644 index 560096f91..000000000 --- a/examples/react-client/fishjam-chat/src/Router.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useConnection } from "@fishjam-cloud/react-client"; - -import { JoinRoomCard } from "./components/JoinRoomCard"; -import { RoomView } from "./components/RoomView"; - -type Props = { - onFishjamIdChange: (fishjamId: string) => void; -}; - -function Router({ onFishjamIdChange }: Props) { - const { peerStatus } = useConnection(); - const isConnected = peerStatus === "connected"; - - return ( -
- {isConnected ? ( - - ) : ( - - )} -
- ); -} - -export default Router; diff --git a/examples/react-client/fishjam-chat/src/components/AudioPlayer.tsx b/examples/react-client/fishjam-chat/src/components/AudioPlayer.tsx deleted file mode 100644 index 868c42869..000000000 --- a/examples/react-client/fishjam-chat/src/components/AudioPlayer.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { FC } from "react"; -import { useEffect, useRef } from "react"; - -interface AudioPlayerProps { - stream?: MediaStream | null; -} - -const AudioPlayer: FC = ({ stream }) => { - const audioRef = useRef(null); - - useEffect(() => { - if (!audioRef.current) return; - audioRef.current.srcObject = stream ?? null; - }, [stream]); - - return
- ); -} diff --git a/examples/react-client/fishjam-chat/src/components/ui/button.tsx b/examples/react-client/fishjam-chat/src/components/ui/button.tsx deleted file mode 100644 index 118028081..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/button.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-stone-950 disabled:pointer-events-none disabled:opacity-50 dark:focus-visible:ring-stone-300", - { - variants: { - variant: { - default: - "bg-stone-900 text-stone-50 shadow hover:bg-stone-900/90 dark:bg-stone-50 dark:text-stone-900 dark:hover:bg-stone-50/90", - destructive: - "bg-red-500 text-stone-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-stone-50 dark:hover:bg-red-900/90", - outline: - "border border-stone-200 bg-white shadow-sm hover:bg-stone-100 hover:text-stone-900 dark:border-stone-800 dark:bg-stone-950 dark:hover:bg-stone-800 dark:hover:text-stone-50", - secondary: - "bg-stone-100 text-stone-900 shadow-sm hover:bg-stone-100/80 dark:bg-stone-800 dark:text-stone-50 dark:hover:bg-stone-800/80", - ghost: - "hover:bg-stone-100 hover:text-stone-900 dark:hover:bg-stone-800 dark:hover:text-stone-50", - link: "text-stone-900 underline-offset-4 hover:underline dark:text-stone-50", - }, - size: { - default: "h-9 px-4 py-2", - sm: "h-8 rounded-md px-3 text-xs", - lg: "h-10 rounded-md px-8", - icon: "h-9 w-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - }, -); - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; -} - -export const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - return ( - - ); - }, -); -Button.displayName = "Button"; diff --git a/examples/react-client/fishjam-chat/src/components/ui/card.tsx b/examples/react-client/fishjam-chat/src/components/ui/card.tsx deleted file mode 100644 index 35c00be23..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/card.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -Card.displayName = "Card"; - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardHeader.displayName = "CardHeader"; - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)); -CardTitle.displayName = "CardTitle"; - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)); -CardDescription.displayName = "CardDescription"; - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)); -CardContent.displayName = "CardContent"; - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardFooter.displayName = "CardFooter"; - -export { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -}; diff --git a/examples/react-client/fishjam-chat/src/components/ui/input.tsx b/examples/react-client/fishjam-chat/src/components/ui/input.tsx deleted file mode 100644 index 43d56fac7..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/input.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -export type InputProps = React.InputHTMLAttributes; - -const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { - return ( - - ); - }, -); -Input.displayName = "Input"; - -export { Input }; diff --git a/examples/react-client/fishjam-chat/src/components/ui/label.tsx b/examples/react-client/fishjam-chat/src/components/ui/label.tsx deleted file mode 100644 index 1d9c5a2c0..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/label.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as LabelPrimitive from "@radix-ui/react-label"; -import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const labelVariants = cva( - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", -); - -const Label = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps ->(({ className, ...props }, ref) => ( - -)); -Label.displayName = LabelPrimitive.Root.displayName; - -export { Label }; diff --git a/examples/react-client/fishjam-chat/src/components/ui/select.tsx b/examples/react-client/fishjam-chat/src/components/ui/select.tsx deleted file mode 100644 index ff25822ee..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/select.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { - CaretSortIcon, - CheckIcon, - ChevronDownIcon, - ChevronUpIcon, -} from "@radix-ui/react-icons"; -import * as SelectPrimitive from "@radix-ui/react-select"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const Select = SelectPrimitive.Root; - -const SelectGroup = SelectPrimitive.Group; - -const SelectValue = SelectPrimitive.Value; - -const SelectTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - span]:line-clamp-1", - className, - )} - {...props} - > - {children} - - - - -)); -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; - -const SelectScrollUpButton = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; - -const SelectScrollDownButton = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -SelectScrollDownButton.displayName = - SelectPrimitive.ScrollDownButton.displayName; - -const SelectContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, position = "popper", ...props }, ref) => ( - - - - - {children} - - - - -)); -SelectContent.displayName = SelectPrimitive.Content.displayName; - -const SelectLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SelectLabel.displayName = SelectPrimitive.Label.displayName; - -const SelectItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)); -SelectItem.displayName = SelectPrimitive.Item.displayName; - -const SelectSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SelectSeparator.displayName = SelectPrimitive.Separator.displayName; - -export { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectScrollDownButton, - SelectScrollUpButton, - SelectSeparator, - SelectTrigger, - SelectValue, -}; diff --git a/examples/react-client/fishjam-chat/src/components/ui/separator.tsx b/examples/react-client/fishjam-chat/src/components/ui/separator.tsx deleted file mode 100644 index 3ef436805..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/separator.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as SeparatorPrimitive from "@radix-ui/react-separator"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const Separator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->( - ( - { className, orientation = "horizontal", decorative = true, ...props }, - ref, - ) => ( - - ), -); -Separator.displayName = SeparatorPrimitive.Root.displayName; - -export { Separator }; diff --git a/examples/react-client/fishjam-chat/src/components/ui/sheet.tsx b/examples/react-client/fishjam-chat/src/components/ui/sheet.tsx deleted file mode 100644 index 2d206277c..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/sheet.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import * as SheetPrimitive from "@radix-ui/react-dialog"; -import { Cross2Icon } from "@radix-ui/react-icons"; -import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const Sheet = SheetPrimitive.Root; - -const SheetTrigger = SheetPrimitive.Trigger; - -const SheetClose = SheetPrimitive.Close; - -const SheetPortal = SheetPrimitive.Portal; - -const SheetOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; - -const sheetVariants = cva( - "fixed z-50 gap-4 bg-white p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out dark:bg-stone-950", - { - variants: { - side: { - top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", - bottom: - "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", - left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", - right: - "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", - }, - }, - defaultVariants: { - side: "right", - }, - }, -); - -interface SheetContentProps - extends React.ComponentPropsWithoutRef, - VariantProps {} - -const SheetContent = React.forwardRef< - React.ElementRef, - SheetContentProps ->(({ side = "right", className, children, ...props }, ref) => ( - - - - - - Close - - {children} - - -)); -SheetContent.displayName = SheetPrimitive.Content.displayName; - -const SheetHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-); -SheetHeader.displayName = "SheetHeader"; - -const SheetFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-); -SheetFooter.displayName = "SheetFooter"; - -const SheetTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SheetTitle.displayName = SheetPrimitive.Title.displayName; - -const SheetDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SheetDescription.displayName = SheetPrimitive.Description.displayName; - -export { - Sheet, - SheetClose, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetOverlay, - SheetPortal, - SheetTitle, - SheetTrigger, -}; diff --git a/examples/react-client/fishjam-chat/src/components/ui/switch.tsx b/examples/react-client/fishjam-chat/src/components/ui/switch.tsx deleted file mode 100644 index 61d4a0eb5..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/switch.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as SwitchPrimitives from "@radix-ui/react-switch"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const Switch = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -Switch.displayName = SwitchPrimitives.Root.displayName; - -export { Switch }; diff --git a/examples/react-client/fishjam-chat/src/components/ui/toaster.tsx b/examples/react-client/fishjam-chat/src/components/ui/toaster.tsx deleted file mode 100644 index 6a8a51599..000000000 --- a/examples/react-client/fishjam-chat/src/components/ui/toaster.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useTheme } from "next-themes"; -import { Toaster as Sonner } from "sonner"; - -type ToasterProps = React.ComponentProps; - -const Toaster = ({ ...props }: ToasterProps) => { - const { theme = "system" } = useTheme(); - - return ( - - ); -}; - -export { Toaster }; diff --git a/examples/react-client/fishjam-chat/src/index.css b/examples/react-client/fishjam-chat/src/index.css deleted file mode 100644 index 9d34fc823..000000000 --- a/examples/react-client/fishjam-chat/src/index.css +++ /dev/null @@ -1,8 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; -@layer base { - :root { - --radius: 0.5rem; - } -} diff --git a/examples/react-client/fishjam-chat/src/lib/consts.ts b/examples/react-client/fishjam-chat/src/lib/consts.ts deleted file mode 100644 index 28b3d2ff4..000000000 --- a/examples/react-client/fishjam-chat/src/lib/consts.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const DEFAULT_FISHJAM_ID = - new URLSearchParams(window.location.search).get("fishjamId") ?? - import.meta.env.VITE_FISHJAM_ID; diff --git a/examples/react-client/fishjam-chat/src/lib/roomManager.ts b/examples/react-client/fishjam-chat/src/lib/roomManager.ts deleted file mode 100644 index 624b32dc6..000000000 --- a/examples/react-client/fishjam-chat/src/lib/roomManager.ts +++ /dev/null @@ -1,26 +0,0 @@ -import axios from "axios"; - -type BasicInfo = { id: string; name: string }; -type RoomManagerResponse = { - peerToken: string; - url: string; - room: BasicInfo; - peer: BasicInfo; -}; - -export const getRoomCredentials = async ( - roomManagerUrl: string, - roomName: string, - peerName: string, - roomType: string, -) => { - // remove duplicate get params (in case user will just copy params from UI) - const url = new URL(roomManagerUrl)!; - url.searchParams.set("roomName", roomName); - url.searchParams.set("peerName", peerName); - url.searchParams.set("roomType", roomType); - - const res = await axios.get(url.toString()); - - return res.data; -}; diff --git a/examples/react-client/fishjam-chat/src/lib/utils.ts b/examples/react-client/fishjam-chat/src/lib/utils.ts deleted file mode 100644 index d6e4b5f07..000000000 --- a/examples/react-client/fishjam-chat/src/lib/utils.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { type ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; - -import type { RoomForm } from "@/types"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} - -export function nonNullablePredicate(value: T): value is NonNullable { - return Boolean(value); -} - -const ROOM_FORM_VALUES_KEY = "room-form-values"; - -export const getPersistedFormValues = (): Partial => { - const persistedValues = localStorage.getItem(ROOM_FORM_VALUES_KEY); - if (!persistedValues) return {}; - - try { - return JSON.parse(persistedValues); - } catch (e) { - console.error("Failed to parse persisted form values", e); - return {}; - } -}; - -export const persistFormValues = (values: RoomForm) => { - localStorage.setItem(ROOM_FORM_VALUES_KEY, JSON.stringify(values)); -}; diff --git a/examples/react-client/fishjam-chat/src/main.tsx b/examples/react-client/fishjam-chat/src/main.tsx deleted file mode 100644 index f1b4514c5..000000000 --- a/examples/react-client/fishjam-chat/src/main.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import "./index.css"; - -import React from "react"; -import ReactDOM from "react-dom/client"; - -import App from "./App.tsx"; -import { Toaster } from "./components/ui/toaster"; - -ReactDOM.createRoot(document.getElementById("root")!).render( - - - - , -); diff --git a/examples/react-client/fishjam-chat/src/types.ts b/examples/react-client/fishjam-chat/src/types.ts deleted file mode 100644 index 60317bcb2..000000000 --- a/examples/react-client/fishjam-chat/src/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { type RoomType } from "@fishjam-cloud/react-client"; - -export type RoomForm = { - roomName: string; - peerName: string; - roomType: RoomType; - fishjamId: string; -}; diff --git a/examples/react-client/fishjam-chat/src/utils/blur/BlurProcessor.tsx b/examples/react-client/fishjam-chat/src/utils/blur/BlurProcessor.tsx deleted file mode 100644 index b4d0d441c..000000000 --- a/examples/react-client/fishjam-chat/src/utils/blur/BlurProcessor.tsx +++ /dev/null @@ -1,261 +0,0 @@ -import type { ImageSegmenterCallback } from "@mediapipe/tasks-vision"; -import { FilesetResolver, ImageSegmenter } from "@mediapipe/tasks-vision"; - -import BlurWorker from "./BlurProcessorWorker?worker"; - -export class BlurProcessor { - private width: number; - private height: number; - - // stores frames of the video - private canvas: HTMLCanvasElement; - private canvasCtx: CanvasRenderingContext2D; - - // downsamples camera feed, used to blur background - private resizedCanvas: HTMLCanvasElement; - private resizedCanvasCtx: CanvasRenderingContext2D; - - private webglCanvas: HTMLCanvasElement; - private webglCanvasCtx: WebGL2RenderingContext; - - private segmenter: ImageSegmenter | null = null; - private prevVideoTime: number = 0; - stream: MediaStream; - track: MediaStreamTrack; - private video: HTMLVideoElement; - private worker = new BlurWorker(); - private worksInForeground = true; - private fps: number; - private destroyed = false; - - constructor(video: MediaStream) { - const trackSettings = video.getVideoTracks()[0].getSettings(); - this.width = trackSettings.width ?? 1280; - this.height = trackSettings.height ?? 720; - - this.canvas = document.createElement("canvas"); - this.canvas.setAttribute("width", "" + this.width / 2); - this.canvas.setAttribute("height", "" + this.height / 2); - this.canvasCtx = this.canvas.getContext("2d")!; - - this.resizedCanvas = document.createElement("canvas"); - this.resizedCanvas.setAttribute("width", "" + this.width / 4); - this.resizedCanvas.setAttribute("height", "" + this.height / 4); - this.resizedCanvasCtx = this.resizedCanvas.getContext("2d")!; - - this.webglCanvas = document.createElement("canvas"); - this.webglCanvas.setAttribute("width", "" + this.width); - this.webglCanvas.setAttribute("height", "" + this.height); - this.webglCanvasCtx = this.webglCanvas.getContext("webgl2")!; - - this.fps = trackSettings.frameRate ?? 24; - this.stream = this.webglCanvas.captureStream(this.fps); - this.track = this.stream.getVideoTracks()[0]; - - this.video = document.createElement("video"); - this.video.srcObject = video; - this.video.muted = true; - this.video.play(); - - this.initMediaPipe(); - this.initWebgl(); - - document.addEventListener("visibilitychange", this.visibilityListener); - this.worker.onmessage = this.onFrameCallback; - this.video.requestVideoFrameCallback(this.onFrameCallback); - } - - private visibilityListener = () => { - if (document.visibilityState === "visible") { - this.worksInForeground = true; - this.worker.postMessage({ type: "stop" }); - } else { - this.worksInForeground = false; - this.worker.postMessage({ type: "start", fps: this.fps }); - } - }; - - private async initMediaPipe() { - const wasm = await FilesetResolver.forVisionTasks( - "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.11/wasm", - ); - this.segmenter = await ImageSegmenter.createFromOptions(wasm, { - baseOptions: { - modelAssetPath: - "https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter_landscape/float16/latest/selfie_segmenter_landscape.tflite", - }, - runningMode: "VIDEO", - outputCategoryMask: true, - outputConfidenceMasks: true, - }); - } - - private async initWebgl() { - const gl = this.webglCanvasCtx; - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); - const program = gl.createProgram()!; - const vs = await this.loadShader( - gl, - gl.VERTEX_SHADER, - "/shaders/blur/vertex.glsl", - ); - const fs = await this.loadShader( - gl, - gl.FRAGMENT_SHADER, - "/shaders/blur/fragment.glsl", - ); - gl.attachShader(program, vs); - gl.attachShader(program, fs); - gl.linkProgram(program); - gl.useProgram(program); - gl.viewport(0, 0, this.width, this.height); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - - const vertexBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); - gl.bufferData( - gl.ARRAY_BUFFER, - new Float32Array([-1, -3, -1, 1, 3, 1]), - gl.STREAM_DRAW, - ); - const a_Position = gl.getAttribLocation(program, "a_Position"); - gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(a_Position); - - const texture = gl.createTexture()!; - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - const textureLoc = gl.getUniformLocation(program, "texture"); - gl.uniform1i(textureLoc, 0); - - const confidenceTexture = gl.createTexture()!; - gl.activeTexture(gl.TEXTURE1); - gl.bindTexture(gl.TEXTURE_2D, confidenceTexture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - const confidenceTextureLoc = gl.getUniformLocation( - program, - "confidenceTexture", - ); - gl.uniform1i(confidenceTextureLoc, 1); - - const resizedTexture = gl.createTexture()!; - gl.activeTexture(gl.TEXTURE2); - gl.bindTexture(gl.TEXTURE_2D, resizedTexture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - const resizedTextureLoc = gl.getUniformLocation(program, "resizedTexture"); - gl.uniform1i(resizedTextureLoc, 2); - } - - private async loadShader( - gl: WebGL2RenderingContext, - type: number, - path: string, - ): Promise { - const shader = gl.createShader(type)!; - gl.shaderSource(shader, await (await fetch(path)).text()); - gl.compileShader(shader); - - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - console.error("Failed to compile shader", { - path: path, - error: gl.getShaderInfoLog(shader), - }); - - throw "Failed to compile shader"; - } - - return shader; - } - - destroy() { - document.removeEventListener("visibilitychange", this.visibilityListener); - this.worker.terminate(); - this.track.stop(); - this.segmenter?.close(); - this.destroyed = true; - - this.canvas.remove(); - this.resizedCanvas.remove(); - this.webglCanvas.remove(); - this.video.remove(); - } - - private onFrameCallback = () => { - if (this.destroyed) return; - - if (!this.segmenter || this.prevVideoTime >= this.video.currentTime) { - if (this.worksInForeground) { - this.video.requestVideoFrameCallback(this.onFrameCallback); - } - return; - } - - this.canvasCtx.drawImage(this.video, 0, 0, this.width / 2, this.height / 2); - - this.resizedCanvasCtx.drawImage( - this.canvas, - 0, - 0, - this.width / 4, - this.height / 4, - ); - - this.prevVideoTime = this.video.currentTime; - this.segmenter.segmentForVideo( - this.canvas, - this.video.currentTime * 1000, - this.onSegmentationReady, - ); - - if (this.worksInForeground) { - this.video.requestVideoFrameCallback(this.onFrameCallback); - } - }; - - private onSegmentationReady: ImageSegmenterCallback = (result) => { - const confidenceMask = result.confidenceMasks![0]; - - const gl = this.webglCanvasCtx; - gl.activeTexture(gl.TEXTURE0); - gl.texImage2D( - gl.TEXTURE_2D, - 0, - gl.RGBA, - gl.RGBA, - gl.UNSIGNED_BYTE, - this.video, - ); - - gl.activeTexture(gl.TEXTURE1); - gl.texImage2D( - gl.TEXTURE_2D, - 0, - gl.LUMINANCE, - confidenceMask.width, - confidenceMask.height, - 0, - gl.LUMINANCE, - gl.UNSIGNED_BYTE, - confidenceMask.getAsUint8Array(), - ); - - gl.activeTexture(gl.TEXTURE2); - gl.texImage2D( - gl.TEXTURE_2D, - 0, - gl.RGBA, - gl.RGBA, - gl.UNSIGNED_BYTE, - this.resizedCanvas, - ); - - gl.drawArrays(gl.TRIANGLES, 0, 3); - }; -} diff --git a/examples/react-client/fishjam-chat/src/utils/blur/BlurProcessorWorker.ts b/examples/react-client/fishjam-chat/src/utils/blur/BlurProcessorWorker.ts deleted file mode 100644 index 216e14daa..000000000 --- a/examples/react-client/fishjam-chat/src/utils/blur/BlurProcessorWorker.ts +++ /dev/null @@ -1,17 +0,0 @@ -let intervalId: number; - -self.onmessage = (event) => { - if (event.data.type === "start") { - intervalId = self.setInterval(animationLoop, 1_000 / event.data.fps); - } else if (event.data.type === "stop") { - self.clearInterval(intervalId); - } -}; - -self.onclose = () => { - self.clearInterval(intervalId); -}; - -function animationLoop() { - self.postMessage("tick"); -} diff --git a/examples/react-client/fishjam-chat/src/vite-env.d.ts b/examples/react-client/fishjam-chat/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2a..000000000 --- a/examples/react-client/fishjam-chat/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/examples/react-client/fishjam-chat/tailwind.config.cjs b/examples/react-client/fishjam-chat/tailwind.config.cjs deleted file mode 100644 index ed0c228c5..000000000 --- a/examples/react-client/fishjam-chat/tailwind.config.cjs +++ /dev/null @@ -1,38 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - darkMode: ["class"], - content: ["./src/**/*.{js,jsx,ts,tsx}"], - theme: { - extend: { - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - colors: {}, - keyframes: { - "accordion-down": { - from: { - height: "0", - }, - to: { - height: "var(--radix-accordion-content-height)", - }, - }, - "accordion-up": { - from: { - height: "var(--radix-accordion-content-height)", - }, - to: { - height: "0", - }, - }, - }, - animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", - }, - }, - }, - plugins: [require("tailwindcss-animate")], -}; diff --git a/examples/react-client/fishjam-chat/tsconfig.app.json b/examples/react-client/fishjam-chat/tsconfig.app.json deleted file mode 100644 index aa6c73a5a..000000000 --- a/examples/react-client/fishjam-chat/tsconfig.app.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"] -} diff --git a/examples/react-client/fishjam-chat/tsconfig.json b/examples/react-client/fishjam-chat/tsconfig.json deleted file mode 100644 index 1e1739310..000000000 --- a/examples/react-client/fishjam-chat/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "files": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.node.json" - } - ], - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - } -} diff --git a/examples/react-client/fishjam-chat/tsconfig.node.json b/examples/react-client/fishjam-chat/tsconfig.node.json deleted file mode 100644 index 3afdd6e38..000000000 --- a/examples/react-client/fishjam-chat/tsconfig.node.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true, - "strict": true, - "noEmit": true - }, - "include": ["vite.config.ts"] -} diff --git a/examples/react-client/fishjam-chat/vite.config.ts b/examples/react-client/fishjam-chat/vite.config.ts deleted file mode 100644 index ebed3c516..000000000 --- a/examples/react-client/fishjam-chat/vite.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import react from "@vitejs/plugin-react-swc"; -import path from "path"; -import { defineConfig } from "vite"; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - server: { - port: 5170, - }, - resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - }, - }, -}); diff --git a/examples/react-client/livestreaming/.env.example b/examples/react-client/livestreaming/.env.example deleted file mode 100644 index ead9defb4..000000000 --- a/examples/react-client/livestreaming/.env.example +++ /dev/null @@ -1 +0,0 @@ -VITE_FISHJAM_ID="" diff --git a/examples/react-client/livestreaming/.prettierignore b/examples/react-client/livestreaming/.prettierignore deleted file mode 100644 index de4d1f007..000000000 --- a/examples/react-client/livestreaming/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules diff --git a/examples/react-client/livestreaming/.prettierrc b/examples/react-client/livestreaming/.prettierrc deleted file mode 100644 index 1c5e96602..000000000 --- a/examples/react-client/livestreaming/.prettierrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "printWidth": 80 -} diff --git a/examples/react-client/livestreaming/README.md b/examples/react-client/livestreaming/README.md deleted file mode 100644 index dfdac718c..000000000 --- a/examples/react-client/livestreaming/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Fishjam Livestream Demo - -This is a demo application that showcases both broadcasting and viewing live streams using Fishjam Cloud. - -## Features - -- **Livestream Streamer**: Start streaming your camera and microphone to a Fishjam room -- **Livestream Viewer**: Watch live streams using WHEP (WebRTC-HTTP Egress Protocol) - -## Getting Started - -1. Install dependencies: - - ```bash - yarn install - ``` - -2. Start the development server: - - ```bash - yarn dev - ``` - -3. Open your browser and navigate to the URL shown in the terminal (usually `http://localhost:5173`) - -## How to Use - -### Broadcasting - -1. Fill in the **Room Manager URL** (your Fishjam room manager endpoint) -2. Enter a **Room Name** for your stream -3. Enter your **Name** as the broadcaster -4. Click "Start Broadcasting" to begin streaming - -### Viewing - -1. Enter the **Token** for the stream you want to watch -2. Click "Connect to Stream" to start viewing diff --git a/examples/react-client/livestreaming/components.json b/examples/react-client/livestreaming/components.json deleted file mode 100644 index 13e1db0b7..000000000 --- a/examples/react-client/livestreaming/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/index.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} diff --git a/examples/react-client/livestreaming/eslint.config.mjs b/examples/react-client/livestreaming/eslint.config.mjs deleted file mode 100644 index 4d1aceb94..000000000 --- a/examples/react-client/livestreaming/eslint.config.mjs +++ /dev/null @@ -1,21 +0,0 @@ -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -import { FlatCompat } from "@eslint/eslintrc"; -import js from "@eslint/js"; -import { defineConfig, globalIgnores } from "eslint/config"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all, -}); - -export default defineConfig([ - globalIgnores(["**/lib", "**/node_modules", "**/dist"]), - { - extends: compat.extends("../../../.eslintrc.js"), - }, -]); diff --git a/examples/react-client/livestreaming/index.html b/examples/react-client/livestreaming/index.html deleted file mode 100644 index 7c0d91e2a..000000000 --- a/examples/react-client/livestreaming/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - Fishjam Broadcast Example - - - - - -
- - - diff --git a/examples/react-client/livestreaming/package.json b/examples/react-client/livestreaming/package.json deleted file mode 100644 index 8b8264a6a..000000000 --- a/examples/react-client/livestreaming/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "@fishjam-example/livestreaming", - "private": true, - "version": "0.0.1", - "type": "module", - "license": "Apache-2.0", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "watch": "tsc --noEmit --watch", - "preview": "vite preview", - "format": "prettier --write . --ignore-path ./.prettierignore", - "format:check": "prettier --check . --ignore-path ./.prettierignore", - "lint": "eslint . --ext .ts,.tsx --fix", - "lint:check": "eslint . --ext .ts,.tsx" - }, - "dependencies": { - "@fishjam-cloud/react-client": "workspace:*", - "@hookform/resolvers": "^5.1.1", - "@radix-ui/react-icons": "^1.3.2", - "@radix-ui/react-label": "^2.1.7", - "@radix-ui/react-select": "^2.1.6", - "@radix-ui/react-slot": "^1.2.3", - "@tailwindcss/vite": "^4.1.11", - "axios": "^1.10.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "lucide-react": "^0.524.0", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-hook-form": "^7.60.0", - "sonner": "^1.6.0", - "tailwind-merge": "^3.3.1", - "tailwindcss": "^4.1.11" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.30.0", - "@types/node": "^24.0.4", - "@types/react": "19.1.0", - "@types/react-dom": "19.1.0", - "@vitejs/plugin-react-swc": "^3.8.1", - "autoprefixer": "^10.4.21", - "eslint": "^9.30.0", - "postcss": "^8.5.3", - "prettier": "^3.6.2", - "tw-animate-css": "^1.3.4", - "typescript": "^5.8.2", - "vite": "^6.2.5", - "vite-plugin-checker": "^0.9.1" - }, - "lint-staged": { - "*": [ - "yarn format" - ], - "*.(js|ts|tsx)": [ - "yarn lint" - ] - } -} diff --git a/examples/react-client/livestreaming/src/components/App.tsx b/examples/react-client/livestreaming/src/components/App.tsx deleted file mode 100644 index 47e0b0092..000000000 --- a/examples/react-client/livestreaming/src/components/App.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useState } from "react"; - -import { FishjamCtxProvider } from "./FishjamContext"; -import { Header } from "./Header"; -import LivestreamStreamer from "./LivestreamStreamer"; -import LivestreamViewer from "./LivestreamViewer"; - -export const App = () => { - const [roomName, setRoomName] = useState("example-livestream"); - - return ( - -
-
- -
-
- - - -
-
-
-
- ); -}; diff --git a/examples/react-client/livestreaming/src/components/ConnectForm.tsx b/examples/react-client/livestreaming/src/components/ConnectForm.tsx deleted file mode 100644 index 087048895..000000000 --- a/examples/react-client/livestreaming/src/components/ConnectForm.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useState } from "react"; -import { useForm } from "react-hook-form"; - -import { useFishjamId } from "@/lib/fishjamContext"; - -import { Button } from "./ui/button"; -import { Card, CardContent } from "./ui/card"; -import { Input } from "./ui/input"; -import { Label } from "./ui/label"; - -type FormValues = { - fishjamId: string; -}; - -export const ConnectForm = () => { - const { fishjamId, setFishjamId } = useFishjamId(); - const [isLocked, setIsLocked] = useState(!!fishjamId); - - const form = useForm({ - defaultValues: { - fishjamId: fishjamId, - }, - }); - - function onSubmit(values: FormValues) { - setIsLocked(true); - setFishjamId(values.fishjamId); - } - - return ( - - -
- -
- - - {isLocked ? ( - - ) : ( - - )} -
- {form.formState.errors.fishjamId ? ( -

- {form.formState.errors.fishjamId.message} -

- ) : ( -

- You can find your Fishjam ID in the{" "} - - Fishjam Dashboard - - . -

- )} -
-
-
- ); -}; diff --git a/examples/react-client/livestreaming/src/components/FishjamContext.tsx b/examples/react-client/livestreaming/src/components/FishjamContext.tsx deleted file mode 100644 index af18c5679..000000000 --- a/examples/react-client/livestreaming/src/components/FishjamContext.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { FishjamProvider } from "@fishjam-cloud/react-client"; -import React, { type FC, type PropsWithChildren } from "react"; - -import { DEFAULT_FISHJAM_ID } from "@/lib/consts"; -import { FishjamContext } from "@/lib/fishjamContext"; - -export const FishjamCtxProvider: FC = ({ children }) => { - const [fishjamId, setFishjamId] = React.useState(DEFAULT_FISHJAM_ID); - - return ( - - {children} - - ); -}; diff --git a/examples/react-client/livestreaming/src/components/Header.tsx b/examples/react-client/livestreaming/src/components/Header.tsx deleted file mode 100644 index cd55b008e..000000000 --- a/examples/react-client/livestreaming/src/components/Header.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { ConnectForm } from "./ConnectForm"; - -export const Header = () => { - return ( -
-

- Fishjam Livestream Demo -

- -

- Broadcast and view live streams with Fishjam -

- - -
- ); -}; diff --git a/examples/react-client/livestreaming/src/components/LivestreamStreamer.tsx b/examples/react-client/livestreaming/src/components/LivestreamStreamer.tsx deleted file mode 100644 index db69ed4f0..000000000 --- a/examples/react-client/livestreaming/src/components/LivestreamStreamer.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import type { StreamerInputs } from "@fishjam-cloud/react-client"; -import { - useCamera, - useInitializeDevices, - useLivestreamStreamer, - useMicrophone, - useSandbox, -} from "@fishjam-cloud/react-client"; -import { AlertCircleIcon, Loader2, MessageCircleWarning } from "lucide-react"; -import type { FC } from "react"; -import { useCallback, useEffect, useState } from "react"; -import { toast } from "sonner"; - -import { Alert, AlertDescription, AlertTitle } from "./ui/alert"; -import { Button } from "./ui/button"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "./ui/card"; -import { Input } from "./ui/input"; -import { Label } from "./ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "./ui/select"; - -type LivestreamStreamerProps = { - roomName: string; - setRoomName: (roomName: string) => void; -}; - -const inputsAreValid = (inputs: { - video?: MediaStream | null; - audio?: MediaStream | null; -}): inputs is StreamerInputs => { - return Boolean(inputs.video || inputs.audio); -}; - -const LivestreamStreamer: FC = ({ - roomName, - setRoomName, -}) => { - const { initializeDevices } = useInitializeDevices(); - const camera = useCamera(); - const microphone = useMicrophone(); - - const [isConnecting, setIsConnecting] = useState(false); - - const { getSandboxLivestream } = useSandbox(); - const { connect, disconnect, isConnected, error } = useLivestreamStreamer(); - - const initializeAndReport = useCallback(async () => { - const { errors } = await initializeDevices(); - if (!errors) return; - - const devices = []; - if (errors.video) devices.push("camera"); - if (errors.audio) devices.push("microphone"); - - toast.error(`Failed to initialize ${devices.join(" and ")}`, { - icon: , - position: "top-center", - }); - }, [initializeDevices]); - - useEffect(() => { - initializeAndReport(); - }, [initializeAndReport]); - - useEffect(() => { - if (isConnected) toast.success("Livestream started!"); - }, [isConnected]); - - const handleStartStreaming = async () => { - if (!roomName) { - toast.error("Please fill in all fields"); - return; - } - - const inputs = { - video: camera.cameraStream, - audio: microphone.microphoneStream, - }; - if (!inputsAreValid(inputs)) { - toast.error("Make sure either the camera or microphone are setup."); - return; - } - - setIsConnecting(true); - try { - const { streamerToken } = await getSandboxLivestream(roomName); - await connect({ inputs, token: streamerToken }); - } catch (e) { - toast.error("Failed to join the room"); - console.error(e); - } finally { - setIsConnecting(false); - } - }; - - const handleStopStreaming = () => { - disconnect(); - toast.success("Streamer left the room"); - }; - - const handleCameraChange = (deviceId: string) => { - camera.selectCamera(deviceId); - }; - - const handleMicrophoneChange = (deviceId: string) => { - microphone.selectMicrophone(deviceId); - }; - - return ( - - - Livestream Streamer - Start streaming to the room - - - -
-
- - setRoomName(e.target.value)} - placeholder="my-livestream" - disabled={isConnected} - /> -
-
- -
- - -
- -
- - -
- {error && ( - - - Failed to publish the stream - -

- Reason: {error} -

-
-
- )} -
- - - {!isConnected ? ( - - ) : ( - - )} - -
- ); -}; - -export default LivestreamStreamer; diff --git a/examples/react-client/livestreaming/src/components/LivestreamViewer.tsx b/examples/react-client/livestreaming/src/components/LivestreamViewer.tsx deleted file mode 100644 index abc17aad9..000000000 --- a/examples/react-client/livestreaming/src/components/LivestreamViewer.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { useLivestreamViewer, useSandbox } from "@fishjam-cloud/react-client"; -import { AlertCircleIcon } from "lucide-react"; -import type { FC } from "react"; -import { useState } from "react"; -import { toast } from "sonner"; - -import { Alert, AlertDescription, AlertTitle } from "./ui/alert"; -import { Button } from "./ui/button"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "./ui/card"; -import { Input } from "./ui/input"; -import { Label } from "./ui/label"; -import VideoPlayer from "./VideoPlayer"; - -type LivestreamViewerProps = { - roomName: string; -}; - -const LivestreamViewer: FC = ({ - roomName: streamerRoomName, -}) => { - const { connect, disconnect, stream, error } = useLivestreamViewer(); - const { getSandboxViewerToken } = useSandbox(); - const [nameOverriden, setNameOverriden] = useState(false); - const [roomName, setRoomName] = useState(streamerRoomName); - - if (!nameOverriden && roomName != streamerRoomName) - setRoomName(streamerRoomName); - - const handleConnect = async () => { - if (!roomName) { - toast.error("Please fill in all fields"); - return; - } - const token = await getSandboxViewerToken(roomName); - await connect({ token }); - }; - - const handleDisconnect = () => { - disconnect(); - toast.success("Disconnected from livestream"); - }; - - return ( - - - Livestream Viewer - Watch the live stream - -
- -
- - { - setNameOverriden(true); - setRoomName(e.target.value); - }} - placeholder="Stream you want to watch" - disabled={!!stream} - /> - {error && ( - - - Failed to view the stream - -

- Reason: {error} -

-
-
- )} -
-
- - {stream && ( - - )} -
-
- - {!stream ? ( - - ) : ( - - )} - -
-
- ); -}; - -export default LivestreamViewer; diff --git a/examples/react-client/livestreaming/src/components/VideoPlayer.tsx b/examples/react-client/livestreaming/src/components/VideoPlayer.tsx deleted file mode 100644 index 4dae60cf9..000000000 --- a/examples/react-client/livestreaming/src/components/VideoPlayer.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { type HTMLAttributes, useEffect, useRef } from "react"; - -type Props = { - stream: MediaStream | null | undefined; -} & HTMLAttributes; - -const VideoPlayer = ({ stream, ...props }: Props) => { - const videoRef = useRef(null); - - useEffect(() => { - if (!videoRef.current) return; - videoRef.current.srcObject = stream ?? null; - }, [stream]); - - return ( -