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
57 changes: 57 additions & 0 deletions .github/workflows/deploy-ui.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: deploy-ui

on:
push:
branches: [master]
paths:
- "ui/**"
- "src/crgutils/**"
- "pyproject.toml"
- ".github/workflows/deploy-ui.yaml"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# Only one deployment at a time; cancel in-progress runs on new push
concurrency:
group: pages
cancel-in-progress: true

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Install Bun
uses: oven-sh/setup-bun@v2

- name: Install UI dependencies
run: cd ui && bun install

- name: Build wheel + UI
run: |
uv build --out-dir ui/dist_pkg
cd ui && bun run build
env:
# Project pages are served at https://<org>.github.io/crgutils/
NEXT_PUBLIC_BASE_PATH: /crgutils

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: ui/out

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
.tmp/

# Generated UI build artifacts (run `just build-ui` to regenerate)
ui/out/
ui/dist_pkg/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
Expand All @@ -17,6 +21,7 @@ downloads/
eggs/
.eggs/
lib/
!ui/lib/
lib64/
parts/
sdist/
Expand Down
15 changes: 15 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ uv run python demo/eval_options.py -t 5 # run only test 5

All Python execution uses `uv run`. All package additions use `uv add`.

## Web UI

```bash
# Build wheel + Next.js static export (output: ui/out/)
just build-ui

# Run the development server (hot-reload)
just dev-ui
```

The UI is a static Next.js SPA hosted on GitHub Pages at `https://time-integral.github.io/crgutils/`.
It runs crgutils entirely in the browser via Pyodide (no server-side Python).
Build pipeline: `uv build → prebuild.mjs (copies wheel to public/_pkg/) → next build`.
CI deploys automatically on pushes to `master` that touch `ui/` or `src/crgutils/` (`.github/workflows/deploy-ui.yaml`).

## Architecture

The library is a pure-Python port of the OpenCRG C API (`c-api/` in `.tmp/OpenCRG-v2.0.0/`). The C source in `.tmp/` is the authoritative reference for algorithm semantics.
Expand Down
19 changes: 19 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ bump-major:
uv run bump-my-version bump major
uv lock

# Sync UI deps, build the crgutils wheel, then the Next.js static export (output: ui/out/)
build-ui:
cd ui && bun install --frozen-lockfile
uv build --out-dir ui/dist_pkg
cd ui && bun run build

# Sync UI deps, refresh the bundled wheel, then run Next.js dev server with hot-reload
dev-ui:
cd ui && bun install --frozen-lockfile
uv build --out-dir ui/dist_pkg
cd ui && node scripts/prebuild.mjs
cd ui && bun run dev

# Build then serve the static output locally (mirrors GitHub Pages)
preview-ui:
uv build --out-dir ui/dist_pkg
cd ui && bun run build
cd ui/out && python3 -m http.server 3000

# Dry-run: preview what a bump would do without changing anything
dry-patch:
uv run bump-my-version bump patch --dry-run --allow-dirty --verbose
Expand Down
45 changes: 45 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# build artifacts (generated by `just build-ui`)
/public/_pkg/
/dist_pkg/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
5 changes: 5 additions & 0 deletions ui/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->
1 change: 1 addition & 0 deletions ui/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
36 changes: 36 additions & 0 deletions ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
Binary file added ui/app/favicon.ico
Binary file not shown.
132 changes: 132 additions & 0 deletions ui/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";

@custom-variant dark (&:is(.dark *));

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-ibm-plex-sans);
--font-mono: var(--font-ibm-plex-mono);
--font-heading: var(--font-ibm-plex-sans);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
}

:root {
--font-ibm-plex-sans: "IBM Plex Sans", "Avenir Next", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
--font-ibm-plex-mono: "IBM Plex Mono", "SFMono-Regular", "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", monospace;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}

.dark {
--background: oklch(0.09 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}

@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
html {
@apply font-sans;
}
}
37 changes: 37 additions & 0 deletions ui/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Metadata } from "next";
import { IBM_Plex_Mono, IBM_Plex_Sans } from "next/font/google";
import "./globals.css";

const ibmPlexSans = IBM_Plex_Sans({
subsets: ["latin"],
weight: ["400", "500", "600", "700"],
variable: "--font-ibm-plex-sans",
display: "swap",
});

const ibmPlexMono = IBM_Plex_Mono({
subsets: ["latin"],
weight: ["400", "500", "600"],
variable: "--font-ibm-plex-mono",
display: "swap",
});

export const metadata: Metadata = {
title: "crgutils",
description: "ASAM OpenCRG file inspector",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
lang="en"
className={`${ibmPlexSans.variable} ${ibmPlexMono.variable} h-full antialiased dark`}
>
<body className="h-full overflow-hidden bg-zinc-950 text-zinc-50 flex flex-col">{children}</body>
</html>
);
}
Loading
Loading