dylanebert HF staff commited on
Commit
e6b949c
β€’
1 Parent(s): 032bcd0

initial commit

Browse files

update readme

serve

.

save serve

Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:alpine
2
+
3
+ WORKDIR /app
4
+ COPY editor/package.json package.json
5
+ RUN npm install
6
+
7
+ COPY editor/ /app
8
+ RUN npm run build
9
+
10
+ EXPOSE 3000
11
+ CMD ["npm", "start"]
README.md CHANGED
@@ -3,9 +3,38 @@ title: Gsplat Editor
3
  emoji: πŸ“š
4
  colorFrom: yellow
5
  colorTo: red
6
- sdk: static
7
  pinned: false
8
  license: mit
 
9
  ---
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  emoji: πŸ“š
4
  colorFrom: yellow
5
  colorTo: red
6
+ sdk: docker
7
  pinned: false
8
  license: mit
9
+ app_port: 3000
10
  ---
11
 
12
+ # gsplat.js editor
13
+
14
+ This simple editor showcases the realtime editing capabilities of gsplat.js.
15
+
16
+ ## Usage
17
+
18
+ - Import gaussian splatting objects from a file (`.ply` or `.splat`) by dragging and dropping them into the editor window.
19
+ - Download splats as a `.splat` file by clicking the download button in the top right corner.s
20
+ - If an object is selected, only that object will be downloaded. Otherwise, all objects will be combined and downloaded.
21
+ - Use the controls below to edit the splats.
22
+
23
+ ## Controls
24
+
25
+ ### Camera
26
+
27
+ - `Middle Mouse` - Orbit camera
28
+ - `Shift + Middle Mouse` - Pan camera
29
+ - `Scroll Wheel` - Zoom camera
30
+
31
+ ### Editing
32
+
33
+ - `Left Mouse` - Select an object / confirm action
34
+ - `Right Mouse` - Cancel action
35
+ - `G` - Grab selected object
36
+ - `R` - Rotate selected object
37
+ - `S` - Scale selected object
38
+ - `X` - Delete selected object / lock to X axis
39
+ - `Y` - Lock to Y axis
40
+ - `Z` - Lock to Z axis
editor/.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
editor/index.html ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="stylesheet" href="style.css" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>gsplat.js - Editor Demo</title>
8
+ </head>
9
+ <body>
10
+ <div id="progress-container">
11
+ <dialog open id="progress-dialog">
12
+ <p>
13
+ <label for="progress-indicator">Loading scene...</label>
14
+ </p>
15
+ <progress max="100" id="progress-indicator"></progress>
16
+ </dialog>
17
+ </div>
18
+
19
+ <button id="download-button">
20
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 32 32">
21
+ <path
22
+ fill="#ddd"
23
+ d="M26 24v4H6v-4H4v4a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-4zm0-10l-1.41-1.41L17 20.17V2h-2v18.17l-7.59-7.58L6 14l10 10z"
24
+ />
25
+ </svg>
26
+ </button>
27
+
28
+ <canvas id="canvas"> </canvas>
29
+ <script type="module" src="/src/main.ts"></script>
30
+ </body>
31
+ </html>
editor/package-lock.json ADDED
@@ -0,0 +1,1718 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "editor",
3
+ "version": "0.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "editor",
9
+ "version": "0.0.0",
10
+ "dependencies": {
11
+ "gsplat": "^1.0.0"
12
+ },
13
+ "devDependencies": {
14
+ "serve": "^14.2.1",
15
+ "typescript": "^5.2.2",
16
+ "vite": "^5.0.0"
17
+ }
18
+ },
19
+ "node_modules/@esbuild/android-arm": {
20
+ "version": "0.19.8",
21
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.8.tgz",
22
+ "integrity": "sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==",
23
+ "cpu": [
24
+ "arm"
25
+ ],
26
+ "dev": true,
27
+ "optional": true,
28
+ "os": [
29
+ "android"
30
+ ],
31
+ "engines": {
32
+ "node": ">=12"
33
+ }
34
+ },
35
+ "node_modules/@esbuild/android-arm64": {
36
+ "version": "0.19.8",
37
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz",
38
+ "integrity": "sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==",
39
+ "cpu": [
40
+ "arm64"
41
+ ],
42
+ "dev": true,
43
+ "optional": true,
44
+ "os": [
45
+ "android"
46
+ ],
47
+ "engines": {
48
+ "node": ">=12"
49
+ }
50
+ },
51
+ "node_modules/@esbuild/android-x64": {
52
+ "version": "0.19.8",
53
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.8.tgz",
54
+ "integrity": "sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==",
55
+ "cpu": [
56
+ "x64"
57
+ ],
58
+ "dev": true,
59
+ "optional": true,
60
+ "os": [
61
+ "android"
62
+ ],
63
+ "engines": {
64
+ "node": ">=12"
65
+ }
66
+ },
67
+ "node_modules/@esbuild/darwin-arm64": {
68
+ "version": "0.19.8",
69
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz",
70
+ "integrity": "sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==",
71
+ "cpu": [
72
+ "arm64"
73
+ ],
74
+ "dev": true,
75
+ "optional": true,
76
+ "os": [
77
+ "darwin"
78
+ ],
79
+ "engines": {
80
+ "node": ">=12"
81
+ }
82
+ },
83
+ "node_modules/@esbuild/darwin-x64": {
84
+ "version": "0.19.8",
85
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz",
86
+ "integrity": "sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==",
87
+ "cpu": [
88
+ "x64"
89
+ ],
90
+ "dev": true,
91
+ "optional": true,
92
+ "os": [
93
+ "darwin"
94
+ ],
95
+ "engines": {
96
+ "node": ">=12"
97
+ }
98
+ },
99
+ "node_modules/@esbuild/freebsd-arm64": {
100
+ "version": "0.19.8",
101
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz",
102
+ "integrity": "sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==",
103
+ "cpu": [
104
+ "arm64"
105
+ ],
106
+ "dev": true,
107
+ "optional": true,
108
+ "os": [
109
+ "freebsd"
110
+ ],
111
+ "engines": {
112
+ "node": ">=12"
113
+ }
114
+ },
115
+ "node_modules/@esbuild/freebsd-x64": {
116
+ "version": "0.19.8",
117
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz",
118
+ "integrity": "sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==",
119
+ "cpu": [
120
+ "x64"
121
+ ],
122
+ "dev": true,
123
+ "optional": true,
124
+ "os": [
125
+ "freebsd"
126
+ ],
127
+ "engines": {
128
+ "node": ">=12"
129
+ }
130
+ },
131
+ "node_modules/@esbuild/linux-arm": {
132
+ "version": "0.19.8",
133
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz",
134
+ "integrity": "sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==",
135
+ "cpu": [
136
+ "arm"
137
+ ],
138
+ "dev": true,
139
+ "optional": true,
140
+ "os": [
141
+ "linux"
142
+ ],
143
+ "engines": {
144
+ "node": ">=12"
145
+ }
146
+ },
147
+ "node_modules/@esbuild/linux-arm64": {
148
+ "version": "0.19.8",
149
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz",
150
+ "integrity": "sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==",
151
+ "cpu": [
152
+ "arm64"
153
+ ],
154
+ "dev": true,
155
+ "optional": true,
156
+ "os": [
157
+ "linux"
158
+ ],
159
+ "engines": {
160
+ "node": ">=12"
161
+ }
162
+ },
163
+ "node_modules/@esbuild/linux-ia32": {
164
+ "version": "0.19.8",
165
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz",
166
+ "integrity": "sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==",
167
+ "cpu": [
168
+ "ia32"
169
+ ],
170
+ "dev": true,
171
+ "optional": true,
172
+ "os": [
173
+ "linux"
174
+ ],
175
+ "engines": {
176
+ "node": ">=12"
177
+ }
178
+ },
179
+ "node_modules/@esbuild/linux-loong64": {
180
+ "version": "0.19.8",
181
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz",
182
+ "integrity": "sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==",
183
+ "cpu": [
184
+ "loong64"
185
+ ],
186
+ "dev": true,
187
+ "optional": true,
188
+ "os": [
189
+ "linux"
190
+ ],
191
+ "engines": {
192
+ "node": ">=12"
193
+ }
194
+ },
195
+ "node_modules/@esbuild/linux-mips64el": {
196
+ "version": "0.19.8",
197
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz",
198
+ "integrity": "sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==",
199
+ "cpu": [
200
+ "mips64el"
201
+ ],
202
+ "dev": true,
203
+ "optional": true,
204
+ "os": [
205
+ "linux"
206
+ ],
207
+ "engines": {
208
+ "node": ">=12"
209
+ }
210
+ },
211
+ "node_modules/@esbuild/linux-ppc64": {
212
+ "version": "0.19.8",
213
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz",
214
+ "integrity": "sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==",
215
+ "cpu": [
216
+ "ppc64"
217
+ ],
218
+ "dev": true,
219
+ "optional": true,
220
+ "os": [
221
+ "linux"
222
+ ],
223
+ "engines": {
224
+ "node": ">=12"
225
+ }
226
+ },
227
+ "node_modules/@esbuild/linux-riscv64": {
228
+ "version": "0.19.8",
229
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz",
230
+ "integrity": "sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==",
231
+ "cpu": [
232
+ "riscv64"
233
+ ],
234
+ "dev": true,
235
+ "optional": true,
236
+ "os": [
237
+ "linux"
238
+ ],
239
+ "engines": {
240
+ "node": ">=12"
241
+ }
242
+ },
243
+ "node_modules/@esbuild/linux-s390x": {
244
+ "version": "0.19.8",
245
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz",
246
+ "integrity": "sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==",
247
+ "cpu": [
248
+ "s390x"
249
+ ],
250
+ "dev": true,
251
+ "optional": true,
252
+ "os": [
253
+ "linux"
254
+ ],
255
+ "engines": {
256
+ "node": ">=12"
257
+ }
258
+ },
259
+ "node_modules/@esbuild/linux-x64": {
260
+ "version": "0.19.8",
261
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz",
262
+ "integrity": "sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==",
263
+ "cpu": [
264
+ "x64"
265
+ ],
266
+ "dev": true,
267
+ "optional": true,
268
+ "os": [
269
+ "linux"
270
+ ],
271
+ "engines": {
272
+ "node": ">=12"
273
+ }
274
+ },
275
+ "node_modules/@esbuild/netbsd-x64": {
276
+ "version": "0.19.8",
277
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz",
278
+ "integrity": "sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==",
279
+ "cpu": [
280
+ "x64"
281
+ ],
282
+ "dev": true,
283
+ "optional": true,
284
+ "os": [
285
+ "netbsd"
286
+ ],
287
+ "engines": {
288
+ "node": ">=12"
289
+ }
290
+ },
291
+ "node_modules/@esbuild/openbsd-x64": {
292
+ "version": "0.19.8",
293
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz",
294
+ "integrity": "sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==",
295
+ "cpu": [
296
+ "x64"
297
+ ],
298
+ "dev": true,
299
+ "optional": true,
300
+ "os": [
301
+ "openbsd"
302
+ ],
303
+ "engines": {
304
+ "node": ">=12"
305
+ }
306
+ },
307
+ "node_modules/@esbuild/sunos-x64": {
308
+ "version": "0.19.8",
309
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz",
310
+ "integrity": "sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==",
311
+ "cpu": [
312
+ "x64"
313
+ ],
314
+ "dev": true,
315
+ "optional": true,
316
+ "os": [
317
+ "sunos"
318
+ ],
319
+ "engines": {
320
+ "node": ">=12"
321
+ }
322
+ },
323
+ "node_modules/@esbuild/win32-arm64": {
324
+ "version": "0.19.8",
325
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz",
326
+ "integrity": "sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==",
327
+ "cpu": [
328
+ "arm64"
329
+ ],
330
+ "dev": true,
331
+ "optional": true,
332
+ "os": [
333
+ "win32"
334
+ ],
335
+ "engines": {
336
+ "node": ">=12"
337
+ }
338
+ },
339
+ "node_modules/@esbuild/win32-ia32": {
340
+ "version": "0.19.8",
341
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz",
342
+ "integrity": "sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==",
343
+ "cpu": [
344
+ "ia32"
345
+ ],
346
+ "dev": true,
347
+ "optional": true,
348
+ "os": [
349
+ "win32"
350
+ ],
351
+ "engines": {
352
+ "node": ">=12"
353
+ }
354
+ },
355
+ "node_modules/@esbuild/win32-x64": {
356
+ "version": "0.19.8",
357
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz",
358
+ "integrity": "sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==",
359
+ "cpu": [
360
+ "x64"
361
+ ],
362
+ "dev": true,
363
+ "optional": true,
364
+ "os": [
365
+ "win32"
366
+ ],
367
+ "engines": {
368
+ "node": ">=12"
369
+ }
370
+ },
371
+ "node_modules/@rollup/rollup-android-arm-eabi": {
372
+ "version": "4.6.1",
373
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.6.1.tgz",
374
+ "integrity": "sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==",
375
+ "cpu": [
376
+ "arm"
377
+ ],
378
+ "dev": true,
379
+ "optional": true,
380
+ "os": [
381
+ "android"
382
+ ]
383
+ },
384
+ "node_modules/@rollup/rollup-android-arm64": {
385
+ "version": "4.6.1",
386
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.6.1.tgz",
387
+ "integrity": "sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==",
388
+ "cpu": [
389
+ "arm64"
390
+ ],
391
+ "dev": true,
392
+ "optional": true,
393
+ "os": [
394
+ "android"
395
+ ]
396
+ },
397
+ "node_modules/@rollup/rollup-darwin-arm64": {
398
+ "version": "4.6.1",
399
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.6.1.tgz",
400
+ "integrity": "sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==",
401
+ "cpu": [
402
+ "arm64"
403
+ ],
404
+ "dev": true,
405
+ "optional": true,
406
+ "os": [
407
+ "darwin"
408
+ ]
409
+ },
410
+ "node_modules/@rollup/rollup-darwin-x64": {
411
+ "version": "4.6.1",
412
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.6.1.tgz",
413
+ "integrity": "sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==",
414
+ "cpu": [
415
+ "x64"
416
+ ],
417
+ "dev": true,
418
+ "optional": true,
419
+ "os": [
420
+ "darwin"
421
+ ]
422
+ },
423
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
424
+ "version": "4.6.1",
425
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.6.1.tgz",
426
+ "integrity": "sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==",
427
+ "cpu": [
428
+ "arm"
429
+ ],
430
+ "dev": true,
431
+ "optional": true,
432
+ "os": [
433
+ "linux"
434
+ ]
435
+ },
436
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
437
+ "version": "4.6.1",
438
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.6.1.tgz",
439
+ "integrity": "sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==",
440
+ "cpu": [
441
+ "arm64"
442
+ ],
443
+ "dev": true,
444
+ "optional": true,
445
+ "os": [
446
+ "linux"
447
+ ]
448
+ },
449
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
450
+ "version": "4.6.1",
451
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.6.1.tgz",
452
+ "integrity": "sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==",
453
+ "cpu": [
454
+ "arm64"
455
+ ],
456
+ "dev": true,
457
+ "optional": true,
458
+ "os": [
459
+ "linux"
460
+ ]
461
+ },
462
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
463
+ "version": "4.6.1",
464
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.6.1.tgz",
465
+ "integrity": "sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==",
466
+ "cpu": [
467
+ "x64"
468
+ ],
469
+ "dev": true,
470
+ "optional": true,
471
+ "os": [
472
+ "linux"
473
+ ]
474
+ },
475
+ "node_modules/@rollup/rollup-linux-x64-musl": {
476
+ "version": "4.6.1",
477
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.6.1.tgz",
478
+ "integrity": "sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==",
479
+ "cpu": [
480
+ "x64"
481
+ ],
482
+ "dev": true,
483
+ "optional": true,
484
+ "os": [
485
+ "linux"
486
+ ]
487
+ },
488
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
489
+ "version": "4.6.1",
490
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.6.1.tgz",
491
+ "integrity": "sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==",
492
+ "cpu": [
493
+ "arm64"
494
+ ],
495
+ "dev": true,
496
+ "optional": true,
497
+ "os": [
498
+ "win32"
499
+ ]
500
+ },
501
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
502
+ "version": "4.6.1",
503
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.6.1.tgz",
504
+ "integrity": "sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==",
505
+ "cpu": [
506
+ "ia32"
507
+ ],
508
+ "dev": true,
509
+ "optional": true,
510
+ "os": [
511
+ "win32"
512
+ ]
513
+ },
514
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
515
+ "version": "4.6.1",
516
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.6.1.tgz",
517
+ "integrity": "sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==",
518
+ "cpu": [
519
+ "x64"
520
+ ],
521
+ "dev": true,
522
+ "optional": true,
523
+ "os": [
524
+ "win32"
525
+ ]
526
+ },
527
+ "node_modules/@zeit/schemas": {
528
+ "version": "2.29.0",
529
+ "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.29.0.tgz",
530
+ "integrity": "sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==",
531
+ "dev": true
532
+ },
533
+ "node_modules/accepts": {
534
+ "version": "1.3.8",
535
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
536
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
537
+ "dev": true,
538
+ "dependencies": {
539
+ "mime-types": "~2.1.34",
540
+ "negotiator": "0.6.3"
541
+ },
542
+ "engines": {
543
+ "node": ">= 0.6"
544
+ }
545
+ },
546
+ "node_modules/ajv": {
547
+ "version": "8.11.0",
548
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
549
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
550
+ "dev": true,
551
+ "dependencies": {
552
+ "fast-deep-equal": "^3.1.1",
553
+ "json-schema-traverse": "^1.0.0",
554
+ "require-from-string": "^2.0.2",
555
+ "uri-js": "^4.2.2"
556
+ },
557
+ "funding": {
558
+ "type": "github",
559
+ "url": "https://github.com/sponsors/epoberezkin"
560
+ }
561
+ },
562
+ "node_modules/ansi-align": {
563
+ "version": "3.0.1",
564
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
565
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
566
+ "dev": true,
567
+ "dependencies": {
568
+ "string-width": "^4.1.0"
569
+ }
570
+ },
571
+ "node_modules/ansi-align/node_modules/ansi-regex": {
572
+ "version": "5.0.1",
573
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
574
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
575
+ "dev": true,
576
+ "engines": {
577
+ "node": ">=8"
578
+ }
579
+ },
580
+ "node_modules/ansi-align/node_modules/emoji-regex": {
581
+ "version": "8.0.0",
582
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
583
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
584
+ "dev": true
585
+ },
586
+ "node_modules/ansi-align/node_modules/string-width": {
587
+ "version": "4.2.3",
588
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
589
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
590
+ "dev": true,
591
+ "dependencies": {
592
+ "emoji-regex": "^8.0.0",
593
+ "is-fullwidth-code-point": "^3.0.0",
594
+ "strip-ansi": "^6.0.1"
595
+ },
596
+ "engines": {
597
+ "node": ">=8"
598
+ }
599
+ },
600
+ "node_modules/ansi-align/node_modules/strip-ansi": {
601
+ "version": "6.0.1",
602
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
603
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
604
+ "dev": true,
605
+ "dependencies": {
606
+ "ansi-regex": "^5.0.1"
607
+ },
608
+ "engines": {
609
+ "node": ">=8"
610
+ }
611
+ },
612
+ "node_modules/ansi-regex": {
613
+ "version": "6.0.1",
614
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
615
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
616
+ "dev": true,
617
+ "engines": {
618
+ "node": ">=12"
619
+ },
620
+ "funding": {
621
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
622
+ }
623
+ },
624
+ "node_modules/ansi-styles": {
625
+ "version": "6.2.1",
626
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
627
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
628
+ "dev": true,
629
+ "engines": {
630
+ "node": ">=12"
631
+ },
632
+ "funding": {
633
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
634
+ }
635
+ },
636
+ "node_modules/arch": {
637
+ "version": "2.2.0",
638
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
639
+ "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
640
+ "dev": true,
641
+ "funding": [
642
+ {
643
+ "type": "github",
644
+ "url": "https://github.com/sponsors/feross"
645
+ },
646
+ {
647
+ "type": "patreon",
648
+ "url": "https://www.patreon.com/feross"
649
+ },
650
+ {
651
+ "type": "consulting",
652
+ "url": "https://feross.org/support"
653
+ }
654
+ ]
655
+ },
656
+ "node_modules/arg": {
657
+ "version": "5.0.2",
658
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
659
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
660
+ "dev": true
661
+ },
662
+ "node_modules/balanced-match": {
663
+ "version": "1.0.2",
664
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
665
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
666
+ "dev": true
667
+ },
668
+ "node_modules/boxen": {
669
+ "version": "7.0.0",
670
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz",
671
+ "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==",
672
+ "dev": true,
673
+ "dependencies": {
674
+ "ansi-align": "^3.0.1",
675
+ "camelcase": "^7.0.0",
676
+ "chalk": "^5.0.1",
677
+ "cli-boxes": "^3.0.0",
678
+ "string-width": "^5.1.2",
679
+ "type-fest": "^2.13.0",
680
+ "widest-line": "^4.0.1",
681
+ "wrap-ansi": "^8.0.1"
682
+ },
683
+ "engines": {
684
+ "node": ">=14.16"
685
+ },
686
+ "funding": {
687
+ "url": "https://github.com/sponsors/sindresorhus"
688
+ }
689
+ },
690
+ "node_modules/brace-expansion": {
691
+ "version": "1.1.11",
692
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
693
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
694
+ "dev": true,
695
+ "dependencies": {
696
+ "balanced-match": "^1.0.0",
697
+ "concat-map": "0.0.1"
698
+ }
699
+ },
700
+ "node_modules/bytes": {
701
+ "version": "3.0.0",
702
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
703
+ "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
704
+ "dev": true,
705
+ "engines": {
706
+ "node": ">= 0.8"
707
+ }
708
+ },
709
+ "node_modules/camelcase": {
710
+ "version": "7.0.1",
711
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
712
+ "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
713
+ "dev": true,
714
+ "engines": {
715
+ "node": ">=14.16"
716
+ },
717
+ "funding": {
718
+ "url": "https://github.com/sponsors/sindresorhus"
719
+ }
720
+ },
721
+ "node_modules/chalk": {
722
+ "version": "5.0.1",
723
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz",
724
+ "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==",
725
+ "dev": true,
726
+ "engines": {
727
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
728
+ },
729
+ "funding": {
730
+ "url": "https://github.com/chalk/chalk?sponsor=1"
731
+ }
732
+ },
733
+ "node_modules/chalk-template": {
734
+ "version": "0.4.0",
735
+ "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
736
+ "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
737
+ "dev": true,
738
+ "dependencies": {
739
+ "chalk": "^4.1.2"
740
+ },
741
+ "engines": {
742
+ "node": ">=12"
743
+ },
744
+ "funding": {
745
+ "url": "https://github.com/chalk/chalk-template?sponsor=1"
746
+ }
747
+ },
748
+ "node_modules/chalk-template/node_modules/ansi-styles": {
749
+ "version": "4.3.0",
750
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
751
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
752
+ "dev": true,
753
+ "dependencies": {
754
+ "color-convert": "^2.0.1"
755
+ },
756
+ "engines": {
757
+ "node": ">=8"
758
+ },
759
+ "funding": {
760
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
761
+ }
762
+ },
763
+ "node_modules/chalk-template/node_modules/chalk": {
764
+ "version": "4.1.2",
765
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
766
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
767
+ "dev": true,
768
+ "dependencies": {
769
+ "ansi-styles": "^4.1.0",
770
+ "supports-color": "^7.1.0"
771
+ },
772
+ "engines": {
773
+ "node": ">=10"
774
+ },
775
+ "funding": {
776
+ "url": "https://github.com/chalk/chalk?sponsor=1"
777
+ }
778
+ },
779
+ "node_modules/cli-boxes": {
780
+ "version": "3.0.0",
781
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
782
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
783
+ "dev": true,
784
+ "engines": {
785
+ "node": ">=10"
786
+ },
787
+ "funding": {
788
+ "url": "https://github.com/sponsors/sindresorhus"
789
+ }
790
+ },
791
+ "node_modules/clipboardy": {
792
+ "version": "3.0.0",
793
+ "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
794
+ "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==",
795
+ "dev": true,
796
+ "dependencies": {
797
+ "arch": "^2.2.0",
798
+ "execa": "^5.1.1",
799
+ "is-wsl": "^2.2.0"
800
+ },
801
+ "engines": {
802
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
803
+ },
804
+ "funding": {
805
+ "url": "https://github.com/sponsors/sindresorhus"
806
+ }
807
+ },
808
+ "node_modules/color-convert": {
809
+ "version": "2.0.1",
810
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
811
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
812
+ "dev": true,
813
+ "dependencies": {
814
+ "color-name": "~1.1.4"
815
+ },
816
+ "engines": {
817
+ "node": ">=7.0.0"
818
+ }
819
+ },
820
+ "node_modules/color-name": {
821
+ "version": "1.1.4",
822
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
823
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
824
+ "dev": true
825
+ },
826
+ "node_modules/compressible": {
827
+ "version": "2.0.18",
828
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
829
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
830
+ "dev": true,
831
+ "dependencies": {
832
+ "mime-db": ">= 1.43.0 < 2"
833
+ },
834
+ "engines": {
835
+ "node": ">= 0.6"
836
+ }
837
+ },
838
+ "node_modules/compression": {
839
+ "version": "1.7.4",
840
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
841
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
842
+ "dev": true,
843
+ "dependencies": {
844
+ "accepts": "~1.3.5",
845
+ "bytes": "3.0.0",
846
+ "compressible": "~2.0.16",
847
+ "debug": "2.6.9",
848
+ "on-headers": "~1.0.2",
849
+ "safe-buffer": "5.1.2",
850
+ "vary": "~1.1.2"
851
+ },
852
+ "engines": {
853
+ "node": ">= 0.8.0"
854
+ }
855
+ },
856
+ "node_modules/concat-map": {
857
+ "version": "0.0.1",
858
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
859
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
860
+ "dev": true
861
+ },
862
+ "node_modules/content-disposition": {
863
+ "version": "0.5.2",
864
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
865
+ "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
866
+ "dev": true,
867
+ "engines": {
868
+ "node": ">= 0.6"
869
+ }
870
+ },
871
+ "node_modules/cross-spawn": {
872
+ "version": "7.0.3",
873
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
874
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
875
+ "dev": true,
876
+ "dependencies": {
877
+ "path-key": "^3.1.0",
878
+ "shebang-command": "^2.0.0",
879
+ "which": "^2.0.1"
880
+ },
881
+ "engines": {
882
+ "node": ">= 8"
883
+ }
884
+ },
885
+ "node_modules/debug": {
886
+ "version": "2.6.9",
887
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
888
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
889
+ "dev": true,
890
+ "dependencies": {
891
+ "ms": "2.0.0"
892
+ }
893
+ },
894
+ "node_modules/deep-extend": {
895
+ "version": "0.6.0",
896
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
897
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
898
+ "dev": true,
899
+ "engines": {
900
+ "node": ">=4.0.0"
901
+ }
902
+ },
903
+ "node_modules/eastasianwidth": {
904
+ "version": "0.2.0",
905
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
906
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
907
+ "dev": true
908
+ },
909
+ "node_modules/emoji-regex": {
910
+ "version": "9.2.2",
911
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
912
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
913
+ "dev": true
914
+ },
915
+ "node_modules/esbuild": {
916
+ "version": "0.19.8",
917
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.8.tgz",
918
+ "integrity": "sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==",
919
+ "dev": true,
920
+ "hasInstallScript": true,
921
+ "bin": {
922
+ "esbuild": "bin/esbuild"
923
+ },
924
+ "engines": {
925
+ "node": ">=12"
926
+ },
927
+ "optionalDependencies": {
928
+ "@esbuild/android-arm": "0.19.8",
929
+ "@esbuild/android-arm64": "0.19.8",
930
+ "@esbuild/android-x64": "0.19.8",
931
+ "@esbuild/darwin-arm64": "0.19.8",
932
+ "@esbuild/darwin-x64": "0.19.8",
933
+ "@esbuild/freebsd-arm64": "0.19.8",
934
+ "@esbuild/freebsd-x64": "0.19.8",
935
+ "@esbuild/linux-arm": "0.19.8",
936
+ "@esbuild/linux-arm64": "0.19.8",
937
+ "@esbuild/linux-ia32": "0.19.8",
938
+ "@esbuild/linux-loong64": "0.19.8",
939
+ "@esbuild/linux-mips64el": "0.19.8",
940
+ "@esbuild/linux-ppc64": "0.19.8",
941
+ "@esbuild/linux-riscv64": "0.19.8",
942
+ "@esbuild/linux-s390x": "0.19.8",
943
+ "@esbuild/linux-x64": "0.19.8",
944
+ "@esbuild/netbsd-x64": "0.19.8",
945
+ "@esbuild/openbsd-x64": "0.19.8",
946
+ "@esbuild/sunos-x64": "0.19.8",
947
+ "@esbuild/win32-arm64": "0.19.8",
948
+ "@esbuild/win32-ia32": "0.19.8",
949
+ "@esbuild/win32-x64": "0.19.8"
950
+ }
951
+ },
952
+ "node_modules/execa": {
953
+ "version": "5.1.1",
954
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
955
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
956
+ "dev": true,
957
+ "dependencies": {
958
+ "cross-spawn": "^7.0.3",
959
+ "get-stream": "^6.0.0",
960
+ "human-signals": "^2.1.0",
961
+ "is-stream": "^2.0.0",
962
+ "merge-stream": "^2.0.0",
963
+ "npm-run-path": "^4.0.1",
964
+ "onetime": "^5.1.2",
965
+ "signal-exit": "^3.0.3",
966
+ "strip-final-newline": "^2.0.0"
967
+ },
968
+ "engines": {
969
+ "node": ">=10"
970
+ },
971
+ "funding": {
972
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
973
+ }
974
+ },
975
+ "node_modules/fast-deep-equal": {
976
+ "version": "3.1.3",
977
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
978
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
979
+ "dev": true
980
+ },
981
+ "node_modules/fast-url-parser": {
982
+ "version": "1.1.3",
983
+ "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
984
+ "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==",
985
+ "dev": true,
986
+ "dependencies": {
987
+ "punycode": "^1.3.2"
988
+ }
989
+ },
990
+ "node_modules/fsevents": {
991
+ "version": "2.3.3",
992
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
993
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
994
+ "dev": true,
995
+ "hasInstallScript": true,
996
+ "optional": true,
997
+ "os": [
998
+ "darwin"
999
+ ],
1000
+ "engines": {
1001
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1002
+ }
1003
+ },
1004
+ "node_modules/get-stream": {
1005
+ "version": "6.0.1",
1006
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
1007
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
1008
+ "dev": true,
1009
+ "engines": {
1010
+ "node": ">=10"
1011
+ },
1012
+ "funding": {
1013
+ "url": "https://github.com/sponsors/sindresorhus"
1014
+ }
1015
+ },
1016
+ "node_modules/gsplat": {
1017
+ "version": "1.0.0",
1018
+ "resolved": "https://registry.npmjs.org/gsplat/-/gsplat-1.0.0.tgz",
1019
+ "integrity": "sha512-wWe/Pq9Pn8F43wZFPyb0hapbmZwHJRjv92BNwBAVRFQa4HEIStBKDmq+yUulEt2FtTcVfPbkjO+8rKiluwTT3w=="
1020
+ },
1021
+ "node_modules/has-flag": {
1022
+ "version": "4.0.0",
1023
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
1024
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
1025
+ "dev": true,
1026
+ "engines": {
1027
+ "node": ">=8"
1028
+ }
1029
+ },
1030
+ "node_modules/human-signals": {
1031
+ "version": "2.1.0",
1032
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
1033
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
1034
+ "dev": true,
1035
+ "engines": {
1036
+ "node": ">=10.17.0"
1037
+ }
1038
+ },
1039
+ "node_modules/ini": {
1040
+ "version": "1.3.8",
1041
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
1042
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
1043
+ "dev": true
1044
+ },
1045
+ "node_modules/is-docker": {
1046
+ "version": "2.2.1",
1047
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
1048
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
1049
+ "dev": true,
1050
+ "bin": {
1051
+ "is-docker": "cli.js"
1052
+ },
1053
+ "engines": {
1054
+ "node": ">=8"
1055
+ },
1056
+ "funding": {
1057
+ "url": "https://github.com/sponsors/sindresorhus"
1058
+ }
1059
+ },
1060
+ "node_modules/is-fullwidth-code-point": {
1061
+ "version": "3.0.0",
1062
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
1063
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
1064
+ "dev": true,
1065
+ "engines": {
1066
+ "node": ">=8"
1067
+ }
1068
+ },
1069
+ "node_modules/is-port-reachable": {
1070
+ "version": "4.0.0",
1071
+ "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz",
1072
+ "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==",
1073
+ "dev": true,
1074
+ "engines": {
1075
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
1076
+ },
1077
+ "funding": {
1078
+ "url": "https://github.com/sponsors/sindresorhus"
1079
+ }
1080
+ },
1081
+ "node_modules/is-stream": {
1082
+ "version": "2.0.1",
1083
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
1084
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
1085
+ "dev": true,
1086
+ "engines": {
1087
+ "node": ">=8"
1088
+ },
1089
+ "funding": {
1090
+ "url": "https://github.com/sponsors/sindresorhus"
1091
+ }
1092
+ },
1093
+ "node_modules/is-wsl": {
1094
+ "version": "2.2.0",
1095
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
1096
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
1097
+ "dev": true,
1098
+ "dependencies": {
1099
+ "is-docker": "^2.0.0"
1100
+ },
1101
+ "engines": {
1102
+ "node": ">=8"
1103
+ }
1104
+ },
1105
+ "node_modules/isexe": {
1106
+ "version": "2.0.0",
1107
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1108
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
1109
+ "dev": true
1110
+ },
1111
+ "node_modules/json-schema-traverse": {
1112
+ "version": "1.0.0",
1113
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
1114
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
1115
+ "dev": true
1116
+ },
1117
+ "node_modules/merge-stream": {
1118
+ "version": "2.0.0",
1119
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
1120
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
1121
+ "dev": true
1122
+ },
1123
+ "node_modules/mime-db": {
1124
+ "version": "1.52.0",
1125
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1126
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1127
+ "dev": true,
1128
+ "engines": {
1129
+ "node": ">= 0.6"
1130
+ }
1131
+ },
1132
+ "node_modules/mime-types": {
1133
+ "version": "2.1.35",
1134
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1135
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1136
+ "dev": true,
1137
+ "dependencies": {
1138
+ "mime-db": "1.52.0"
1139
+ },
1140
+ "engines": {
1141
+ "node": ">= 0.6"
1142
+ }
1143
+ },
1144
+ "node_modules/mimic-fn": {
1145
+ "version": "2.1.0",
1146
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
1147
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
1148
+ "dev": true,
1149
+ "engines": {
1150
+ "node": ">=6"
1151
+ }
1152
+ },
1153
+ "node_modules/minimatch": {
1154
+ "version": "3.1.2",
1155
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1156
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1157
+ "dev": true,
1158
+ "dependencies": {
1159
+ "brace-expansion": "^1.1.7"
1160
+ },
1161
+ "engines": {
1162
+ "node": "*"
1163
+ }
1164
+ },
1165
+ "node_modules/minimist": {
1166
+ "version": "1.2.8",
1167
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
1168
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
1169
+ "dev": true,
1170
+ "funding": {
1171
+ "url": "https://github.com/sponsors/ljharb"
1172
+ }
1173
+ },
1174
+ "node_modules/ms": {
1175
+ "version": "2.0.0",
1176
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1177
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1178
+ "dev": true
1179
+ },
1180
+ "node_modules/nanoid": {
1181
+ "version": "3.3.7",
1182
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
1183
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
1184
+ "dev": true,
1185
+ "funding": [
1186
+ {
1187
+ "type": "github",
1188
+ "url": "https://github.com/sponsors/ai"
1189
+ }
1190
+ ],
1191
+ "bin": {
1192
+ "nanoid": "bin/nanoid.cjs"
1193
+ },
1194
+ "engines": {
1195
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1196
+ }
1197
+ },
1198
+ "node_modules/negotiator": {
1199
+ "version": "0.6.3",
1200
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1201
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1202
+ "dev": true,
1203
+ "engines": {
1204
+ "node": ">= 0.6"
1205
+ }
1206
+ },
1207
+ "node_modules/npm-run-path": {
1208
+ "version": "4.0.1",
1209
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
1210
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
1211
+ "dev": true,
1212
+ "dependencies": {
1213
+ "path-key": "^3.0.0"
1214
+ },
1215
+ "engines": {
1216
+ "node": ">=8"
1217
+ }
1218
+ },
1219
+ "node_modules/on-headers": {
1220
+ "version": "1.0.2",
1221
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
1222
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
1223
+ "dev": true,
1224
+ "engines": {
1225
+ "node": ">= 0.8"
1226
+ }
1227
+ },
1228
+ "node_modules/onetime": {
1229
+ "version": "5.1.2",
1230
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
1231
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
1232
+ "dev": true,
1233
+ "dependencies": {
1234
+ "mimic-fn": "^2.1.0"
1235
+ },
1236
+ "engines": {
1237
+ "node": ">=6"
1238
+ },
1239
+ "funding": {
1240
+ "url": "https://github.com/sponsors/sindresorhus"
1241
+ }
1242
+ },
1243
+ "node_modules/path-is-inside": {
1244
+ "version": "1.0.2",
1245
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
1246
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
1247
+ "dev": true
1248
+ },
1249
+ "node_modules/path-key": {
1250
+ "version": "3.1.1",
1251
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
1252
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
1253
+ "dev": true,
1254
+ "engines": {
1255
+ "node": ">=8"
1256
+ }
1257
+ },
1258
+ "node_modules/path-to-regexp": {
1259
+ "version": "2.2.1",
1260
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
1261
+ "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==",
1262
+ "dev": true
1263
+ },
1264
+ "node_modules/picocolors": {
1265
+ "version": "1.0.0",
1266
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
1267
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
1268
+ "dev": true
1269
+ },
1270
+ "node_modules/postcss": {
1271
+ "version": "8.4.31",
1272
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
1273
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
1274
+ "dev": true,
1275
+ "funding": [
1276
+ {
1277
+ "type": "opencollective",
1278
+ "url": "https://opencollective.com/postcss/"
1279
+ },
1280
+ {
1281
+ "type": "tidelift",
1282
+ "url": "https://tidelift.com/funding/github/npm/postcss"
1283
+ },
1284
+ {
1285
+ "type": "github",
1286
+ "url": "https://github.com/sponsors/ai"
1287
+ }
1288
+ ],
1289
+ "dependencies": {
1290
+ "nanoid": "^3.3.6",
1291
+ "picocolors": "^1.0.0",
1292
+ "source-map-js": "^1.0.2"
1293
+ },
1294
+ "engines": {
1295
+ "node": "^10 || ^12 || >=14"
1296
+ }
1297
+ },
1298
+ "node_modules/punycode": {
1299
+ "version": "1.4.1",
1300
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
1301
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
1302
+ "dev": true
1303
+ },
1304
+ "node_modules/range-parser": {
1305
+ "version": "1.2.0",
1306
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
1307
+ "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==",
1308
+ "dev": true,
1309
+ "engines": {
1310
+ "node": ">= 0.6"
1311
+ }
1312
+ },
1313
+ "node_modules/rc": {
1314
+ "version": "1.2.8",
1315
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
1316
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
1317
+ "dev": true,
1318
+ "dependencies": {
1319
+ "deep-extend": "^0.6.0",
1320
+ "ini": "~1.3.0",
1321
+ "minimist": "^1.2.0",
1322
+ "strip-json-comments": "~2.0.1"
1323
+ },
1324
+ "bin": {
1325
+ "rc": "cli.js"
1326
+ }
1327
+ },
1328
+ "node_modules/registry-auth-token": {
1329
+ "version": "3.3.2",
1330
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
1331
+ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
1332
+ "dev": true,
1333
+ "dependencies": {
1334
+ "rc": "^1.1.6",
1335
+ "safe-buffer": "^5.0.1"
1336
+ }
1337
+ },
1338
+ "node_modules/registry-url": {
1339
+ "version": "3.1.0",
1340
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
1341
+ "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
1342
+ "dev": true,
1343
+ "dependencies": {
1344
+ "rc": "^1.0.1"
1345
+ },
1346
+ "engines": {
1347
+ "node": ">=0.10.0"
1348
+ }
1349
+ },
1350
+ "node_modules/require-from-string": {
1351
+ "version": "2.0.2",
1352
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
1353
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
1354
+ "dev": true,
1355
+ "engines": {
1356
+ "node": ">=0.10.0"
1357
+ }
1358
+ },
1359
+ "node_modules/rollup": {
1360
+ "version": "4.6.1",
1361
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.6.1.tgz",
1362
+ "integrity": "sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==",
1363
+ "dev": true,
1364
+ "bin": {
1365
+ "rollup": "dist/bin/rollup"
1366
+ },
1367
+ "engines": {
1368
+ "node": ">=18.0.0",
1369
+ "npm": ">=8.0.0"
1370
+ },
1371
+ "optionalDependencies": {
1372
+ "@rollup/rollup-android-arm-eabi": "4.6.1",
1373
+ "@rollup/rollup-android-arm64": "4.6.1",
1374
+ "@rollup/rollup-darwin-arm64": "4.6.1",
1375
+ "@rollup/rollup-darwin-x64": "4.6.1",
1376
+ "@rollup/rollup-linux-arm-gnueabihf": "4.6.1",
1377
+ "@rollup/rollup-linux-arm64-gnu": "4.6.1",
1378
+ "@rollup/rollup-linux-arm64-musl": "4.6.1",
1379
+ "@rollup/rollup-linux-x64-gnu": "4.6.1",
1380
+ "@rollup/rollup-linux-x64-musl": "4.6.1",
1381
+ "@rollup/rollup-win32-arm64-msvc": "4.6.1",
1382
+ "@rollup/rollup-win32-ia32-msvc": "4.6.1",
1383
+ "@rollup/rollup-win32-x64-msvc": "4.6.1",
1384
+ "fsevents": "~2.3.2"
1385
+ }
1386
+ },
1387
+ "node_modules/safe-buffer": {
1388
+ "version": "5.1.2",
1389
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1390
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
1391
+ "dev": true
1392
+ },
1393
+ "node_modules/serve": {
1394
+ "version": "14.2.1",
1395
+ "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.1.tgz",
1396
+ "integrity": "sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA==",
1397
+ "dev": true,
1398
+ "dependencies": {
1399
+ "@zeit/schemas": "2.29.0",
1400
+ "ajv": "8.11.0",
1401
+ "arg": "5.0.2",
1402
+ "boxen": "7.0.0",
1403
+ "chalk": "5.0.1",
1404
+ "chalk-template": "0.4.0",
1405
+ "clipboardy": "3.0.0",
1406
+ "compression": "1.7.4",
1407
+ "is-port-reachable": "4.0.0",
1408
+ "serve-handler": "6.1.5",
1409
+ "update-check": "1.5.4"
1410
+ },
1411
+ "bin": {
1412
+ "serve": "build/main.js"
1413
+ },
1414
+ "engines": {
1415
+ "node": ">= 14"
1416
+ }
1417
+ },
1418
+ "node_modules/serve-handler": {
1419
+ "version": "6.1.5",
1420
+ "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz",
1421
+ "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==",
1422
+ "dev": true,
1423
+ "dependencies": {
1424
+ "bytes": "3.0.0",
1425
+ "content-disposition": "0.5.2",
1426
+ "fast-url-parser": "1.1.3",
1427
+ "mime-types": "2.1.18",
1428
+ "minimatch": "3.1.2",
1429
+ "path-is-inside": "1.0.2",
1430
+ "path-to-regexp": "2.2.1",
1431
+ "range-parser": "1.2.0"
1432
+ }
1433
+ },
1434
+ "node_modules/serve-handler/node_modules/mime-db": {
1435
+ "version": "1.33.0",
1436
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
1437
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
1438
+ "dev": true,
1439
+ "engines": {
1440
+ "node": ">= 0.6"
1441
+ }
1442
+ },
1443
+ "node_modules/serve-handler/node_modules/mime-types": {
1444
+ "version": "2.1.18",
1445
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
1446
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
1447
+ "dev": true,
1448
+ "dependencies": {
1449
+ "mime-db": "~1.33.0"
1450
+ },
1451
+ "engines": {
1452
+ "node": ">= 0.6"
1453
+ }
1454
+ },
1455
+ "node_modules/shebang-command": {
1456
+ "version": "2.0.0",
1457
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
1458
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1459
+ "dev": true,
1460
+ "dependencies": {
1461
+ "shebang-regex": "^3.0.0"
1462
+ },
1463
+ "engines": {
1464
+ "node": ">=8"
1465
+ }
1466
+ },
1467
+ "node_modules/shebang-regex": {
1468
+ "version": "3.0.0",
1469
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
1470
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
1471
+ "dev": true,
1472
+ "engines": {
1473
+ "node": ">=8"
1474
+ }
1475
+ },
1476
+ "node_modules/signal-exit": {
1477
+ "version": "3.0.7",
1478
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
1479
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
1480
+ "dev": true
1481
+ },
1482
+ "node_modules/source-map-js": {
1483
+ "version": "1.0.2",
1484
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
1485
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
1486
+ "dev": true,
1487
+ "engines": {
1488
+ "node": ">=0.10.0"
1489
+ }
1490
+ },
1491
+ "node_modules/string-width": {
1492
+ "version": "5.1.2",
1493
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
1494
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
1495
+ "dev": true,
1496
+ "dependencies": {
1497
+ "eastasianwidth": "^0.2.0",
1498
+ "emoji-regex": "^9.2.2",
1499
+ "strip-ansi": "^7.0.1"
1500
+ },
1501
+ "engines": {
1502
+ "node": ">=12"
1503
+ },
1504
+ "funding": {
1505
+ "url": "https://github.com/sponsors/sindresorhus"
1506
+ }
1507
+ },
1508
+ "node_modules/strip-ansi": {
1509
+ "version": "7.1.0",
1510
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
1511
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
1512
+ "dev": true,
1513
+ "dependencies": {
1514
+ "ansi-regex": "^6.0.1"
1515
+ },
1516
+ "engines": {
1517
+ "node": ">=12"
1518
+ },
1519
+ "funding": {
1520
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
1521
+ }
1522
+ },
1523
+ "node_modules/strip-final-newline": {
1524
+ "version": "2.0.0",
1525
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
1526
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
1527
+ "dev": true,
1528
+ "engines": {
1529
+ "node": ">=6"
1530
+ }
1531
+ },
1532
+ "node_modules/strip-json-comments": {
1533
+ "version": "2.0.1",
1534
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
1535
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
1536
+ "dev": true,
1537
+ "engines": {
1538
+ "node": ">=0.10.0"
1539
+ }
1540
+ },
1541
+ "node_modules/supports-color": {
1542
+ "version": "7.2.0",
1543
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
1544
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
1545
+ "dev": true,
1546
+ "dependencies": {
1547
+ "has-flag": "^4.0.0"
1548
+ },
1549
+ "engines": {
1550
+ "node": ">=8"
1551
+ }
1552
+ },
1553
+ "node_modules/type-fest": {
1554
+ "version": "2.19.0",
1555
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
1556
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
1557
+ "dev": true,
1558
+ "engines": {
1559
+ "node": ">=12.20"
1560
+ },
1561
+ "funding": {
1562
+ "url": "https://github.com/sponsors/sindresorhus"
1563
+ }
1564
+ },
1565
+ "node_modules/typescript": {
1566
+ "version": "5.3.2",
1567
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
1568
+ "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
1569
+ "dev": true,
1570
+ "bin": {
1571
+ "tsc": "bin/tsc",
1572
+ "tsserver": "bin/tsserver"
1573
+ },
1574
+ "engines": {
1575
+ "node": ">=14.17"
1576
+ }
1577
+ },
1578
+ "node_modules/update-check": {
1579
+ "version": "1.5.4",
1580
+ "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz",
1581
+ "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==",
1582
+ "dev": true,
1583
+ "dependencies": {
1584
+ "registry-auth-token": "3.3.2",
1585
+ "registry-url": "3.1.0"
1586
+ }
1587
+ },
1588
+ "node_modules/uri-js": {
1589
+ "version": "4.4.1",
1590
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
1591
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
1592
+ "dev": true,
1593
+ "dependencies": {
1594
+ "punycode": "^2.1.0"
1595
+ }
1596
+ },
1597
+ "node_modules/uri-js/node_modules/punycode": {
1598
+ "version": "2.3.1",
1599
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
1600
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
1601
+ "dev": true,
1602
+ "engines": {
1603
+ "node": ">=6"
1604
+ }
1605
+ },
1606
+ "node_modules/vary": {
1607
+ "version": "1.1.2",
1608
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1609
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1610
+ "dev": true,
1611
+ "engines": {
1612
+ "node": ">= 0.8"
1613
+ }
1614
+ },
1615
+ "node_modules/vite": {
1616
+ "version": "5.0.4",
1617
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.4.tgz",
1618
+ "integrity": "sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==",
1619
+ "dev": true,
1620
+ "dependencies": {
1621
+ "esbuild": "^0.19.3",
1622
+ "postcss": "^8.4.31",
1623
+ "rollup": "^4.2.0"
1624
+ },
1625
+ "bin": {
1626
+ "vite": "bin/vite.js"
1627
+ },
1628
+ "engines": {
1629
+ "node": "^18.0.0 || >=20.0.0"
1630
+ },
1631
+ "funding": {
1632
+ "url": "https://github.com/vitejs/vite?sponsor=1"
1633
+ },
1634
+ "optionalDependencies": {
1635
+ "fsevents": "~2.3.3"
1636
+ },
1637
+ "peerDependencies": {
1638
+ "@types/node": "^18.0.0 || >=20.0.0",
1639
+ "less": "*",
1640
+ "lightningcss": "^1.21.0",
1641
+ "sass": "*",
1642
+ "stylus": "*",
1643
+ "sugarss": "*",
1644
+ "terser": "^5.4.0"
1645
+ },
1646
+ "peerDependenciesMeta": {
1647
+ "@types/node": {
1648
+ "optional": true
1649
+ },
1650
+ "less": {
1651
+ "optional": true
1652
+ },
1653
+ "lightningcss": {
1654
+ "optional": true
1655
+ },
1656
+ "sass": {
1657
+ "optional": true
1658
+ },
1659
+ "stylus": {
1660
+ "optional": true
1661
+ },
1662
+ "sugarss": {
1663
+ "optional": true
1664
+ },
1665
+ "terser": {
1666
+ "optional": true
1667
+ }
1668
+ }
1669
+ },
1670
+ "node_modules/which": {
1671
+ "version": "2.0.2",
1672
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
1673
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
1674
+ "dev": true,
1675
+ "dependencies": {
1676
+ "isexe": "^2.0.0"
1677
+ },
1678
+ "bin": {
1679
+ "node-which": "bin/node-which"
1680
+ },
1681
+ "engines": {
1682
+ "node": ">= 8"
1683
+ }
1684
+ },
1685
+ "node_modules/widest-line": {
1686
+ "version": "4.0.1",
1687
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
1688
+ "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
1689
+ "dev": true,
1690
+ "dependencies": {
1691
+ "string-width": "^5.0.1"
1692
+ },
1693
+ "engines": {
1694
+ "node": ">=12"
1695
+ },
1696
+ "funding": {
1697
+ "url": "https://github.com/sponsors/sindresorhus"
1698
+ }
1699
+ },
1700
+ "node_modules/wrap-ansi": {
1701
+ "version": "8.1.0",
1702
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
1703
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
1704
+ "dev": true,
1705
+ "dependencies": {
1706
+ "ansi-styles": "^6.1.0",
1707
+ "string-width": "^5.0.1",
1708
+ "strip-ansi": "^7.0.1"
1709
+ },
1710
+ "engines": {
1711
+ "node": ">=12"
1712
+ },
1713
+ "funding": {
1714
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
1715
+ }
1716
+ }
1717
+ }
1718
+ }
editor/package.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "editor",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview",
10
+ "start": "serve dist"
11
+ },
12
+ "devDependencies": {
13
+ "serve": "^14.2.1",
14
+ "typescript": "^5.2.2",
15
+ "vite": "^5.0.0"
16
+ },
17
+ "dependencies": {
18
+ "gsplat": "^1.0.0"
19
+ }
20
+ }
editor/src/Action.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ export interface Action {
2
+ execute(): void;
3
+ undo(): void;
4
+ }
editor/src/Controls.ts ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { InputHandler } from "./InputHandler";
2
+
3
+ class Controls {
4
+ private _inputHandlers: InputHandler[];
5
+
6
+ constructor(inputHandlers: InputHandler[], canvas: HTMLCanvasElement) {
7
+ this._inputHandlers = inputHandlers;
8
+
9
+ window.addEventListener("keydown", this.handleInput.bind(this));
10
+ canvas.addEventListener("mousemove", this.handleInput.bind(this));
11
+ canvas.addEventListener("click", this.handleInput.bind(this));
12
+ canvas.addEventListener("contextmenu", this.handleInput.bind(this));
13
+ }
14
+
15
+ handleInput(event: Event) {
16
+ for (const handler of this._inputHandlers) {
17
+ handler.handleInput(event);
18
+ }
19
+ }
20
+ }
21
+
22
+ export { Controls };
editor/src/DefaultMode.ts ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { UndoManager } from "./UndoManager";
2
+ import { ModeManager } from "./ModeManager";
3
+ import { SelectionManager } from "./SelectionManager";
4
+ import { InputMode } from "./InputMode";
5
+ import { Engine } from "./Engine";
6
+
7
+ class DefaultMode implements InputMode {
8
+ exit: () => void;
9
+
10
+ constructor(engine: Engine) {
11
+ const handleEnterGrabMode = () => {
12
+ if (SelectionManager.selectedSplat) {
13
+ ModeManager.enterMode("grab");
14
+ }
15
+ };
16
+
17
+ const handleEnterRotateMode = () => {
18
+ if (SelectionManager.selectedSplat) {
19
+ ModeManager.enterMode("rotate");
20
+ }
21
+ };
22
+
23
+ const handleEnterScaleMode = () => {
24
+ if (SelectionManager.selectedSplat) {
25
+ ModeManager.enterMode("scale");
26
+ }
27
+ };
28
+
29
+ const handleDelete = () => {
30
+ if (SelectionManager.selectedSplat) {
31
+ engine.scene.removeObject(SelectionManager.selectedSplat);
32
+ SelectionManager.selectedSplat = null;
33
+ }
34
+ };
35
+
36
+ const handleUndo = () => {
37
+ UndoManager.undo();
38
+ };
39
+
40
+ const handleClearSelection = (event: KeyboardEvent) => {
41
+ if (event.altKey) {
42
+ SelectionManager.selectedSplat = null;
43
+ }
44
+ };
45
+
46
+ const handleClick = () => {
47
+ const mousePosition = engine.mouseManager.currentMousePosition;
48
+ const result = engine.intersectionTester.testPoint(mousePosition.x, mousePosition.y);
49
+ if (result !== null) {
50
+ SelectionManager.selectedSplat = result;
51
+ } else {
52
+ SelectionManager.selectedSplat = null;
53
+ }
54
+ };
55
+
56
+ engine.keyboardManager.registerKey("g", handleEnterGrabMode);
57
+ engine.keyboardManager.registerKey("r", handleEnterRotateMode);
58
+ engine.keyboardManager.registerKey("s", handleEnterScaleMode);
59
+ engine.keyboardManager.registerKey("x", handleDelete);
60
+ engine.keyboardManager.registerKey("z", handleUndo);
61
+ engine.keyboardManager.registerKey("a", handleClearSelection);
62
+ engine.mouseManager.registerMouse("click", handleClick);
63
+ engine.orbitControls.enabled = true;
64
+
65
+ this.exit = () => {
66
+ engine.keyboardManager.unregisterKey("g");
67
+ engine.keyboardManager.unregisterKey("r");
68
+ engine.keyboardManager.unregisterKey("s");
69
+ engine.keyboardManager.unregisterKey("x");
70
+ engine.keyboardManager.unregisterKey("z");
71
+ engine.keyboardManager.unregisterKey("a");
72
+ engine.mouseManager.unregisterMouse("click");
73
+ engine.orbitControls.enabled = false;
74
+ };
75
+ }
76
+ }
77
+
78
+ export { DefaultMode };
editor/src/Engine.ts ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { OrbitControls } from "./OrbitControls";
3
+ import { GridProgram } from "./programs/GridProgram";
4
+ import { AxisProgram } from "./programs/AxisProgram";
5
+ import { ModeManager } from "./ModeManager";
6
+ import { Controls } from "./Controls";
7
+ import { MouseManager } from "./MouseManager";
8
+ import { KeyboardManager } from "./KeyboardManager";
9
+ import { DefaultMode } from "./DefaultMode";
10
+ import { GrabMode } from "./GrabMode";
11
+ import { RotateMode } from "./RotateMode";
12
+ import { ScaleMode } from "./ScaleMode";
13
+
14
+ class Engine {
15
+ private _scene: SPLAT.Scene;
16
+ private _camera: SPLAT.Camera;
17
+ private _renderer: SPLAT.WebGLRenderer;
18
+ private _orbitControls: OrbitControls;
19
+ private _intersectionTester: SPLAT.IntersectionTester;
20
+ private _keyboardManager: KeyboardManager;
21
+ private _mouseManager: MouseManager;
22
+
23
+ constructor(canvas: HTMLCanvasElement) {
24
+ this._scene = new SPLAT.Scene();
25
+ this._camera = new SPLAT.Camera();
26
+ this._camera.data.setSize(canvas.clientWidth, canvas.clientHeight);
27
+
28
+ this._renderer = new SPLAT.WebGLRenderer(canvas);
29
+ this._renderer.addProgram(new AxisProgram(this._renderer, []));
30
+ this._renderer.addProgram(new GridProgram(this._renderer, []));
31
+ this._orbitControls = new OrbitControls(this._camera, canvas);
32
+ this._intersectionTester = new SPLAT.IntersectionTester(this._renderer.renderProgram);
33
+
34
+ this._keyboardManager = new KeyboardManager();
35
+ this._mouseManager = new MouseManager(canvas);
36
+ new Controls([this._keyboardManager, this._mouseManager], canvas);
37
+
38
+ ModeManager.registerMode("default", () => new DefaultMode(this));
39
+ ModeManager.registerMode("grab", () => new GrabMode(this));
40
+ ModeManager.registerMode("rotate", () => new RotateMode(this));
41
+ ModeManager.registerMode("scale", () => new ScaleMode(this));
42
+ ModeManager.enterMode("default");
43
+ }
44
+
45
+ update() {
46
+ this._orbitControls.update();
47
+ this._renderer.render(this._scene, this._camera);
48
+ }
49
+
50
+ get scene(): SPLAT.Scene {
51
+ return this._scene;
52
+ }
53
+
54
+ get camera(): SPLAT.Camera {
55
+ return this._camera;
56
+ }
57
+
58
+ get renderer(): SPLAT.WebGLRenderer {
59
+ return this._renderer;
60
+ }
61
+
62
+ get orbitControls(): OrbitControls {
63
+ return this._orbitControls;
64
+ }
65
+
66
+ get intersectionTester(): SPLAT.IntersectionTester {
67
+ return this._intersectionTester;
68
+ }
69
+
70
+ get keyboardManager(): KeyboardManager {
71
+ return this._keyboardManager;
72
+ }
73
+
74
+ get mouseManager(): MouseManager {
75
+ return this._mouseManager;
76
+ }
77
+ }
78
+
79
+ export { Engine };
editor/src/GrabMode.ts ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { InputMode } from "./InputMode";
3
+ import { Engine } from "./Engine";
4
+ import { ModeManager } from "./ModeManager";
5
+ import { SelectionManager } from "./SelectionManager";
6
+ import { UndoManager } from "./UndoManager";
7
+ import { MoveAction } from "./MoveAction";
8
+
9
+ class GrabMode implements InputMode {
10
+ exit: () => void;
11
+
12
+ constructor(engine: Engine) {
13
+ const splat = SelectionManager.selectedSplat as SPLAT.Splat;
14
+ const initialMousePosition = engine.mouseManager.currentMousePosition.clone();
15
+ const initialPosition = splat.position.clone();
16
+
17
+ let axis: "x" | "y" | "z" | null = null;
18
+
19
+ const handleClick = () => {
20
+ const action = new MoveAction(splat, initialPosition, splat.position);
21
+ UndoManager.do(action);
22
+ ModeManager.enterMode("default");
23
+ };
24
+
25
+ const handleCancel = () => {
26
+ splat.position = initialPosition;
27
+ ModeManager.enterMode("default");
28
+ };
29
+
30
+ const handleMouseMove = () => {
31
+ const plane = new SPLAT.Plane(engine.camera.forward, initialPosition);
32
+ const initialDirection = engine.camera.screenPointToRay(initialMousePosition.x, initialMousePosition.y);
33
+ const newDirection = engine.camera.screenPointToRay(
34
+ engine.mouseManager.currentMousePosition.x,
35
+ engine.mouseManager.currentMousePosition.y,
36
+ );
37
+ const initialIntersection = plane.intersect(engine.camera.position, initialDirection);
38
+ const newIntersection = plane.intersect(engine.camera.position, newDirection);
39
+ if (initialIntersection && newIntersection) {
40
+ let delta = newIntersection.subtract(initialIntersection);
41
+ switch (axis) {
42
+ case "x":
43
+ delta = new SPLAT.Vector3(delta.x, 0, 0);
44
+ break;
45
+ case "y":
46
+ delta = new SPLAT.Vector3(0, delta.y, 0);
47
+ break;
48
+ case "z":
49
+ delta = new SPLAT.Vector3(0, 0, delta.z);
50
+ break;
51
+ }
52
+ splat.position = initialPosition.add(delta);
53
+ }
54
+ };
55
+
56
+ const handleAxisX = () => {
57
+ if (axis === "x") {
58
+ axis = null;
59
+ } else {
60
+ axis = "x";
61
+ }
62
+ handleMouseMove();
63
+ };
64
+
65
+ const handleAxisY = () => {
66
+ if (axis === "y") {
67
+ axis = null;
68
+ } else {
69
+ axis = "y";
70
+ }
71
+ handleMouseMove();
72
+ };
73
+
74
+ const handleAxisZ = () => {
75
+ if (axis === "z") {
76
+ axis = null;
77
+ } else {
78
+ axis = "z";
79
+ }
80
+ handleMouseMove();
81
+ };
82
+
83
+ engine.mouseManager.registerMouse("click", handleClick);
84
+ engine.mouseManager.registerMouse("contextmenu", handleCancel);
85
+ engine.mouseManager.registerMouse("mousemove", handleMouseMove);
86
+ engine.keyboardManager.registerKey("Escape", handleCancel);
87
+ engine.keyboardManager.registerKey("g", handleCancel);
88
+ engine.keyboardManager.registerKey("x", handleAxisX);
89
+ engine.keyboardManager.registerKey("y", handleAxisY);
90
+ engine.keyboardManager.registerKey("z", handleAxisZ);
91
+
92
+ this.exit = () => {
93
+ engine.mouseManager.unregisterMouse("click");
94
+ engine.mouseManager.unregisterMouse("contextmenu");
95
+ engine.mouseManager.unregisterMouse("mousemove");
96
+ engine.keyboardManager.unregisterKey("Escape");
97
+ engine.keyboardManager.unregisterKey("g");
98
+ engine.keyboardManager.unregisterKey("x");
99
+ engine.keyboardManager.unregisterKey("y");
100
+ engine.keyboardManager.unregisterKey("z");
101
+ };
102
+ }
103
+ }
104
+
105
+ export { GrabMode };
editor/src/InputHandler.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ export interface InputHandler {
2
+ handleInput(event: Event): void;
3
+ }
editor/src/InputMode.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ export interface InputMode {
2
+ exit(): void;
3
+ }
editor/src/KeyboardManager.ts ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { InputHandler } from "./InputHandler";
2
+
3
+ class KeyboardManager implements InputHandler {
4
+ private _keyMap: Map<string, (event: KeyboardEvent) => void>;
5
+
6
+ constructor() {
7
+ this._keyMap = new Map();
8
+ }
9
+
10
+ registerKey(key: string, callback: (event: KeyboardEvent) => void) {
11
+ this._keyMap.set(key, callback);
12
+ }
13
+
14
+ unregisterKey(key: string) {
15
+ this._keyMap.delete(key);
16
+ }
17
+
18
+ handleInput(event: KeyboardEvent) {
19
+ const callback = this._keyMap.get(event.key);
20
+ if (callback) {
21
+ callback(event);
22
+ }
23
+ }
24
+ }
25
+
26
+ export { KeyboardManager };
editor/src/ModeManager.ts ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { InputMode } from "./InputMode";
2
+
3
+ class ModeManager {
4
+ private static _instance: ModeManager;
5
+
6
+ private static _modeFactories: Map<string, () => InputMode> = new Map();
7
+ private static _currentMode: InputMode | null = null;
8
+
9
+ public static get instance(): ModeManager {
10
+ if (!ModeManager._instance) {
11
+ ModeManager._instance = new ModeManager();
12
+ }
13
+ return ModeManager._instance;
14
+ }
15
+
16
+ public static registerMode(name: string, factory: () => InputMode) {
17
+ this._modeFactories.set(name, factory);
18
+ }
19
+
20
+ public static enterMode(name: string) {
21
+ const factory = this._modeFactories.get(name);
22
+ if (factory) {
23
+ this._currentMode?.exit();
24
+ this._currentMode = factory();
25
+ } else {
26
+ console.error(`No mode registered with name ${name}.`);
27
+ }
28
+ }
29
+
30
+ public static exitCurrentMode() {
31
+ this._currentMode?.exit();
32
+ this._currentMode = null;
33
+ }
34
+ }
35
+
36
+ export { ModeManager };
editor/src/MouseManager.ts ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { InputHandler } from "./InputHandler";
3
+
4
+ class MouseManager implements InputHandler {
5
+ private _canvas: HTMLCanvasElement;
6
+ private _mouseMap: Map<string, (event: MouseEvent) => void>;
7
+ private _currentMousePosition: SPLAT.Vector3;
8
+
9
+ constructor(canvas: HTMLCanvasElement) {
10
+ this._canvas = canvas;
11
+ this._mouseMap = new Map();
12
+ this._currentMousePosition = new SPLAT.Vector3();
13
+ }
14
+
15
+ registerMouse(key: string, callback: (event: MouseEvent) => void) {
16
+ this._mouseMap.set(key, callback);
17
+ }
18
+
19
+ unregisterMouse(key: string) {
20
+ this._mouseMap.delete(key);
21
+ }
22
+
23
+ handleInput(event: MouseEvent) {
24
+ const x = (event.clientX / this._canvas.clientWidth) * 2 - 1;
25
+ const y = -(event.clientY / this._canvas.clientHeight) * 2 + 1;
26
+ this._currentMousePosition = new SPLAT.Vector3(x, y, 0);
27
+ const callback = this._mouseMap.get(event.type);
28
+ if (callback) {
29
+ callback(event);
30
+ }
31
+ }
32
+
33
+ get currentMousePosition(): SPLAT.Vector3 {
34
+ return this._currentMousePosition;
35
+ }
36
+ }
37
+
38
+ export { MouseManager };
editor/src/MoveAction.ts ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { Action } from "./Action";
3
+
4
+ class MoveAction implements Action {
5
+ private _object: SPLAT.Splat;
6
+ private _oldPosition: SPLAT.Vector3;
7
+ private _newPosition: SPLAT.Vector3;
8
+
9
+ constructor(object: SPLAT.Splat, oldPosition: SPLAT.Vector3, newPosition: SPLAT.Vector3) {
10
+ this._object = object;
11
+ this._oldPosition = oldPosition;
12
+ this._newPosition = newPosition;
13
+ }
14
+
15
+ execute(): void {
16
+ this._object.position = this._newPosition;
17
+ }
18
+
19
+ undo(): void {
20
+ this._object.position = this._oldPosition;
21
+ }
22
+ }
23
+
24
+ export { MoveAction };
editor/src/OrbitControls.ts ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+
3
+ class OrbitControls {
4
+ public enabled: boolean = true;
5
+
6
+ minAngle: number = -90;
7
+ maxAngle: number = 90;
8
+ minZoom: number = 0.1;
9
+ maxZoom: number = 30;
10
+ orbitSpeed: number = 1.75;
11
+ panSpeed: number = 1.25;
12
+ zoomSpeed: number = 1.75;
13
+ dampening: number = 0.5;
14
+ setCameraTarget: (newTarget: SPLAT.Vector3) => void = () => {};
15
+ update: () => void;
16
+ dispose: () => void;
17
+
18
+ constructor(
19
+ camera: SPLAT.Camera,
20
+ canvas: HTMLElement,
21
+ alpha: number = 0.5,
22
+ beta: number = 0.5,
23
+ radius: number = 5,
24
+ inputTarget: SPLAT.Vector3 = new SPLAT.Vector3(),
25
+ ) {
26
+ let target = inputTarget.clone();
27
+
28
+ let desiredTarget = target.clone();
29
+ let desiredAlpha = alpha;
30
+ let desiredBeta = beta;
31
+ let desiredRadius = radius;
32
+
33
+ let dragging = false;
34
+ let panning = false;
35
+ let lastDist = 0;
36
+ let lastX = 0;
37
+ let lastY = 0;
38
+
39
+ let isUpdatingCamera = false;
40
+
41
+ const onCameraChange = () => {
42
+ if (isUpdatingCamera) return;
43
+
44
+ const eulerRotation = camera.rotation.toEuler();
45
+ desiredAlpha = -eulerRotation.y;
46
+ desiredBeta = -eulerRotation.x;
47
+
48
+ const x = camera.position.x - desiredRadius * Math.sin(desiredAlpha) * Math.cos(desiredBeta);
49
+ const y = camera.position.y + desiredRadius * Math.sin(desiredBeta);
50
+ const z = camera.position.z + desiredRadius * Math.cos(desiredAlpha) * Math.cos(desiredBeta);
51
+
52
+ desiredTarget = new SPLAT.Vector3(x, y, z);
53
+ };
54
+
55
+ camera.addEventListener("objectChanged", onCameraChange);
56
+
57
+ this.setCameraTarget = (newTarget: SPLAT.Vector3) => {
58
+ const dx = newTarget.x - camera.position.x;
59
+ const dy = newTarget.y - camera.position.y;
60
+ const dz = newTarget.z - camera.position.z;
61
+ desiredRadius = Math.sqrt(dx * dx + dy * dy + dz * dz);
62
+ desiredBeta = Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
63
+ desiredAlpha = -Math.atan2(dx, dz);
64
+ desiredTarget = new SPLAT.Vector3(newTarget.x, newTarget.y, newTarget.z);
65
+ };
66
+
67
+ const computeZoomNorm = () => {
68
+ return 0.1 + (0.9 * (desiredRadius - this.minZoom)) / (this.maxZoom - this.minZoom);
69
+ };
70
+
71
+ const onMouseDown = (e: MouseEvent) => {
72
+ preventDefault(e);
73
+
74
+ if (!this.enabled) return;
75
+
76
+ if (e.button === 1) {
77
+ dragging = true;
78
+ panning = e.shiftKey;
79
+ lastX = e.clientX;
80
+ lastY = e.clientY;
81
+ }
82
+ };
83
+
84
+ const onMouseUp = (e: MouseEvent) => {
85
+ preventDefault(e);
86
+
87
+ if (e.button === 1) {
88
+ dragging = false;
89
+ panning = false;
90
+ }
91
+ };
92
+
93
+ const onMouseMove = (e: MouseEvent) => {
94
+ preventDefault(e);
95
+
96
+ if (!this.enabled || !dragging || !camera) return;
97
+
98
+ const dx = e.clientX - lastX;
99
+ const dy = e.clientY - lastY;
100
+
101
+ if (panning) {
102
+ const zoomNorm = computeZoomNorm();
103
+ const panX = -dx * this.panSpeed * 0.01 * zoomNorm;
104
+ const panY = -dy * this.panSpeed * 0.01 * zoomNorm;
105
+ const R = SPLAT.Matrix3.RotationFromQuaternion(camera.rotation).buffer;
106
+ const right = new SPLAT.Vector3(R[0], R[3], R[6]);
107
+ const up = new SPLAT.Vector3(R[1], R[4], R[7]);
108
+ desiredTarget = desiredTarget.add(right.multiply(panX));
109
+ desiredTarget = desiredTarget.add(up.multiply(panY));
110
+ } else {
111
+ desiredAlpha -= dx * this.orbitSpeed * 0.003;
112
+ desiredBeta += dy * this.orbitSpeed * 0.003;
113
+ desiredBeta = Math.min(
114
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
115
+ (this.maxAngle * Math.PI) / 180,
116
+ );
117
+ }
118
+
119
+ lastX = e.clientX;
120
+ lastY = e.clientY;
121
+ };
122
+
123
+ const onWheel = (e: WheelEvent) => {
124
+ preventDefault(e);
125
+
126
+ if (!this.enabled) return;
127
+
128
+ const zoomNorm = computeZoomNorm();
129
+ desiredRadius += e.deltaY * this.zoomSpeed * 0.025 * zoomNorm;
130
+ desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
131
+ };
132
+
133
+ const onTouchStart = (e: TouchEvent) => {
134
+ preventDefault(e);
135
+
136
+ if (!this.enabled) return;
137
+
138
+ if (e.touches.length === 1) {
139
+ dragging = true;
140
+ panning = false;
141
+ lastX = e.touches[0].clientX;
142
+ lastY = e.touches[0].clientY;
143
+ lastDist = 0;
144
+ } else if (e.touches.length === 2) {
145
+ dragging = true;
146
+ panning = true;
147
+ lastX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
148
+ lastY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
149
+ const distX = e.touches[0].clientX - e.touches[1].clientX;
150
+ const distY = e.touches[0].clientY - e.touches[1].clientY;
151
+ lastDist = Math.sqrt(distX * distX + distY * distY);
152
+ }
153
+ };
154
+
155
+ const onTouchEnd = (e: TouchEvent) => {
156
+ preventDefault(e);
157
+
158
+ dragging = false;
159
+ panning = false;
160
+ };
161
+
162
+ const onTouchMove = (e: TouchEvent) => {
163
+ preventDefault(e);
164
+
165
+ if (!this.enabled || !dragging || !camera) return;
166
+
167
+ if (panning) {
168
+ const zoomNorm = computeZoomNorm();
169
+
170
+ const distX = e.touches[0].clientX - e.touches[1].clientX;
171
+ const distY = e.touches[0].clientY - e.touches[1].clientY;
172
+ const dist = Math.sqrt(distX * distX + distY * distY);
173
+ const delta = lastDist - dist;
174
+ desiredRadius += delta * this.zoomSpeed * 0.1 * zoomNorm;
175
+ desiredRadius = Math.min(Math.max(desiredRadius, this.minZoom), this.maxZoom);
176
+ lastDist = dist;
177
+
178
+ const touchX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
179
+ const touchY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
180
+ const dx = touchX - lastX;
181
+ const dy = touchY - lastY;
182
+ const R = SPLAT.Matrix3.RotationFromQuaternion(camera.rotation).buffer;
183
+ const right = new SPLAT.Vector3(R[0], R[3], R[6]);
184
+ const up = new SPLAT.Vector3(R[1], R[4], R[7]);
185
+ desiredTarget = desiredTarget.add(right.multiply(-dx * this.panSpeed * 0.025 * zoomNorm));
186
+ desiredTarget = desiredTarget.add(up.multiply(-dy * this.panSpeed * 0.025 * zoomNorm));
187
+ lastX = touchX;
188
+ lastY = touchY;
189
+ } else {
190
+ const dx = e.touches[0].clientX - lastX;
191
+ const dy = e.touches[0].clientY - lastY;
192
+
193
+ desiredAlpha -= dx * this.orbitSpeed * 0.003;
194
+ desiredBeta += dy * this.orbitSpeed * 0.003;
195
+ desiredBeta = Math.min(
196
+ Math.max(desiredBeta, (this.minAngle * Math.PI) / 180),
197
+ (this.maxAngle * Math.PI) / 180,
198
+ );
199
+
200
+ lastX = e.touches[0].clientX;
201
+ lastY = e.touches[0].clientY;
202
+ }
203
+ };
204
+
205
+ const lerp = (a: number, b: number, t: number) => {
206
+ return (1 - t) * a + t * b;
207
+ };
208
+
209
+ this.update = () => {
210
+ isUpdatingCamera = true;
211
+
212
+ alpha = lerp(alpha, desiredAlpha, this.dampening);
213
+ beta = lerp(beta, desiredBeta, this.dampening);
214
+ radius = lerp(radius, desiredRadius, this.dampening);
215
+ target = target.lerp(desiredTarget, this.dampening);
216
+
217
+ const x = target.x + radius * Math.sin(alpha) * Math.cos(beta);
218
+ const y = target.y - radius * Math.sin(beta);
219
+ const z = target.z - radius * Math.cos(alpha) * Math.cos(beta);
220
+ camera.position = new SPLAT.Vector3(x, y, z);
221
+
222
+ const direction = target.subtract(camera.position).normalize();
223
+ const rx = Math.asin(-direction.y);
224
+ const ry = Math.atan2(direction.x, direction.z);
225
+ camera.rotation = SPLAT.Quaternion.FromEuler(new SPLAT.Vector3(rx, ry, 0));
226
+
227
+ isUpdatingCamera = false;
228
+ };
229
+
230
+ const preventDefault = (e: Event) => {
231
+ e.preventDefault();
232
+ e.stopPropagation();
233
+ };
234
+
235
+ this.dispose = () => {
236
+ canvas.removeEventListener("dragenter", preventDefault);
237
+ canvas.removeEventListener("dragover", preventDefault);
238
+ canvas.removeEventListener("dragleave", preventDefault);
239
+ canvas.removeEventListener("contextmenu", preventDefault);
240
+
241
+ canvas.removeEventListener("mousedown", onMouseDown);
242
+ canvas.removeEventListener("mousemove", onMouseMove);
243
+ canvas.removeEventListener("wheel", onWheel);
244
+
245
+ canvas.removeEventListener("touchstart", onTouchStart);
246
+ canvas.removeEventListener("touchend", onTouchEnd);
247
+ canvas.removeEventListener("touchmove", onTouchMove);
248
+ };
249
+
250
+ canvas.addEventListener("dragenter", preventDefault);
251
+ canvas.addEventListener("dragover", preventDefault);
252
+ canvas.addEventListener("dragleave", preventDefault);
253
+ canvas.addEventListener("contextmenu", preventDefault);
254
+
255
+ canvas.addEventListener("mousedown", onMouseDown);
256
+ canvas.addEventListener("mouseup", onMouseUp);
257
+ canvas.addEventListener("mousemove", onMouseMove);
258
+ canvas.addEventListener("wheel", onWheel);
259
+
260
+ canvas.addEventListener("touchstart", onTouchStart);
261
+ canvas.addEventListener("touchend", onTouchEnd);
262
+ canvas.addEventListener("touchmove", onTouchMove);
263
+
264
+ this.update();
265
+ }
266
+ }
267
+
268
+ export { OrbitControls };
editor/src/RotateAction.ts ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { Action } from "./Action";
3
+
4
+ class RotateAction implements Action {
5
+ private _object: SPLAT.Splat;
6
+ private _oldRotation: SPLAT.Quaternion;
7
+ private _newRotation: SPLAT.Quaternion;
8
+
9
+ constructor(object: SPLAT.Splat, oldRotation: SPLAT.Quaternion, newRotation: SPLAT.Quaternion) {
10
+ this._object = object;
11
+ this._oldRotation = oldRotation;
12
+ this._newRotation = newRotation;
13
+ }
14
+
15
+ execute(): void {
16
+ this._object.rotation = this._newRotation;
17
+ }
18
+
19
+ undo(): void {
20
+ this._object.rotation = this._oldRotation;
21
+ }
22
+ }
23
+
24
+ export { RotateAction };
editor/src/RotateMode.ts ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { InputMode } from "./InputMode";
3
+ import { Engine } from "./Engine";
4
+ import { ModeManager } from "./ModeManager";
5
+ import { SelectionManager } from "./SelectionManager";
6
+ import { UndoManager } from "./UndoManager";
7
+ import { RotateAction } from "./RotateAction";
8
+
9
+ class RotateMode implements InputMode {
10
+ exit: () => void;
11
+
12
+ constructor(engine: Engine) {
13
+ const splat = SelectionManager.selectedSplat as SPLAT.Splat;
14
+ const initialMousePosition = engine.mouseManager.currentMousePosition.clone();
15
+ const initialRotation = splat.rotation.clone();
16
+
17
+ let axis: "x" | "y" | "z" | null = null;
18
+
19
+ const handleClick = () => {
20
+ const action = new RotateAction(splat, initialRotation, splat.rotation);
21
+ UndoManager.do(action);
22
+ ModeManager.enterMode("default");
23
+ };
24
+
25
+ const handleCancel = () => {
26
+ splat.rotation = initialRotation;
27
+ ModeManager.enterMode("default");
28
+ };
29
+
30
+ const handleMouseMove = () => {
31
+ const plane = new SPLAT.Plane(engine.camera.forward, splat.position);
32
+ const initialDirection = engine.camera.screenPointToRay(initialMousePosition.x, initialMousePosition.y);
33
+ const newDirection = engine.camera.screenPointToRay(
34
+ engine.mouseManager.currentMousePosition.x,
35
+ engine.mouseManager.currentMousePosition.y,
36
+ );
37
+ const initialIntersection = plane.intersect(engine.camera.position, initialDirection);
38
+ const newIntersection = plane.intersect(engine.camera.position, newDirection);
39
+ if (initialIntersection && newIntersection) {
40
+ const initialVector = initialIntersection.subtract(splat.position).normalize();
41
+ const newVector = newIntersection.subtract(splat.position).normalize();
42
+ const cross = initialVector.cross(newVector);
43
+ const dot = initialVector.dot(newVector);
44
+ const delta = Math.acos(dot);
45
+ let rotationAxis = cross.normalize();
46
+ const sign = Math.sign(rotationAxis.dot(engine.camera.forward));
47
+ switch (axis) {
48
+ case "x":
49
+ rotationAxis = new SPLAT.Vector3(1, 0, 0).multiply(sign);
50
+ break;
51
+ case "y":
52
+ rotationAxis = new SPLAT.Vector3(0, 1, 0).multiply(sign);
53
+ break;
54
+ case "z":
55
+ rotationAxis = new SPLAT.Vector3(0, 0, 1).multiply(sign);
56
+ break;
57
+ }
58
+ const rotation = SPLAT.Quaternion.FromAxisAngle(rotationAxis, delta);
59
+ splat.rotation = rotation.multiply(initialRotation);
60
+ }
61
+ };
62
+
63
+ const handleAxisX = () => {
64
+ if (axis === "x") {
65
+ axis = null;
66
+ } else {
67
+ axis = "x";
68
+ }
69
+ handleMouseMove();
70
+ };
71
+
72
+ const handleAxisY = () => {
73
+ if (axis === "y") {
74
+ axis = null;
75
+ } else {
76
+ axis = "y";
77
+ }
78
+ handleMouseMove();
79
+ };
80
+
81
+ const handleAxisZ = () => {
82
+ if (axis === "z") {
83
+ axis = null;
84
+ } else {
85
+ axis = "z";
86
+ }
87
+ handleMouseMove();
88
+ };
89
+
90
+ engine.mouseManager.registerMouse("click", handleClick);
91
+ engine.mouseManager.registerMouse("contextmenu", handleCancel);
92
+ engine.mouseManager.registerMouse("mousemove", handleMouseMove);
93
+ engine.keyboardManager.registerKey("Escape", handleCancel);
94
+ engine.keyboardManager.registerKey("r", handleCancel);
95
+ engine.keyboardManager.registerKey("x", handleAxisX);
96
+ engine.keyboardManager.registerKey("y", handleAxisY);
97
+ engine.keyboardManager.registerKey("z", handleAxisZ);
98
+
99
+ this.exit = () => {
100
+ engine.mouseManager.unregisterMouse("click");
101
+ engine.mouseManager.unregisterMouse("contextmenu");
102
+ engine.mouseManager.unregisterMouse("mousemove");
103
+ engine.keyboardManager.unregisterKey("Escape");
104
+ engine.keyboardManager.unregisterKey("r");
105
+ engine.keyboardManager.unregisterKey("x");
106
+ engine.keyboardManager.unregisterKey("y");
107
+ engine.keyboardManager.unregisterKey("z");
108
+ };
109
+ }
110
+ }
111
+
112
+ export { RotateMode };
editor/src/ScaleAction.ts ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { Action } from "./Action";
3
+
4
+ class ScaleAction implements Action {
5
+ private _object: SPLAT.Splat;
6
+ private _oldScale: SPLAT.Vector3;
7
+ private _newScale: SPLAT.Vector3;
8
+
9
+ constructor(object: SPLAT.Splat, oldScale: SPLAT.Vector3, newScale: SPLAT.Vector3) {
10
+ this._object = object;
11
+ this._oldScale = oldScale;
12
+ this._newScale = newScale;
13
+ }
14
+
15
+ execute(): void {
16
+ this._object.scale = this._newScale;
17
+ }
18
+
19
+ undo(): void {
20
+ this._object.scale = this._oldScale;
21
+ }
22
+ }
23
+
24
+ export { ScaleAction };
editor/src/ScaleMode.ts ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { InputMode } from "./InputMode";
3
+ import { Engine } from "./Engine";
4
+ import { ModeManager } from "./ModeManager";
5
+ import { SelectionManager } from "./SelectionManager";
6
+ import { UndoManager } from "./UndoManager";
7
+ import { ScaleAction } from "./ScaleAction";
8
+
9
+ class ScaleMode implements InputMode {
10
+ exit: () => void;
11
+
12
+ constructor(engine: Engine) {
13
+ const splat = SelectionManager.selectedSplat as SPLAT.Splat;
14
+ const initialMousePosition = engine.mouseManager.currentMousePosition.clone();
15
+ const initialScale = splat.scale.clone();
16
+
17
+ let axis: "x" | "y" | "z" | null = null;
18
+
19
+ const handleClick = () => {
20
+ const action = new ScaleAction(splat, initialScale, splat.scale);
21
+ UndoManager.do(action);
22
+ ModeManager.enterMode("default");
23
+ };
24
+
25
+ const handleCancel = () => {
26
+ splat.scale = initialScale;
27
+ ModeManager.enterMode("default");
28
+ };
29
+
30
+ const handleMouseMove = () => {
31
+ const plane = new SPLAT.Plane(engine.camera.forward, splat.position);
32
+ const initialDirection = engine.camera.screenPointToRay(initialMousePosition.x, initialMousePosition.y);
33
+ const newDirection = engine.camera.screenPointToRay(
34
+ engine.mouseManager.currentMousePosition.x,
35
+ engine.mouseManager.currentMousePosition.y,
36
+ );
37
+ const initialIntersection = plane.intersect(engine.camera.position, initialDirection);
38
+ const newIntersection = plane.intersect(engine.camera.position, newDirection);
39
+ if (initialIntersection && newIntersection) {
40
+ const initialDistance = initialIntersection.subtract(splat.position).magnitude();
41
+ const distance = newIntersection.subtract(splat.position).magnitude();
42
+ const scaleRatio = distance / initialDistance;
43
+ let scaleAxis = SPLAT.Vector3.One();
44
+ switch (axis) {
45
+ case "x":
46
+ scaleAxis = new SPLAT.Vector3(1, 0, 0);
47
+ break;
48
+ case "y":
49
+ scaleAxis = new SPLAT.Vector3(0, 1, 0);
50
+ break;
51
+ case "z":
52
+ scaleAxis = new SPLAT.Vector3(0, 0, 1);
53
+ break;
54
+ }
55
+ scaleAxis = splat.rotation.apply(scaleAxis);
56
+ scaleAxis = new SPLAT.Vector3(Math.abs(scaleAxis.x), Math.abs(scaleAxis.y), Math.abs(scaleAxis.z));
57
+ const scaling = scaleAxis.multiply(scaleRatio).add(SPLAT.Vector3.One().subtract(scaleAxis));
58
+ splat.scale = initialScale.multiply(scaling);
59
+ }
60
+ };
61
+
62
+ const handleAxisX = () => {
63
+ if (axis === "x") {
64
+ axis = null;
65
+ } else {
66
+ axis = "x";
67
+ }
68
+ handleMouseMove();
69
+ };
70
+
71
+ const handleAxisY = () => {
72
+ if (axis === "y") {
73
+ axis = null;
74
+ } else {
75
+ axis = "y";
76
+ }
77
+ handleMouseMove();
78
+ };
79
+
80
+ const handleAxisZ = () => {
81
+ if (axis === "z") {
82
+ axis = null;
83
+ } else {
84
+ axis = "z";
85
+ }
86
+ handleMouseMove();
87
+ };
88
+
89
+ engine.mouseManager.registerMouse("click", handleClick);
90
+ engine.mouseManager.registerMouse("contextmenu", handleCancel);
91
+ engine.mouseManager.registerMouse("mousemove", handleMouseMove);
92
+ engine.keyboardManager.registerKey("Escape", handleCancel);
93
+ engine.keyboardManager.registerKey("s", handleCancel);
94
+ engine.keyboardManager.registerKey("x", handleAxisX);
95
+ engine.keyboardManager.registerKey("y", handleAxisY);
96
+ engine.keyboardManager.registerKey("z", handleAxisZ);
97
+
98
+ this.exit = () => {
99
+ engine.mouseManager.unregisterMouse("click");
100
+ engine.mouseManager.unregisterMouse("contextmenu");
101
+ engine.mouseManager.unregisterMouse("mousemove");
102
+ engine.keyboardManager.unregisterKey("Escape");
103
+ engine.keyboardManager.unregisterKey("s");
104
+ engine.keyboardManager.unregisterKey("x");
105
+ engine.keyboardManager.unregisterKey("y");
106
+ engine.keyboardManager.unregisterKey("z");
107
+ };
108
+ }
109
+ }
110
+
111
+ export { ScaleMode };
editor/src/SelectionManager.ts ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+
3
+ class SelectionManager {
4
+ private static _instance: SelectionManager;
5
+
6
+ private static _selectedSplat: SPLAT.Splat | null;
7
+
8
+ public static get instance(): SelectionManager {
9
+ if (!SelectionManager._instance) {
10
+ SelectionManager._instance = new SelectionManager();
11
+ }
12
+ return SelectionManager._instance;
13
+ }
14
+
15
+ public static get selectedSplat(): SPLAT.Splat | null {
16
+ return this._selectedSplat;
17
+ }
18
+
19
+ public static set selectedSplat(splat: SPLAT.Splat | null) {
20
+ if (this._selectedSplat) {
21
+ this._selectedSplat.selected = false;
22
+ }
23
+ this._selectedSplat = splat;
24
+ if (this._selectedSplat) {
25
+ this._selectedSplat.selected = true;
26
+ }
27
+ }
28
+ }
29
+
30
+ export { SelectionManager };
editor/src/UndoManager.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Action } from "./Action";
2
+
3
+ class UndoManager {
4
+ private static _instance: UndoManager;
5
+ private static _actionStack: Action[] = [];
6
+
7
+ public static get instance(): UndoManager {
8
+ if (!UndoManager._instance) {
9
+ UndoManager._instance = new UndoManager();
10
+ }
11
+ return UndoManager._instance;
12
+ }
13
+
14
+ public static do(action: Action) {
15
+ action.execute();
16
+ this._actionStack.push(action);
17
+ }
18
+
19
+ public static undo() {
20
+ const action = this._actionStack.pop();
21
+ if (action) {
22
+ action.undo();
23
+ }
24
+ }
25
+ }
26
+
27
+ export { UndoManager };
editor/src/main.ts ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+ import { Engine } from "./Engine";
3
+ import { SelectionManager } from "./SelectionManager";
4
+
5
+ const canvas = document.getElementById("canvas") as HTMLCanvasElement;
6
+ const progressDialog = document.getElementById("progress-dialog") as HTMLDialogElement;
7
+ const progressIndicator = document.getElementById("progress-indicator") as HTMLProgressElement;
8
+ const downloadButton = document.getElementById("download-button") as HTMLButtonElement;
9
+
10
+ const engine = new Engine(canvas);
11
+
12
+ let loading = false;
13
+ async function selectFile(file: File) {
14
+ if (loading) return;
15
+ SelectionManager.selectedSplat = null;
16
+ loading = true;
17
+ // Check if .splat file
18
+ if (file.name.endsWith(".splat")) {
19
+ await SPLAT.Loader.LoadFromFileAsync(file, engine.scene, (progress: number) => {
20
+ console.log("Loading SPLAT file: " + progress);
21
+ });
22
+ } else if (file.name.endsWith(".ply")) {
23
+ const format = "";
24
+ // const format = "polycam"; // Uncomment to load a Polycam PLY file
25
+ await SPLAT.PLYLoader.LoadFromFileAsync(
26
+ file,
27
+ engine.scene,
28
+ (progress: number) => {
29
+ console.log("Loading PLY file: " + progress);
30
+ },
31
+ format
32
+ );
33
+ }
34
+ loading = false;
35
+ }
36
+
37
+ async function main() {
38
+ const url = "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/bonsai/bonsai-7k-mini.splat";
39
+ await SPLAT.Loader.LoadAsync(url, engine.scene, (progress) => (progressIndicator.value = progress * 100));
40
+ progressDialog.close();
41
+ engine.renderer.backgroundColor = new SPLAT.Color32(64, 64, 64, 255);
42
+
43
+ const handleResize = () => {
44
+ engine.renderer.setSize(canvas.clientWidth, canvas.clientHeight);
45
+ };
46
+
47
+ const frame = () => {
48
+ engine.update();
49
+
50
+ requestAnimationFrame(frame);
51
+ };
52
+
53
+ handleResize();
54
+ window.addEventListener("resize", handleResize);
55
+
56
+ requestAnimationFrame(frame);
57
+
58
+ document.addEventListener("drop", (e) => {
59
+ e.preventDefault();
60
+ e.stopPropagation();
61
+
62
+ if (e.dataTransfer != null) {
63
+ selectFile(e.dataTransfer.files[0]);
64
+ }
65
+ });
66
+
67
+ downloadButton.addEventListener("click", () => {
68
+ if (SelectionManager.selectedSplat !== null) {
69
+ SelectionManager.selectedSplat.saveToFile();
70
+ } else {
71
+ engine.scene.saveToFile();
72
+ }
73
+ });
74
+ }
75
+
76
+ main();
editor/src/programs/AxisProgram.ts ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+
3
+ const axisVertexShader = /*glsl*/ `#version 300 es
4
+ uniform mat4 projection, view;
5
+
6
+ in vec3 position;
7
+
8
+ void main() {
9
+ gl_Position = projection * view * vec4(position, 1.0);
10
+ }
11
+ `;
12
+
13
+ const axisFragmentShader = /*glsl*/ `#version 300 es
14
+ precision mediump float;
15
+ uniform vec4 axisColor;
16
+ out vec4 outColor;
17
+
18
+ void main() {
19
+ outColor = axisColor;
20
+ }
21
+ `;
22
+
23
+ class AxisProgram extends SPLAT.ShaderProgram {
24
+ protected _initialize: () => void;
25
+ protected _resize: () => void;
26
+ protected _render: () => void;
27
+ protected _dispose: () => void;
28
+
29
+ constructor(renderer: SPLAT.WebGLRenderer, passes: SPLAT.ShaderPass[]) {
30
+ super(renderer, passes);
31
+
32
+ const gl = renderer.gl;
33
+
34
+ let vertexBuffer: WebGLBuffer;
35
+
36
+ let positionAttribute: number;
37
+
38
+ let u_projection: WebGLUniformLocation;
39
+ let u_view: WebGLUniformLocation;
40
+ let u_color: WebGLUniformLocation;
41
+
42
+ const xVertices = new Float32Array([-50, 0, 0, 50, 0, 0]);
43
+ const yVertices = new Float32Array([0, -50, 0, 0, 50, 0]);
44
+ const zVertices = new Float32Array([0, 0, -50, 0, 0, 50]);
45
+
46
+ const xColor = new Float32Array([1, 0, 0, 0.5]);
47
+ const yColor = new Float32Array([0, 1, 0, 0.5]);
48
+ const zColor = new Float32Array([0, 0.5, 1, 0.5]);
49
+
50
+ this._initialize = () => {
51
+ vertexBuffer = gl.createBuffer() as WebGLBuffer;
52
+
53
+ positionAttribute = gl.getAttribLocation(this.program, "position");
54
+ gl.enableVertexAttribArray(positionAttribute);
55
+
56
+ u_projection = gl.getUniformLocation(this.program, "projection") as WebGLUniformLocation;
57
+ u_view = gl.getUniformLocation(this.program, "view") as WebGLUniformLocation;
58
+ u_color = gl.getUniformLocation(this.program, "axisColor") as WebGLUniformLocation;
59
+ };
60
+
61
+ const drawAxis = (vertices: Float32Array, color: Float32Array) => {
62
+ gl.uniform4fv(u_color, color);
63
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
64
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
65
+ gl.vertexAttribPointer(positionAttribute, 3, gl.FLOAT, false, 0, 0);
66
+ gl.drawArrays(gl.LINES, 0, 2);
67
+ };
68
+
69
+ this._resize = () => {};
70
+
71
+ this._render = () => {
72
+ if (!this._camera) {
73
+ throw new Error("Camera not set");
74
+ }
75
+
76
+ gl.enable(gl.BLEND);
77
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
78
+
79
+ gl.uniformMatrix4fv(u_projection, false, this._camera.data.projectionMatrix.buffer);
80
+ gl.uniformMatrix4fv(u_view, false, this._camera.data.viewMatrix.buffer);
81
+
82
+ drawAxis(xVertices, xColor);
83
+ drawAxis(yVertices, yColor);
84
+ drawAxis(zVertices, zColor);
85
+ };
86
+
87
+ this._dispose = () => {};
88
+ }
89
+
90
+ protected _getVertexSource() {
91
+ return axisVertexShader;
92
+ }
93
+
94
+ protected _getFragmentSource() {
95
+ return axisFragmentShader;
96
+ }
97
+ }
98
+
99
+ export { AxisProgram };
editor/src/programs/GridProgram.ts ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as SPLAT from "gsplat";
2
+
3
+ const gridVertexShader = /*glsl*/ `#version 300 es
4
+ uniform mat4 projection, view;
5
+ uniform vec2 viewport;
6
+
7
+ in vec2 position;
8
+
9
+ void main() {
10
+ int numLines = 100;
11
+ float lineOffset = float(gl_VertexID / 4 - numLines / 2);
12
+ vec4 worldPosition;
13
+
14
+ if (gl_VertexID % 4 == 0) {
15
+ worldPosition = view * vec4(-50, 0.0, lineOffset, 1.0);
16
+ } else if (gl_VertexID % 4 == 1) {
17
+ worldPosition = view * vec4(50, 0.0, lineOffset, 1.0);
18
+ } else if (gl_VertexID % 4 == 2) {
19
+ worldPosition = view * vec4(lineOffset, 0.0, -50, 1.0);
20
+ } else {
21
+ worldPosition = view * vec4(lineOffset, 0.0, 50, 1.0);
22
+ }
23
+
24
+ gl_Position = projection * worldPosition;
25
+ }
26
+ `;
27
+
28
+ const gridFragmentShader = /*glsl*/ `#version 300 es
29
+ precision mediump float;
30
+ out vec4 outColor;
31
+
32
+ void main() {
33
+ outColor = vec4(1.0, 1.0, 1.0, 0.1);
34
+ }
35
+ `;
36
+
37
+ class GridProgram extends SPLAT.ShaderProgram {
38
+ protected _initialize: () => void;
39
+ protected _resize: () => void;
40
+ protected _render: () => void;
41
+ protected _dispose: () => void;
42
+
43
+ constructor(renderer: SPLAT.WebGLRenderer, passes: SPLAT.ShaderPass[]) {
44
+ super(renderer, passes);
45
+
46
+ const gl = renderer.gl;
47
+
48
+ let u_projection: WebGLUniformLocation;
49
+ let u_view: WebGLUniformLocation;
50
+ let u_viewport: WebGLUniformLocation;
51
+
52
+ this._initialize = () => {
53
+ u_projection = gl.getUniformLocation(this.program, "projection") as WebGLUniformLocation;
54
+ u_view = gl.getUniformLocation(this.program, "view") as WebGLUniformLocation;
55
+ u_viewport = gl.getUniformLocation(this.program, "viewport") as WebGLUniformLocation;
56
+ };
57
+
58
+ this._resize = () => {
59
+ gl.uniform2fv(u_viewport, new Float32Array([renderer.canvas.width, renderer.canvas.height]));
60
+ };
61
+
62
+ this._render = () => {
63
+ if (!this._camera) {
64
+ throw new Error("Camera not set");
65
+ }
66
+
67
+ gl.enable(gl.BLEND);
68
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
69
+
70
+ gl.uniformMatrix4fv(u_projection, false, this._camera.data.projectionMatrix.buffer);
71
+ gl.uniformMatrix4fv(u_view, false, this._camera.data.viewMatrix.buffer);
72
+
73
+ gl.drawArrays(gl.LINES, 0, 400);
74
+ };
75
+
76
+ this._dispose = () => {};
77
+ }
78
+
79
+ protected _getVertexSource() {
80
+ return gridVertexShader;
81
+ }
82
+
83
+ protected _getFragmentSource() {
84
+ return gridFragmentShader;
85
+ }
86
+ }
87
+
88
+ export { GridProgram };
editor/src/vite-env.d.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ /// <reference types="vite/client" />
editor/style.css ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body,
2
+ html {
3
+ margin: 0;
4
+ padding: 0;
5
+ overflow: hidden;
6
+ background-color: #fff;
7
+ font-family: sans-serif;
8
+ }
9
+
10
+ canvas {
11
+ width: 100vw;
12
+ height: 100vh;
13
+ }
14
+
15
+ dialog {
16
+ width: 100%;
17
+ text-align: center;
18
+ max-width: 20em;
19
+ color: white;
20
+ background-color: #000;
21
+ border: none;
22
+ position: relative;
23
+ transform: translate(-50%, -50%);
24
+ }
25
+
26
+ #progress-container {
27
+ position: absolute;
28
+ top: 50%;
29
+ left: 50%;
30
+ }
31
+
32
+ progress {
33
+ width: 100%;
34
+ height: 1em;
35
+ border: none;
36
+ background-color: #fff;
37
+ color: #eee;
38
+ }
39
+
40
+ progress::-webkit-progress-bar {
41
+ background-color: #333;
42
+ }
43
+
44
+ progress::-webkit-progress-value {
45
+ background-color: #eee;
46
+ }
47
+
48
+ progress::-moz-progress-bar {
49
+ background-color: #eee;
50
+ }
51
+
52
+ #download-button {
53
+ position: absolute;
54
+ top: 8px;
55
+ right: 8px;
56
+ background-color: #000000cc;
57
+ border: none;
58
+ padding: 6px;
59
+ margin: 0;
60
+ border-radius: 2px;
61
+ cursor: pointer;
62
+ }
63
+
64
+ #download-button:hover {
65
+ background-color: #000000ee;
66
+ }
67
+
68
+ #download-button:active {
69
+ background-color: #000000cc;
70
+ }
71
+
72
+ #download-button:focus {
73
+ outline: none;
74
+ }
editor/tsconfig.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "dist",
4
+ "target": "ES2020",
5
+ "useDefineForClassFields": true,
6
+ "module": "ESNext",
7
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "node",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": false,
15
+
16
+ /* Linting */
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noFallthroughCasesInSwitch": true
21
+ },
22
+ "include": ["src"]
23
+ }