diff --git a/package.json b/package.json
index cb4c39e..31317c7 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,10 @@
"@tanstack/react-router": "^1.136.18",
"@tanstack/react-router-devtools": "^1.136.18",
"clsx": "^2.1.1",
+ "konva": "^10.0.11",
"react": "^19.2.0",
- "react-dom": "^19.2.0"
+ "react-dom": "^19.2.0",
+ "react-konva": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
diff --git a/src/features/cameras/components/CameraGrid.tsx b/src/features/cameras/components/CameraGrid.tsx
new file mode 100644
index 0000000..eb5c2b3
--- /dev/null
+++ b/src/features/cameras/components/CameraGrid.tsx
@@ -0,0 +1,11 @@
+import VideoFeedCard from "./VideoFeedCard";
+
+const CameraGrid = () => {
+ return (
+
+
+
+ );
+};
+
+export default CameraGrid;
diff --git a/src/features/cameras/components/VideoFeedCard.tsx b/src/features/cameras/components/VideoFeedCard.tsx
new file mode 100644
index 0000000..2d5dc8c
--- /dev/null
+++ b/src/features/cameras/components/VideoFeedCard.tsx
@@ -0,0 +1,13 @@
+import Card from "../../../ui/Card";
+
+import VideoFeedGridPainter from "./VideoFeedGridPainter";
+
+const VideoFeedCard = () => {
+ return (
+
+
+
+ );
+};
+
+export default VideoFeedCard;
diff --git a/src/features/cameras/components/VideoFeedGridPainter.tsx b/src/features/cameras/components/VideoFeedGridPainter.tsx
new file mode 100644
index 0000000..3ecdba0
--- /dev/null
+++ b/src/features/cameras/components/VideoFeedGridPainter.tsx
@@ -0,0 +1,75 @@
+import { useRef, type RefObject } from "react";
+import { Stage, Layer, Image, Rect } from "react-konva";
+import { useCreateVideoSnapshot } from "../hooks/useGetvideoSnapshots";
+
+const VideoFeedGridPainter = () => {
+ const { latestBitmapRef } = useCreateVideoSnapshot();
+ const isDrawing = useRef(false);
+
+ const rows = 100;
+ const cols = 100;
+ const size = 10;
+ const gap = 0;
+
+ const squares = [];
+ for (let row = 0; row < rows; row++) {
+ for (let col = 0; col < cols; col++) {
+ squares.push(
+ ,
+ );
+ }
+ }
+
+ const getCoords = (e) => {
+ isDrawing.current = true;
+ };
+
+ const handleMouseMove = (e) => {
+ if (!isDrawing.current) {
+ return;
+ }
+ const pos = e.target.getStage().getPointerPosition();
+ console.log(pos);
+ };
+
+ const handleMouseUp = () => {
+ isDrawing.current = false;
+ };
+
+ const draw = (bmp: RefObject) => {
+ if (!bmp || !bmp.current) {
+ return;
+ } else {
+ const frame = bmp.current;
+ return frame;
+ }
+ };
+
+ return (
+
+
+
+
+ {squares}
+
+ );
+};
+
+export default VideoFeedGridPainter;
diff --git a/src/features/cameras/hooks/useGetVideoFeed.ts b/src/features/cameras/hooks/useGetVideoFeed.ts
new file mode 100644
index 0000000..bedbaeb
--- /dev/null
+++ b/src/features/cameras/hooks/useGetVideoFeed.ts
@@ -0,0 +1,22 @@
+import { useQuery } from "@tanstack/react-query";
+
+const getfeed = async () => {
+ const response = await fetch(`http://100.115.148.59/TargetDetectionColour-preview`, {
+ signal: AbortSignal.timeout(300000),
+ cache: "no-store",
+ });
+ if (!response.ok) {
+ throw new Error(`Cannot reach endpoint (${response.status})`);
+ }
+ return response.blob();
+};
+
+export const useGetVideoFeed = () => {
+ const videoQuery = useQuery({
+ queryKey: ["getfeed"],
+ queryFn: getfeed,
+ refetchInterval: 500,
+ });
+
+ return { videoQuery };
+};
diff --git a/src/features/cameras/hooks/useGetvideoSnapshots.ts b/src/features/cameras/hooks/useGetvideoSnapshots.ts
new file mode 100644
index 0000000..68a6946
--- /dev/null
+++ b/src/features/cameras/hooks/useGetvideoSnapshots.ts
@@ -0,0 +1,25 @@
+import { useEffect, useRef } from "react";
+import { useGetVideoFeed } from "./useGetVideoFeed";
+
+export const useCreateVideoSnapshot = () => {
+ const latestBitmapRef = useRef(null);
+ const { videoQuery } = useGetVideoFeed();
+
+ const snapShot = videoQuery?.data;
+
+ useEffect(() => {
+ async function createBitmap() {
+ if (!snapShot) return;
+
+ try {
+ const bitmap = await createImageBitmap(snapShot);
+ latestBitmapRef.current = bitmap;
+ } catch (error) {
+ console.log(error);
+ }
+ }
+ createBitmap();
+ }, [snapShot]);
+
+ return { latestBitmapRef };
+};
diff --git a/src/routes/baywatch.tsx b/src/routes/baywatch.tsx
index 8ec01dd..9271b97 100644
--- a/src/routes/baywatch.tsx
+++ b/src/routes/baywatch.tsx
@@ -1,9 +1,15 @@
-import { createFileRoute } from '@tanstack/react-router'
+import { createFileRoute } from "@tanstack/react-router";
+import CameraGrid from "../features/cameras/components/CameraGrid";
-export const Route = createFileRoute('/baywatch')({
+export const Route = createFileRoute("/baywatch")({
component: RouteComponent,
-})
+});
function RouteComponent() {
- return Hello "/baywatch"!
+ return (
+
+
Cameras
+
+
+ );
}
diff --git a/src/ui/Header.tsx b/src/ui/Header.tsx
index c1934dd..31d2d2a 100644
--- a/src/ui/Header.tsx
+++ b/src/ui/Header.tsx
@@ -18,7 +18,7 @@ const Header = () => {
- Baywatch
+ Cameras
Output
diff --git a/yarn.lock b/yarn.lock
index f9e9fcd..2c31fb9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1011,6 +1011,16 @@
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c"
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
+"@types/react-reconciler@^0.28.9":
+ version "0.28.9"
+ resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.9.tgz#d24b4864c384e770c83275b3fe73fba00269c83b"
+ integrity sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==
+
+"@types/react-reconciler@^0.32.2":
+ version "0.32.3"
+ resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.32.3.tgz#eb4b346f367f29f07628032934d30a4f3f9eaba7"
+ integrity sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA==
+
"@types/react@^19.2.5":
version "19.2.6"
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.6.tgz#d27db1ff45012d53980f5589fda925278e1249ca"
@@ -1726,6 +1736,13 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+its-fine@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/its-fine/-/its-fine-2.0.0.tgz#a90b18a3ee4c211a1fb6faac2abcc2b682ce1f21"
+ integrity sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==
+ dependencies:
+ "@types/react-reconciler" "^0.28.9"
+
jiti@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92"
@@ -1775,6 +1792,11 @@ keyv@^4.5.4:
dependencies:
json-buffer "3.0.1"
+konva@^10.0.11:
+ version "10.0.11"
+ resolved "https://registry.yarnpkg.com/konva/-/konva-10.0.11.tgz#f63d3422625d13513094b646a2f09359d644542a"
+ integrity sha512-h0O6YNrwdgfb76kzkiMIqkyufUxE6GPcNwJZhYalnZ5qnYBEuxSRk62fQwtJqygGP5hdZKzJs2ea1ivVP+zetw==
+
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@@ -2044,6 +2066,23 @@ react-dom@^19.2.0:
dependencies:
scheduler "^0.27.0"
+react-konva@^19.2.0:
+ version "19.2.0"
+ resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-19.2.0.tgz#b4cc5d73cd6d642569e4df36a0139996c3dcf8e6"
+ integrity sha512-Ofifq/rdNvff50+Lj8x86WSfoeQDvdysOlsXMMrpD2uWmDxUPrEYSRLt27iCfdovQZL6xinKRpX9VaL9xDwXDQ==
+ dependencies:
+ "@types/react-reconciler" "^0.32.2"
+ its-fine "^2.0.0"
+ react-reconciler "0.33.0"
+ scheduler "0.27.0"
+
+react-reconciler@0.33.0:
+ version "0.33.0"
+ resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.33.0.tgz#9dd20208d45baa5b0b4701781f858236657f15e1"
+ integrity sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==
+ dependencies:
+ scheduler "^0.27.0"
+
react-refresh@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062"
@@ -2125,7 +2164,7 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
-scheduler@^0.27.0:
+scheduler@0.27.0, scheduler@^0.27.0:
version "0.27.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==