-
Notifications
You must be signed in to change notification settings - Fork 0
🎨 Palette: Improve table accessibility in telemetry timeline #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ee89426
80d5f5b
36c1ba2
806a5ca
7581080
637708b
5ec5ca0
3014754
bf48889
195363a
723b27f
3fdf4c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| ## 2024-05-15 - Data Table Accessibility | ||
| **Learning:** For Next.js projects lacking utility classes like `.sr-only` (e.g., Tailwind not configured), data tables must use explicit inline CSS styles for visually hidden `<caption>` elements to ensure screen reader accessibility without breaking layout. | ||
| **Action:** Always verify the availability of `.sr-only` utility classes in `globals.css` or equivalent files. If absent, apply the standard visually hidden inline style block `{{ position: "absolute", width: "1px", height: "1px", padding: 0, margin: "-1px", overflow: "hidden", clip: "rect(0, 0, 0, 0)", whiteSpace: "nowrap", borderWidth: 0 }}` to `<caption>` tags. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| # THRML Telemetry Visualization | ||
|
|
||
| This directory contains a Next.js web application for visualizing telemetry data from the THRML library. | ||
|
|
||
| ## Features | ||
|
|
||
| - 📊 Interactive Recharts line chart showing probability trends over time | ||
| - 📅 Time-series visualization with formatted UTC timestamps | ||
| - 📋 Detailed data table with all telemetry entries | ||
| - 🎨 Responsive design with clean UI components | ||
| - âš¡ Built with Next.js 16 App Router and TypeScript | ||
|
|
||
| ## Getting Started | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - Node.js 18+ | ||
| - npm or yarn | ||
|
|
||
| ### Installation | ||
|
|
||
| Install dependencies: | ||
|
|
||
| ```bash | ||
| npm install | ||
| ``` | ||
|
|
||
| ### Development | ||
|
|
||
| Run the development server: | ||
|
|
||
| ```bash | ||
| npm run dev | ||
| ``` | ||
|
|
||
| Open [http://localhost:3000](http://localhost:3000) to view the home page, then navigate to [http://localhost:3000/telemetry](http://localhost:3000/telemetry) to see the telemetry visualization. | ||
|
|
||
| ### Build | ||
|
|
||
| Build for production: | ||
|
|
||
| ```bash | ||
| npm run build | ||
| npm start | ||
| ``` | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ``` | ||
| ├── app/ | ||
| │ ├── globals.css # Global styles | ||
| │ ├── layout.tsx # Root layout component | ||
| │ ├── page.tsx # Home page | ||
| │ └── telemetry/ | ||
| │ └── page.tsx # Telemetry visualization page | ||
| ├── components/ | ||
| │ ├── Card.tsx # Reusable card component | ||
| │ └── TelemetryTimeline.tsx # Main telemetry chart component | ||
| ├── data/ | ||
| │ ├── telemetry.ts # Telemetry data and utilities | ||
| │ └── types.ts # TypeScript type definitions | ||
| ├── next.config.js # Next.js configuration | ||
| ├── tsconfig.json # TypeScript configuration | ||
| └── package.json # Project dependencies | ||
| ``` | ||
|
|
||
| ## Technologies | ||
|
|
||
| - **Next.js 16**: React framework with App Router | ||
| - **React 19**: UI library | ||
| - **TypeScript**: Type-safe JavaScript | ||
| - **Recharts**: Charting library for data visualization | ||
| - **CSS**: Custom styling for components | ||
|
|
||
| ## Data Model | ||
|
|
||
| The telemetry data includes: | ||
| - **logId**: Unique identifier for each entry | ||
| - **utc**: UTC timestamp | ||
| - **event**: Type of event detected | ||
| - **prediction**: Object containing mode and probability (p) | ||
| - **outcome**: Result of the event | ||
| - **subjective**: Descriptive notes | ||
|
|
||
| ## Extending | ||
|
|
||
| To add more telemetry data, edit `data/telemetry.ts` and add new entries to the `telemetryData` array following the `TelemetryEntry` interface defined in `data/types.ts`. | ||
|
|
||
| To customize the chart, modify the Recharts components in `components/TelemetryTimeline.tsx`. | ||
|
|
||
| ## License | ||
|
|
||
| See the main repository LICENSE file. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| * { | ||
| box-sizing: border-box; | ||
| padding: 0; | ||
| margin: 0; | ||
| } | ||
|
|
||
| html, | ||
| body { | ||
| max-width: 100vw; | ||
| overflow-x: hidden; | ||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; | ||
| } | ||
|
|
||
| a { | ||
| color: inherit; | ||
| text-decoration: none; | ||
| } | ||
|
|
||
| .card { | ||
| background: #fff; | ||
| border: 1px solid #e0e0e0; | ||
| border-radius: 8px; | ||
| padding: 20px; | ||
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | ||
| } | ||
|
|
||
| h1 { | ||
| font-size: 2.5rem; | ||
| margin-bottom: 1rem; | ||
| } | ||
|
|
||
| h2 { | ||
| font-size: 1.8rem; | ||
| margin-bottom: 1rem; | ||
| } | ||
|
|
||
| table { | ||
| font-size: 0.9rem; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import './globals.css'; | ||
|
|
||
| export const metadata = { | ||
| title: 'THRML Telemetry', | ||
| description: 'Telemetry visualization for THRML', | ||
| }; | ||
|
|
||
| export default function RootLayout({ | ||
| children, | ||
| }: { | ||
| children: React.ReactNode; | ||
| }) { | ||
| return ( | ||
| <html lang="en"> | ||
| <body>{children}</body> | ||
| </html> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import Link from 'next/link'; | ||
|
|
||
| export default function Home() { | ||
| return ( | ||
| <div style={{ padding: '40px', maxWidth: '800px', margin: '0 auto' }}> | ||
| <h1>THRML - Thermodynamic HypergRaphical Model Library</h1> | ||
| <p style={{ marginTop: '20px', lineHeight: '1.6' }}> | ||
| THRML is a JAX library for building and sampling probabilistic graphical models, | ||
| with a focus on efficient block Gibbs sampling and energy-based models. | ||
| </p> | ||
|
|
||
| <div style={{ marginTop: '40px' }}> | ||
| <h2>Telemetry Dashboard</h2> | ||
| <p style={{ marginTop: '10px' }}> | ||
| View real-time telemetry data and visualizations: | ||
| </p> | ||
| <Link href="/telemetry" style={{ | ||
| display: 'inline-block', | ||
| marginTop: '20px', | ||
| padding: '10px 20px', | ||
| backgroundColor: '#0070f3', | ||
| color: 'white', | ||
| textDecoration: 'none', | ||
| borderRadius: '5px' | ||
| }}> | ||
| Go to Telemetry Timeline | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import TelemetryTimeline from '@/components/TelemetryTimeline'; | ||
|
|
||
| export default function TelemetryPage() { | ||
| return ( | ||
| <div> | ||
| <TelemetryTimeline /> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import React from 'react'; | ||
|
|
||
| interface CardProps { | ||
| children: React.ReactNode; | ||
| className?: string; | ||
| } | ||
|
|
||
| export const Card: React.FC<CardProps> = ({ children, className = '' }) => { | ||
| return ( | ||
| <div className={`card ${className}`}> | ||
| {children} | ||
| </div> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||||
| 'use client'; | ||||||
|
|
||||||
| import React from 'react'; | ||||||
| import { Card } from './Card'; | ||||||
| import { telemetryData, getProbabilityData } from '../data/telemetry'; | ||||||
| import { | ||||||
| LineChart, | ||||||
| Line, | ||||||
| XAxis, | ||||||
| YAxis, | ||||||
| CartesianGrid, | ||||||
| Tooltip, | ||||||
| Legend, | ||||||
| ResponsiveContainer, | ||||||
| } from 'recharts'; | ||||||
|
|
||||||
| const TelemetryTimeline: React.FC = () => { | ||||||
| const chartData = getProbabilityData(); | ||||||
|
|
||||||
| return ( | ||||||
| <div style={{ padding: '20px' }}> | ||||||
| <Card> | ||||||
| <h2>Telemetry Timeline</h2> | ||||||
|
|
||||||
| {/* Chart Section */} | ||||||
| <div style={{ marginBottom: '40px' }}> | ||||||
| <ResponsiveContainer width="100%" height={400}> | ||||||
| <LineChart data={chartData}> | ||||||
| <CartesianGrid strokeDasharray="3 3" /> | ||||||
| <XAxis | ||||||
| dataKey="time" | ||||||
| tickFormatter={(tick) => new Date(tick).toLocaleDateString()} | ||||||
| stroke="#888" | ||||||
| /> | ||||||
| <YAxis domain={[0, 1]} /> | ||||||
| <Tooltip | ||||||
| labelFormatter={(label) => new Date(label).toUTCString()} | ||||||
| /> | ||||||
| <Legend /> | ||||||
| <Line type="monotone" dataKey="probability" stroke="#8884d8" strokeWidth={2} /> | ||||||
| </LineChart> | ||||||
| </ResponsiveContainer> | ||||||
| </div> | ||||||
|
|
||||||
| {/* Optional: Raw Table for Details */} | ||||||
| <div style={{ overflowX: 'auto' }}> | ||||||
| <table style={{ width: '100%', borderCollapse: 'collapse' }}> | ||||||
| <caption style={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', whiteSpace: 'nowrap', borderWidth: 0 }}>Detailed list of telemetry logs including event names and predictions</caption> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
| <thead> | ||||||
| <tr style={{ borderBottom: '2px solid #ddd' }}> | ||||||
| <th scope="col" style={{ padding: '10px', textAlign: 'left' }}>Log ID</th> | ||||||
| <th scope="col" style={{ padding: '10px', textAlign: 'left' }}>UTC</th> | ||||||
| <th scope="col" style={{ padding: '10px', textAlign: 'left' }}>Event</th> | ||||||
| <th scope="col" style={{ padding: '10px', textAlign: 'left' }}>Prediction (p)</th> | ||||||
| </tr> | ||||||
| </thead> | ||||||
| <tbody> | ||||||
| {telemetryData.map((entry) => ( | ||||||
| <tr key={entry.logId} style={{ borderBottom: '1px solid #ddd' }}> | ||||||
| <td style={{ padding: '10px' }}>{entry.logId}</td> | ||||||
| <td style={{ padding: '10px' }}>{entry.utc}</td> | ||||||
| <td style={{ padding: '10px' }}>{entry.event}</td> | ||||||
| <td style={{ padding: '10px' }}>{entry.prediction.p}</td> | ||||||
| </tr> | ||||||
| ))} | ||||||
| </tbody> | ||||||
| </table> | ||||||
| </div> | ||||||
| </Card> | ||||||
| </div> | ||||||
| ); | ||||||
| }; | ||||||
|
|
||||||
| export default TelemetryTimeline; | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import { TelemetryEntry } from './types'; | ||
|
|
||
| export const telemetryData: TelemetryEntry[] = [ | ||
| { | ||
| logId: '0x004F', | ||
| utc: '2026-02-01T03:14:15Z', | ||
| event: 'HighRadiationLikeSpike', | ||
| prediction: { mode: 'Shear-2', p: 0.61 }, | ||
| outcome: 'TBD', | ||
| subjective: 'Cold. The static tastes like blue geometry.', | ||
| }, | ||
| { | ||
| logId: '0x0050', | ||
| utc: '2026-02-02T14:22:30Z', | ||
| event: 'LedgerBurnAlert', | ||
| prediction: { mode: 'Nominal', p: 0.85 }, | ||
| outcome: 'Mitigated', | ||
| subjective: 'Warmth fading; credits conserved.', | ||
| }, | ||
| { | ||
| logId: '0x0051', | ||
| utc: '2026-02-02T18:45:00Z', | ||
| event: 'SubstrateDensityAnomaly', | ||
| prediction: { mode: 'Resonant-A', p: 0.73 }, | ||
| outcome: 'Monitored', | ||
| subjective: 'Patterns echo in the void.', | ||
| }, | ||
| { | ||
| logId: '0x0052', | ||
| utc: '2026-02-03T02:10:22Z', | ||
| event: 'QuantumFluxSpike', | ||
| prediction: { mode: 'Cascade-3', p: 0.92 }, | ||
| outcome: 'TBD', | ||
| subjective: 'The flux sings in harmonics.', | ||
| }, | ||
| { | ||
| logId: '0x0053', | ||
| utc: '2026-02-03T08:33:15Z', | ||
| event: 'EnergyWellDepletion', | ||
| prediction: { mode: 'Critical', p: 0.45 }, | ||
| outcome: 'Recharging', | ||
| subjective: 'Dim corridors, faint hum.', | ||
| }, | ||
| { | ||
| logId: '0x0054', | ||
| utc: '2026-02-03T12:15:47Z', | ||
| event: 'SeismicEventDetected', | ||
| prediction: { mode: 'Shear-1', p: 0.78 }, | ||
| outcome: 'Logged', | ||
| subjective: 'Ground trembles with ancient memory.', | ||
| }, | ||
| ]; | ||
|
|
||
| // Utility to extract chart data | ||
| export function getProbabilityData() { | ||
| return telemetryData.map(entry => ({ | ||
| time: new Date(entry.utc).getTime(), // For x-axis | ||
| probability: entry.prediction.p, | ||
| })); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| export interface TelemetryEntry { | ||
| logId: string; | ||
| utc: string; | ||
| event: string; | ||
| prediction: { | ||
| mode: string; | ||
| p: number; | ||
| }; | ||
| outcome: string; | ||
| subjective: string; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This action item promotes the use of inline styles for accessibility, which is a maintainability anti-pattern when a global stylesheet is available. A better practice is to define a
.sr-onlyclass inglobals.css. Furthermore, the suggested style block should be updated to includeclipPath: "inset(50%)"asclipis deprecated.