From 42e4d10c80d3ed800bbbce74cb06c0896d7ec1f0 Mon Sep 17 00:00:00 2001 From: 2584433 Date: Mon, 6 Oct 2025 08:00:59 +0000 Subject: [PATCH 1/5] style: added automatic hooks --- .githooks/commit-msg | 17 +++++++++++++++++ .githooks/pre-commit | 18 ++++++++++++++++++ .githooks/prepare-commit-msg | 9 +++++++++ README.md | 9 +++++++++ src/App.tsx | 2 +- 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100755 .githooks/commit-msg create mode 100755 .githooks/pre-commit create mode 100755 .githooks/prepare-commit-msg diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100755 index 0000000..dd14401 --- /dev/null +++ b/.githooks/commit-msg @@ -0,0 +1,17 @@ +#!/bin/sh + +commit_msg_file=$1 +commit_msg=$(cat "$commit_msg_file") + +if echo "$commit_msg" | grep -Eq "^(feat|fix|refactor|perf|style|test|docs|build|chore|revert): .+"; then + if echo "$commit_msg" | grep -Eq "^(ref|close):\sN25B-.+"; then + echo "🎉 commit message is Valid" + exit 0 + else + echo "❌ Commit message invalid! Must end with [ref/close]: N25B-000" + exit 1 + fi +else + echo "❌ Commit message invalid! Must start with : " + exit 1 +fi \ No newline at end of file diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..391d279 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,18 @@ +#!/bin/sh + +# Get current branch +branch=$(git rev-parse --abbrev-ref HEAD) + +if echo "$branch" | grep -Eq "(dev|main)"; then + echo 0 +fi + +# allowed pattern +if echo "$branch" | grep -Eq "^(feat|fix|refactor|perf|style|test|docs|build|chore|revert)\/\w+-\w+-\w+"; then + echo "✅ Branch name valid: $branch" + exit 0 +else + echo "❌ Invalid branch name: $branch" + echo "Branch must be named / (must have 2 stipes - -)" + exit 1 +fi \ No newline at end of file diff --git a/.githooks/prepare-commit-msg b/.githooks/prepare-commit-msg new file mode 100755 index 0000000..5b706c1 --- /dev/null +++ b/.githooks/prepare-commit-msg @@ -0,0 +1,9 @@ +#!/bin/sh + +echo "#: + +#[optional body] + +#[optional footer(s)] + +#[ref/close]: " > $1 \ No newline at end of file diff --git a/README.md b/README.md index 18905eb..14ab2a9 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,12 @@ npm run dev ``` It should automatically reload when you save changes. + +## GitHooks + +To activate automatic commits/branch name checks in Windows run: + +```shell +git config core.hooksPath .githooks +``` + diff --git a/src/App.tsx b/src/App.tsx index fe7cb61..80f6643 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,4 +13,4 @@ function App(){ ) } -export default App +export default App \ No newline at end of file From a9f0a8dabd3cf7b6d536eadfc53ad7ee206190c5 Mon Sep 17 00:00:00 2001 From: 2584433 Date: Tue, 7 Oct 2025 14:38:31 +0000 Subject: [PATCH 2/5] readme change --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14ab2a9..6e97243 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,14 @@ It should automatically reload when you save changes. ## GitHooks -To activate automatic commits/branch name checks in Windows run: +To activate automatic commits/branch name checks run: ```shell -git config core.hooksPath .githooks +git config --local core.hooksPath .githooks ``` +If your commit fails its either: +branch name != /description-of-branch +commit name != : description of the commit. + : N25B-Num's + From c515f32023f789a8dff40c9e01864204a23d0a5e Mon Sep 17 00:00:00 2001 From: Twirre Meulenbelt <43213592+TwirreM@users.noreply.github.com> Date: Wed, 8 Oct 2025 13:07:47 +0200 Subject: [PATCH 3/5] chore: fix branch naming regex pattern The previous pre-commit script allowed only branch names with three words. Should allow one to 6 words. ref: N25B-89 --- .githooks/pre-commit | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 391d279..ed801d8 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -8,11 +8,11 @@ if echo "$branch" | grep -Eq "(dev|main)"; then fi # allowed pattern -if echo "$branch" | grep -Eq "^(feat|fix|refactor|perf|style|test|docs|build|chore|revert)\/\w+-\w+-\w+"; then +if echo "$branch" | grep -Eq "^(feat|fix|refactor|perf|style|test|docs|build|chore|revert)\/\w+(-\w+){0,5}$"; then echo "✅ Branch name valid: $branch" exit 0 else echo "❌ Invalid branch name: $branch" - echo "Branch must be named / (must have 2 stipes - -)" + echo "Branch must be named / (must have one to six words separated by a dash)" exit 1 fi \ No newline at end of file From 46cc5a087d2f4996c9184b143c38f067c042ec6f Mon Sep 17 00:00:00 2001 From: Twirre Meulenbelt <43213592+TwirreM@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:03:44 +0200 Subject: [PATCH 4/5] feat: basic home page Created a basic home page with links, a header with a link to home, some basic reusable CSS classes. ref: N25B-38 --- src/App.css | 137 ++++++++++++++------- src/App.tsx | 20 +-- src/components/components.tsx | 5 +- src/index.css | 18 +-- src/pages/Home/Home.module.css | 18 +++ src/pages/Home/Home.tsx | 47 ++----- src/pages/TemplatePage/Template.module.css | 4 - src/pages/TemplatePage/Template.tsx | 13 -- vite.config.ts | 5 + 9 files changed, 148 insertions(+), 119 deletions(-) delete mode 100644 src/pages/TemplatePage/Template.module.css diff --git a/src/App.css b/src/App.css index dcb46cf..ab28aa0 100644 --- a/src/App.css +++ b/src/App.css @@ -1,24 +1,3 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - - .logopepper { height: 8em; padding: 1.5em; @@ -32,27 +11,21 @@ filter: drop-shadow(0 0 10em #4eff14aa); } -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - - @keyframes logo-pepper-spin { - from { - transform: rotate(-20deg); + 0% { + transform: rotate(0); } - to { + 25% { transform: rotate(20deg); } + 75% { + transform: rotate(-20deg); + } + 100% { + transform: rotate(0); + } } - - @keyframes logo-pepper-scale { from { transform: scale(1,1); @@ -63,19 +36,13 @@ } @media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; + .logopepper:hover { + animation: logo-pepper-spin infinite 1s linear; } } @media (prefers-reduced-motion: no-preference) { - .logopepper { - animation: logo-pepper-spin infinite 1s linear alternate; - } -} - -@media (prefers-reduced-motion: no-preference) { - .logoPepperScaling { + .logoPepperScaling:hover { animation: logo-pepper-scale infinite 1s linear alternate; } } @@ -113,3 +80,83 @@ button.movePage.right{ button.movePage:hover{ background-color: rgb(0, 176, 176); } + + + +header { + position: sticky; + top: 0; + left: 0; + right: 0; + + padding: 1rem; + + display: flex; + gap: 1rem; + align-items: center; + justify-content: center; + + backdrop-filter: blur(10px); + z-index: 1; /* Otherwise any translated elements render above the blur?? */ +} + +main { + padding: 1rem 0; +} + +.flex-row { + display: flex; + flex-direction: row; +} +.flex-col { + display: flex; + flex-direction: column; +} + +.flex-1 { + flex: 1; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.align-center { + align-items: center; +} +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} + +.gap-sm { + gap: .25rem; +} +.gap-md { + gap: .5rem; +} +.gap-lg { + gap: 1rem; +} + +.padding-sm { + padding: .25rem; +} +.padding-md { + padding: .5rem; +} +.padding-lg { + padding: 1rem; +} + +.round-sm { + border-radius: .25rem; +} +.round-md { + border-radius: .5rem; +} +.round-lg { + border-radius: 1rem; +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 80f6643..acec25d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,16 +1,22 @@ -import { Routes, Route } from 'react-router' +import { Routes, Route, Link } from 'react-router' import './App.css' import TemplatePage from './pages/TemplatePage/Template.tsx' import Home from './pages/Home/Home.tsx' function App(){ - return ( - - } /> - } /> - +
+
+ Home +
+
+ + } /> + } /> + +
+
) } -export default App \ No newline at end of file +export default App diff --git a/src/components/components.tsx b/src/components/components.tsx index d323843..24dd429 100644 --- a/src/components/components.tsx +++ b/src/components/components.tsx @@ -1,14 +1,11 @@ -// src/components/Counter.tsx import { useState } from 'react' -//import style from './Counter.module.css' // optional, if you want a CSS module for reset button -import '../App.css' function Counter() { const [count, setCount] = useState(0) return (
- - - -

- Edit src/App.tsx and save to test HMR -

- -

- Click on the Vite and React logos to learn more -

- +
+ Template → +
+
) } diff --git a/src/pages/TemplatePage/Template.module.css b/src/pages/TemplatePage/Template.module.css deleted file mode 100644 index 8526661..0000000 --- a/src/pages/TemplatePage/Template.module.css +++ /dev/null @@ -1,4 +0,0 @@ -button.reset:hover { - background-color: yellow; -} - diff --git a/src/pages/TemplatePage/Template.tsx b/src/pages/TemplatePage/Template.tsx index 4cb3118..dc24adf 100644 --- a/src/pages/TemplatePage/Template.tsx +++ b/src/pages/TemplatePage/Template.tsx @@ -1,22 +1,9 @@ -import { useState } from 'react' -import { Link } from 'react-router' import Counter from '../../components/components.tsx' -import style from './Template.module.css' - -//this is your css file where you can style your buttons and such -//you can still use css parts from App.css, but also overwrite them function TemplatePage() { - - return ( <> - {/* here you link to the homepage, in App.tsx you can link new pages */} - - ) } diff --git a/vite.config.ts b/vite.config.ts index 8b0f57b..aa7de4f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,4 +4,9 @@ import react from '@vitejs/plugin-react' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + css: { + modules: { + localsConvention: "camelCase", + } + }, }) From d8ed8df9826b3de48a0b50fd7293577fb1a49eb3 Mon Sep 17 00:00:00 2001 From: Twirre Meulenbelt <43213592+TwirreM@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:56:34 +0200 Subject: [PATCH 5/5] feat: update robot interaction page This page is now fancier, shows messages streaming from the Control Backend. ref: N25B-164 --- src/App.tsx | 2 + src/pages/Home/Home.tsx | 1 + src/pages/Robot/Robot.tsx | 94 +++++++++++++++++++++++++++ src/pages/ServerComms/ServerComms.css | 0 src/pages/ServerComms/ServerComms.tsx | 80 ----------------------- 5 files changed, 97 insertions(+), 80 deletions(-) create mode 100644 src/pages/Robot/Robot.tsx delete mode 100644 src/pages/ServerComms/ServerComms.css delete mode 100644 src/pages/ServerComms/ServerComms.tsx diff --git a/src/App.tsx b/src/App.tsx index acec25d..803b84c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import { Routes, Route, Link } from 'react-router' import './App.css' import TemplatePage from './pages/TemplatePage/Template.tsx' import Home from './pages/Home/Home.tsx' +import Robot from './pages/Robot/Robot.tsx'; function App(){ return ( @@ -13,6 +14,7 @@ function App(){ } /> } /> + } /> diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 71f6b96..cb70de0 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -11,6 +11,7 @@ function Home() {
+ Robot Interaction → Template →
diff --git a/src/pages/Robot/Robot.tsx b/src/pages/Robot/Robot.tsx new file mode 100644 index 0000000..0038dd9 --- /dev/null +++ b/src/pages/Robot/Robot.tsx @@ -0,0 +1,94 @@ +import { useState, useEffect, useRef } from 'react' + +export default function Robot() { + const [message, setMessage] = useState(''); + + const [listening, setListening] = useState(false); + const [conversation, setConversation] = useState<{"role": "user" | "assistant", "content": string}[]>([]) + const conversationRef = useRef(null); + const [conversationIndex, setConversationIndex] = useState(0); + + const sendMessage = async () => { + try { + const response = await fetch("http://localhost:8000/message", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ message }), + }); + const data = await response.json(); + console.log(data); + } catch (error) { + console.error("Error sending message: ", error); + } + }; + + useEffect(() => { + const eventSource = new EventSource("http://localhost:8000/sse"); + + eventSource.onmessage = (event) => { + try { + const data = JSON.parse(event.data); + if ("voice_active" in data) setListening(data.voice_active); + if ("speech" in data) setConversation(conversation => [...conversation, {"role": "user", "content": data.speech}]); + if ("llm_response" in data) setConversation(conversation => [...conversation, {"role": "assistant", "content": data.llm_response}]); + } catch { + console.log("Unparsable SSE message:", event.data); + } + }; + + return () => { + eventSource.close(); + }; + }, [conversationIndex]); + + useEffect(() => { + if (!conversationRef || !conversationRef.current) return; + conversationRef.current.scrollTop = conversationRef.current.scrollHeight; + }, [conversation]); + + return ( + <> +

Robot interaction

+

Force robot speech

+
+ setMessage(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && sendMessage().then(() => setMessage(""))} + placeholder="Enter a message" + /> + +
+
+

Conversation

+

Listening {listening ? "🟢" : "🔴"}

+
+ {conversation.map((item, i) => ( +

{item["content"]}

+ ))} +
+
+ + +
+
+ + ); +} diff --git a/src/pages/ServerComms/ServerComms.css b/src/pages/ServerComms/ServerComms.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/pages/ServerComms/ServerComms.tsx b/src/pages/ServerComms/ServerComms.tsx deleted file mode 100644 index c16ec81..0000000 --- a/src/pages/ServerComms/ServerComms.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useState, useEffect } from 'react' -import { Link } from 'react-router' -//import Counter from '../../components/components.tsx' - - -//this is your css file where you can style your buttons and such -//you can still use css parts from App.css, but also overwrite them - -function ServerComms() { - const [message, setMessage] = useState(''); - const [sseMessage, setSseMessage] = useState(''); - const [spoken, setSpoken] = useState(""); - - const sendMessage = async () => { - try { - const response = await fetch("http://localhost:8000/message", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ message }), - }); - const data = await response.json(); - console.log(data); - } catch (error) { - console.error("Error sending message: ", error); - } - }; - - useEffect(() => { - const eventSource = new EventSource("http://localhost:8000/sse"); - - eventSource.onmessage = (event) => { - setSseMessage(event.data); - - try { - const data = JSON.parse(event.data); - if (data.speech) setSpoken(data.speech); - } catch {} - }; - - return () => { - eventSource.close(); - }; - }, []); - - return ( -
-
- setMessage(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && sendMessage().then(() => setMessage(""))} - placeholder="Enter a message" - /> - -
-
-

Message from Server (SSE):

-

{sseMessage}

-
-
-

Spoken text (SSE):

-

{spoken}

-
-
- {/* here you link to the homepage, in App.tsx you can link new pages */} - - -
-
- - - ); -} - -export default ServerComms \ No newline at end of file