diff --git a/client/index.html b/client/index.html
new file mode 100644
index 0000000..1a6d93c
--- /dev/null
+++ b/client/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ MorseQuest
+
+
+
+
+
+
+
+
+
diff --git a/client/src/main.tsx b/client/src/main.tsx
new file mode 100644
index 0000000..a94e4fe
--- /dev/null
+++ b/client/src/main.tsx
@@ -0,0 +1,8 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+ MorseQuest loading...
+
+)
diff --git a/client/src/test-setup.ts b/client/src/test-setup.ts
new file mode 100644
index 0000000..df9df73
--- /dev/null
+++ b/client/src/test-setup.ts
@@ -0,0 +1,28 @@
+// Mock AudioContext for tests
+class MockAudioContext {
+ currentTime = 0
+ destination = {}
+ state: AudioContextState = 'running'
+ createOscillator() {
+ return {
+ connect: () => {},
+ start: () => {},
+ stop: () => {},
+ frequency: { value: 0 },
+ type: 'sine' as OscillatorType,
+ }
+ }
+ createGain() {
+ return {
+ connect: () => {},
+ gain: {
+ value: 1,
+ setValueAtTime: () => {},
+ linearRampToValueAtTime: () => {},
+ },
+ }
+ }
+ async resume() {}
+ async suspend() {}
+}
+;(global as any).AudioContext = MockAudioContext
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0238523
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "morsequest",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "concurrently \"node --watch server/server.js\" \"vite\"",
+ "build": "vite build",
+ "start": "node server/server.js",
+ "test": "npm run test:server && npm run test:client",
+ "test:server": "node --test server/tests/db.test.js server/tests/auth.test.js server/tests/progress.test.js server/tests/admin.test.js",
+ "test:client": "vitest run"
+ },
+ "dependencies": {
+ "better-sqlite3": "^11.0.0",
+ "nodemailer": "^6.9.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/better-sqlite3": "^7.6.0",
+ "@types/react": "^18.2.0",
+ "@types/react-dom": "^18.2.0",
+ "@vitejs/plugin-react": "^4.2.0",
+ "concurrently": "^8.2.0",
+ "jsdom": "^24.0.0",
+ "typescript": "^5.3.0",
+ "vite": "^5.1.0",
+ "vitest": "^1.3.0"
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..88a7562
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false
+ },
+ "include": ["client/src"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..fc59890
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,22 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ root: 'client',
+ plugins: [react()],
+ build: {
+ outDir: 'dist',
+ emptyOutDir: true,
+ },
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': 'http://localhost:3001',
+ },
+ },
+ test: {
+ environment: 'jsdom',
+ globals: true,
+ setupFiles: ['src/test-setup.ts'],
+ },
+})