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 ( -
-
- Next.js logo -
-

- To get started, edit the{" "} - - page.tsx - {" "} - file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
-
-
- ); + 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