diff --git a/app/globals.css b/app/globals.css
index a2dc41e..c0fefd1 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,26 +1,25 @@
@import "tailwindcss";
-:root {
- --background: #ffffff;
- --foreground: #171717;
-}
-
-@theme inline {
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background: #0a0a0a;
- --foreground: #ededed;
- }
-}
-
-body {
- background: var(--background);
- color: var(--foreground);
- font-family: Arial, Helvetica, sans-serif;
-}
+:root { --paper:#EEF1F4; --ink:#14202B; --blue:#1F5FBF; --red:#D6362B; --amber:#E7A21B; --line:#C9D1DA; }
+@media (prefers-color-scheme: dark) { :root { --paper:#16202A; --ink:#E6ECF2; --line:#2E3C4A; } }
+body { background:var(--paper); color:var(--ink); font:14px/1.4 "Segoe UI", system-ui, sans-serif; }
+.bar { display:flex; flex-wrap:wrap; gap:10px; align-items:center; padding:10px 16px; border-bottom:3px solid var(--blue); position:sticky; top:0; background:var(--paper); z-index:2; }
+.bar h1 { font-size:18px; font-weight:700; margin-right:8px; }
+input { padding:6px 8px; border:1px solid var(--line); background:transparent; color:inherit; border-radius:3px; }
+.target { width:230px; } .filter { margin-left:auto; width:200px; }
+button { cursor:pointer; }
+.go { padding:6px 14px; background:var(--blue); color:#fff; border-radius:3px; }
+.pill { padding:3px 10px; border-radius:99px; font-weight:600; }
+.pill.on { background:var(--blue); color:#fff; } .pill.off { background:var(--amber); color:#14202B; }
+main { padding:16px; }
+.pins { display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:12px; margin-bottom:20px; }
+.card { border:1px solid var(--line); border-left:4px solid var(--red); padding:10px 12px; border-radius:3px; }
+.big { font-size:34px; font-weight:600; font-variant-numeric:tabular-nums; margin:4px 0; word-break:break-all; }
+.empty { color:#7A8794; }
+.link { text-align:left; font-weight:600; }
+.link:hover { color:var(--blue); text-decoration:underline; }
+table { width:100%; border-collapse:collapse; }
+th { text-align:left; padding:6px 8px; border-bottom:2px solid var(--ink); }
+td { padding:4px 8px; border-bottom:1px solid var(--line); vertical-align:middle; }
+td.val { font-family:ui-monospace, Consolas, monospace; font-variant-numeric:tabular-nums; cursor:pointer; max-width:420px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
+:focus-visible { outline:2px solid var(--amber); outline-offset:2px; }
diff --git a/app/layout.tsx b/app/layout.tsx
index 9852c15..7041472 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,29 +1,15 @@
import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
-const geistSans = Geist({
- variable: "--font-geist-sans",
- subsets: ["latin"],
-});
-
-const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
- subsets: ["latin"],
-});
-
export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
+ title: "FRC Dash",
+ description: "Dashboard NetworkTables 4 para FRC",
};
export default function RootLayout({ children }: LayoutProps<"/">) {
return (
-
-
{children}
+
+ {children}
);
}
diff --git a/app/page.tsx b/app/page.tsx
index c887311..96d0416 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,69 +1,4 @@
-import Image from "next/image";
export default function Home() {
- return (
-
-
-
-
-
- To get started, edit the{" "}
-
- page.tsx
- {" "}
- file.
-
-
- Looking for a starting point or more instructions? Head over to{" "}
-
- Templates
- {" "}
- or the{" "}
-
- Learning
- {" "}
- center.
-
-
-
-
-
- );
+ return ;
}
diff --git a/lib/NTclient.ts b/lib/NTclient.ts
new file mode 100644
index 0000000..ea10232
--- /dev/null
+++ b/lib/NTclient.ts
@@ -0,0 +1,48 @@
+"use client";
+import { useCallback, useEffect, useRef, useState } from "react";
+import { NT4Client } from "./nt4";
+
+export interface Entry { name: string; type: string; value: unknown; hist: number[]; }
+const HIST = 200;
+
+/** Mantém os dados do NT em um Map mutável e re-renderiza no máximo 1x por frame. */
+export function NTclient() {
+ const client = useRef(null);
+ const data = useRef(new Map());
+ const raf = useRef(0);
+ const [connected, setConnected] = useState(false);
+ const [, setTick] = useState(0);
+
+ const bump = () => {
+ if (raf.current) return;
+ raf.current = requestAnimationFrame(() => { raf.current = 0; setTick((t) => t + 1); });
+ };
+
+ const connect = useCallback((host: string) => {
+ client.current?.close();
+ data.current.clear();
+ setConnected(false);
+ client.current = new NT4Client(host, {
+ onAnnounce: (t) => {
+ if (!data.current.has(t.name)) data.current.set(t.name, { name: t.name, type: t.type, value: undefined, hist: [] });
+ bump();
+ },
+ onUnannounce: (t) => { data.current.delete(t.name); bump(); },
+ onData: (t, _ts, v) => {
+ const e = data.current.get(t.name);
+ if (!e) return;
+ e.value = v;
+ if (typeof v === "number") { e.hist.push(v); if (e.hist.length > HIST) e.hist.shift(); }
+ bump();
+ },
+ onConnect: () => { setConnected(true); client.current?.subscribeAll(0.05); },
+ onDisconnect: () => setConnected(false),
+ });
+ }, []);
+
+ const set = useCallback((name: string, value: unknown) => client.current?.set(name, value), []);
+
+ useEffect(() => () => { client.current?.close(); cancelAnimationFrame(raf.current); }, []);
+
+ return { connected, entries: data.current, connect, set };
+}
diff --git a/lib/nt4.ts b/lib/nt4.ts
index 4e56ec5..28a47e9 100644
--- a/lib/nt4.ts
+++ b/lib/nt4.ts
@@ -60,7 +60,7 @@ export class NT4Client {
private connect(): void {
if (this.closed) return;
- const url = `ws://${this.host}:5810/nt/FRCDash_${this.clientId}`;
+ const url = `ws://${this.host}:5810/nt/megadash_${this.clientId}`;
const ws = new WebSocket(url, "networktables.first.wpi.edu");
ws.binaryType = "arraybuffer";
ws.onopen = () => {
diff --git a/package.json b/package.json
index ebd3023..aa31544 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"lint": "eslint"
},
"dependencies": {
+ "@msgpack/msgpack": "^3.0.0",
"@ntcore-ts/client": "^1.0.0",
"@ntcore-ts/react": "^1.0.0",
"next": "16.3.8",
@@ -25,4 +26,4 @@
"tailwindcss": "^4",
"typescript": "^5"
}
-}
+}
\ No newline at end of file