diff --git a/page/index.html b/page/index.html
index 6fa1d4ce..4d886350 100644
--- a/page/index.html
+++ b/page/index.html
@@ -4,7 +4,6 @@
-
Sogen - Windows User Space Emulator
diff --git a/page/package-lock.json b/page/package-lock.json
index 509f7fd6..2a8edaff 100644
--- a/page/package-lock.json
+++ b/page/package-lock.json
@@ -26,6 +26,7 @@
"react": "^19.0.0",
"react-bootstrap-icons": "^1.11.5",
"react-dom": "^19.0.0",
+ "react-helmet": "^6.1.0",
"react-router-dom": "^7.5.1",
"react-window": "^1.8.11",
"tailwind-merge": "^3.2.0",
@@ -37,6 +38,7 @@
"@types/node": "^22.14.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
+ "@types/react-helmet": "^6.1.11",
"@vitejs/plugin-react": "^4.4.1",
"eslint": "^9.25.1",
"eslint-plugin-react-hooks": "^6.0.0",
@@ -2619,6 +2621,16 @@
"@types/react": "^19.0.0"
}
},
+ "node_modules/@types/react-helmet": {
+ "version": "6.1.11",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz",
+ "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/react-window": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
@@ -4493,6 +4505,36 @@
"react": "^19.1.0"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "license": "MIT"
+ },
+ "node_modules/react-helmet": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
+ "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.1.1",
+ "react-side-effect": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0"
+ }
+ },
+ "node_modules/react-helmet/node_modules/react-side-effect": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
+ "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.3.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
diff --git a/page/package.json b/page/package.json
index c9c07fc1..54a9862a 100644
--- a/page/package.json
+++ b/page/package.json
@@ -28,6 +28,7 @@
"react": "^19.0.0",
"react-bootstrap-icons": "^1.11.5",
"react-dom": "^19.0.0",
+ "react-helmet": "^6.1.0",
"react-router-dom": "^7.5.1",
"react-window": "^1.8.11",
"tailwind-merge": "^3.2.0",
@@ -39,6 +40,7 @@
"@types/node": "^22.14.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
+ "@types/react-helmet": "^6.1.11",
"@vitejs/plugin-react": "^4.4.1",
"eslint": "^9.25.1",
"eslint-plugin-react-hooks": "^6.0.0",
diff --git a/page/src/App.tsx b/page/src/App.tsx
index 8aa5caec..555c5c5a 100644
--- a/page/src/App.tsx
+++ b/page/src/App.tsx
@@ -7,14 +7,14 @@ import "./App.css";
function App() {
return (
-
-
-
- } />
- } />
-
-
-
+
+
+
+ } />
+ } />
+
+
+
);
}
diff --git a/page/src/Header.tsx b/page/src/Header.tsx
new file mode 100644
index 00000000..fa8a1ea9
--- /dev/null
+++ b/page/src/Header.tsx
@@ -0,0 +1,13 @@
+import { Helmet } from 'react-helmet';
+
+export interface HeaderProps {
+ title: string;
+}
+
+export function Header(props: HeaderProps) {
+ return (
+
+ {props.title}
+
+ );
+}
diff --git a/page/src/LandingPage.tsx b/page/src/LandingPage.tsx
index 02eef848..f967775d 100644
--- a/page/src/LandingPage.tsx
+++ b/page/src/LandingPage.tsx
@@ -11,6 +11,7 @@ import {
Github,
Play,
} from "lucide-react";
+import { Header } from "./Header";
export function LandingPage() {
const features = [
@@ -53,6 +54,7 @@ export function LandingPage() {
return (
<>
+
{/* Hero Section */}
diff --git a/page/src/Playground.tsx b/page/src/Playground.tsx
index 8051d860..d2914d96 100644
--- a/page/src/Playground.tsx
+++ b/page/src/Playground.tsx
@@ -1,34 +1,39 @@
-import { useState, useRef } from 'react'
-import { Output } from '@/components/output'
+import { useState, useRef } from "react";
+import { Output } from "@/components/output";
-import { AppSidebar } from "@/components/app-sidebar"
-import { Separator } from "@/components/ui/separator"
+import { AppSidebar } from "@/components/app-sidebar";
+import { Separator } from "@/components/ui/separator";
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
-} from "@/components/ui/sidebar"
-import { Button } from './components/ui/button'
+} from "@/components/ui/sidebar";
+import { Button } from "./components/ui/button";
-import { Emulator, UserFile } from './emulator';
-import { getFilesystem } from './filesystem';
+import { Emulator, UserFile } from "./emulator";
+import { getFilesystem } from "./filesystem";
-import './App.css'
-import { Popover, PopoverContent, PopoverTrigger } from './components/ui/popover'
+import "./App.css";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "./components/ui/popover";
-import { createDefaultSettings } from './settings';
-import { SettingsMenu } from './components/settings-menu';
+import { createDefaultSettings } from "./settings";
+import { SettingsMenu } from "./components/settings-menu";
-import { PlayFill, StopFill, GearFill } from 'react-bootstrap-icons';
-import { StatusIndicator } from './components/status-indicator'
+import { PlayFill, StopFill, GearFill } from "react-bootstrap-icons";
+import { StatusIndicator } from "./components/status-indicator";
+import { Header } from "./Header";
function selectAndReadFile(): Promise {
return new Promise((resolve, reject) => {
- const fileInput = document.createElement('input');
- fileInput.type = 'file';
- fileInput.accept = '.exe';
+ const fileInput = document.createElement("input");
+ fileInput.type = "file";
+ fileInput.accept = ".exe";
- fileInput.addEventListener('change', function (event) {
+ fileInput.addEventListener("change", function (event) {
const file = (event as any).target.files[0];
if (file) {
const reader = new FileReader();
@@ -37,17 +42,17 @@ function selectAndReadFile(): Promise {
const arrayBuffer = e.target?.result;
resolve({
name: file.name,
- data: arrayBuffer as ArrayBuffer
+ data: arrayBuffer as ArrayBuffer,
});
};
reader.onerror = function (e: ProgressEvent) {
- reject(new Error('Error reading file: ' + e.target?.error));
+ reject(new Error("Error reading file: " + e.target?.error));
};
reader.readAsArrayBuffer(file);
} else {
- reject(new Error('No file selected'));
+ reject(new Error("No file selected"));
}
});
@@ -91,25 +96,35 @@ export function Playground() {
}
return (
+ <>
+
-
+
- )
+ >
+ );
}
-