├── .gitignore
├── README.md
├── index.html
├── package.json
├── pnpm-lock.yaml
├── public
└── .gitkeep
└── src
├── demo.js
├── main.js
└── style.css
/.gitignore:
--------------------------------------------------------------------------------
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?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tweakpane plugin to add Undo/Redo feature to your panes
2 |
3 | ## Installation
4 |
5 | ```bash
6 | npm install github:cosmicshelter/tweakpane-undo-redo-plugin
7 | ```
8 |
9 | ## Usage
10 |
11 | ```js
12 | import addUndoRedoFeature from 'tweakpane-undo-redo-plugin';
13 | import * as Tweakpane from 'tweakpane';
14 |
15 | addUndoRedoFeature(Tweakpane);
16 | ```
17 |
18 | If needed, you can call clear to empty the action history stack :
19 |
20 | ```js
21 | const undoRedoFeature = addUndoRedoFeature(Tweakpane);
22 | undoRedoFeature.clear();
23 | ```
24 |
25 | You can also call destroy to clear the history and remove the keydown event listener :
26 |
27 | ```js
28 | const undoRedoFeature = addUndoRedoFeature(Tweakpane);
29 | undoRedoFeature.destroy();
30 | ```
31 |
32 | ## Disclamer
33 |
34 | I had to make my way around the official Tweakpane API to make this work, using internal methods etc...
35 | It might not work properly for Tweakpane versions other than v4.0.5.
36 |
37 | ## Roadmap
38 |
39 | - More testing
40 | - Handle blades
41 | - Handle other plugins
42 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Tweakpane Undo Redo Plugin - Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tweakpane-undo-redo-plugin",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "main": "src/main.js",
7 | "scripts": {
8 | "dev": "vite",
9 | "build": "vite build",
10 | "preview": "vite preview"
11 | },
12 | "devDependencies": {
13 | "vite": "^6.3.1"
14 | },
15 | "dependencies": {
16 | "three": "^0.175.0",
17 | "tweakpane": "^4.0.5"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '6.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | dependencies:
8 | three:
9 | specifier: ^0.175.0
10 | version: 0.175.0
11 | tweakpane:
12 | specifier: ^4.0.5
13 | version: 4.0.5
14 |
15 | devDependencies:
16 | vite:
17 | specifier: ^6.3.1
18 | version: 6.3.1
19 |
20 | packages:
21 |
22 | /@esbuild/aix-ppc64@0.25.2:
23 | resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==}
24 | engines: {node: '>=18'}
25 | cpu: [ppc64]
26 | os: [aix]
27 | requiresBuild: true
28 | dev: true
29 | optional: true
30 |
31 | /@esbuild/android-arm64@0.25.2:
32 | resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==}
33 | engines: {node: '>=18'}
34 | cpu: [arm64]
35 | os: [android]
36 | requiresBuild: true
37 | dev: true
38 | optional: true
39 |
40 | /@esbuild/android-arm@0.25.2:
41 | resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==}
42 | engines: {node: '>=18'}
43 | cpu: [arm]
44 | os: [android]
45 | requiresBuild: true
46 | dev: true
47 | optional: true
48 |
49 | /@esbuild/android-x64@0.25.2:
50 | resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==}
51 | engines: {node: '>=18'}
52 | cpu: [x64]
53 | os: [android]
54 | requiresBuild: true
55 | dev: true
56 | optional: true
57 |
58 | /@esbuild/darwin-arm64@0.25.2:
59 | resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==}
60 | engines: {node: '>=18'}
61 | cpu: [arm64]
62 | os: [darwin]
63 | requiresBuild: true
64 | dev: true
65 | optional: true
66 |
67 | /@esbuild/darwin-x64@0.25.2:
68 | resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==}
69 | engines: {node: '>=18'}
70 | cpu: [x64]
71 | os: [darwin]
72 | requiresBuild: true
73 | dev: true
74 | optional: true
75 |
76 | /@esbuild/freebsd-arm64@0.25.2:
77 | resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==}
78 | engines: {node: '>=18'}
79 | cpu: [arm64]
80 | os: [freebsd]
81 | requiresBuild: true
82 | dev: true
83 | optional: true
84 |
85 | /@esbuild/freebsd-x64@0.25.2:
86 | resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==}
87 | engines: {node: '>=18'}
88 | cpu: [x64]
89 | os: [freebsd]
90 | requiresBuild: true
91 | dev: true
92 | optional: true
93 |
94 | /@esbuild/linux-arm64@0.25.2:
95 | resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==}
96 | engines: {node: '>=18'}
97 | cpu: [arm64]
98 | os: [linux]
99 | requiresBuild: true
100 | dev: true
101 | optional: true
102 |
103 | /@esbuild/linux-arm@0.25.2:
104 | resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==}
105 | engines: {node: '>=18'}
106 | cpu: [arm]
107 | os: [linux]
108 | requiresBuild: true
109 | dev: true
110 | optional: true
111 |
112 | /@esbuild/linux-ia32@0.25.2:
113 | resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==}
114 | engines: {node: '>=18'}
115 | cpu: [ia32]
116 | os: [linux]
117 | requiresBuild: true
118 | dev: true
119 | optional: true
120 |
121 | /@esbuild/linux-loong64@0.25.2:
122 | resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==}
123 | engines: {node: '>=18'}
124 | cpu: [loong64]
125 | os: [linux]
126 | requiresBuild: true
127 | dev: true
128 | optional: true
129 |
130 | /@esbuild/linux-mips64el@0.25.2:
131 | resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==}
132 | engines: {node: '>=18'}
133 | cpu: [mips64el]
134 | os: [linux]
135 | requiresBuild: true
136 | dev: true
137 | optional: true
138 |
139 | /@esbuild/linux-ppc64@0.25.2:
140 | resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==}
141 | engines: {node: '>=18'}
142 | cpu: [ppc64]
143 | os: [linux]
144 | requiresBuild: true
145 | dev: true
146 | optional: true
147 |
148 | /@esbuild/linux-riscv64@0.25.2:
149 | resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==}
150 | engines: {node: '>=18'}
151 | cpu: [riscv64]
152 | os: [linux]
153 | requiresBuild: true
154 | dev: true
155 | optional: true
156 |
157 | /@esbuild/linux-s390x@0.25.2:
158 | resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==}
159 | engines: {node: '>=18'}
160 | cpu: [s390x]
161 | os: [linux]
162 | requiresBuild: true
163 | dev: true
164 | optional: true
165 |
166 | /@esbuild/linux-x64@0.25.2:
167 | resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==}
168 | engines: {node: '>=18'}
169 | cpu: [x64]
170 | os: [linux]
171 | requiresBuild: true
172 | dev: true
173 | optional: true
174 |
175 | /@esbuild/netbsd-arm64@0.25.2:
176 | resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==}
177 | engines: {node: '>=18'}
178 | cpu: [arm64]
179 | os: [netbsd]
180 | requiresBuild: true
181 | dev: true
182 | optional: true
183 |
184 | /@esbuild/netbsd-x64@0.25.2:
185 | resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==}
186 | engines: {node: '>=18'}
187 | cpu: [x64]
188 | os: [netbsd]
189 | requiresBuild: true
190 | dev: true
191 | optional: true
192 |
193 | /@esbuild/openbsd-arm64@0.25.2:
194 | resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==}
195 | engines: {node: '>=18'}
196 | cpu: [arm64]
197 | os: [openbsd]
198 | requiresBuild: true
199 | dev: true
200 | optional: true
201 |
202 | /@esbuild/openbsd-x64@0.25.2:
203 | resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==}
204 | engines: {node: '>=18'}
205 | cpu: [x64]
206 | os: [openbsd]
207 | requiresBuild: true
208 | dev: true
209 | optional: true
210 |
211 | /@esbuild/sunos-x64@0.25.2:
212 | resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==}
213 | engines: {node: '>=18'}
214 | cpu: [x64]
215 | os: [sunos]
216 | requiresBuild: true
217 | dev: true
218 | optional: true
219 |
220 | /@esbuild/win32-arm64@0.25.2:
221 | resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==}
222 | engines: {node: '>=18'}
223 | cpu: [arm64]
224 | os: [win32]
225 | requiresBuild: true
226 | dev: true
227 | optional: true
228 |
229 | /@esbuild/win32-ia32@0.25.2:
230 | resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==}
231 | engines: {node: '>=18'}
232 | cpu: [ia32]
233 | os: [win32]
234 | requiresBuild: true
235 | dev: true
236 | optional: true
237 |
238 | /@esbuild/win32-x64@0.25.2:
239 | resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==}
240 | engines: {node: '>=18'}
241 | cpu: [x64]
242 | os: [win32]
243 | requiresBuild: true
244 | dev: true
245 | optional: true
246 |
247 | /@rollup/rollup-android-arm-eabi@4.40.0:
248 | resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==}
249 | cpu: [arm]
250 | os: [android]
251 | requiresBuild: true
252 | dev: true
253 | optional: true
254 |
255 | /@rollup/rollup-android-arm64@4.40.0:
256 | resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==}
257 | cpu: [arm64]
258 | os: [android]
259 | requiresBuild: true
260 | dev: true
261 | optional: true
262 |
263 | /@rollup/rollup-darwin-arm64@4.40.0:
264 | resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==}
265 | cpu: [arm64]
266 | os: [darwin]
267 | requiresBuild: true
268 | dev: true
269 | optional: true
270 |
271 | /@rollup/rollup-darwin-x64@4.40.0:
272 | resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==}
273 | cpu: [x64]
274 | os: [darwin]
275 | requiresBuild: true
276 | dev: true
277 | optional: true
278 |
279 | /@rollup/rollup-freebsd-arm64@4.40.0:
280 | resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==}
281 | cpu: [arm64]
282 | os: [freebsd]
283 | requiresBuild: true
284 | dev: true
285 | optional: true
286 |
287 | /@rollup/rollup-freebsd-x64@4.40.0:
288 | resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==}
289 | cpu: [x64]
290 | os: [freebsd]
291 | requiresBuild: true
292 | dev: true
293 | optional: true
294 |
295 | /@rollup/rollup-linux-arm-gnueabihf@4.40.0:
296 | resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==}
297 | cpu: [arm]
298 | os: [linux]
299 | requiresBuild: true
300 | dev: true
301 | optional: true
302 |
303 | /@rollup/rollup-linux-arm-musleabihf@4.40.0:
304 | resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==}
305 | cpu: [arm]
306 | os: [linux]
307 | requiresBuild: true
308 | dev: true
309 | optional: true
310 |
311 | /@rollup/rollup-linux-arm64-gnu@4.40.0:
312 | resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==}
313 | cpu: [arm64]
314 | os: [linux]
315 | requiresBuild: true
316 | dev: true
317 | optional: true
318 |
319 | /@rollup/rollup-linux-arm64-musl@4.40.0:
320 | resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==}
321 | cpu: [arm64]
322 | os: [linux]
323 | requiresBuild: true
324 | dev: true
325 | optional: true
326 |
327 | /@rollup/rollup-linux-loongarch64-gnu@4.40.0:
328 | resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==}
329 | cpu: [loong64]
330 | os: [linux]
331 | requiresBuild: true
332 | dev: true
333 | optional: true
334 |
335 | /@rollup/rollup-linux-powerpc64le-gnu@4.40.0:
336 | resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==}
337 | cpu: [ppc64]
338 | os: [linux]
339 | requiresBuild: true
340 | dev: true
341 | optional: true
342 |
343 | /@rollup/rollup-linux-riscv64-gnu@4.40.0:
344 | resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==}
345 | cpu: [riscv64]
346 | os: [linux]
347 | requiresBuild: true
348 | dev: true
349 | optional: true
350 |
351 | /@rollup/rollup-linux-riscv64-musl@4.40.0:
352 | resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==}
353 | cpu: [riscv64]
354 | os: [linux]
355 | requiresBuild: true
356 | dev: true
357 | optional: true
358 |
359 | /@rollup/rollup-linux-s390x-gnu@4.40.0:
360 | resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==}
361 | cpu: [s390x]
362 | os: [linux]
363 | requiresBuild: true
364 | dev: true
365 | optional: true
366 |
367 | /@rollup/rollup-linux-x64-gnu@4.40.0:
368 | resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==}
369 | cpu: [x64]
370 | os: [linux]
371 | requiresBuild: true
372 | dev: true
373 | optional: true
374 |
375 | /@rollup/rollup-linux-x64-musl@4.40.0:
376 | resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==}
377 | cpu: [x64]
378 | os: [linux]
379 | requiresBuild: true
380 | dev: true
381 | optional: true
382 |
383 | /@rollup/rollup-win32-arm64-msvc@4.40.0:
384 | resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==}
385 | cpu: [arm64]
386 | os: [win32]
387 | requiresBuild: true
388 | dev: true
389 | optional: true
390 |
391 | /@rollup/rollup-win32-ia32-msvc@4.40.0:
392 | resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==}
393 | cpu: [ia32]
394 | os: [win32]
395 | requiresBuild: true
396 | dev: true
397 | optional: true
398 |
399 | /@rollup/rollup-win32-x64-msvc@4.40.0:
400 | resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==}
401 | cpu: [x64]
402 | os: [win32]
403 | requiresBuild: true
404 | dev: true
405 | optional: true
406 |
407 | /@types/estree@1.0.7:
408 | resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
409 | dev: true
410 |
411 | /esbuild@0.25.2:
412 | resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==}
413 | engines: {node: '>=18'}
414 | hasBin: true
415 | requiresBuild: true
416 | optionalDependencies:
417 | '@esbuild/aix-ppc64': 0.25.2
418 | '@esbuild/android-arm': 0.25.2
419 | '@esbuild/android-arm64': 0.25.2
420 | '@esbuild/android-x64': 0.25.2
421 | '@esbuild/darwin-arm64': 0.25.2
422 | '@esbuild/darwin-x64': 0.25.2
423 | '@esbuild/freebsd-arm64': 0.25.2
424 | '@esbuild/freebsd-x64': 0.25.2
425 | '@esbuild/linux-arm': 0.25.2
426 | '@esbuild/linux-arm64': 0.25.2
427 | '@esbuild/linux-ia32': 0.25.2
428 | '@esbuild/linux-loong64': 0.25.2
429 | '@esbuild/linux-mips64el': 0.25.2
430 | '@esbuild/linux-ppc64': 0.25.2
431 | '@esbuild/linux-riscv64': 0.25.2
432 | '@esbuild/linux-s390x': 0.25.2
433 | '@esbuild/linux-x64': 0.25.2
434 | '@esbuild/netbsd-arm64': 0.25.2
435 | '@esbuild/netbsd-x64': 0.25.2
436 | '@esbuild/openbsd-arm64': 0.25.2
437 | '@esbuild/openbsd-x64': 0.25.2
438 | '@esbuild/sunos-x64': 0.25.2
439 | '@esbuild/win32-arm64': 0.25.2
440 | '@esbuild/win32-ia32': 0.25.2
441 | '@esbuild/win32-x64': 0.25.2
442 | dev: true
443 |
444 | /fdir@6.4.3(picomatch@4.0.2):
445 | resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
446 | peerDependencies:
447 | picomatch: ^3 || ^4
448 | peerDependenciesMeta:
449 | picomatch:
450 | optional: true
451 | dependencies:
452 | picomatch: 4.0.2
453 | dev: true
454 |
455 | /fsevents@2.3.3:
456 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
457 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
458 | os: [darwin]
459 | requiresBuild: true
460 | dev: true
461 | optional: true
462 |
463 | /nanoid@3.3.11:
464 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
465 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
466 | hasBin: true
467 | dev: true
468 |
469 | /picocolors@1.1.1:
470 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
471 | dev: true
472 |
473 | /picomatch@4.0.2:
474 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
475 | engines: {node: '>=12'}
476 | dev: true
477 |
478 | /postcss@8.5.3:
479 | resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
480 | engines: {node: ^10 || ^12 || >=14}
481 | dependencies:
482 | nanoid: 3.3.11
483 | picocolors: 1.1.1
484 | source-map-js: 1.2.1
485 | dev: true
486 |
487 | /rollup@4.40.0:
488 | resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==}
489 | engines: {node: '>=18.0.0', npm: '>=8.0.0'}
490 | hasBin: true
491 | dependencies:
492 | '@types/estree': 1.0.7
493 | optionalDependencies:
494 | '@rollup/rollup-android-arm-eabi': 4.40.0
495 | '@rollup/rollup-android-arm64': 4.40.0
496 | '@rollup/rollup-darwin-arm64': 4.40.0
497 | '@rollup/rollup-darwin-x64': 4.40.0
498 | '@rollup/rollup-freebsd-arm64': 4.40.0
499 | '@rollup/rollup-freebsd-x64': 4.40.0
500 | '@rollup/rollup-linux-arm-gnueabihf': 4.40.0
501 | '@rollup/rollup-linux-arm-musleabihf': 4.40.0
502 | '@rollup/rollup-linux-arm64-gnu': 4.40.0
503 | '@rollup/rollup-linux-arm64-musl': 4.40.0
504 | '@rollup/rollup-linux-loongarch64-gnu': 4.40.0
505 | '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0
506 | '@rollup/rollup-linux-riscv64-gnu': 4.40.0
507 | '@rollup/rollup-linux-riscv64-musl': 4.40.0
508 | '@rollup/rollup-linux-s390x-gnu': 4.40.0
509 | '@rollup/rollup-linux-x64-gnu': 4.40.0
510 | '@rollup/rollup-linux-x64-musl': 4.40.0
511 | '@rollup/rollup-win32-arm64-msvc': 4.40.0
512 | '@rollup/rollup-win32-ia32-msvc': 4.40.0
513 | '@rollup/rollup-win32-x64-msvc': 4.40.0
514 | fsevents: 2.3.3
515 | dev: true
516 |
517 | /source-map-js@1.2.1:
518 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
519 | engines: {node: '>=0.10.0'}
520 | dev: true
521 |
522 | /three@0.175.0:
523 | resolution: {integrity: sha512-nNE3pnTHxXN/Phw768u0Grr7W4+rumGg/H6PgeseNJojkJtmeHJfZWi41Gp2mpXl1pg1pf1zjwR4McM1jTqkpg==}
524 | dev: false
525 |
526 | /tinyglobby@0.2.12:
527 | resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
528 | engines: {node: '>=12.0.0'}
529 | dependencies:
530 | fdir: 6.4.3(picomatch@4.0.2)
531 | picomatch: 4.0.2
532 | dev: true
533 |
534 | /tweakpane@4.0.5:
535 | resolution: {integrity: sha512-rxEXdSI+ArlG1RyO6FghC4ZUX8JkEfz8F3v1JuteXSV0pEtHJzyo07fcDG+NsJfN5L39kSbCYbB9cBGHyuI/tQ==}
536 | dev: false
537 |
538 | /vite@6.3.1:
539 | resolution: {integrity: sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==}
540 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
541 | hasBin: true
542 | peerDependencies:
543 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
544 | jiti: '>=1.21.0'
545 | less: '*'
546 | lightningcss: ^1.21.0
547 | sass: '*'
548 | sass-embedded: '*'
549 | stylus: '*'
550 | sugarss: '*'
551 | terser: ^5.16.0
552 | tsx: ^4.8.1
553 | yaml: ^2.4.2
554 | peerDependenciesMeta:
555 | '@types/node':
556 | optional: true
557 | jiti:
558 | optional: true
559 | less:
560 | optional: true
561 | lightningcss:
562 | optional: true
563 | sass:
564 | optional: true
565 | sass-embedded:
566 | optional: true
567 | stylus:
568 | optional: true
569 | sugarss:
570 | optional: true
571 | terser:
572 | optional: true
573 | tsx:
574 | optional: true
575 | yaml:
576 | optional: true
577 | dependencies:
578 | esbuild: 0.25.2
579 | fdir: 6.4.3(picomatch@4.0.2)
580 | picomatch: 4.0.2
581 | postcss: 8.5.3
582 | rollup: 4.40.0
583 | tinyglobby: 0.2.12
584 | optionalDependencies:
585 | fsevents: 2.3.3
586 | dev: true
587 |
--------------------------------------------------------------------------------
/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicshelter/tweakpane-undo-redo-plugin/634ab65e8ba0a3918fe6b872784ec579aa21ffdf/public/.gitkeep
--------------------------------------------------------------------------------
/src/demo.js:
--------------------------------------------------------------------------------
1 | import './style.css'
2 | import * as THREE from 'three';
3 | import * as Tweakpane from 'tweakpane';
4 | // import addUndoRedoFeature from './main';
5 |
6 | // addUndoRedoFeature(Tweakpane);
7 |
8 | /**
9 | * Setup Three
10 | */
11 | const scene = new THREE.Scene();
12 |
13 | const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
14 | camera.position.z = 5;
15 |
16 | const renderer = new THREE.WebGLRenderer();
17 | renderer.setSize(window.innerWidth, window.innerHeight);
18 | document.body.appendChild(renderer.domElement);
19 |
20 | const geometry = new THREE.BoxGeometry(1, 1, 1);
21 | const material = new THREE.MeshNormalMaterial();
22 | const cube = new THREE.Mesh(geometry, material);
23 | scene.add(cube);
24 |
25 | /**
26 | * Render loop
27 | */
28 | function animate() {
29 | cube.rotation.x += 0.005;
30 | cube.rotation.y += 0.005;
31 |
32 | renderer.render(scene, camera);
33 | }
34 |
35 | renderer.setAnimationLoop(animate);
36 |
37 | /**
38 | * Setup Pane
39 | */
40 | const pane = new Tweakpane.Pane({ title: 'Tweakpane Undo / Redo' });
41 |
42 | const cubeFolder = pane.addFolder({ title: 'Cube' });
43 | cubeFolder.addBinding(cube.scale, 'x');
44 | cubeFolder.addBinding(cube.scale, 'y');
45 | cubeFolder.addBinding(cube.scale, 'z');
46 |
47 | const cameraFolder = pane.addFolder({ title: 'Camera' });
48 | const cameraTabs = cameraFolder.addTab({ pages: [{ title: 'Position' }, { title: 'Settings' }] });
49 |
50 | cameraTabs.pages[0].addBinding(camera.position, 'x');
51 | cameraTabs.pages[0].addBinding(camera.position, 'y');
52 | cameraTabs.pages[0].addBinding(camera.position, 'z');
53 |
54 | cameraTabs.pages[1].addBinding(camera, 'near').on('change', () => { camera.updateProjectionMatrix() });
55 | cameraTabs.pages[1].addBinding(camera, 'far').on('change', () => { camera.updateProjectionMatrix() });
56 | cameraTabs.pages[1].addBinding(camera, 'fov').on('change', () => { camera.updateProjectionMatrix() });
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | export default function addUndoRedoFeature(Tweakpane) {
2 | let undoStack = [];
3 | let redoStack = [];
4 |
5 | function undo() {
6 | if (undoStack.length === 0) return;
7 |
8 | const undoData = undoStack.pop();
9 | undoData.binding.isUndoAction = true;
10 | undoData.binding.controller.value.setRawValue(undoData.value);
11 | }
12 |
13 | function redo() {
14 | if (redoStack.length === 0) return;
15 |
16 | const redoData = redoStack.pop();
17 | redoData.binding.isRedoAction = true;
18 | redoData.binding.controller.value.setRawValue(redoData.value);
19 | }
20 |
21 | function bindingUndoRedoMiddleware(binding, options) {
22 | binding.isReadOnly = options[2] && options[2].readonly;
23 |
24 | if (binding.isReadOnly) return binding;
25 |
26 | binding.previousValues = [];
27 |
28 | binding.controller.value.emitter.on('beforechange', (e) => {
29 | const previousValue = e.sender.rawValue;
30 | binding.previousValues.push(previousValue);
31 | });
32 |
33 | binding.on('change', (e) => {
34 | if (!e.last) return;
35 |
36 | if (binding.isUndoAction) {
37 | redoStack.push({ binding, value: binding.previousValues[0] });
38 | binding.isUndoAction = false;
39 | } else if (binding.isRedoAction) {
40 | undoStack.push({ binding, value: binding.previousValues[0] });
41 | binding.isRedoAction = false;
42 | } else {
43 | redoStack = [];
44 | undoStack.push({ binding, value: binding.previousValues[0] });
45 | }
46 |
47 | binding.previousValues = [];
48 | });
49 |
50 | return binding;
51 | }
52 |
53 | function keydownHandler(e) {
54 | const cmd = e.metaKey || e.ctrlKey;
55 | const z = e.key === 'z';
56 | const Z = e.key === 'Z';
57 |
58 | if (cmd && z) undo();
59 | else if (cmd && Z) redo();
60 | }
61 |
62 | window.addEventListener('keydown', keydownHandler);
63 |
64 | Object.assign(Tweakpane.TabPageApi.prototype, {
65 | addBinding: function(...options) {
66 | const binding = this.rackApi_.addBinding(...options);
67 | return bindingUndoRedoMiddleware(binding, options);
68 | },
69 | });
70 |
71 | Object.assign(Tweakpane.FolderApi.prototype, {
72 | addBinding: function(...options) {
73 | const binding = this.rackApi_.addBinding(...options);
74 | return bindingUndoRedoMiddleware(binding, options);
75 | },
76 | });
77 |
78 | return {
79 | clear() {
80 | redoStack = [];
81 | undoStack = [];
82 | },
83 | destroy: () => {
84 | redoStack = [];
85 | undoStack = [];
86 | window.removeEventListener('keydown', keydownHandler);
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | canvas {
2 | position: fixed;
3 | left: 0;
4 | top: 0;
5 | }
6 |
7 | .tp-dfwv {
8 | right: 400px !important;
9 | }
--------------------------------------------------------------------------------