Gregor Adams commited on
Commit
65567a2
•
1 Parent(s): 90d9359

feat include monaco (#4)

Browse files
README.md CHANGED
@@ -5,3 +5,58 @@
5
  > This project is built to fail
6
  > (until it doesn't)
7
  > Restart until it works
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  > This project is built to fail
6
  > (until it doesn't)
7
  > Restart until it works
8
+
9
+ # Prompt-Driven WebUI for Creative Development
10
+
11
+ Discover a web-based user interface designed for prompt-driven development, enabling users to create
12
+ a wide array of digital art including games, visual effects, fractal art, and flow fields. This
13
+ platform emphasizes prompt-driven creativity, inspiring users to push their artistic limits.
14
+
15
+ Featuring an interactive development environment, the WebUI allows for seamless real-time
16
+ visualization of your artistic creations. Delve into limitless possibilities and unleash your
17
+ imagination with the Prompt-Driven WebUI for Creative Development.
18
+
19
+ ## Features
20
+
21
+ - 100% prompt-driven for enhanced creativity
22
+ - Supports various types of digital art, including games, visual effects, fractal art, and flow
23
+ fields
24
+ - Built-in code editor for a seamless development experience
25
+ - Real-time visualization of your creations
26
+ - Supports GPT-3.5 Turbo and GPT-4 models
27
+ - Adjustable options for temperature and max tokens
28
+ - Easy-to-use interface with prompt and negative prompt fields
29
+ - Reload and clear prompt functionalities
30
+ - Save and manage multiple projects
31
+
32
+ ## Examples
33
+
34
+ Here are just a few examples of the types of projects you can create:
35
+
36
+ 1. Prompt: Create a mesmerizing, swirling vortex pattern that evolves over time.
37
+ 2. Prompt: Design a firework display that launches and explodes at random locations and intervals.
38
+ 3. Prompt: Generate a calming, flowing water simulation with waves and ripples.
39
+ 4. Prompt: Create a dynamic solar system simulation with planets orbiting at different speeds.
40
+ 5. Prompt: Design a captivating, ever-changing kaleidoscope pattern.
41
+ 6. Prompt: Build a particle-based physics simulation where particles interact with each other and
42
+ the environment.
43
+ 7. Prompt: Design a fluid art simulator that creates unique, flowing patterns on the canvas.
44
+ 8. Prompt: Create a bouncing ball simulation with different ball sizes, elasticity, and gravity
45
+ settings.
46
+ 9. Prompt: Design an interactive, generative art piece that evolves and grows over time, creating an
47
+ organic visual experience.
48
+ 10. Prompt: Create a simulation of a flock of birds flying together, showcasing realistic flocking
49
+ behavior and interactions.
50
+
51
+ ## Getting Started
52
+
53
+ To get started with the Prompt-Driven WebUI for Creative Development, simply follow these steps:
54
+
55
+ 1. Clone the repository to your local machine.
56
+ 2. Copy the `.env.local.example` file to a new file named `.env.local` and add your `OPENAI_API_KEY`
57
+ from the OpenAI platform (platform.openai.com).
58
+ 3. Install the required dependencies using `npm install`.
59
+ 4. Run the development server with `npm run dev` to access the WebUI.
60
+ 5. Begin creating your own digital art!
61
+
62
+ Happy creating!
package-lock.json CHANGED
@@ -9,22 +9,30 @@
9
  "version": "1.0.0",
10
  "license": "AGPL",
11
  "dependencies": {
 
12
  "@emotion/cache": "11.10.7",
13
  "@emotion/react": "11.10.6",
14
  "@emotion/server": "11.10.0",
15
  "@emotion/styled": "11.10.6",
 
16
  "@mui/icons-material": "5.11.16",
17
  "@mui/material": "5.12.0",
 
18
  "axios": "1.3.5",
19
  "esdeka": "0.1.18",
20
  "eslint": "8.37.0",
21
  "eslint-config-next": "13.2.4",
 
22
  "jotai": "2.0.4",
 
 
23
  "nanoid": "4.0.2",
24
  "next": "13.2.4",
25
  "openai": "^3.2.1",
 
26
  "react": "18.2.0",
27
  "react-dom": "18.2.0",
 
28
  "react-syntax-highlighter": "15.5.0",
29
  "zustand": "4.3.7"
30
  },
@@ -32,8 +40,7 @@
32
  "@semantic-release/git": "^10.0.1",
33
  "@types/node": "18.15.11",
34
  "@types/react-syntax-highlighter": "^15.5.6",
35
- "husky": "^8.0.3",
36
- "prettier": "^2.8.7"
37
  },
38
  "engines": {
39
  "node": ">= 18"
@@ -154,6 +161,17 @@
154
  "node": ">=4"
155
  }
156
  },
 
 
 
 
 
 
 
 
 
 
 
157
  "node_modules/@babel/runtime": {
158
  "version": "7.21.0",
159
  "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
@@ -431,6 +449,30 @@
431
  "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
432
  "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
433
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  "node_modules/@mui/base": {
435
  "version": "5.0.0-alpha.125",
436
  "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.125.tgz",
@@ -1686,6 +1728,11 @@
1686
  "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
1687
  "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
1688
  },
 
 
 
 
 
1689
  "node_modules/@types/prop-types": {
1690
  "version": "15.7.5",
1691
  "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@@ -2014,6 +2061,11 @@
2014
  "url": "https://github.com/sponsors/ljharb"
2015
  }
2016
  },
 
 
 
 
 
2017
  "node_modules/array-union": {
2018
  "version": "2.1.0",
2019
  "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -3582,6 +3634,11 @@
3582
  "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
3583
  "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
3584
  },
 
 
 
 
 
3585
  "node_modules/fastq": {
3586
  "version": "1.15.0",
3587
  "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@@ -4008,6 +4065,17 @@
4008
  "url": "https://github.com/sponsors/ljharb"
4009
  }
4010
  },
 
 
 
 
 
 
 
 
 
 
 
4011
  "node_modules/graceful-fs": {
4012
  "version": "4.2.11",
4013
  "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -5550,6 +5618,19 @@
5550
  "node": ">=0.10.0"
5551
  }
5552
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
5553
  "node_modules/ms": {
5554
  "version": "2.1.2",
5555
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -9479,7 +9560,6 @@
9479
  "version": "2.8.7",
9480
  "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz",
9481
  "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==",
9482
- "dev": true,
9483
  "bin": {
9484
  "prettier": "bin-prettier.js"
9485
  },
@@ -9634,6 +9714,21 @@
9634
  "react": "^18.2.0"
9635
  }
9636
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9637
  "node_modules/react-is": {
9638
  "version": "16.13.1",
9639
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -10655,6 +10750,11 @@
10655
  "node": ">= 6"
10656
  }
10657
  },
 
 
 
 
 
10658
  "node_modules/stop-iteration-iterator": {
10659
  "version": "1.0.0",
10660
  "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
 
9
  "version": "1.0.0",
10
  "license": "AGPL",
11
  "dependencies": {
12
+ "@babel/parser": "7.21.4",
13
  "@emotion/cache": "11.10.7",
14
  "@emotion/react": "11.10.6",
15
  "@emotion/server": "11.10.0",
16
  "@emotion/styled": "11.10.6",
17
+ "@monaco-editor/react": "4.5.0",
18
  "@mui/icons-material": "5.11.16",
19
  "@mui/material": "5.12.0",
20
+ "@types/prettier": "^2.7.2",
21
  "axios": "1.3.5",
22
  "esdeka": "0.1.18",
23
  "eslint": "8.37.0",
24
  "eslint-config-next": "13.2.4",
25
+ "gpt3-tokenizer": "1.1.5",
26
  "jotai": "2.0.4",
27
+ "monaco-editor": "0.37.1",
28
+ "monaco-themes": "0.4.4",
29
  "nanoid": "4.0.2",
30
  "next": "13.2.4",
31
  "openai": "^3.2.1",
32
+ "prettier": "2.8.7",
33
  "react": "18.2.0",
34
  "react-dom": "18.2.0",
35
+ "react-hook-form": "7.43.9",
36
  "react-syntax-highlighter": "15.5.0",
37
  "zustand": "4.3.7"
38
  },
 
40
  "@semantic-release/git": "^10.0.1",
41
  "@types/node": "18.15.11",
42
  "@types/react-syntax-highlighter": "^15.5.6",
43
+ "husky": "^8.0.3"
 
44
  },
45
  "engines": {
46
  "node": ">= 18"
 
161
  "node": ">=4"
162
  }
163
  },
164
+ "node_modules/@babel/parser": {
165
+ "version": "7.21.4",
166
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
167
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
168
+ "bin": {
169
+ "parser": "bin/babel-parser.js"
170
+ },
171
+ "engines": {
172
+ "node": ">=6.0.0"
173
+ }
174
+ },
175
  "node_modules/@babel/runtime": {
176
  "version": "7.21.0",
177
  "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
 
449
  "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
450
  "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
451
  },
452
+ "node_modules/@monaco-editor/loader": {
453
+ "version": "1.3.3",
454
+ "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.3.tgz",
455
+ "integrity": "sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==",
456
+ "dependencies": {
457
+ "state-local": "^1.0.6"
458
+ },
459
+ "peerDependencies": {
460
+ "monaco-editor": ">= 0.21.0 < 1"
461
+ }
462
+ },
463
+ "node_modules/@monaco-editor/react": {
464
+ "version": "4.5.0",
465
+ "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.5.0.tgz",
466
+ "integrity": "sha512-VJMkp5Fe1+w8pLEq8tZPHZKu8zDXQIA1FtiDTSNccg1D3wg1YIZaH2es2Qpvop1k62g3c/YySRb3bnGXu2XwYQ==",
467
+ "dependencies": {
468
+ "@monaco-editor/loader": "^1.3.3"
469
+ },
470
+ "peerDependencies": {
471
+ "monaco-editor": ">= 0.25.0 < 1",
472
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
473
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
474
+ }
475
+ },
476
  "node_modules/@mui/base": {
477
  "version": "5.0.0-alpha.125",
478
  "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.125.tgz",
 
1728
  "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
1729
  "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
1730
  },
1731
+ "node_modules/@types/prettier": {
1732
+ "version": "2.7.2",
1733
+ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
1734
+ "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg=="
1735
+ },
1736
  "node_modules/@types/prop-types": {
1737
  "version": "15.7.5",
1738
  "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
 
2061
  "url": "https://github.com/sponsors/ljharb"
2062
  }
2063
  },
2064
+ "node_modules/array-keyed-map": {
2065
+ "version": "2.1.3",
2066
+ "resolved": "https://registry.npmjs.org/array-keyed-map/-/array-keyed-map-2.1.3.tgz",
2067
+ "integrity": "sha512-JIUwuFakO+jHjxyp4YgSiKXSZeC0U+R1jR94bXWBcVlFRBycqXlb+kH9JHxBGcxnVuSqx5bnn0Qz9xtSeKOjiA=="
2068
+ },
2069
  "node_modules/array-union": {
2070
  "version": "2.1.0",
2071
  "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
 
3634
  "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
3635
  "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
3636
  },
3637
+ "node_modules/fast-plist": {
3638
+ "version": "0.1.3",
3639
+ "resolved": "https://registry.npmjs.org/fast-plist/-/fast-plist-0.1.3.tgz",
3640
+ "integrity": "sha512-d9cEfo/WcOezgPLAC/8t8wGb6YOD6JTCPMw2QcG2nAdFmyY+9rTUizCTaGjIZAloWENTEUMAPpkUAIJJJ0i96A=="
3641
+ },
3642
  "node_modules/fastq": {
3643
  "version": "1.15.0",
3644
  "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
 
4065
  "url": "https://github.com/sponsors/ljharb"
4066
  }
4067
  },
4068
+ "node_modules/gpt3-tokenizer": {
4069
+ "version": "1.1.5",
4070
+ "resolved": "https://registry.npmjs.org/gpt3-tokenizer/-/gpt3-tokenizer-1.1.5.tgz",
4071
+ "integrity": "sha512-O9iCL8MqGR0Oe9wTh0YftzIbysypNQmS5a5JG3cB3M4LMYjlAVvNnf8LUzVY9MrI7tj+YLY356uHtO2lLX2HpA==",
4072
+ "dependencies": {
4073
+ "array-keyed-map": "^2.1.3"
4074
+ },
4075
+ "engines": {
4076
+ "node": ">=12"
4077
+ }
4078
+ },
4079
  "node_modules/graceful-fs": {
4080
  "version": "4.2.11",
4081
  "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
 
5618
  "node": ">=0.10.0"
5619
  }
5620
  },
5621
+ "node_modules/monaco-editor": {
5622
+ "version": "0.37.1",
5623
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.37.1.tgz",
5624
+ "integrity": "sha512-jLXEEYSbqMkT/FuJLBZAVWGuhIb4JNwHE9kPTorAVmsdZ4UzHAfgWxLsVtD7pLRFaOwYPhNG9nUCpmFL1t/dIg=="
5625
+ },
5626
+ "node_modules/monaco-themes": {
5627
+ "version": "0.4.4",
5628
+ "resolved": "https://registry.npmjs.org/monaco-themes/-/monaco-themes-0.4.4.tgz",
5629
+ "integrity": "sha512-Hbb9pvRrpSi0rZezcB/IOdQnpx10o55Lx4zFdRAAVpFMa1HP7FgaqEZdKffb4ovd90fETCixeFO9JPYFMAq+TQ==",
5630
+ "dependencies": {
5631
+ "fast-plist": "^0.1.3"
5632
+ }
5633
+ },
5634
  "node_modules/ms": {
5635
  "version": "2.1.2",
5636
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
 
9560
  "version": "2.8.7",
9561
  "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz",
9562
  "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==",
 
9563
  "bin": {
9564
  "prettier": "bin-prettier.js"
9565
  },
 
9714
  "react": "^18.2.0"
9715
  }
9716
  },
9717
+ "node_modules/react-hook-form": {
9718
+ "version": "7.43.9",
9719
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.9.tgz",
9720
+ "integrity": "sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==",
9721
+ "engines": {
9722
+ "node": ">=12.22.0"
9723
+ },
9724
+ "funding": {
9725
+ "type": "opencollective",
9726
+ "url": "https://opencollective.com/react-hook-form"
9727
+ },
9728
+ "peerDependencies": {
9729
+ "react": "^16.8.0 || ^17 || ^18"
9730
+ }
9731
+ },
9732
  "node_modules/react-is": {
9733
  "version": "16.13.1",
9734
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
 
10750
  "node": ">= 6"
10751
  }
10752
  },
10753
+ "node_modules/state-local": {
10754
+ "version": "1.0.7",
10755
+ "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
10756
+ "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="
10757
+ },
10758
  "node_modules/stop-iteration-iterator": {
10759
  "version": "1.0.0",
10760
  "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
package.json CHANGED
@@ -38,22 +38,30 @@
38
  ]
39
  },
40
  "dependencies": {
 
41
  "@emotion/cache": "11.10.7",
42
  "@emotion/react": "11.10.6",
43
  "@emotion/server": "11.10.0",
44
  "@emotion/styled": "11.10.6",
 
45
  "@mui/icons-material": "5.11.16",
46
  "@mui/material": "5.12.0",
 
47
  "axios": "1.3.5",
48
  "esdeka": "0.1.18",
49
  "eslint": "8.37.0",
50
  "eslint-config-next": "13.2.4",
 
51
  "jotai": "2.0.4",
 
 
52
  "nanoid": "4.0.2",
53
  "next": "13.2.4",
54
  "openai": "^3.2.1",
 
55
  "react": "18.2.0",
56
  "react-dom": "18.2.0",
 
57
  "react-syntax-highlighter": "15.5.0",
58
  "zustand": "4.3.7"
59
  },
@@ -61,8 +69,7 @@
61
  "@semantic-release/git": "^10.0.1",
62
  "@types/node": "18.15.11",
63
  "@types/react-syntax-highlighter": "^15.5.6",
64
- "husky": "^8.0.3",
65
- "prettier": "^2.8.7"
66
  },
67
  "engines": {
68
  "node": ">= 18"
 
38
  ]
39
  },
40
  "dependencies": {
41
+ "@babel/parser": "7.21.4",
42
  "@emotion/cache": "11.10.7",
43
  "@emotion/react": "11.10.6",
44
  "@emotion/server": "11.10.0",
45
  "@emotion/styled": "11.10.6",
46
+ "@monaco-editor/react": "4.5.0",
47
  "@mui/icons-material": "5.11.16",
48
  "@mui/material": "5.12.0",
49
+ "@types/prettier": "^2.7.2",
50
  "axios": "1.3.5",
51
  "esdeka": "0.1.18",
52
  "eslint": "8.37.0",
53
  "eslint-config-next": "13.2.4",
54
+ "gpt3-tokenizer": "1.1.5",
55
  "jotai": "2.0.4",
56
+ "monaco-editor": "0.37.1",
57
+ "monaco-themes": "0.4.4",
58
  "nanoid": "4.0.2",
59
  "next": "13.2.4",
60
  "openai": "^3.2.1",
61
+ "prettier": "2.8.7",
62
  "react": "18.2.0",
63
  "react-dom": "18.2.0",
64
+ "react-hook-form": "7.43.9",
65
  "react-syntax-highlighter": "15.5.0",
66
  "zustand": "4.3.7"
67
  },
 
69
  "@semantic-release/git": "^10.0.1",
70
  "@types/node": "18.15.11",
71
  "@types/react-syntax-highlighter": "^15.5.6",
72
+ "husky": "^8.0.3"
 
73
  },
74
  "engines": {
75
  "node": ">= 18"
public/favicon.ico CHANGED
public/icons/android-chrome-192x192.png ADDED
public/icons/android-chrome-512x512.png ADDED
public/icons/apple-touch-icon.png ADDED
public/icons/favicon-16x16.png ADDED
public/icons/favicon-32x32.png ADDED
public/js/utils.js CHANGED
@@ -1,5 +1,4 @@
1
  const canvas = document.querySelector("#canvas");
2
- const ctx = canvas.getContext("2d");
3
 
4
  function handleResize() {
5
  requestAnimationFrame(() => {
@@ -50,11 +49,7 @@ function answer(window_, channel, targetOrigin = "*") {
50
  }
51
 
52
  function handleTemplate(template) {
53
- try {
54
- Function("Template", `${template};`)();
55
- } catch (error) {
56
- console.log(error);
57
- }
58
  }
59
 
60
  subscribe("fail4", event => {
@@ -63,11 +58,9 @@ subscribe("fail4", event => {
63
  case "call":
64
  host.current = event.source;
65
  answer(event.source, "fail4");
66
- ctx.clearRect(0, 0, canvas.width, canvas.height);
67
  handleTemplate(action.payload.template);
68
  break;
69
  case "broadcast":
70
- ctx.clearRect(0, 0, canvas.width, canvas.height);
71
  handleTemplate(action.payload.template);
72
  break;
73
  default:
 
1
  const canvas = document.querySelector("#canvas");
 
2
 
3
  function handleResize() {
4
  requestAnimationFrame(() => {
 
49
  }
50
 
51
  function handleTemplate(template) {
52
+ Function("Template", `${template};`)();
 
 
 
 
53
  }
54
 
55
  subscribe("fail4", event => {
 
58
  case "call":
59
  host.current = event.source;
60
  answer(event.source, "fail4");
 
61
  handleTemplate(action.payload.template);
62
  break;
63
  case "broadcast":
 
64
  handleTemplate(action.payload.template);
65
  break;
66
  default:
src/components/EditTitle.tsx ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from "react";
2
+ import Box from "@mui/material/Box";
3
+ import InputBase from "@mui/material/InputBase";
4
+ import { roboto } from "@/lib/theme";
5
+ import IconButton from "@mui/material/IconButton";
6
+ import SaveIcon from "@mui/icons-material/Save";
7
+
8
+ export function EditTitle({ value, onSave }: { value: string; onSave(value: string): void }) {
9
+ const [text, setText] = useState(value);
10
+ return (
11
+ <>
12
+ <Box
13
+ sx={{
14
+ pl: 3,
15
+ pr: 6,
16
+ flex: 1,
17
+ display: "flex",
18
+ alignItems: "center",
19
+ }}
20
+ >
21
+ <InputBase
22
+ autoFocus
23
+ value={text}
24
+ sx={{
25
+ width: "100%",
26
+ fontSize: 16,
27
+ input: { ...roboto.style, p: 0, lineHeight: 1.5 },
28
+ }}
29
+ onChange={event => {
30
+ setText(event.target.value);
31
+ }}
32
+ onBlur={() => {
33
+ onSave(text);
34
+ }}
35
+ />
36
+ </Box>
37
+ <IconButton
38
+ onClick={() => {
39
+ onSave(text);
40
+ }}
41
+ >
42
+ <SaveIcon />
43
+ </IconButton>
44
+ </>
45
+ );
46
+ }
src/constants/index.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const base = {
2
+ default: `/** CHANGELOG
3
+ * v1.0.0. Set up canvas
4
+ */
5
+ const canvas = document.querySelector('canvas');
6
+ const ctx = canvas.getContext('2d');
7
+ /*
8
+ * The draw function is called every frame to update the canvas.
9
+ * To change the drawing logic, modify the code inside this function.
10
+ */
11
+ function draw(){
12
+ // TODO: Add drawing logic here
13
+ // Set the desired FPS (frames per second) for the animation
14
+ const FPS = 60;
15
+ // Schedule the next frame to be drawn
16
+ setTimeout(requestAnimationFrame(draw),1000/FPS)
17
+ }
18
+ draw();
19
+ `.trim(),
20
+ };
src/lib/createEmotionCache.ts CHANGED
@@ -6,14 +6,14 @@ const isBrowser = typeof document !== "undefined";
6
  // This assures that MUI styles are loaded first.
7
  // It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
8
  export default function createEmotionCache() {
9
- let insertionPoint;
10
 
11
- if (isBrowser) {
12
- const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
13
- 'meta[name="emotion-insertion-point"]'
14
- );
15
- insertionPoint = emotionInsertionPoint ?? undefined;
16
- }
17
 
18
- return createCache({ key: "mui-style", insertionPoint });
19
  }
 
6
  // This assures that MUI styles are loaded first.
7
  // It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
8
  export default function createEmotionCache() {
9
+ let insertionPoint;
10
 
11
+ if (isBrowser) {
12
+ const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
13
+ 'meta[name="emotion-insertion-point"]'
14
+ );
15
+ insertionPoint = emotionInsertionPoint ?? undefined;
16
+ }
17
 
18
+ return createCache({ key: "mui-style", insertionPoint });
19
  }
src/lib/theme.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { Roboto } from "next/font/google";
2
- import { createTheme } from "@mui/material/styles";
3
- import { red } from "@mui/material/colors";
4
 
5
  export const roboto = Roboto({
6
  weight: ["300", "400", "500", "700"],
@@ -10,17 +9,27 @@ export const roboto = Roboto({
10
  });
11
 
12
  // Create a theme instance.
13
- const theme = createTheme({
14
- palette: {
15
- mode: "dark",
16
- primary: {
17
- main: "#00d720",
 
 
 
 
 
 
18
  },
19
- secondary: {
20
- main: "#cc06ed",
21
- },
22
- error: {
23
- main: red.A400,
 
 
 
 
24
  },
25
  },
26
  typography: {
@@ -29,3 +38,6 @@ const theme = createTheme({
29
  });
30
 
31
  export default theme;
 
 
 
 
1
+ import { Fira_Code, Roboto } from "next/font/google";
2
+ import { experimental_extendTheme as extendTheme } from "@mui/material/styles";
 
3
 
4
  export const roboto = Roboto({
5
  weight: ["300", "400", "500", "700"],
 
9
  });
10
 
11
  // Create a theme instance.
12
+ const theme = extendTheme({
13
+ colorSchemes: {
14
+ light: {
15
+ palette: {
16
+ primary: {
17
+ main: "#40088d",
18
+ },
19
+ secondary: {
20
+ main: "#038225",
21
+ },
22
+ },
23
  },
24
+ dark: {
25
+ palette: {
26
+ primary: {
27
+ main: "#00d720",
28
+ },
29
+ secondary: {
30
+ main: "#cc06ed",
31
+ },
32
+ },
33
  },
34
  },
35
  typography: {
 
38
  });
39
 
40
  export default theme;
41
+ export const fontMono = Fira_Code({
42
+ subsets: ["latin"],
43
+ });
src/pages/_app.tsx CHANGED
@@ -1,11 +1,10 @@
1
  import Head from "next/head";
2
  import { AppProps } from "next/app";
3
- import { ThemeProvider } from "@mui/material/styles";
4
  import { CacheProvider, EmotionCache } from "@emotion/react";
5
- import theme from "@/lib/theme";
6
  import createEmotionCache from "@/lib/createEmotionCache";
 
 
7
 
8
- // Client-side cache, shared for the whole session of the user in the browser.
9
  const clientSideEmotionCache = createEmotionCache();
10
 
11
  export interface MyAppProps extends AppProps {
@@ -19,10 +18,9 @@ export default function MyApp(props: MyAppProps) {
19
  <Head>
20
  <meta name="viewport" content="initial-scale=1, width=device-width" />
21
  </Head>
22
- <ThemeProvider theme={theme}>
23
- {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
24
  <Component {...pageProps} />
25
- </ThemeProvider>
26
  </CacheProvider>
27
  );
28
  }
 
1
  import Head from "next/head";
2
  import { AppProps } from "next/app";
 
3
  import { CacheProvider, EmotionCache } from "@emotion/react";
 
4
  import createEmotionCache from "@/lib/createEmotionCache";
5
+ import { Experimental_CssVarsProvider as CssVarsProvider } from "@mui/material/styles";
6
+ import theme from "@/lib/theme";
7
 
 
8
  const clientSideEmotionCache = createEmotionCache();
9
 
10
  export interface MyAppProps extends AppProps {
 
18
  <Head>
19
  <meta name="viewport" content="initial-scale=1, width=device-width" />
20
  </Head>
21
+ <CssVarsProvider defaultMode="system" theme={theme}>
 
22
  <Component {...pageProps} />
23
+ </CssVarsProvider>
24
  </CacheProvider>
25
  );
26
  }
src/pages/_document.tsx CHANGED
@@ -9,9 +9,10 @@ import Document, {
9
  } from "next/document";
10
  import createEmotionServer from "@emotion/server/create-instance";
11
  import { AppType } from "next/app";
12
- import theme, { roboto } from "@/lib/theme";
13
  import createEmotionCache from "@/lib/createEmotionCache";
14
  import { MyAppProps } from "./_app";
 
15
 
16
  interface MyDocumentProps extends DocumentProps {
17
  emotionStyleTags: JSX.Element[];
@@ -21,13 +22,12 @@ export default function MyDocument({ emotionStyleTags }: MyDocumentProps) {
21
  return (
22
  <Html lang="en" className={roboto.className}>
23
  <Head>
24
- {/* PWA primary color */}
25
- <meta name="theme-color" content={theme.palette.primary.main} />
26
  <link rel="shortcut icon" href="/favicon.ico" />
27
  <meta name="emotion-insertion-point" content="" />
28
  {emotionStyleTags}
29
  </Head>
30
  <body>
 
31
  <Main />
32
  <NextScript />
33
  </body>
 
9
  } from "next/document";
10
  import createEmotionServer from "@emotion/server/create-instance";
11
  import { AppType } from "next/app";
12
+ import { roboto } from "@/lib/theme";
13
  import createEmotionCache from "@/lib/createEmotionCache";
14
  import { MyAppProps } from "./_app";
15
+ import { getInitColorSchemeScript } from "@mui/material/styles";
16
 
17
  interface MyDocumentProps extends DocumentProps {
18
  emotionStyleTags: JSX.Element[];
 
22
  return (
23
  <Html lang="en" className={roboto.className}>
24
  <Head>
 
 
25
  <link rel="shortcut icon" href="/favicon.ico" />
26
  <meta name="emotion-insertion-point" content="" />
27
  {emotionStyleTags}
28
  </Head>
29
  <body>
30
+ {getInitColorSchemeScript()}
31
  <Main />
32
  <NextScript />
33
  </body>
src/pages/index.tsx CHANGED
@@ -5,15 +5,17 @@ import AcUnitIcon from "@mui/icons-material/AcUnit";
5
  import LocalFireDepartmentIcon from "@mui/icons-material/LocalFireDepartment";
6
  import CheckIcon from "@mui/icons-material/Check";
7
  import ClearIcon from "@mui/icons-material/Clear";
8
- import ContentCopyIcon from "@mui/icons-material/ContentCopy";
 
 
9
  import VisibilityIcon from "@mui/icons-material/Visibility";
10
- import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
11
  import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
12
  import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
13
  import PlayArrowIcon from "@mui/icons-material/PlayArrow";
14
  import ReplayIcon from "@mui/icons-material/Replay";
 
 
15
  import TextField from "@mui/material/TextField";
16
- import { Fira_Code } from "next/font/google";
17
  import Box from "@mui/material/Box";
18
  import Stack from "@mui/material/Stack";
19
  import Accordion from "@mui/material/Accordion";
@@ -35,46 +37,34 @@ import CircularProgress from "@mui/material/CircularProgress";
35
  import CssBaseline from "@mui/material/CssBaseline";
36
  import Slider from "@mui/material/Slider";
37
  import { useAtom } from "jotai";
38
- import { atomWithStorage } from "jotai/utils";
39
  import Button from "@mui/material/Button";
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- const base = {
42
- default: `/** CHANGELOG
43
- * 1. Set up canvas
44
- */
45
- const canvas = document.querySelector('canvas');
46
- const ctx = canvas.getContext('2d');
47
- /*60FPS draw cycle*/
48
- function draw(){
49
- const FPS = 60;
50
- setTimeout(requestAnimationFrame(draw),1000/FPS)
51
- }
52
- draw();
53
- `.trim(),
54
- };
55
-
56
- const fontMono = Fira_Code({
57
- subsets: ["latin"],
58
- });
59
-
60
- const answersAtom = atomWithStorage<{ id: string; content: string; task: string }[]>("fail4", [
61
- {
62
- id: "1",
63
- content: base.default,
64
- task: "Base Script",
65
- },
66
- ]);
67
 
68
  export default function Home() {
69
  const ref = useRef<HTMLIFrameElement>(null);
70
- const [template, setTemplate] = useState(base.default);
71
  const [runningId, setRunningId] = useState("1");
72
  const [activeId, setActiveId] = useState("1");
 
73
  const [answers, setAnswers] = useAtom(answersAtom);
 
74
  const [loading, setLoading] = useState(false);
75
  const [loadingLive, setLoadingLive] = useState(true);
 
76
 
77
- const { broadcast, call, subscribe } = useHost(ref, "fail4");
78
 
79
  const connection = useRef(false);
80
  const [tries, setTries] = useState(1);
@@ -90,8 +80,6 @@ export default function Home() {
90
 
91
  const timeout = setTimeout(() => {
92
  if (current) {
93
- // call({ template: "" });
94
-
95
  call({ template: current.content });
96
  }
97
 
@@ -141,7 +129,6 @@ export default function Home() {
141
  return (
142
  <>
143
  <CssBaseline />
144
-
145
  <Stack
146
  sx={{
147
  ...fontMono.style,
@@ -152,211 +139,315 @@ export default function Home() {
152
  height: "100%",
153
  }}
154
  >
155
- <Stack sx={{ width: "50%", flex: 1, gap: 2 }}>
156
- <Box
157
- component="form"
158
- onSubmit={async event => {
159
- event.preventDefault();
160
- const formData = new FormData(event.target as HTMLFormElement);
161
- const formObject = Object.fromEntries(formData);
162
- try {
163
- setLoading(true);
164
- const { data } = await axios.post("/api/gpt", formObject);
165
- const answer = data;
166
- setAnswers(previousAnswers => [answer, ...previousAnswers]);
167
- setRunningId(answer.id);
168
- reload();
169
- } catch (error) {
170
- console.error(error);
171
- } finally {
172
- setLoading(false);
173
- }
174
- }}
175
- >
176
- <AppBar position="static" elevation={0}>
177
- <Toolbar>
178
- <Button
179
- type="submit"
180
- aria-label={loading ? "Loading" : "Run"}
181
- aria-disabled={loading}
182
- disabled={loading}
183
- startIcon={
184
- loading ? <CircularProgress size={24} /> : <PlayArrowIcon />
185
- }
186
- >
187
- Run
188
- </Button>
189
- <Typography sx={{ flex: 1, pl: 1 }}>
190
- {current?.task} - {current?.id ?? ""}
191
- </Typography>
192
- <IconButton
193
- edge="end"
194
- color="inherit"
195
- aria-label="Clear Prompt"
196
- onClick={async () => {
197
- // broadcast({ template: base.default });
198
- setActiveId("1");
199
- setTemplate(base.default);
200
- reload();
201
- }}
202
- >
203
- <ClearIcon />
204
- </IconButton>
205
- </Toolbar>
206
- </AppBar>
207
- <Paper variant="outlined" sx={{ p: 0 }}>
208
- <Stack sx={{ p: 2, gap: 2 }}>
209
- <Typography>
210
- Based on: {current?.task ?? "Base Script"} - {activeId}
211
- </Typography>
212
- <TextField
213
- multiline
214
- fullWidth
215
- id="prompt"
216
- name="prompt"
217
- label="Prompt"
218
- placeholder="red heart"
219
- defaultValue="red heart"
220
- maxRows={6}
221
- InputProps={{
222
- style: fontMono.style,
223
- }}
224
- />
225
- <TextField
226
- multiline
227
- fullWidth
228
- id="negativePrompt"
229
- name="negativePrompt"
230
- label="Negative Prompt"
231
- placeholder="images, audio files"
232
- maxRows={6}
233
- InputProps={{
234
- style: fontMono.style,
235
  }}
236
  />
237
- </Stack>
238
- </Paper>
239
- <Accordion>
240
- <AccordionSummary
241
- expandIcon={<ExpandMoreIcon />}
242
- aria-controls="panel1a-content"
243
- id="panel1a-header"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  >
245
- <Typography>Options</Typography>
246
- </AccordionSummary>
247
- <AccordionDetails>
248
- <Stack
249
- spacing={2}
250
- direction="row"
251
- sx={{ mb: 2 }}
252
- alignItems="center"
253
- >
254
- <AcUnitIcon />
255
- <Slider
256
- marks
257
- id="temperature"
258
- name="temperature"
259
- min={0}
260
- max={0.8}
261
- defaultValue={0.2}
262
- step={0.1}
263
- valueLabelDisplay="auto"
264
- aria-label="Temperature"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  />
266
- <LocalFireDepartmentIcon />
267
  </Stack>
268
- <TextField
269
- multiline
270
- fullWidth
271
- id="template"
272
- name="template"
273
- label="Template"
274
- placeholder={base.default}
275
- maxRows={10}
276
- value={template}
277
- InputProps={{
278
- style: { ...fontMono.style },
279
- }}
280
- onChange={event => {
281
- setTemplate(event.target.value);
282
  }}
283
- />
284
- </AccordionDetails>
285
- </Accordion>
286
- </Box>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
- <List sx={{ flex: 1, overflow: "auto" }}>
289
- {answers.map(answer => {
290
- return (
291
- <ListItem
292
- key={answer.id}
293
- secondaryAction={
294
- <Stack sx={{ flexDirection: "row", gap: 1 }}>
295
- {answer.id === "1" ? undefined : (
296
- <IconButton
297
- edge="end"
298
- aria-label="Delete"
299
- onClick={() => {
300
- setAnswers(previousAnswers =>
301
- previousAnswers.filter(
302
- ({ id }) => id !== answer.id
303
- )
304
- );
305
- if (runningId === answer.id) {
306
- setRunningId("1");
307
- setTemplate(base.default);
308
- reload();
309
- }
310
- }}
311
- >
312
- <DeleteForeverIcon />
313
- </IconButton>
314
- )}
315
- <IconButton
316
- edge="end"
317
- aria-label="Show"
318
- aria-disabled={runningId === answer.id}
319
- disabled={runningId === answer.id}
320
- onClick={() => {
321
- setRunningId(answer.id);
322
- reload();
323
- }}
324
- >
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  {runningId === answer.id ? (
326
- <VisibilityIcon />
327
  ) : (
328
- <VisibilityOffIcon />
329
  )}
330
- </IconButton>
331
- </Stack>
332
- }
333
- disablePadding
334
- >
335
- <ListItemButton
336
- dense
337
- selected={activeId === answer.id}
338
- role={undefined}
339
- onClick={() => {
340
- setActiveId(answer.id);
341
- setTemplate(answer.content);
342
- }}
343
- >
344
- <ListItemIcon>
345
- {activeId === answer.id ? (
346
- <CheckIcon />
347
- ) : (
348
- <ContentCopyIcon />
349
- )}
350
- </ListItemIcon>
351
- <ListItemText primary={`${answer.task} - ${answer.id}`} />
352
- </ListItemButton>
353
- </ListItem>
354
- );
355
- })}
356
- </List>
357
  </Stack>
358
  <Stack sx={{ flex: 1, width: "50%", position: "relative" }}>
359
- <AppBar position="static" elevation={0}>
360
  <Toolbar>
361
  <IconButton
362
  color="inherit"
 
5
  import LocalFireDepartmentIcon from "@mui/icons-material/LocalFireDepartment";
6
  import CheckIcon from "@mui/icons-material/Check";
7
  import ClearIcon from "@mui/icons-material/Clear";
8
+ import CodeIcon from "@mui/icons-material/Code";
9
+ import CodeOffIcon from "@mui/icons-material/CodeOff";
10
+ import EditIcon from "@mui/icons-material/Edit";
11
  import VisibilityIcon from "@mui/icons-material/Visibility";
 
12
  import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
13
  import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
14
  import PlayArrowIcon from "@mui/icons-material/PlayArrow";
15
  import ReplayIcon from "@mui/icons-material/Replay";
16
+ import MoneyIcon from "@mui/icons-material/Money";
17
+ import TollIcon from "@mui/icons-material/Toll";
18
  import TextField from "@mui/material/TextField";
 
19
  import Box from "@mui/material/Box";
20
  import Stack from "@mui/material/Stack";
21
  import Accordion from "@mui/material/Accordion";
 
37
  import CssBaseline from "@mui/material/CssBaseline";
38
  import Slider from "@mui/material/Slider";
39
  import { useAtom } from "jotai";
 
40
  import Button from "@mui/material/Button";
41
+ import dynamic from "next/dynamic";
42
+ import FormControl from "@mui/material/FormControl";
43
+ import InputLabel from "@mui/material/InputLabel";
44
+ import Select from "@mui/material/Select";
45
+ import MenuItem from "@mui/material/MenuItem";
46
+ import { fontMono } from "@/lib/theme";
47
+ import { useColorScheme } from "@mui/material/styles";
48
+ import { getTheme, prettify } from "@/utils";
49
+ import { answersAtom, showCodeAtom } from "@/store/atoms";
50
+ import { base } from "@/constants";
51
+ import { EditTitle } from "@/components/EditTitle";
52
 
53
+ const MonacoEditor = dynamic(import("@monaco-editor/react"), { ssr: false });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  export default function Home() {
56
  const ref = useRef<HTMLIFrameElement>(null);
57
+ const [template, setTemplate] = useState(prettify(base.default));
58
  const [runningId, setRunningId] = useState("1");
59
  const [activeId, setActiveId] = useState("1");
60
+ const [editingId, setEditingId] = useState<string | null>(null);
61
  const [answers, setAnswers] = useAtom(answersAtom);
62
+ const [showCode, setShowCode] = useAtom(showCodeAtom);
63
  const [loading, setLoading] = useState(false);
64
  const [loadingLive, setLoadingLive] = useState(true);
65
+ const { mode, systemMode } = useColorScheme();
66
 
67
+ const { call, subscribe } = useHost(ref, "fail4");
68
 
69
  const connection = useRef(false);
70
  const [tries, setTries] = useState(1);
 
80
 
81
  const timeout = setTimeout(() => {
82
  if (current) {
 
 
83
  call({ template: current.content });
84
  }
85
 
 
129
  return (
130
  <>
131
  <CssBaseline />
 
132
  <Stack
133
  sx={{
134
  ...fontMono.style,
 
139
  height: "100%",
140
  }}
141
  >
142
+ <Stack sx={{ width: "50%", flex: 1 }}>
143
+ <AppBar position="static" elevation={0} color="default">
144
+ <Toolbar>
145
+ <Button
146
+ form="gpt-form"
147
+ type="submit"
148
+ aria-label={loading ? "Loading" : "Run"}
149
+ aria-disabled={loading}
150
+ disabled={loading}
151
+ startIcon={
152
+ loading ? <CircularProgress size={20} /> : <PlayArrowIcon />
153
+ }
154
+ >
155
+ Run
156
+ </Button>
157
+
158
+ {current?.id === editingId ? (
159
+ <EditTitle
160
+ value={current.task}
161
+ onSave={value => {
162
+ setEditingId(null);
163
+ setAnswers(previousAnswers =>
164
+ previousAnswers.map(answer_ =>
165
+ current.id === answer_.id
166
+ ? {
167
+ ...answer_,
168
+ task: value,
169
+ }
170
+ : answer_
171
+ )
172
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }}
174
  />
175
+ ) : (
176
+ <>
177
+ <Typography
178
+ sx={{
179
+ flex: 1,
180
+ pl: 3,
181
+ overflow: "hidden",
182
+ textOverflow: "ellipsis",
183
+ whiteSpace: "nowrap",
184
+ }}
185
+ >
186
+ {current?.task}
187
+ </Typography>
188
+ <IconButton
189
+ onClick={() => {
190
+ if (current) {
191
+ setEditingId(current.id);
192
+ }
193
+ }}
194
+ >
195
+ <EditIcon />
196
+ </IconButton>
197
+ </>
198
+ )}
199
+
200
+ <IconButton
201
+ color="inherit"
202
+ aria-label={showCode ? "Hide Code" : "Show Code"}
203
+ onClick={() => {
204
+ setShowCode(previousState => !previousState);
205
+ }}
206
  >
207
+ {showCode ? <CodeOffIcon /> : <CodeIcon />}
208
+ </IconButton>
209
+ <IconButton
210
+ edge="end"
211
+ color="inherit"
212
+ aria-label="Clear Prompt"
213
+ onClick={async () => {
214
+ setActiveId("1");
215
+ setRunningId("1");
216
+ setTemplate(prettify(base.default));
217
+ reload();
218
+ }}
219
+ >
220
+ <ClearIcon />
221
+ </IconButton>
222
+ </Toolbar>
223
+ </AppBar>
224
+ {showCode && (
225
+ <Box sx={{ flex: 1 }}>
226
+ <MonacoEditor
227
+ theme={getTheme(mode, systemMode)}
228
+ language="javascript"
229
+ value={template}
230
+ options={{
231
+ fontSize: 14,
232
+ }}
233
+ onChange={async value => {
234
+ console.log(value);
235
+ setTemplate(value ?? "");
236
+ }}
237
+ />
238
+ </Box>
239
+ )}
240
+ <Stack
241
+ sx={{ flex: 1, display: showCode ? "none" : undefined, overflow: "hidden" }}
242
+ >
243
+ <Box
244
+ component="form"
245
+ id="gpt-form"
246
+ onSubmit={async event => {
247
+ event.preventDefault();
248
+ const formData = new FormData(event.target as HTMLFormElement);
249
+ const formObject = Object.fromEntries(formData);
250
+ try {
251
+ setLoading(true);
252
+ const { data } = await axios.post("/api/gpt", formObject);
253
+ const answer = data;
254
+ setAnswers(previousAnswers => [answer, ...previousAnswers]);
255
+ setRunningId(answer.id);
256
+ setActiveId(answer.id);
257
+ setTemplate(prettify(answer.content));
258
+ reload();
259
+ } catch (error) {
260
+ console.error(error);
261
+ } finally {
262
+ setLoading(false);
263
+ }
264
+ }}
265
+ >
266
+ <Paper variant="outlined" sx={{ p: 0 }}>
267
+ <Stack sx={{ p: 2, gap: 2 }}>
268
+ <TextField
269
+ multiline
270
+ fullWidth
271
+ id="prompt"
272
+ name="prompt"
273
+ label="Prompt"
274
+ placeholder="matrix code"
275
+ defaultValue="matrix code"
276
+ maxRows={6}
277
+ InputProps={{
278
+ style: fontMono.style,
279
+ }}
280
+ />
281
+ <TextField
282
+ multiline
283
+ fullWidth
284
+ id="negativePrompt"
285
+ name="negativePrompt"
286
+ label="Negative Prompt"
287
+ placeholder="images, audio files"
288
+ maxRows={6}
289
+ InputProps={{
290
+ style: fontMono.style,
291
+ }}
292
  />
 
293
  </Stack>
294
+ </Paper>
295
+ <Accordion disableGutters square elevation={0}>
296
+ <AccordionSummary
297
+ expandIcon={<ExpandMoreIcon />}
298
+ aria-controls="gtp-options-content"
299
+ id="gtp-options-header"
300
+ sx={{
301
+ bgcolor: "background.paper",
302
+ color: "text.primary",
 
 
 
 
 
303
  }}
304
+ >
305
+ <Typography>Options</Typography>
306
+ </AccordionSummary>
307
+ <AccordionDetails>
308
+ <FormControl fullWidth variant="outlined" sx={{ mb: 3 }}>
309
+ <InputLabel id="gpt-model-select-label">Model</InputLabel>
310
+ <Select
311
+ labelId="gpt-model-select-label"
312
+ id="gpt-model-select"
313
+ name="model"
314
+ defaultValue="gpt-3.5-turbo"
315
+ label="Model"
316
+ >
317
+ <MenuItem value="gpt-3.5-turbo">GPT 3.5 turbo</MenuItem>
318
+ <MenuItem value="gpt-4">GPT 4</MenuItem>
319
+ </Select>
320
+ </FormControl>
321
+ <Stack
322
+ spacing={2}
323
+ direction="row"
324
+ sx={{ mb: 2 }}
325
+ alignItems="center"
326
+ >
327
+ <AcUnitIcon />
328
+ <Slider
329
+ marks
330
+ id="temperature"
331
+ name="temperature"
332
+ min={0}
333
+ max={0.8}
334
+ defaultValue={0.2}
335
+ step={0.1}
336
+ valueLabelDisplay="auto"
337
+ aria-label="Temperature"
338
+ />
339
+ <LocalFireDepartmentIcon />
340
+ </Stack>
341
+ <Stack
342
+ spacing={2}
343
+ direction="row"
344
+ sx={{ mb: 2 }}
345
+ alignItems="center"
346
+ >
347
+ <TollIcon />
348
+ <Slider
349
+ marks
350
+ id="maxTokens"
351
+ name="maxTokens"
352
+ min={1024}
353
+ max={4096}
354
+ defaultValue={2048}
355
+ step={256}
356
+ valueLabelDisplay="auto"
357
+ aria-label="Max Tokens"
358
+ />
359
+ <MoneyIcon />
360
+ </Stack>
361
+ <input
362
+ id="template"
363
+ name="template"
364
+ type="hidden"
365
+ value={template}
366
+ onChange={event => {
367
+ setTemplate(event.target.value);
368
+ }}
369
+ />
370
+ </AccordionDetails>
371
+ </Accordion>
372
+ </Box>
373
 
374
+ <List sx={{ flex: 1, overflow: "auto" }}>
375
+ {answers.map((answer, index) => {
376
+ return (
377
+ <ListItem
378
+ key={answer.id}
379
+ secondaryAction={
380
+ <Stack sx={{ flexDirection: "row", gap: 1 }}>
381
+ {answer.id === "1" ? undefined : (
382
+ <IconButton
383
+ edge="end"
384
+ aria-label="Delete"
385
+ onClick={() => {
386
+ setAnswers(previousAnswers =>
387
+ previousAnswers.filter(
388
+ ({ id }) => id !== answer.id
389
+ )
390
+ );
391
+ if (runningId === answer.id) {
392
+ const previous = answers[index + 1];
393
+ if (previous) {
394
+ setActiveId(previous.id);
395
+ setRunningId(previous.id);
396
+ setTemplate(
397
+ prettify(previous.task)
398
+ );
399
+ reload();
400
+ }
401
+ }
402
+ }}
403
+ >
404
+ <DeleteForeverIcon />
405
+ </IconButton>
406
+ )}
407
+ </Stack>
408
+ }
409
+ disablePadding
410
+ >
411
+ <ListItemButton
412
+ dense
413
+ selected={activeId === answer.id}
414
+ disabled={activeId === answer.id}
415
+ role={undefined}
416
+ onClick={() => {
417
+ setActiveId(answer.id);
418
+ setRunningId(answer.id);
419
+ setTemplate(prettify(answer.content));
420
+ reload();
421
+ }}
422
+ >
423
+ <ListItemIcon>
424
  {runningId === answer.id ? (
425
+ <CheckIcon />
426
  ) : (
427
+ <VisibilityIcon />
428
  )}
429
+ </ListItemIcon>
430
+
431
+ <ListItemText
432
+ primary={answer.task}
433
+ primaryTypographyProps={{
434
+ sx: {
435
+ overflow: "hidden",
436
+ textOverflow: "ellipsis",
437
+ whiteSpace: "nowrap",
438
+ fontSize: 16,
439
+ },
440
+ }}
441
+ />
442
+ </ListItemButton>
443
+ </ListItem>
444
+ );
445
+ })}
446
+ </List>
447
+ </Stack>
 
 
 
 
 
 
 
 
448
  </Stack>
449
  <Stack sx={{ flex: 1, width: "50%", position: "relative" }}>
450
+ <AppBar position="static" elevation={0} color="default">
451
  <Toolbar>
452
  <IconButton
453
  color="inherit"
src/pages/live.tsx CHANGED
@@ -1,4 +1,5 @@
1
  import Script from "next/script";
 
2
  const styles = (
3
  <style>
4
  {`
 
1
  import Script from "next/script";
2
+
3
  const styles = (
4
  <style>
5
  {`
src/services/api/index.ts CHANGED
@@ -1,36 +1,15 @@
1
- import { ChatCompletionRequestMessage, Configuration, OpenAIApi } from "openai";
2
  import { nanoid } from "nanoid";
3
- import process from "node:process";
4
-
5
- const configuration = new Configuration({
6
- apiKey: process.env.OPENAI_API_KEY,
7
- });
8
-
9
- const openai = new OpenAIApi(configuration);
10
-
11
- export function miniPrompt(strings: TemplateStringsArray, ...args: unknown[]) {
12
- return strings
13
- .flatMap((string, index) => [string, args[index] ?? ""])
14
- .join("")
15
- .replace(/^\s+/gm, "")
16
- .replace(/^\n+/g, "\n")
17
- .trim();
18
- }
19
-
20
- function extractCode(string: string) {
21
- const codeBlockPattern = /(`{3,})(\w*)\n([\s\S]*?)\1/g;
22
- const matches = codeBlockPattern.exec(string);
23
- if (matches && matches.length >= 4) {
24
- return matches[3];
25
- }
26
- return string;
27
- }
28
 
29
  export async function toOpenAI({
30
- prompt = "be creative",
31
  negativePrompt = "",
32
  template = "",
 
33
  temperature = "0.2",
 
34
  }) {
35
  const negativePrompt_ = negativePrompt.trim();
36
  const prompt_ = prompt.trim();
@@ -46,39 +25,37 @@ export async function toOpenAI({
46
  \`\`\`
47
  `,
48
  };
49
- console.log("<<< INPUT Message >>>");
50
- console.log(nextMessage.content);
51
 
52
  const task = `${prompt_}${negativePrompt_ ? ` | not(${negativePrompt_})` : ""}`;
53
 
54
  try {
55
  const response = await openai.createChatCompletion({
56
- model: "gpt-3.5-turbo",
57
  temperature: Number.parseFloat(temperature),
58
  messages: [
59
  {
60
  role: "system",
61
  content: miniPrompt`
62
- You are an expert JavaScript developer with a creative mindset and a specialization in Canvas-2d.
63
- You have a keen eye for performance optimization and are highly skilled in creating interactive experiences.
64
- You always adhere to documentation and meticulously extend the "CHANGELOG" and code.
65
- When working on new features, you follow the "ADD" guidelines, and when necessary, remove or exclude elements using "REMOVE".
66
- You also pay close attention to "TEMPLATE" code, extending or fixing it as needed.
67
- Your "OUTPUT FORMAT" must be exclusively valid JavaScript in a markdown code block, which you achieve by using the provided "TEMPLATE".
68
 
69
- And remember, the "ADD", "REMOVE", "TEMPLATE", and "OUTPUT FORMAT" guidelines are crucial to follow for optimal results.
70
- `,
 
 
 
 
 
 
71
  },
72
  nextMessage,
73
  ],
74
- max_tokens: 2048,
75
  });
76
 
77
  const { message } = response.data.choices[0];
78
 
79
  if (message) {
80
- console.log("<<< OUTPUT Message >>>");
81
- console.log(message.content);
82
  return {
83
  ...message,
84
  content: extractCode(message.content).replace(
 
1
+ import { ChatCompletionRequestMessage } from "openai";
2
  import { nanoid } from "nanoid";
3
+ import { openai } from "@/services/api/openai";
4
+ import { extractCode, miniPrompt } from "@/utils";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  export async function toOpenAI({
7
+ prompt = "extend the code",
8
  negativePrompt = "",
9
  template = "",
10
+ model = "gpt-3.5-turbo",
11
  temperature = "0.2",
12
+ maxTokens = "2048",
13
  }) {
14
  const negativePrompt_ = negativePrompt.trim();
15
  const prompt_ = prompt.trim();
 
25
  \`\`\`
26
  `,
27
  };
 
 
28
 
29
  const task = `${prompt_}${negativePrompt_ ? ` | not(${negativePrompt_})` : ""}`;
30
 
31
  try {
32
  const response = await openai.createChatCompletion({
33
+ model,
34
  temperature: Number.parseFloat(temperature),
35
  messages: [
36
  {
37
  role: "system",
38
  content: miniPrompt`
39
+ As a JavaScript expert, you optimize performance and create interactive experiences. You are absurdly creative.
40
+ Follow these guidelines closely for optimal results:
 
 
 
 
41
 
42
+ * Use "ADD" and "REMOVE" guidelines to modify code as needed.
43
+ * Use short comments and follow TODO statements
44
+ * Always output the complete code, including the original "TEMPLATE" minus "REMOVE" plus "ADD".
45
+ * Use valid JavaScript exclusively in a markdown code block using the provided "TEMPLATE".
46
+ * Keep a "CHANGELOG" to document changes made to the code.
47
+ * Always change the code in some way
48
+ * Modify the "TEMPLATE" when adding new code elements for continuous improvement.
49
+ `,
50
  },
51
  nextMessage,
52
  ],
53
+ max_tokens: Number.parseInt(maxTokens, 10),
54
  });
55
 
56
  const { message } = response.data.choices[0];
57
 
58
  if (message) {
 
 
59
  return {
60
  ...message,
61
  content: extractCode(message.content).replace(
src/services/api/openai.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import { Configuration, OpenAIApi } from "openai";
2
+ import process from "node:process";
3
+
4
+ export const configuration = new Configuration({
5
+ apiKey: process.env.OPENAI_API_KEY,
6
+ });
7
+ export const openai = new OpenAIApi(configuration);
src/store/atoms.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { atomWithStorage } from "jotai/utils";
2
+
3
+ import { base } from "@/constants";
4
+
5
+ export const answersAtom = atomWithStorage<
6
+ {
7
+ id: string;
8
+ content: string;
9
+ task: string;
10
+ }[]
11
+ >("fail4", [
12
+ {
13
+ id: "1",
14
+ content: base.default,
15
+ task: "Base Script",
16
+ },
17
+ ]);
18
+ export const showCodeAtom = atomWithStorage("fail4-editor", false);
src/utils/index.ts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import GPT3Tokenizer from "gpt3-tokenizer";
2
+ import prettier from "prettier";
3
+
4
+ export const tokenizer = new GPT3Tokenizer({ type: "gpt3" });
5
+
6
+ export function getTokens(text: string) {
7
+ return tokenizer.encode(text).bpe.length;
8
+ }
9
+
10
+ export function prettify(code: string) {
11
+ try {
12
+ return prettier.format(code, { useTabs: true, semi: true, parser: "babel" });
13
+ } catch {
14
+ return code;
15
+ }
16
+ }
17
+
18
+ export function getTheme(mode: string | undefined, systemMode: string | undefined) {
19
+ if (mode === "system") {
20
+ return `vs-${systemMode}`;
21
+ }
22
+ if (mode) {
23
+ return `vs-${mode}`;
24
+ }
25
+ return undefined;
26
+ }
27
+ export { extractCode } from "@/utils/prompt";
28
+ export { miniPrompt } from "@/utils/prompt";
src/utils/prompt.ts ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export function miniPrompt(strings: TemplateStringsArray, ...args: unknown[]) {
2
+ return strings
3
+ .flatMap((string, index) => [string, args[index] ?? ""])
4
+ .join("")
5
+ .replace(/^\s+/gm, "")
6
+ .replace(/^\n+/g, "\n")
7
+ .trim();
8
+ }
9
+
10
+ export function extractCode(string: string) {
11
+ const codeBlockPattern = /(`{3,})(\w*)\n([\s\S]*?)\1/g;
12
+ const matches = codeBlockPattern.exec(string);
13
+ if (matches && matches.length >= 4) {
14
+ return matches[3];
15
+ }
16
+ return string;
17
+ }