Skip to content

Commit d41bc88

Browse files
KebanFiruclaude
andcommitted
feat: migrate blog storage to Supabase
Replace file-based KV storage with Supabase (PostgreSQL) for blog post persistence. Each CRUD operation issues a direct SQL query via @supabase/supabase-js. Adds CI secrets for the new database env vars. Required env vars: SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 29d86dd commit d41bc88

122 files changed

Lines changed: 12314 additions & 4943 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pipeline.yaml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ on:
99
- main
1010

1111
jobs:
12+
check-lint:
13+
name: Check lint
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v6
17+
18+
- uses: actions/setup-node@v6
19+
with:
20+
node-version: 22
21+
22+
- run: npm install > /dev/null 2>&1
23+
24+
- run: npm run lint > /dev/null 2>&1
25+
1226
unit-tests:
1327
name: Jest unit tests
1428
runs-on: ubuntu-latest
@@ -26,7 +40,7 @@ jobs:
2640
deploy:
2741
name: Deploy to Vercel
2842
runs-on: ubuntu-latest
29-
needs: [unit-tests]
43+
needs: [unit-tests, check-lint]
3044
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
3145

3246
steps:
@@ -44,5 +58,11 @@ jobs:
4458
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
4559

4660
- run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} > /dev/null 2>&1
61+
env:
62+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
63+
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
4764

4865
- run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} > /dev/null 2>&1
66+
env:
67+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
68+
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ pnpm-lock.yaml
3636
# vercel
3737
.vercel
3838

39-
/data/posts.json
40-
4139
# typescript
4240
*.tsbuildinfo
4341
next-env.d.ts

README.md

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ It is designed for education, experimentation, and fast visual iteration.
1414
- Turing Machines: deterministic and nondeterministic, single-tape and multi-tape transitions
1515
- Mealy Machines
1616
- Moore Machines
17+
- GNFA (Generalized NFA) — for FA → regex extraction
18+
- CFG (Context-Free Grammar) — skeleton extraction from saved automata
1719

1820
## What You Can Do
1921

@@ -36,10 +38,10 @@ It is designed for education, experimentation, and fast visual iteration.
3638

3739
### Conversion and Generation Tools
3840

39-
- Regex conversion tool for finite automata workflows
40-
- NFA to DFA conversion for selected FA models
41-
- FA to GNFA generation
42-
- CFG generation and grammar-oriented artifacts from selected models
41+
- Regex → NFA via Thompson's construction
42+
- NFA → minimal DFA via subset construction and minimization
43+
- FA GNFA for regular expression extraction via state elimination
44+
- CFG skeleton generation from saved finite automata
4345

4446
### Simulation and Visual Feedback
4547

@@ -54,6 +56,53 @@ It is designed for education, experimentation, and fast visual iteration.
5456
- Regex summary views for finite automata cards
5557
- Machine summaries (model-specific tuple/style summaries)
5658

59+
## AI Accessibility — MCP Server
60+
61+
The simulator exposes an HTTP [Model Context Protocol](https://modelcontextprotocol.io/) endpoint at `POST /api/mcp`. AI agents (Claude Code, Cursor, GPT, etc.) can use it to simulate and build automata without touching the canvas.
62+
63+
The endpoint implements JSON-RPC 2.0 and exposes 17 tools:
64+
65+
**Simulate**
66+
67+
| Tool | Description |
68+
|------|-------------|
69+
| `simulate_fa` | Test a string against a DFA or NFA |
70+
| `simulate_pda` | Test a string against a deterministic PDA |
71+
| `simulate_ndpa` | Test a string against a nondeterministic PDA |
72+
| `simulate_tm` | Test a string against a single/multi-tape TM |
73+
| `simulate_mealy` | Run a Mealy machine, return output sequence |
74+
| `simulate_moore` | Run a Moore machine, return output sequence |
75+
76+
**Convert**
77+
78+
| Tool | Description |
79+
|------|-------------|
80+
| `regex_to_nfa` | Convert regex to NFA via Thompson's construction |
81+
| `nfa_to_dfa` | Determinize NFA → minimal DFA |
82+
| `fa_to_regex` | Extract regex from FA via GNFA elimination |
83+
| `get_transition_table` | Render any automaton as a transition table |
84+
| `get_automata_info` | Full reference: types, tuple definitions, syntax |
85+
86+
**Canvas links** — returns a URL that opens the automaton directly on the simulator canvas
87+
88+
| Tool | Machine |
89+
|------|---------|
90+
| `build_fa_link` | DFA / NFA |
91+
| `build_pda_link` | Deterministic PDA |
92+
| `build_ndpa_link` | Nondeterministic PDA |
93+
| `build_tm_link` | Single/multi-tape Turing Machine |
94+
| `build_mealy_link` | Mealy machine |
95+
| `build_moore_link` | Moore machine |
96+
97+
**Quick test:**
98+
```bash
99+
curl -X POST https://www.theoryofcomputationsimulator.com/api/mcp \
100+
-H "Content-Type: application/json" \
101+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
102+
```
103+
104+
An OpenAPI 3.1 spec is available at [`/openapi.json`](https://www.theoryofcomputationsimulator.com/openapi.json). Full reference for LLM agents is at [`/llms.txt`](https://www.theoryofcomputationsimulator.com/llms.txt).
105+
57106
## Transition Label Syntax Reference
58107

59108
### Finite Automata
@@ -118,21 +167,45 @@ It is designed for education, experimentation, and fast visual iteration.
118167
2. Reload later from the workspace loader.
119168
3. Continue editing with previous state and saved cards restored.
120169

121-
## Technical Highlights
122-
123-
- Strongly typed model layer for state and alphabet handling
124-
- Regex simplification utilities integrated with FA summary flows
125-
- Language-equivalence-aware conversion safeguards in finite automata paths
126-
- Shared parsing/codec utilities for pushdown and machine snapshots
127-
- Refactored common FA view helpers to reduce duplicated logic
170+
### 5) Use with an AI Agent
171+
172+
Ask an AI agent to design an automaton. It calls `build_fa_link` and returns a URL that opens it on the canvas:
173+
174+
```bash
175+
curl -X POST https://www.theoryofcomputationsimulator.com/api/mcp \
176+
-H "Content-Type: application/json" \
177+
-d '{
178+
"jsonrpc": "2.0", "id": 1,
179+
"method": "tools/call",
180+
"params": {
181+
"name": "build_fa_link",
182+
"arguments": {
183+
"states": ["q0","q1","q2"],
184+
"alphabet": ["a","b"],
185+
"transitions": [
186+
{"from":"q0","to":"q1","symbol":"a"},
187+
{"from":"q1","to":"q2","symbol":"b"},
188+
{"from":"q2","to":"q2","symbol":"b"}
189+
],
190+
"start_state": "q0",
191+
"accept_states": ["q2"]
192+
}
193+
}
194+
}'
195+
```
196+
197+
The response includes a `url` field. Open it and the automaton appears on the canvas with states positioned automatically.
128198

129199
## Technology Stack
130200

131-
- Next.js 16
201+
- Next.js 16 (App Router)
132202
- React 19
133-
- TypeScript 5
203+
- TypeScript 6 (strict mode)
134204
- Tailwind CSS 4
135205
- Lucide React
206+
- Biome (linting & formatting)
207+
- Vercel KV (blog post storage)
208+
- Jest 30 (unit tests — 231 tests across 15 suites)
136209

137210
## Notes
138211

app/admin/login/page.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
2-
import React from "react";
32
import { useRouter, useSearchParams } from "next/navigation";
3+
import React from "react";
44

55
function LoginForm() {
66
const router = useRouter();
@@ -24,24 +24,31 @@ function LoginForm() {
2424

2525
if (res.ok) {
2626
const raw = params.get("from") ?? "";
27-
const from = raw.startsWith("/") && !raw.startsWith("//") ? raw : "/admin";
27+
const from =
28+
raw.startsWith("/") && !raw.startsWith("//") ? raw : "/admin";
2829
router.push(from);
2930
} else {
3031
setError("Invalid password");
3132
}
3233
}
3334

3435
return (
35-
<form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column", gap: 16 }}>
36+
<form
37+
onSubmit={handleSubmit}
38+
style={{ display: "flex", flexDirection: "column", gap: 16 }}
39+
>
3640
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
37-
<label style={{ fontSize: 12, fontWeight: 600, color: "var(--text-muted)" }}>
41+
<label
42+
htmlFor="admin-password"
43+
style={{ fontSize: 12, fontWeight: 600, color: "var(--text-muted)" }}
44+
>
3845
Password
3946
</label>
4047
<input
48+
id="admin-password"
4149
type="password"
4250
value={password}
4351
onChange={(e) => setPassword(e.target.value)}
44-
autoFocus
4552
required
4653
style={{
4754
padding: "10px 14px",
@@ -54,7 +61,9 @@ function LoginForm() {
5461
}}
5562
/>
5663
{error && (
57-
<p style={{ fontSize: 12, color: "var(--danger)", margin: 0 }}>{error}</p>
64+
<p style={{ fontSize: 12, color: "var(--danger)", margin: 0 }}>
65+
{error}
66+
</p>
5867
)}
5968
</div>
6069

@@ -95,8 +104,17 @@ export default function AdminLogin() {
95104
className="ui-panel"
96105
style={{ width: "100%", maxWidth: 360, padding: "40px 36px" }}
97106
>
98-
<p className="ui-kicker" style={{ marginBottom: 8 }}>Admin</p>
99-
<h1 style={{ fontSize: 22, fontWeight: 700, letterSpacing: "-0.02em", marginBottom: 28 }}>
107+
<p className="ui-kicker" style={{ marginBottom: 8 }}>
108+
Admin
109+
</p>
110+
<h1
111+
style={{
112+
fontSize: 22,
113+
fontWeight: 700,
114+
letterSpacing: "-0.02em",
115+
marginBottom: 28,
116+
}}
117+
>
100118
Sign in
101119
</h1>
102120

0 commit comments

Comments
 (0)