diff --git a/packages/www/package.json b/packages/www/package.json
index 96c96968..fe20260c 100644
--- a/packages/www/package.json
+++ b/packages/www/package.json
@@ -27,6 +27,7 @@
"@solid-primitives/storage": "^4.3.1",
"@solidjs/router": "^0.15.3",
"modern-normalize": "^3.0.1",
- "solid-js": "^1.9.5"
+ "solid-js": "^1.9.5",
+ "solid-notifications": "^1.1.2"
}
}
\ No newline at end of file
diff --git a/packages/www/src/App.tsx b/packages/www/src/App.tsx
index efd3487d..59e032ad 100644
--- a/packages/www/src/App.tsx
+++ b/packages/www/src/App.tsx
@@ -7,6 +7,7 @@ import '@fontsource/geist-sans/700.css';
import '@fontsource/geist-sans/800.css';
import '@fontsource/geist-sans/900.css';
import { TeamCreate } from './pages/new';
+import { PlayComponent } from './pages/play';
import { styled } from "@macaron-css/solid";
import { useStorage } from './providers/account';
import { darkClass, lightClass, theme } from './ui/theme';
@@ -116,6 +117,7 @@ export const App: Component = () => {
{WorkspaceRoute} */}
+
{
diff --git a/packages/www/src/index.tsx b/packages/www/src/index.tsx
index 608b9165..0bada0f9 100644
--- a/packages/www/src/index.tsx
+++ b/packages/www/src/index.tsx
@@ -8,6 +8,7 @@ import { render } from "solid-js/web";
import "modern-normalize/modern-normalize.css";
import { App } from "./App";
import { StorageProvider } from "./providers/account";
+import { ToastProvider, Toaster } from "solid-notifications";
const root = document.getElementById("root");
@@ -19,9 +20,12 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
render(
() => (
-
-
-
+
+
+
+
+
+
),
root!
);
\ No newline at end of file
diff --git a/packages/www/src/pages/play.tsx b/packages/www/src/pages/play.tsx
new file mode 100644
index 00000000..669f97fe
--- /dev/null
+++ b/packages/www/src/pages/play.tsx
@@ -0,0 +1,149 @@
+import { Text } from "@nestri/www/ui/text";
+import { createSignal, createEffect, onCleanup, onMount } from "solid-js";
+import { useParams } from "@solidjs/router";
+import { Modal } from "@nestri/ui";
+import { Keyboard, Mouse, WebRTCStream } from "@nestri/input";
+
+export function PlayComponent() {
+ const params = useParams();
+ const id = params.id;
+
+ const [showBannerModal, setShowBannerModal] = createSignal(false);
+ const [showButtonModal, setShowButtonModal] = createSignal(false);
+ const [gamepadConnected, setGamepadConnected] = createSignal(false);
+ const [buttonPressed, setButtonPressed] = createSignal(null);
+ const [leftStickX, setLeftStickX] = createSignal(0);
+ const [leftStickY, setLeftStickY] = createSignal(0);
+ const [hasStream, setHasStream] = createSignal(false);
+ const [showOffline, setShowOffline] = createSignal(false);
+
+ const [canvas, setCanvas] = createSignal(undefined);
+ let video: HTMLVideoElement;
+ let webrtc: WebRTCStream;
+ let nestriMouse: Mouse , nestriKeyboard: Keyboard;
+
+ const initializeInputDevices = () => {
+ const canvasElement = canvas();
+ if (!canvasElement || !webrtc) return;
+ try {
+ nestriMouse = new Mouse({ canvas: canvasElement, webrtc });
+ nestriKeyboard = new Keyboard({ canvas: canvasElement, webrtc });
+ console.log("Input devices initialized successfully");
+ } catch (error) {
+ console.error("Failed to initialize input devices:", error);
+ }
+ };
+
+ /*const initializeGamepad = () => {
+ console.log("Initializing gamepad...");
+
+ const updateGamepadState = () => {
+ const gamepads = navigator.getGamepads();
+ const gamepad = gamepads[0];
+ if (gamepad) {
+ setButtonPressed(gamepad.buttons.findIndex(btn => btn.pressed) !== -1 ? "Button pressed" : null);
+ setLeftStickX(Number(gamepad.axes[0].toFixed(2)));
+ setLeftStickY(Number(gamepad.axes[1].toFixed(2)));
+ }
+ requestAnimationFrame(updateGamepadState);
+ };
+
+ window.addEventListener("gamepadconnected", () => {
+ setGamepadConnected(true);
+ console.log("Gamepad connected!");
+ updateGamepadState();
+ });
+
+ window.addEventListener("gamepaddisconnected", () => {
+ setGamepadConnected(false);
+ console.log("Gamepad disconnected!");
+ });
+ };*/
+
+ const lockPlay = async () => {
+ const canvasElement = canvas();
+ if (!canvasElement || !hasStream()) return;
+ try {
+ await canvasElement.requestPointerLock();
+ await canvasElement.requestFullscreen();
+ //initializeGamepad();
+ } catch (error) {
+ console.error("Error during lock sequence:", error);
+ }
+ };
+
+ const setupPointerLockListener = () => {
+ document.addEventListener("pointerlockchange", () => {
+ const canvasElement = canvas();
+ if (!canvasElement) return;
+ if (document.pointerLockElement === canvasElement) {
+ initializeInputDevices();
+ } else {
+ nestriKeyboard?.dispose();
+ nestriMouse?.dispose();
+ }
+ });
+ };
+
+ const handleVideoInput = async () => {
+ const canvasElement = canvas();
+ if (!video || !canvasElement) return;
+
+ try {
+
+ await video.play();
+ if (canvasElement && video) {
+ canvasElement.width = video.videoWidth;
+ canvasElement.height = video.videoHeight;
+
+
+ const ctx = canvasElement.getContext("2d");
+ const renderer = () => {
+ if (ctx && hasStream() && video) {
+ ctx.drawImage(video, 0, 0);
+ video.requestVideoFrameCallback(renderer);
+ }
+ };
+
+ video.requestVideoFrameCallback(renderer);
+ }
+ } catch (error) {
+ console.error("Error playing video:", error);
+ }
+ };
+
+
+ onMount(() => {
+ setupPointerLockListener();
+ video = document.createElement("video");
+ video.style.visibility = "hidden";
+ webrtc = new WebRTCStream("https://relay.dathorse.com", id, async (mediaStream) => {
+ if (video && mediaStream) {
+ video.srcObject = mediaStream;
+ setHasStream(true);
+ setShowOffline(false);
+ await handleVideoInput();
+ } else {
+ setShowOffline(true);
+ setHasStream(false);
+ }
+ });
+ });
+
+ onCleanup(() => {
+ nestriKeyboard?.dispose();
+ nestriMouse?.dispose();
+ });
+
+ return (
+ <>
+ {showOffline() ? (
+
+ Offline
+
+ ) : (
+
+ )}
+ >
+ );
+ }
\ No newline at end of file