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-11-20 - Accessible Visually Hidden Elements without Global Utilities
**Learning:** The `thrml` repository web app (`app/globals.css`) does not provide standard utility classes like `.sr-only` for visually hiding content (e.g., screen-reader captions) while keeping it accessible.
**Action:** When a screen-reader element needs to be visually hidden without breaking layout and no utility class exists, apply an explicit inline style pattern instead: `style={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', whiteSpace: 'nowrap', borderWidth: 0 }}`. This ensures accessibility guidelines are met within the constraints of the project architecture.
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

Recommending a verbose inline style for a common utility like 'visually hidden' content is suboptimal for maintainability. It is better to add a .sr-only utility class to app/globals.css once and reuse it across the project. This centralizes the logic and keeps the JSX clean. Additionally, the clip property is deprecated; clipPath: 'inset(50%)' is the modern standard.

Suggested change
**Action:** When a screen-reader element needs to be visually hidden without breaking layout and no utility class exists, apply an explicit inline style pattern instead: `style={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', whiteSpace: 'nowrap', borderWidth: 0 }}`. This ensures accessibility guidelines are met within the constraints of the project architecture.
**Action:** When a screen-reader element needs to be visually hidden, the preferred approach is to add a .sr-only utility class to app/globals.css. If inline styles must be used, apply a consistent pattern that avoids deprecated properties: style={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clipPath: 'inset(50%)', whiteSpace: 'nowrap', border: 0 }}. This ensures accessibility guidelines are met while maintaining code quality.

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>
);
};
76 changes: 76 additions & 0 deletions components/TelemetryTimeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'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 }}>
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 property is deprecated in favor of clip-path. For better future-proofing, use clipPath: 'inset(50%)'. Furthermore, using inline styles for this common pattern is hard to maintain. Consider adding a .sr-only class to app/globals.css instead.

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 }}>
<caption style={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clipPath: 'inset(50%)', whiteSpace: 'nowrap', border: 0 }}>

Telemetry event details including log ID, UTC time, event type, and prediction probability.
</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