Skip to content

Commit 4bdafb1

Browse files
Merge pull request #5 from rohas-dev/feat/workbench-ui
Add Rohas Workbench - Next.js Dashboard for Local Development
2 parents 24dbbcd + e40288f commit 4bdafb1

62 files changed

Lines changed: 11266 additions & 5 deletions

Some content is hidden

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

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,10 @@ target
1919
# and can be added to the global gitignore or merged into this file. For a more nuclear
2020
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
2121
#.idea/
22+
23+
# Workbench frontend artifacts
24+
workbench/node_modules
25+
workbench/.next
26+
workbench/out
27+
workbench/.turbo
28+
workbench/.vercel

examples/hello-world/config/rohas.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ enable_cors = true
1111
[adapter]
1212
type = "memory"
1313
buffer_size = 1000
14+
15+
[workbench]
16+
allowed_origins = []
17+
api_key = "FRmz/+AjTYCUFlBMio479A=="
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
//cron DailyCleanup {
2-
// schedule: "*/2 * * * * *"
3-
//}
1+
cron DailyCleanup {
2+
schedule: "*/2 * * * * *"
3+
}

examples/hello-world/src/generated/state.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,80 @@ class TriggeredEvent(BaseModel):
77
payload: Dict[str, Any]
88

99

10+
class Logger:
11+
"""Logger for handlers to emit structured logs."""
12+
13+
def __init__(self, handler_name: str, log_fn: Any):
14+
self._handler_name = handler_name
15+
self._log_fn = log_fn
16+
17+
def info(self, message: str, **kwargs: Any) -> None:
18+
"""Log an info message.
19+
20+
Args:
21+
message: Log message
22+
**kwargs: Additional fields to include in the log
23+
"""
24+
if self._log_fn:
25+
self._log_fn("info", self._handler_name, message, kwargs)
26+
27+
def error(self, message: str, **kwargs: Any) -> None:
28+
"""Log an error message.
29+
30+
Args:
31+
message: Log message
32+
**kwargs: Additional fields to include in the log
33+
"""
34+
if self._log_fn:
35+
self._log_fn("error", self._handler_name, message, kwargs)
36+
37+
def warning(self, message: str, **kwargs: Any) -> None:
38+
"""Log a warning message.
39+
40+
Args:
41+
message: Log message
42+
**kwargs: Additional fields to include in the log
43+
"""
44+
if self._log_fn:
45+
self._log_fn("warn", self._handler_name, message, kwargs)
46+
47+
def warn(self, message: str, **kwargs: Any) -> None:
48+
"""Log a warning message (alias for warning).
49+
50+
Args:
51+
message: Log message
52+
**kwargs: Additional fields to include in the log
53+
"""
54+
self.warning(message, **kwargs)
55+
56+
def debug(self, message: str, **kwargs: Any) -> None:
57+
"""Log a debug message.
58+
59+
Args:
60+
message: Log message
61+
**kwargs: Additional fields to include in the log
62+
"""
63+
if self._log_fn:
64+
self._log_fn("debug", self._handler_name, message, kwargs)
65+
66+
def trace(self, message: str, **kwargs: Any) -> None:
67+
"""Log a trace message.
68+
69+
Args:
70+
message: Log message
71+
**kwargs: Additional fields to include in the log
72+
"""
73+
if self._log_fn:
74+
self._log_fn("trace", self._handler_name, message, kwargs)
75+
76+
1077
class State:
1178
"""Context object for handlers to trigger events and access runtime state."""
1279

13-
def __init__(self):
80+
def __init__(self, handler_name: Optional[str] = None, log_fn: Optional[Any] = None):
1481
self._triggers: List[TriggeredEvent] = []
1582
self._auto_trigger_payloads: Dict[str, Dict[str, Any]] = {}
83+
self.logger = Logger(handler_name or "unknown", log_fn)
1684

1785
def trigger_event(self, event_name: str, payload: Dict[str, Any]) -> None:
1886
"""Manually trigger an event with the given payload.

examples/hello-world/src/handlers/api/test.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,12 @@ async def handle_test(req: TestRequest, state: State) -> TestResponse:
2424
state.trigger_event('ManualTrigger', {
2525
'payload': 'Hello, world!'
2626
})
27-
27+
28+
state.logger.info('Hello, world!')
29+
state.logger.error('Hello, world!')
30+
state.logger.warning('Hello, world!')
31+
state.logger.warn('Hello, world!')
32+
state.logger.debug('Hello, world!')
33+
state.logger.trace('Hello, world!')
34+
2835
return TestResponse(data="Hello, world!")

workbench/.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts

workbench/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Workbench
2+
3+
Rohas Workbench is a Next.js dashboard that pairs generated Rohas projects with a modern shadcn/ui front-end. It automatically detects the nearest `config/rohas.toml`, parses your local schemas/handlers, and surfaces health signals without needing a backend service.
4+
5+
## Stack
6+
7+
- Next.js 16 / React 19 (App Router)
8+
- Tailwind CSS 3.4 + shadcn/ui primitives (`src/components/ui/*`)
9+
- next-themes for dark/light modes
10+
- `toml` parser to read Cargo metadata straight from disk
11+
12+
## Getting started
13+
14+
```bash
15+
pnpm install
16+
pnpm dev
17+
```
18+
19+
Visit http://localhost:3000 to view the dashboard. Updating schema files under `./schema` or handlers under `./src/handlers` immediately updates the inventory, because Workbench reads directly from disk on every request.
20+
21+
## Scripts
22+
23+
```bash
24+
pnpm lint # next lint (core-web-vitals rules)
25+
pnpm typecheck # tsc --noEmit
26+
pnpm format # prettier on src/**/*.{ts,tsx}
27+
pnpm build && pnpm start
28+
```
29+
30+
## Project layout & routes
31+
32+
- `src/app/(workbench)` – Overview, Schemas, Schema Graph, Workflows, Tracing, CLI, Settings routes
33+
- `src/app/(workbench)/workflows/[slug]` – interactive workflow viewer
34+
- `src/app/(workbench)/tracing` – schema-derived trace explorer
35+
- `src/components/ui` – reusable shadcn primitives (button, card, tabs, etc.)
36+
- `src/components/workbench` – domain widgets (sidebar, header, schema browser, workflow tools)
37+
- `src/stores/workbench-store.ts` – Zustand store for cross-route search & navigation state
38+
- `src/lib/project.ts` / `src/lib/workbench-data.ts` – filesystem helpers that discover schema, handler, and workflow assets
39+
40+
Because the UI executes inside the same repo as the generated Rohas project, no network calls are required—Workbench renders everything from local state, which keeps the feedback loop fast even when offline.
41+
42+
## Project root detection
43+
44+
Workbench walks up from the app directory until it finds `config/rohas.toml`, which becomes the project root. Override this detection with:
45+
46+
```bash
47+
ROHAS_PROJECT_ROOT=/absolute/path/to/project pnpm dev
48+
```
49+
50+
The override is especially helpful when running the dashboard from a globally installed bundle or when the project structure deviates from the default generator layout.

workbench/eslint.config.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { defineConfig, globalIgnores } from "eslint/config";
2+
import nextVitals from "eslint-config-next/core-web-vitals";
3+
import nextTs from "eslint-config-next/typescript";
4+
5+
const eslintConfig = defineConfig([
6+
...nextVitals,
7+
...nextTs,
8+
// Override default ignores of eslint-config-next.
9+
globalIgnores([
10+
// Default ignores of eslint-config-next:
11+
".next/**",
12+
"out/**",
13+
"build/**",
14+
"next-env.d.ts",
15+
]),
16+
]);
17+
18+
export default eslintConfig;

workbench/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { NextConfig } from "next";
2+
3+
const nextConfig: NextConfig = {
4+
/* config options here */
5+
};
6+
7+
export default nextConfig;

workbench/package.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"name": "rohas-workbench",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "eslint . --ext ts,tsx --max-warnings=0",
10+
"typecheck": "tsc --noEmit",
11+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
12+
},
13+
"dependencies": {
14+
"@monaco-editor/react": "^4.7.0",
15+
"@radix-ui/react-avatar": "^1.0.4",
16+
"@radix-ui/react-dropdown-menu": "^2.0.6",
17+
"@radix-ui/react-label": "^2.0.2",
18+
"@radix-ui/react-popover": "^1.0.7",
19+
"@radix-ui/react-scroll-area": "^1.0.5",
20+
"@radix-ui/react-separator": "^1.0.3",
21+
"@radix-ui/react-slot": "^1.0.2",
22+
"@radix-ui/react-tabs": "^1.0.4",
23+
"@radix-ui/react-tooltip": "^1.0.7",
24+
"@types/react-syntax-highlighter": "^15.5.13",
25+
"@xyflow/react": "^12.9.3",
26+
"class-variance-authority": "^0.7.0",
27+
"clsx": "^2.1.1",
28+
"dagre": "^0.8.5",
29+
"lucide-react": "^0.475.0",
30+
"next": "16.0.5",
31+
"next-themes": "^0.4.4",
32+
"react": "19.2.0",
33+
"react-dom": "19.2.0",
34+
"react-resizable-panels": "^3.0.6",
35+
"react-syntax-highlighter": "^16.1.0",
36+
"tailwind-merge": "^2.5.2",
37+
"tailwindcss-animate": "^1.0.7",
38+
"toml": "^3.0.0",
39+
"zustand": "^4.5.2"
40+
},
41+
"devDependencies": {
42+
"@types/dagre": "^0.7.53",
43+
"@types/node": "^20",
44+
"@types/react": "^19",
45+
"@types/react-dom": "^19",
46+
"autoprefixer": "^10.4.20",
47+
"eslint": "^9",
48+
"eslint-config-next": "16.0.5",
49+
"postcss": "^8.4.47",
50+
"prettier": "^3.3.3",
51+
"tailwindcss": "^3.4.13",
52+
"typescript": "^5.6.3"
53+
}
54+
}

0 commit comments

Comments
 (0)