Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 23 additions & 24 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -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; }
22 changes: 4 additions & 18 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
<html lang="pt-BR" className="h-full antialiased">
<body className="min-h-full">{children}</body>
</html>
);
}
67 changes: 1 addition & 66 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,4 @@
import Image from "next/image";

export default function Home() {
return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert h-5 w-[100px]"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the{" "}
<code className="rounded bg-black/[.06] px-1.5 py-0.5 font-mono text-[0.9em] dark:bg-white/[.08]">
page.tsx
</code>{" "}
file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert h-[14px] w-4"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={14}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</div>
</main>
</div>
);
return <section/>;
}
48 changes: 48 additions & 0 deletions lib/NTclient.ts
Original file line number Diff line number Diff line change
@@ -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<NT4Client | null>(null);
const data = useRef(new Map<string, Entry>());
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 };
}
2 changes: 1 addition & 1 deletion lib/nt4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@

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 = () => {
Expand All @@ -81,7 +81,7 @@
}

private onText(raw: string): void {
for (const msg of JSON.parse(raw) as { method: string; params: any }[]) {

Check failure on line 84 in lib/nt4.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type

Check failure on line 84 in lib/nt4.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
const p = msg.params;
if (msg.method === "announce") {
const t: Topic = { name: p.name, id: p.id, type: p.type, pubuid: p.pubid ?? this.published.get(p.name)?.pubuid ?? 0, properties: p.properties ?? {} };
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -25,4 +26,4 @@
"tailwindcss": "^4",
"typescript": "^5"
}
}
}
Loading