Skip to content

Commit 121d3d4

Browse files
committed
Initial commit
1 parent bd99960 commit 121d3d4

32 files changed

+3329
-2880
lines changed

README.md

Lines changed: 161 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,175 @@
1-
# React + TypeScript + Vite
1+
# Hive Labaratory Component Documentation
22

3-
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
3+
`Labaratory` is a fully featured React component that provides a modern GraphQL playground experience (collections, tabs, history, preflight scripts, environment variables, tests, etc.). It ships with an opinionated UI and a context provider that exposes granular hooks for integrating with your own storage or analytics layers.
44

5-
Currently, two official plugins are available:
5+
This document explains how to embed the component, what data it needs, and how to react to user changes through the available callbacks.
66

7-
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
7+
---
98

10-
## React Compiler
9+
## Getting Started
1110

12-
The React Compiler is enabled on this template. See [this documentation](https://react.dev/learn/react-compiler) for more information.
11+
```tsx
12+
import { Labaratory } from "@/components/labaratory/labaratory";
1313

14-
Note: This will impact Vite dev & build performances.
14+
export const Playground = () => {
15+
return (
16+
<Labaratory
17+
defaultEndpoint="https://api.spacex.land/graphql/"
18+
onEndpointChange={(endpoint) => {
19+
localStorage.setItem("lab-endpoint", endpoint ?? "");
20+
}}
21+
defaultOperations={[]}
22+
onOperationCreate={(operation) => console.log("created", operation)}
23+
onOperationUpdate={(operation) => console.log("updated", operation)}
24+
onOperationDelete={(operation) => console.log("deleted", operation)}
25+
/>
26+
);
27+
};
28+
```
29+
30+
The component renders the full UI and injects a `LabaratoryProvider`, so any nested component can call `useLabaratory()` to access the current state.
31+
32+
---
33+
34+
## Data Flow Overview
35+
36+
`Labaratory` is controlled via two complementary mechanisms:
37+
38+
1. **Default Values**`default*` props let you hydrate the playground from persisted data (e.g., localStorage, database). These are only read during initialization.
39+
2. **Event Callbacks**`on*` props fire whenever users create/update/delete entities within the playground. Use them to keep external storage in sync or to trigger side effects (analytics, notifications, etc.).
40+
41+
If you provide both a default value and a callback for the same entity, you can make the playground fully persistent without touching its internals.
42+
43+
---
44+
45+
## Props Reference
46+
47+
### Endpoint
48+
49+
- `defaultEndpoint?: string | null`
50+
- `onEndpointChange?: (endpoint: string | null) => void`
51+
52+
### Collections
53+
54+
- `defaultCollections?: LabaratoryCollection[]`
55+
- `onCollectionsChange?: (collections: LabaratoryCollection[]) => void`
56+
- `onCollectionCreate?: (collection: LabaratoryCollection) => void`
57+
- `onCollectionUpdate?: (collection: LabaratoryCollection) => void`
58+
- `onCollectionDelete?: (collection: LabaratoryCollection) => void`
59+
- `onCollectionOperationCreate?: (collection: LabaratoryCollection, operation: LabaratoryCollectionOperation) => void`
60+
- `onCollectionOperationUpdate?: (collection: LabaratoryCollection, operation: LabaratoryCollectionOperation) => void`
61+
- `onCollectionOperationDelete?: (collection: LabaratoryCollection, operation: LabaratoryCollectionOperation) => void`
62+
63+
### Operations
64+
65+
- `defaultOperations?: LabaratoryOperation[]`
66+
- `defaultActiveOperationId?: string`
67+
- `onOperationsChange?: (operations: LabaratoryOperation[]) => void`
68+
- `onActiveOperationIdChange?: (operationId: string) => void`
69+
- `onOperationCreate?: (operation: LabaratoryOperation) => void`
70+
- `onOperationUpdate?: (operation: LabaratoryOperation) => void`
71+
- `onOperationDelete?: (operation: LabaratoryOperation) => void`
72+
73+
### History
74+
75+
- `defaultHistory?: LabaratoryHistory[]`
76+
- `onHistoryChange?: (history: LabaratoryHistory[]) => void`
77+
- `onHistoryCreate?: (history: LabaratoryHistory) => void`
78+
- `onHistoryUpdate?: (history: LabaratoryHistory) => void`
79+
- `onHistoryDelete?: (history: LabaratoryHistory) => void`
80+
81+
### Tabs
82+
83+
- `defaultTabs?: LabaratoryTab[]`
84+
- `defaultActiveTabId?: string | null`
85+
- `onTabsChange?: (tabs: LabaratoryTab[]) => void`
86+
- `onActiveTabIdChange?: (tabId: string | null) => void`
1587

16-
## Expanding the ESLint configuration
88+
### Preflight Script
1789

18-
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
90+
- `defaultPreflight?: LabaratoryPreflight | null`
91+
- `onPreflightChange?: (preflight: LabaratoryPreflight | null) => void`
1992

20-
```js
21-
export default defineConfig([
22-
globalIgnores(['dist']),
23-
{
24-
files: ['**/*.{ts,tsx}'],
25-
extends: [
26-
// Other configs...
93+
### Environment Variables
2794

28-
// Remove tseslint.configs.recommended and replace with this
29-
tseslint.configs.recommendedTypeChecked,
30-
// Alternatively, use this for stricter rules
31-
tseslint.configs.strictTypeChecked,
32-
// Optionally, add this for stylistic rules
33-
tseslint.configs.stylisticTypeChecked,
95+
- `defaultEnv?: LabaratoryEnv | null`
96+
- `onEnvChange?: (env: LabaratoryEnv | null) => void`
3497

35-
// Other configs...
36-
],
37-
languageOptions: {
38-
parserOptions: {
39-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
40-
tsconfigRootDir: import.meta.dirname,
41-
},
42-
// other options...
43-
},
44-
},
45-
])
98+
### Settings
99+
100+
- `defaultSettings?: LabaratorySettings | null`
101+
- `onSettingsChange?: (settings: LabaratorySettings | null) => void`
102+
103+
### Tests
104+
105+
- `defaultTests?: LabaratoryTest[]`
106+
- `onTestsChange?: (tests: LabaratoryTest[]) => void`
107+
108+
### Dialog Helpers
109+
110+
`useLabaratory()` also exposes `openAddCollectionDialog`, `openUpdateEndpointDialog`, and `openAddTestDialog` so that external buttons can toggle the built-in dialogs.
111+
112+
---
113+
114+
## Consuming State via `useLabaratory`
115+
116+
Inside any descendant of `Labaratory`, call the hook to access live state and actions:
117+
118+
```tsx
119+
import { useLabaratory } from "@/components/labaratory/context";
120+
121+
const RunButton = () => {
122+
const { runActiveOperation, endpoint } = useLabaratory();
123+
124+
return (
125+
<button
126+
disabled={!endpoint}
127+
onClick={() => runActiveOperation(endpoint!, { env: { variables: {} } })}
128+
>
129+
Run
130+
</button>
131+
);
132+
};
46133
```
47134

48-
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
49-
50-
```js
51-
// eslint.config.js
52-
import reactX from 'eslint-plugin-react-x'
53-
import reactDom from 'eslint-plugin-react-dom'
54-
55-
export default defineConfig([
56-
globalIgnores(['dist']),
57-
{
58-
files: ['**/*.{ts,tsx}'],
59-
extends: [
60-
// Other configs...
61-
// Enable lint rules for React
62-
reactX.configs['recommended-typescript'],
63-
// Enable lint rules for React DOM
64-
reactDom.configs.recommended,
65-
],
66-
languageOptions: {
67-
parserOptions: {
68-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
69-
tsconfigRootDir: import.meta.dirname,
70-
},
71-
// other options...
72-
},
73-
},
74-
])
135+
All actions returned by the hook (collections, operations, history, tabs, preflight, env, settings, tests) stay in sync with the UI.
136+
137+
---
138+
139+
## Persistence Example
140+
141+
The snippet below demonstrates how to persist operations and history to `localStorage` using the granular callbacks:
142+
143+
```tsx
144+
const STORAGE_KEY = "labaratory-data";
145+
146+
const loadData = () => {
147+
try {
148+
return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "{}");
149+
} catch {
150+
return {};
151+
}
152+
};
153+
154+
const save = (partial: any) => {
155+
const current = loadData();
156+
localStorage.setItem(STORAGE_KEY, JSON.stringify({ ...current, ...partial }));
157+
};
158+
159+
const data = loadData();
160+
161+
export function PersistentPlayground() {
162+
return (
163+
<Labaratory
164+
defaultOperations={data.operations ?? []}
165+
onOperationsChange={(operations) => save({ operations })}
166+
onOperationCreate={(operation) =>
167+
console.info("operation created", operation)
168+
}
169+
defaultHistory={data.history ?? []}
170+
onHistoryDelete={(history) => console.info("history deleted", history)}
171+
onHistoryChange={(history) => save({ history })}
172+
/>
173+
);
174+
}
75175
```

electron/main.cjs

Lines changed: 0 additions & 33 deletions
This file was deleted.

electron/preload.cjs

Lines changed: 0 additions & 5 deletions
This file was deleted.

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<title>Hive Laboratory</title>
88

99
<link href="/src/index.css" rel="stylesheet">
10-
<script src="https://cdn.jsdelivr.net/npm/react-scan/dist/auto.global.js"></script>
10+
<!-- <script src="https://cdn.jsdelivr.net/npm/react-scan/dist/auto.global.js"></script> -->
1111

1212
</head>
1313
<body class="dark w-full h-full">

package.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"private": true,
44
"version": "0.0.0",
55
"type": "module",
6-
"main": "electron/main.cjs",
76
"build": {
87
"appId": "com.guild.hive.laboratory",
98
"productName": "Hive Laboratory",
@@ -20,15 +19,16 @@
2019
"dev": "vite",
2120
"dev:electron": "VITE_TARGET=electron concurrently \"vite\" \"wait-on http://localhost:5173 && electron .\"",
2221
"build": "tsc -b && vite build",
23-
"dist:electron": "VITE_TARGET=electron vite build && electron-builder",
2422
"lint": "eslint .",
2523
"preview": "vite preview"
2624
},
2725
"dependencies": {
26+
"@dagrejs/dagre": "^1.1.8",
2827
"@dnd-kit/core": "^6.3.1",
2928
"@dnd-kit/modifiers": "^9.0.0",
3029
"@dnd-kit/sortable": "^10.0.0",
3130
"@dnd-kit/utilities": "^3.2.2",
31+
"@mlc-ai/web-llm": "^0.2.79",
3232
"@monaco-editor/react": "4.8.0-rc.2",
3333
"@radix-ui/react-alert-dialog": "^1.1.15",
3434
"@radix-ui/react-checkbox": "^1.3.3",
@@ -48,18 +48,21 @@
4848
"@tanstack/react-form": "^1.23.8",
4949
"@tanstack/react-router": "^1.135.0",
5050
"@tanstack/react-router-devtools": "^1.135.0",
51+
"@xyflow/react": "^12.9.3",
5152
"class-variance-authority": "^0.7.1",
5253
"clsx": "^2.1.1",
5354
"cmdk": "^1.1.1",
55+
"crypto-js": "^4.2.0",
5456
"date-fns": "^4.1.0",
5557
"esbuild": "^0.25.11",
5658
"graphql": "^16.11.0",
5759
"graphql-ws": "^6.0.6",
5860
"lodash": "^4.17.21",
5961
"lucide-react": "^0.548.0",
6062
"lz-string": "^1.5.0",
61-
"monaco-editor": "^0.54.0",
63+
"monaco-editor": "^0.52.2",
6264
"monaco-graphql": "^1.7.2",
65+
"monacopilot": "^1.2.7",
6366
"next-themes": "^0.4.6",
6467
"react": "^19.1.1",
6568
"react-dom": "^19.1.1",
@@ -73,15 +76,14 @@
7376
"devDependencies": {
7477
"@eslint/js": "^9.36.0",
7578
"@tanstack/router-plugin": "^1.135.0",
79+
"@types/crypto-js": "^4.2.2",
7680
"@types/lodash": "^4.17.20",
7781
"@types/node": "^24.6.0",
7882
"@types/react": "^19.1.16",
7983
"@types/react-dom": "^19.1.9",
8084
"@vitejs/plugin-react": "^5.0.4",
8185
"babel-plugin-react-compiler": "^19.1.0-rc.3",
8286
"concurrently": "^9.2.1",
83-
"electron": "^39.1.1",
84-
"electron-builder": "^26.0.12",
8587
"eslint": "^9.36.0",
8688
"eslint-plugin-react-hooks": "^5.2.0",
8789
"eslint-plugin-react-refresh": "^0.4.22",
@@ -96,5 +98,9 @@
9698
"overrides": {
9799
"vite": "npm:[email protected]"
98100
}
101+
},
102+
"peerDependencies": {
103+
"react": "^19.0.0",
104+
"react-dom": "^19.0.0"
99105
}
100106
}

0 commit comments

Comments
 (0)