chore: drop liveblocks dependency
Remove @liveblocks/client and @liveblocks/zustand; add ws and a server npm script. Update README (fork attribution preserved, WS backend usage), .env.example (VITE_RT_URL), and pin the /blocs-pro/ base in vite.config.
This commit is contained in:
+3
-1
@@ -1 +1,3 @@
|
||||
VITE_LIVEBLOCKS_API_KEY=YOU_LIVEBLOCKS_API_KEY
|
||||
# Realtime WebSocket backend URL. Optional.
|
||||
# If unset: dev -> ws://localhost:8090 ; prod (https) -> wss://<host>/blocs-rt
|
||||
VITE_RT_URL=ws://localhost:8090
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
# 3D Lego-like Brick Builder Web App
|
||||
|
||||
Build and collaborate in real-time on 3D models using rectangular bricks. This multi-user application allows users to create intricate designs in a 3D space using Lego-like bricks.
|
||||
Build and collaborate in real-time on 3D models using rectangular bricks. This multi-user application lets several people create designs together in a shared 3D space using Lego-like bricks.
|
||||
|
||||

|
||||
|
||||
> Fork of [bhushan6/lego-builder](https://github.com/bhushan6/lego-builder) (MIT — see `LICENSE`, attribution preserved). The original used the **Liveblocks** SaaS for real-time collaboration; this fork replaces it with a **self-hosted, sovereign WebSocket backend** (`server/index.js`) — no third-party SaaS, no API key.
|
||||
|
||||
## Features
|
||||
|
||||
- **3D Rendering**: Utilizes `three.js` and `react-three-fiber` for efficient and smooth 3D rendering.
|
||||
- **Real-time Collaboration**: Integrated with `liveblocks` to enable users to collaborate in real-time.
|
||||
- **Intuitive UI**: Built with `radix ui` to provide a seamless user experience.
|
||||
- **State Management**: `Zustand` is being used as state management solution.
|
||||
- **Real-time Collaboration**: Self-hosted WebSocket relay (`server/index.js`) synchronizes the shared build (`bricks`, `cursorColors`) and per-user presence/cursors across everyone in a room.
|
||||
- **Intuitive UI**: Built with `radix ui`.
|
||||
- **State Management**: `Zustand`, with a custom `roomSync` middleware (`src/store/roomSync.js`) that exposes the same store surface the components expect.
|
||||
|
||||
## Technologies Used
|
||||
|
||||
@@ -17,64 +19,70 @@ Build and collaborate in real-time on 3D models using rectangular bricks. This m
|
||||
- [three.js](https://threejs.org/)
|
||||
- [react-three-fiber](https://github.com/pmndrs/react-three-fiber)
|
||||
- [radix ui](https://www.radix-ui.com/)
|
||||
- [liveblocks](https://liveblocks.io/)
|
||||
- [zustand](https://docs.pmnd.rs/zustand/getting-started/introduction)
|
||||
- [ws](https://github.com/websockets/ws) — WebSocket server for the real-time backend
|
||||
- [Vite](https://vitejs.dev/)
|
||||
|
||||
## Live Demo
|
||||
## Real-time architecture
|
||||
|
||||
Experience the app live [here](<[your_live_project_link](https://lego-builder.vercel.app/)>).
|
||||
- **`server/index.js`** — a standalone Node WebSocket relay (uses `ws`, no build step). One server hosts many rooms; per room it keeps the current shared storage snapshot (`{ bricks, cursorColors }`) plus the connected clients. Clients connect with `ws(s)://HOST/?room=<id>`. JSON protocol:
|
||||
- server → client: `init`, `join`, `leave`, `presence`, `storage`, `event`
|
||||
- client → server: `presence`, `storage` (last-writer-wins), `event` (ephemeral cursors, relayed to others only, never persisted)
|
||||
- **`src/store/roomSync.js`** — a Zustand middleware that drops in for `@liveblocks/zustand`. It exposes `state.liveblocks.{enterRoom, leaveRoom, status, others, room}` where `room` has `broadcastEvent`, `subscribe("event", cb)` and no-op `history.undo/redo` stubs — so the existing components work unchanged.
|
||||
|
||||
## Local Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Yarn](https://yarnpkg.com/) or [npm](https://www.npmjs.com/)
|
||||
- [Node.js](https://nodejs.org/) (18+; tested on Node 26)
|
||||
- [npm](https://www.npmjs.com/)
|
||||
|
||||
### Setup
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/bhushan6/lego-builder.git
|
||||
```
|
||||
|
||||
2. Navigate to the project directory:
|
||||
|
||||
```bash
|
||||
git clone https://git.saillant.cc/electron-rare/lego-builder.git
|
||||
cd lego-builder
|
||||
```
|
||||
|
||||
3. Install the dependencies:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
or
|
||||
2. Install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
4. Create a .env file in the root directory and add your Liveblocks public API key:
|
||||
3. (Optional) point the client at a specific realtime server. Create `.env`:
|
||||
|
||||
```bash
|
||||
echo "VITE_LIVEBLOCKS_API_KEY=your_liveblocks_public_api_key" > .env
|
||||
echo "VITE_RT_URL=ws://localhost:8090" > .env
|
||||
```
|
||||
|
||||
Replace your_liveblocks_public_api_key with your actual Liveblocks public API key.
|
||||
If unset, the client falls back to `ws://localhost:8090` in dev, or
|
||||
`wss://<your-host>/blocs-rt` when served over HTTPS in production (that path
|
||||
is routed to the relay by the deployment infra).
|
||||
|
||||
4. Start the realtime server (in one terminal):
|
||||
|
||||
5. Start the development server:
|
||||
```bash
|
||||
yarn dev
|
||||
npm run server # listens on $PORT (default 8090)
|
||||
```
|
||||
or
|
||||
|
||||
5. Start the dev server (in another terminal):
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
6. Open your browser and navigate to http://127.0.0.1:5173/ (or the port you've configured).
|
||||
|
||||
6. Open the app at the URL Vite prints (note the `/blocs-pro/` base path,
|
||||
e.g. http://localhost:5173/blocs-pro/). Open it in two browsers/tabs and
|
||||
join the **same Room ID** to collaborate.
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
npm run build # outputs dist/, served under base /blocs-pro/
|
||||
```
|
||||
|
||||
## Contribution
|
||||
|
||||
|
||||
Generated
+28
-49
@@ -8,8 +8,6 @@
|
||||
"name": "lego-builder",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@liveblocks/client": "^1.2.2",
|
||||
"@liveblocks/zustand": "^1.2.2",
|
||||
"@radix-ui/colors": "^2.0.1",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dialog": "^1.0.4",
|
||||
@@ -29,6 +27,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"three": "^0.155.0",
|
||||
"use-keyboard-shortcut": "^1.1.6",
|
||||
"ws": "^8.18.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -984,31 +983,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@liveblocks/client": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/client/-/client-1.2.2.tgz",
|
||||
"integrity": "sha512-cav7NIoZNGmOC07p/spaCiNwLL/eeKKTAscLb2wN5myXG+xWlf6sj6zIxMOSA1eTDcDDvDMsnkLUviUUK0QGWA==",
|
||||
"dependencies": {
|
||||
"@liveblocks/core": "1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@liveblocks/core": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/core/-/core-1.2.2.tgz",
|
||||
"integrity": "sha512-1WALetZ600EN3D4VaSpXse2Plp5+7os78B1kAANlOmf/NtiQKIoEBE40xYBVd4QAJgX6YIYGjOsQQja0CUqs7g=="
|
||||
},
|
||||
"node_modules/@liveblocks/zustand": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/zustand/-/zustand-1.2.2.tgz",
|
||||
"integrity": "sha512-F8xTtR3yE2SJ5FYHMpdAu6mheSBubjc4AekNaoePg2uV8BfZljNT1VrU+1SKCCEddV1e96OOYQqP+iBJcryYhg==",
|
||||
"dependencies": {
|
||||
"@liveblocks/client": "1.2.2",
|
||||
"@liveblocks/core": "1.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zustand": "^4.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@mediapipe/tasks-vision": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.2.tgz",
|
||||
@@ -5283,6 +5257,27 @@
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.21.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
|
||||
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
@@ -5937,28 +5932,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"@liveblocks/client": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/client/-/client-1.2.2.tgz",
|
||||
"integrity": "sha512-cav7NIoZNGmOC07p/spaCiNwLL/eeKKTAscLb2wN5myXG+xWlf6sj6zIxMOSA1eTDcDDvDMsnkLUviUUK0QGWA==",
|
||||
"requires": {
|
||||
"@liveblocks/core": "1.2.2"
|
||||
}
|
||||
},
|
||||
"@liveblocks/core": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/core/-/core-1.2.2.tgz",
|
||||
"integrity": "sha512-1WALetZ600EN3D4VaSpXse2Plp5+7os78B1kAANlOmf/NtiQKIoEBE40xYBVd4QAJgX6YIYGjOsQQja0CUqs7g=="
|
||||
},
|
||||
"@liveblocks/zustand": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/zustand/-/zustand-1.2.2.tgz",
|
||||
"integrity": "sha512-F8xTtR3yE2SJ5FYHMpdAu6mheSBubjc4AekNaoePg2uV8BfZljNT1VrU+1SKCCEddV1e96OOYQqP+iBJcryYhg==",
|
||||
"requires": {
|
||||
"@liveblocks/client": "1.2.2",
|
||||
"@liveblocks/core": "1.2.2"
|
||||
}
|
||||
},
|
||||
"@mediapipe/tasks-vision": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.2.tgz",
|
||||
@@ -8827,6 +8800,12 @@
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.21.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
|
||||
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
|
||||
"requires": {}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
|
||||
+3
-3
@@ -7,11 +7,10 @@
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"server": "node server/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@liveblocks/client": "^1.2.2",
|
||||
"@liveblocks/zustand": "^1.2.2",
|
||||
"@radix-ui/colors": "^2.0.1",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dialog": "^1.0.4",
|
||||
@@ -31,6 +30,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"three": "^0.155.0",
|
||||
"use-keyboard-shortcut": "^1.1.6",
|
||||
"ws": "^8.18.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,5 +3,6 @@ import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: '/blocs-pro/',
|
||||
plugins: [react()],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user