Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .Jules/palette.md
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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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-only class in globals.css. Furthermore, the suggested style block should be updated to include clipPath: "inset(50%)" as clip is deprecated.

13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,15 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.vscode/
.vscode/

# Next.js
/.next/
/out/
next-env.d.ts

# Node.js
/node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
93 changes: 93 additions & 0 deletions WEB_README.md
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.
39 changes: 39 additions & 0 deletions app/globals.css
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;
}
18 changes: 18 additions & 0 deletions app/layout.tsx
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>
);
}
31 changes: 31 additions & 0 deletions app/page.tsx
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>
);
}
9 changes: 9 additions & 0 deletions app/telemetry/page.tsx
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>
);
}
14 changes: 14 additions & 0 deletions components/Card.tsx
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>
);
};
74 changes: 74 additions & 0 deletions components/TelemetryTimeline.tsx
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>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The clip CSS property is deprecated. For better compatibility with modern browsers and adherence to current standards, it is recommended to use clipPath: 'inset(50%)' alongside it. Additionally, consider defining a reusable .sr-only class in app/globals.css instead of using verbose inline styles to improve maintainability and keep the JSX clean.

Suggested change
<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>
<caption style={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', clipPath: 'inset(50%)', whiteSpace: 'nowrap', borderWidth: 0 }}>Detailed list of telemetry logs including event names and predictions</caption>

<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;
60 changes: 60 additions & 0 deletions data/telemetry.ts
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,
}));
}
11 changes: 11 additions & 0 deletions data/types.ts
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;
}
Loading
Loading