topazcube 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +202 -0
- package/README.md +1 -0
- package/dist/topazcube.js +1527 -0
- package/package.json +15 -9
- package/src/topazcube.js +1 -2
- package/test/todo/client/App.jsx +0 -224
- package/test/todo/client/index.css +0 -13
- package/test/todo/client/index.html +0 -13
- package/test/todo/client/main.jsx +0 -7
- package/test/todo/client/package.json +0 -24
- package/test/todo/client/vite.config.js +0 -10
- package/test/todo/server/package.json +0 -19
- package/test/todo/server/todoserver.js +0 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "topazcube",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "TopazCube is a real-time collaborative document editing, and multiplayer game library.",
|
|
5
5
|
"author": "László Matuska @BitOfGold",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -14,10 +14,9 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"src",
|
|
17
|
-
"
|
|
17
|
+
"dist",
|
|
18
18
|
"LICENSE",
|
|
19
|
-
"README.md"
|
|
20
|
-
"package.json"
|
|
19
|
+
"README.md"
|
|
21
20
|
],
|
|
22
21
|
"keywords": [
|
|
23
22
|
"multiplayer",
|
|
@@ -33,8 +32,15 @@
|
|
|
33
32
|
],
|
|
34
33
|
"sideEffects": false,
|
|
35
34
|
"exports": {
|
|
36
|
-
"
|
|
37
|
-
|
|
35
|
+
".": {
|
|
36
|
+
"import": "./dist/topazcube.js"
|
|
37
|
+
},
|
|
38
|
+
"./client": {
|
|
39
|
+
"import": "./dist/topazcube.js"
|
|
40
|
+
},
|
|
41
|
+
"./server": {
|
|
42
|
+
"import": "./src/server.js"
|
|
43
|
+
}
|
|
38
44
|
},
|
|
39
45
|
"type": "module",
|
|
40
46
|
"scripts": {
|
|
@@ -43,13 +49,13 @@
|
|
|
43
49
|
"preview": "vite preview --host 0.0.0.0 --port 8800"
|
|
44
50
|
},
|
|
45
51
|
"devDependencies": {
|
|
46
|
-
"vite": "^6.2
|
|
47
|
-
"vite-plugin-glsl": "^1.3.3"
|
|
52
|
+
"vite": "^6.3.2"
|
|
48
53
|
},
|
|
49
54
|
"dependencies": {
|
|
50
|
-
"@msgpack/msgpack": "^3.1.
|
|
55
|
+
"@msgpack/msgpack": "^3.1.1",
|
|
51
56
|
"fast-json-patch": "^3.1.1",
|
|
52
57
|
"mongodb": "^6.15.0",
|
|
58
|
+
"util": "^0.12.5",
|
|
53
59
|
"ws": "^8.18.1"
|
|
54
60
|
}
|
|
55
61
|
}
|
package/src/topazcube.js
CHANGED
package/test/todo/client/App.jsx
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react"
|
|
2
|
-
import TopazCubeClient from "../../src/client.js"
|
|
3
|
-
import "./index.css"
|
|
4
|
-
import { GripHorizontal, Trash2 } from "lucide-react"
|
|
5
|
-
|
|
6
|
-
function App() {
|
|
7
|
-
// Client instance
|
|
8
|
-
let [client, setClient] = useState({})
|
|
9
|
-
const [isConnected, setIsConnected] = useState(false)
|
|
10
|
-
|
|
11
|
-
// Document state (list of todos)
|
|
12
|
-
let [doc, setDoc] = useState({})
|
|
13
|
-
// Sorted todos by order field
|
|
14
|
-
const sortedTodos = doc.todos ? [...doc.todos].filter((todo) => !todo.deleted).sort((a, b) => a.order - b.order) : []
|
|
15
|
-
|
|
16
|
-
// New todo input
|
|
17
|
-
const [newTodo, setNewTodo] = useState("")
|
|
18
|
-
|
|
19
|
-
// For drag and drop reordering
|
|
20
|
-
const [draggedItem, setDraggedItem] = useState(null)
|
|
21
|
-
const [movingUp, setMovingUp] = useState(false)
|
|
22
|
-
const [dragOverIndex, setDragOverIndex] = useState(null)
|
|
23
|
-
|
|
24
|
-
// Initialize client
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
class TodoClient extends TopazCubeClient {
|
|
27
|
-
constructor() {
|
|
28
|
-
super({
|
|
29
|
-
url: "ws://192.168.0.200:4799",
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
onConnect() {
|
|
34
|
-
setIsConnected(true)
|
|
35
|
-
this.subscribe("todos")
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
onDisconnect() {
|
|
39
|
-
setIsConnected(false)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
onMessage(message) {
|
|
43
|
-
console.log("orig onMessage", message)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
onChange(name) {
|
|
47
|
-
console.log('onChange ez', this.document)
|
|
48
|
-
setDoc({ ...this.document })
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
let nc = new TodoClient()
|
|
53
|
-
setClient(nc)
|
|
54
|
-
nc.connect()
|
|
55
|
-
|
|
56
|
-
return () => {
|
|
57
|
-
nc.destroy()
|
|
58
|
-
}
|
|
59
|
-
}, [])
|
|
60
|
-
|
|
61
|
-
// Add new todo
|
|
62
|
-
const addTodo = () => {
|
|
63
|
-
if (newTodo) {
|
|
64
|
-
doc.todos.push({
|
|
65
|
-
title: newTodo,
|
|
66
|
-
completed: false,
|
|
67
|
-
order: doc.todos.length,
|
|
68
|
-
})
|
|
69
|
-
setNewTodo("")
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Drag and drop handlers
|
|
74
|
-
|
|
75
|
-
const handleDragStart = (e, todo, index) => {
|
|
76
|
-
setDraggedItem(todo)
|
|
77
|
-
e.dataTransfer.effectAllowed = "move"
|
|
78
|
-
// Required for Firefox
|
|
79
|
-
e.dataTransfer.setData("text/plain", index)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const handleDragOver = (e, overTodo, overIndex) => {
|
|
83
|
-
e.preventDefault()
|
|
84
|
-
if (!draggedItem || draggedItem === overTodo) return
|
|
85
|
-
setDragOverIndex(overIndex)
|
|
86
|
-
if (draggedItem.order <= overTodo.order) {
|
|
87
|
-
setMovingUp(false)
|
|
88
|
-
} else {
|
|
89
|
-
setMovingUp(true)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const handleDragEnd = () => {
|
|
94
|
-
setDraggedItem(null)
|
|
95
|
-
setDragOverIndex(null)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const handleDrop = (e, dropTodo) => {
|
|
99
|
-
e.preventDefault()
|
|
100
|
-
if (!draggedItem || draggedItem === dropTodo) return
|
|
101
|
-
|
|
102
|
-
// Get current orders
|
|
103
|
-
const currentOrders = sortedTodos.map((todo) => todo.order)
|
|
104
|
-
|
|
105
|
-
// Find the current order of dragged and drop items
|
|
106
|
-
const draggedOrder = draggedItem.order
|
|
107
|
-
const dropOrder = dropTodo.order
|
|
108
|
-
|
|
109
|
-
// Reorder the items
|
|
110
|
-
if (draggedOrder < dropOrder) {
|
|
111
|
-
// Moving down
|
|
112
|
-
doc.todos.forEach((todo) => {
|
|
113
|
-
if (todo === draggedItem) {
|
|
114
|
-
todo.order = dropOrder
|
|
115
|
-
} else if (todo.order > draggedOrder && todo.order <= dropOrder) {
|
|
116
|
-
todo.order--
|
|
117
|
-
}
|
|
118
|
-
})
|
|
119
|
-
} else {
|
|
120
|
-
// Moving up
|
|
121
|
-
doc.todos.forEach((todo) => {
|
|
122
|
-
if (todo === draggedItem) {
|
|
123
|
-
todo.order = dropOrder
|
|
124
|
-
} else if (todo.order >= dropOrder && todo.order < draggedOrder) {
|
|
125
|
-
todo.order++
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
setDoc({ ...doc })
|
|
131
|
-
setDraggedItem(null)
|
|
132
|
-
setDragOverIndex(null)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return (
|
|
136
|
-
<>
|
|
137
|
-
<div className="mx-auto w-full md:max-w-2xl p-2">
|
|
138
|
-
<h1>TopazCube Client Test</h1>
|
|
139
|
-
<h2>Todo list: '{doc.title}'</h2>
|
|
140
|
-
{sortedTodos.length > 0 ? (
|
|
141
|
-
<div>
|
|
142
|
-
<ul>
|
|
143
|
-
{sortedTodos.map(
|
|
144
|
-
(todo, index) =>
|
|
145
|
-
!todo.deleted && (
|
|
146
|
-
<li
|
|
147
|
-
key={index}
|
|
148
|
-
className={`flex items-center gap-2 py-1
|
|
149
|
-
${index % 2 == 0 ? "bg-gray-200" : ""} ${
|
|
150
|
-
dragOverIndex === index ? "border-" + (movingUp ? "t" : "b") + "-3 border-blue-500" : ""
|
|
151
|
-
}`}
|
|
152
|
-
draggable={true}
|
|
153
|
-
onDragStart={(e) => handleDragStart(e, todo)}
|
|
154
|
-
onDragOver={(e) => handleDragOver(e, todo, index)}
|
|
155
|
-
onDragEnd={handleDragEnd}
|
|
156
|
-
onDrop={(e) => handleDrop(e, todo)}>
|
|
157
|
-
<button className="!cursor-move">
|
|
158
|
-
<GripHorizontal />
|
|
159
|
-
</button>
|
|
160
|
-
<input
|
|
161
|
-
type="checkbox"
|
|
162
|
-
className="cursor-pointer w-5 h-5"
|
|
163
|
-
checked={todo.completed}
|
|
164
|
-
onChange={() => {
|
|
165
|
-
todo.completed = !todo.completed
|
|
166
|
-
}}
|
|
167
|
-
/>
|
|
168
|
-
<span className="flex-grow ml-2 cursor-move">
|
|
169
|
-
<span className={`${todo.completed ? "line-through opacity-30" : "font-bold"} cursor-text`}>
|
|
170
|
-
{todo.title}
|
|
171
|
-
</span>
|
|
172
|
-
</span>
|
|
173
|
-
<button
|
|
174
|
-
onClick={() => {
|
|
175
|
-
todo.deleted = true
|
|
176
|
-
}}>
|
|
177
|
-
<Trash2 />
|
|
178
|
-
</button>
|
|
179
|
-
</li>
|
|
180
|
-
)
|
|
181
|
-
)}
|
|
182
|
-
</ul>
|
|
183
|
-
</div>
|
|
184
|
-
) : (
|
|
185
|
-
<p>No todos yet. Add some!</p>
|
|
186
|
-
)}
|
|
187
|
-
<br />
|
|
188
|
-
<div className="flex gap-2">
|
|
189
|
-
<input
|
|
190
|
-
type="text"
|
|
191
|
-
className="flex-grow border-2 border-gray-400 px-2 py-1 rounded-md"
|
|
192
|
-
placeholder="New Todo"
|
|
193
|
-
value={newTodo}
|
|
194
|
-
onChange={(e) => setNewTodo(e.target.value)}
|
|
195
|
-
/>
|
|
196
|
-
<button
|
|
197
|
-
className={`bg-gray-300 ${
|
|
198
|
-
newTodo ? "text-black" : "text-gray-400 opacity-50 !cursor-not-allowed"
|
|
199
|
-
} font-bold border-2 border-gray-400 px-4 py-2 rounded-md`}
|
|
200
|
-
onClick={addTodo}
|
|
201
|
-
onKeyDown={(e) => {
|
|
202
|
-
if (e.key === "Enter") {
|
|
203
|
-
addTodo()
|
|
204
|
-
}
|
|
205
|
-
}}>
|
|
206
|
-
Add Todo
|
|
207
|
-
</button>
|
|
208
|
-
</div>
|
|
209
|
-
<br />
|
|
210
|
-
<pre>{JSON.stringify(doc, null, 2)}</pre>
|
|
211
|
-
<br />
|
|
212
|
-
</div>
|
|
213
|
-
{!isConnected ? (
|
|
214
|
-
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50">
|
|
215
|
-
<h2 className="text-white text-xl font-bold">Connecting...</h2>
|
|
216
|
-
</div>
|
|
217
|
-
) : (
|
|
218
|
-
<></>
|
|
219
|
-
)}
|
|
220
|
-
</>
|
|
221
|
-
)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export default App
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>TopazCube Client Test</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="root"></div>
|
|
11
|
-
<script type="module" src="main.jsx"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "topazcube-test-client",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.0.1",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "vite --host 0.0.0.0 --port 4800"
|
|
8
|
-
},
|
|
9
|
-
"devDependencies": {
|
|
10
|
-
"vite": "^6.2.0"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@msgpack/msgpack": "^3.1.0",
|
|
14
|
-
"@tailwindcss/vite": "^4.0.9",
|
|
15
|
-
"@vitejs/plugin-react": "^4.3.4",
|
|
16
|
-
"collections": "^5.1.13",
|
|
17
|
-
"fast-json-patch": "^3.1.1",
|
|
18
|
-
"lucide-react": "^0.476.0",
|
|
19
|
-
"react": "^19.0.0",
|
|
20
|
-
"react-dom": "^19.0.0",
|
|
21
|
-
"tailwindcss": "^4.0.9",
|
|
22
|
-
"ws": "^8.18.1"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "topazcube-test-server",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.0.1",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "reset && nodemon todoserver.js"
|
|
8
|
-
},
|
|
9
|
-
"devDependencies": {
|
|
10
|
-
"vite": "^6.2.0"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@msgpack/msgpack": "^3.1.0",
|
|
14
|
-
"collections": "^5.1.13",
|
|
15
|
-
"fast-json-patch": "^3.1.1",
|
|
16
|
-
"mongodb": "^6.15.0",
|
|
17
|
-
"ws": "^8.18.1"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import TopazCubeServer from "../../src/server.js"
|
|
2
|
-
|
|
3
|
-
class TodoServer extends TopazCubeServer {
|
|
4
|
-
constructor() {
|
|
5
|
-
super({
|
|
6
|
-
port: 4799,
|
|
7
|
-
})
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
canCreate(client, name) {
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
onCreate(name) {
|
|
15
|
-
return {
|
|
16
|
-
todos: [],
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
canUpdate(client, name) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
onHydrate(name, doc) {
|
|
25
|
-
doc.random = Math.random()
|
|
26
|
-
}
|
|
27
|
-
onUpdate(name, doc, dt) {}
|
|
28
|
-
onMessage(client, message) {}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let server = new TodoServer()
|