├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── icon.png
├── icon_nomargin.png
├── icon_title.png
├── package-lock.json
├── package.json
├── src
├── app.html
├── lib
│ ├── MenuModals
│ │ ├── CreateBlock.svelte
│ │ ├── ExtensionColors.svelte
│ │ ├── color.js
│ │ ├── createblock.js
│ │ └── extensionColorExample.svg
│ ├── NavigationBar
│ │ ├── Button.svelte
│ │ ├── Divider.svelte
│ │ └── NavigationBar.svelte
│ ├── StyledComponents
│ │ └── ToolboxButton.svelte
│ ├── Toolbox
│ │ └── Toolbox.xml
│ └── index.js
├── resources
│ ├── blocks
│ │ ├── control.js
│ │ ├── core.js
│ │ └── generic.js
│ ├── compiler
│ │ ├── compileVarSection.js
│ │ ├── index.js
│ │ ├── randomNumberGen.js
│ │ └── xmlToCode.js
│ ├── events
│ │ └── index.js
│ ├── fileDialog
│ │ └── index.js
│ ├── javascriptGenerator
│ │ └── index.js
│ ├── preload
│ │ └── index.js
│ └── register
│ │ └── index.js
└── routes
│ └── +page.svelte
├── static
├── favicon.png
├── favicon_any.png
├── favicon_dark.png
└── images
│ ├── blockBuilder
│ ├── block_boolean.svg
│ ├── block_empty.svg
│ ├── block_input.svg
│ ├── block_input_angle.svg
│ ├── block_input_color.svg
│ ├── block_input_matrix.svg
│ ├── block_input_note.svg
│ ├── block_input_number.svg
│ ├── block_input_text.svg
│ ├── block_label.svg
│ ├── block_label_image.svg
│ └── block_label_none.svg
│ ├── close.svg
│ ├── dump.html
│ ├── icon.png
│ ├── theme_switcher.svg
│ └── turn_right.svg
├── svelte.config.js
└── vite.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 | vite.config.js.timestamp-*
10 | vite.config.ts.timestamp-*
11 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 | resolution-mode=highest
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 JeremyGamer13
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # TurboBuilder
4 | Create extensions for TurboWarp using block-based coding.
5 |
6 | ## In development
7 | This project is not finished and is still being worked on. Expect bugs and problems that may prevent the site from working.
8 |
9 | ## Running locally
10 |
11 | 1. Clone the repo
12 | 2. Run `npm i --force` in a terminal inside the folder where the repo is
13 | 3. Run `npm run dev` in a terminal inside the folder where the repo is
14 | 4. Visit http://localhost:5173/
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyGamer13/turbobuilder/231a2d5ed4fa2595f0affb0f2efcff614fb75418/icon.png
--------------------------------------------------------------------------------
/icon_nomargin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyGamer13/turbobuilder/231a2d5ed4fa2595f0affb0f2efcff614fb75418/icon_nomargin.png
--------------------------------------------------------------------------------
/icon_title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyGamer13/turbobuilder/231a2d5ed4fa2595f0affb0f2efcff614fb75418/icon_title.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "turbobuilder",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite dev",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "devDependencies": {
11 | "@sveltejs/adapter-auto": "^2.0.0",
12 | "@sveltejs/kit": "^1.20.4",
13 | "svelte": "^4.0.5",
14 | "vite": "^4.4.2"
15 | },
16 | "type": "module",
17 | "dependencies": {
18 | "@blockly/continuous-toolbox": "^5.0.2",
19 | "@blockly/theme-dark": "^6.0.1",
20 | "@sveltejs/adapter-vercel": "^3.0.2",
21 | "file-saver": "^2.0.5",
22 | "js-beautify": "^1.14.9",
23 | "jszip": "^3.10.1",
24 | "prismjs": "^1.29.0",
25 | "svelte-blockly": "^0.1.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | TurboBuilder - Make extensions with blocks
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
103 | %sveltekit.head%
104 |
105 |
106 |
107 |
TurboBuilder is currently in development. Features may not work correctly here.
108 |
112 |
113 |
114 |
115 |
192 | %sveltekit.body%
193 |
194 |
195 |
--------------------------------------------------------------------------------
/src/lib/MenuModals/CreateBlock.svelte:
--------------------------------------------------------------------------------
1 |
156 |
157 |
158 |
159 |
162 |
163 |
164 |
165 |
166 |
294 |
295 |
301 |
302 |
303 |
304 |
456 |
--------------------------------------------------------------------------------
/src/lib/MenuModals/ExtensionColors.svelte:
--------------------------------------------------------------------------------
1 |
55 |
56 |
57 |
58 |
61 |
62 |
65 |
Choose the colors used in your extension's blocks & menu.
66 |
84 |
87 |
88 | Include Teritary Color
89 |
90 |
91 |
92 |
95 |
104 |
105 |
114 |
115 |
116 |
117 |
123 |
124 |
125 |
126 |
281 |
--------------------------------------------------------------------------------
/src/lib/MenuModals/color.js:
--------------------------------------------------------------------------------
1 | class Color {
2 | static hexToRGB(h) {
3 | let r = 0, g = 0, b = 0;
4 |
5 | // 3 digits
6 | if (h.length == 4) {
7 | r = "0x" + h[1] + h[1];
8 | g = "0x" + h[2] + h[2];
9 | b = "0x" + h[3] + h[3];
10 |
11 | // 6 digits
12 | } else if (h.length == 7) {
13 | r = "0x" + h[1] + h[2];
14 | g = "0x" + h[3] + h[4];
15 | b = "0x" + h[5] + h[6];
16 | }
17 |
18 | return { r: +r, g: +g, b: +b };
19 | }
20 | static rgbToHex(r, g, b) {
21 | r = r.toString(16);
22 | g = g.toString(16);
23 | b = b.toString(16);
24 |
25 | if (r.length == 1)
26 | r = "0" + r;
27 | if (g.length == 1)
28 | g = "0" + g;
29 | if (b.length == 1)
30 | b = "0" + b;
31 |
32 | return "#" + r + g + b;
33 | }
34 |
35 | static hexToHSL(H) {
36 | // Convert hex to RGB first
37 | let r = 0, g = 0, b = 0;
38 | if (H.length == 4) {
39 | r = "0x" + H[1] + H[1];
40 | g = "0x" + H[2] + H[2];
41 | b = "0x" + H[3] + H[3];
42 | } else if (H.length == 7) {
43 | r = "0x" + H[1] + H[2];
44 | g = "0x" + H[3] + H[4];
45 | b = "0x" + H[5] + H[6];
46 | }
47 | // Then to HSL
48 | r /= 255;
49 | g /= 255;
50 | b /= 255;
51 | let cmin = Math.min(r, g, b),
52 | cmax = Math.max(r, g, b),
53 | delta = cmax - cmin,
54 | h = 0,
55 | s = 0,
56 | l = 0;
57 |
58 | if (delta == 0)
59 | h = 0;
60 | else if (cmax == r)
61 | h = ((g - b) / delta) % 6;
62 | else if (cmax == g)
63 | h = (b - r) / delta + 2;
64 | else
65 | h = (r - g) / delta + 4;
66 |
67 | h = Math.round(h * 60);
68 |
69 | if (h < 0)
70 | h += 360;
71 |
72 | l = (cmax + cmin) / 2;
73 | s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
74 | s = +(s * 100).toFixed(1);
75 | l = +(l * 100).toFixed(1);
76 |
77 | return { h, s, l };
78 | }
79 | static hslToHex(h, s, l) {
80 | s /= 100;
81 | l /= 100;
82 |
83 | let c = (1 - Math.abs(2 * l - 1)) * s,
84 | x = c * (1 - Math.abs((h / 60) % 2 - 1)),
85 | m = l - c / 2,
86 | r = 0,
87 | g = 0,
88 | b = 0;
89 |
90 | if (0 <= h && h < 60) {
91 | r = c; g = x; b = 0;
92 | } else if (60 <= h && h < 120) {
93 | r = x; g = c; b = 0;
94 | } else if (120 <= h && h < 180) {
95 | r = 0; g = c; b = x;
96 | } else if (180 <= h && h < 240) {
97 | r = 0; g = x; b = c;
98 | } else if (240 <= h && h < 300) {
99 | r = x; g = 0; b = c;
100 | } else if (300 <= h && h < 360) {
101 | r = c; g = 0; b = x;
102 | }
103 | // Having obtained RGB, convert channels to hex
104 | r = Math.round((r + m) * 255).toString(16);
105 | g = Math.round((g + m) * 255).toString(16);
106 | b = Math.round((b + m) * 255).toString(16);
107 |
108 | // Prepend 0s, if necessary
109 | if (r.length == 1)
110 | r = "0" + r;
111 | if (g.length == 1)
112 | g = "0" + g;
113 | if (b.length == 1)
114 | b = "0" + b;
115 |
116 | return "#" + r + g + b;
117 | }
118 | }
119 |
120 | export default Color;
--------------------------------------------------------------------------------
/src/lib/MenuModals/createblock.js:
--------------------------------------------------------------------------------
1 | class ModalScript {
2 | static onopened = () => { };
3 | static open = () => {
4 | ModalScript.onopened();
5 | };
6 | }
7 |
8 | export default ModalScript;
--------------------------------------------------------------------------------
/src/lib/MenuModals/extensionColorExample.svg:
--------------------------------------------------------------------------------
1 |
4 |
24 |
25 |
27 |
29 |
31 |
33 | abc
36 |
37 |
39 |
41 | item 1
44 |
47 |
48 | text
50 | boolean
53 | open menu
58 | square menu
61 |
63 | item 1
66 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/lib/NavigationBar/Button.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
42 |
--------------------------------------------------------------------------------
/src/lib/NavigationBar/Divider.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/lib/NavigationBar/NavigationBar.svelte:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
80 |
--------------------------------------------------------------------------------
/src/lib/StyledComponents/ToolboxButton.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
42 |
--------------------------------------------------------------------------------
/src/lib/Toolbox/Toolbox.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | // place files you want to import through the `$lib` alias in this folder.
2 |
--------------------------------------------------------------------------------
/src/resources/blocks/control.js:
--------------------------------------------------------------------------------
1 | import javascriptGenerator from '../javascriptGenerator';
2 | import registerBlock from '../register';
3 |
4 | const categoryPrefix = 'control_';
5 | const categoryColor = '#FFAB19';
6 |
7 | function register() {
8 | // if <> then {}
9 | registerBlock(`${categoryPrefix}ifthen`, {
10 | message0: 'if %1 then %2 %3',
11 | args0: [
12 | {
13 | "type": "input_value",
14 | "name": "CONDITION",
15 | "check": "Boolean"
16 | },
17 | {
18 | "type": "input_dummy"
19 | },
20 | {
21 | "type": "input_statement",
22 | "name": "BLOCKS"
23 | }
24 | ],
25 | previousStatement: null,
26 | nextStatement: null,
27 | inputsInline: true,
28 | colour: categoryColor
29 | }, (block) => {
30 | const CONDITION = javascriptGenerator.valueToCode(block, 'CONDITION', javascriptGenerator.ORDER_ATOMIC);
31 | const BLOCKS = javascriptGenerator.statementToCode(block, 'BLOCKS');
32 | const code = `if (${CONDITION ? `Boolean(${CONDITION})` : 'false'}) { ${BLOCKS} };`;
33 | return `${code}\n`;
34 | })
35 | // if <> then {} else {}
36 | registerBlock(`${categoryPrefix}ifthenelse`, {
37 | message0: 'if %1 then %2 %3 else %4 %5',
38 | args0: [
39 | {
40 | "type": "input_value",
41 | "name": "CONDITION",
42 | "check": "Boolean"
43 | },
44 | {
45 | "type": "input_dummy"
46 | },
47 | {
48 | "type": "input_statement",
49 | "name": "BLOCKS"
50 | },
51 | {
52 | "type": "input_dummy"
53 | },
54 | {
55 | "type": "input_statement",
56 | "name": "BLOCKS2"
57 | }
58 | ],
59 | previousStatement: null,
60 | nextStatement: null,
61 | inputsInline: true,
62 | colour: categoryColor
63 | }, (block) => {
64 | const CONDITION = javascriptGenerator.valueToCode(block, 'CONDITION', javascriptGenerator.ORDER_ATOMIC);
65 | const BLOCKS = javascriptGenerator.statementToCode(block, 'BLOCKS');
66 | const BLOCKS2 = javascriptGenerator.statementToCode(block, 'BLOCKS2');
67 | const code = `if (${CONDITION ? `Boolean(${CONDITION})` : 'false'}) { ${BLOCKS} } else { ${BLOCKS2} };`;
68 | return `${code}\n`;
69 | })
70 | }
71 |
72 | export default register;
73 |
--------------------------------------------------------------------------------
/src/resources/blocks/core.js:
--------------------------------------------------------------------------------
1 | import javascriptGenerator from '../javascriptGenerator';
2 | import registerBlock from '../register';
3 |
4 | const categoryPrefix = 'core_';
5 | const categoryColor = '#ff4b4b';
6 |
7 | function register() {
8 | // used in block creation menu
9 | registerBlock(`${categoryPrefix}builderblock`, {
10 | message0: '⠀', // empty character breaks block shape
11 | args0: [],
12 | previousStatement: null,
13 | nextStatement: null,
14 | inputsInline: true,
15 | colour: categoryColor
16 | }, () => {
17 | return `void;`;
18 | });
19 | }
20 |
21 | export default register;
22 |
--------------------------------------------------------------------------------
/src/resources/blocks/generic.js:
--------------------------------------------------------------------------------
1 | import javascriptGenerator from '../javascriptGenerator';
2 | import registerBlock from '../register';
3 |
4 | const categoryPrefix = 'generic_';
5 | const categoryColor = '#fff';
6 |
7 | function register() {
8 | // number
9 | registerBlock(`${categoryPrefix}number`, {
10 | message0: '%1',
11 | args0: [
12 | {
13 | "type": "field_number",
14 | "name": "NUMBER",
15 | "value": 0
16 | }
17 | ],
18 | output: "Number",
19 | inputsInline: true,
20 | colour: categoryColor
21 | }, (block) => {
22 | const NUMBER = block.getFieldValue('NUMBER');
23 | const code = `Number(${NUMBER})`;
24 | return [code, javascriptGenerator.ORDER_NONE];
25 | })
26 | // text
27 | registerBlock(`${categoryPrefix}text`, {
28 | message0: '%1',
29 | args0: [
30 | {
31 | "type": "field_input",
32 | "name": "TEXT",
33 | "text": ""
34 | }
35 | ],
36 | output: "String",
37 | inputsInline: true,
38 | colour: categoryColor
39 | }, (block) => {
40 | const TEXT = block.getFieldValue('TEXT');
41 | const code = `String(${JSON.stringify(TEXT)})`;
42 | return [code, javascriptGenerator.ORDER_NONE];
43 | })
44 | // boolean
45 | registerBlock(`${categoryPrefix}boolean`, {
46 | message0: '%1',
47 | args0: [
48 | {
49 | "type": "field_dropdown",
50 | "name": "STATE",
51 | "options": [
52 | ["True", "true"],
53 | ["False", "false"],
54 | ["Random", "Boolean(Math.round(Math.random()))"]
55 | ]
56 | }
57 | ],
58 | output: "Boolean",
59 | inputsInline: true,
60 | colour: categoryColor
61 | }, (block) => {
62 | const code = block.getFieldValue('STATE');
63 | return [code, javascriptGenerator.ORDER_NONE];
64 | })
65 | }
66 |
67 | export default register;
68 |
--------------------------------------------------------------------------------
/src/resources/compiler/compileVarSection.js:
--------------------------------------------------------------------------------
1 | const throwAwayVars = {}; // used for repeat loops
2 | const compileVars = {};
3 | compileVars._idx = 0;
4 | compileVars.new = () => {
5 | const _listLow = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
6 | const _listHigh = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
7 | const _listSym = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '@', '#', '$', '%', '&', '(', ')', '_', '-', '+', '=', '[', ']', '|'];
8 | const list = [].concat(_listLow, _listHigh, _listSym);
9 | let str = '';
10 | for (let i = 0; i < 100; i++) {
11 | str += list[Math.round(Math.random() * (list.length - 1))];
12 | };
13 | return str;
14 | };
15 | compileVars.next = () => {
16 | compileVars._idx++;
17 | return `v${compileVars._idx}`;
18 | };
--------------------------------------------------------------------------------
/src/resources/compiler/index.js:
--------------------------------------------------------------------------------
1 | // compile functions
2 | import raw_randomNumberGen from './randomNumberGen.js?raw';
3 | import raw_compileVarSection from './compileVarSection.js?raw';
4 |
5 | import javascriptGenerator from '../javascriptGenerator';
6 |
7 | class Compiler {
8 | /**
9 | * Generates JavaScript code from the provided workspace & info.
10 | * @param {Blockly.Workspace} workspace
11 | * @param {object} extensionMetadata
12 | * @param {object} imageStates
13 | * @returns {string} Generated code.
14 | */
15 | compile(workspace, extensionMetadata, imageStates) {
16 | const code = javascriptGenerator.workspaceToCode(workspace);
17 |
18 | const headerCode = [
19 | `/*`,
20 | ` This extension was made with TurboBuilder!`,
21 | ` https://turbobuilder.vercel.app/`,
22 | `*/`,
23 | `(function (Scratch) {`,
24 | `const variables = {};`
25 | ];
26 | const classRegistry = {
27 | top: [
28 | `class Extension {`
29 | ],
30 | extensionInfo: {},
31 | bottom: [
32 | `}`
33 | ]
34 | }
35 | const footerCode = [
36 | `Scratch.extensions.register(new Extension());`,
37 | `})(Scratch);`
38 | ];
39 |
40 | if (imageStates) {
41 | if (imageStates.icon.image) {
42 | // add icon uri
43 | const url = imageStates.icon.image;
44 | classRegistry.extensionInfo.blockIconURI = url;
45 | }
46 | if (imageStates.menuicon.image) {
47 | // add icon uri
48 | const url = imageStates.menuicon.image;
49 | classRegistry.extensionInfo.menuIconURI = url;
50 | }
51 | }
52 | if (extensionMetadata) {
53 | classRegistry.extensionInfo.id = extensionMetadata.id;
54 | classRegistry.extensionInfo.name = extensionMetadata.name;
55 | if (extensionMetadata.docsURL) {
56 | classRegistry.extensionInfo.docsURI = extensionMetadata.docsURL;
57 | }
58 | if (extensionMetadata.color1) {
59 | classRegistry.extensionInfo.color1 = extensionMetadata.color1;
60 | }
61 | if (extensionMetadata.color2) {
62 | classRegistry.extensionInfo.color2 = extensionMetadata.color2;
63 | }
64 | if (extensionMetadata.color3) {
65 | classRegistry.extensionInfo.color3 = extensionMetadata.color3;
66 | }
67 | }
68 |
69 | return [].concat(headerCode, classRegistry.top, [
70 | `getInfo() {`,
71 | `return ${JSON.stringify(classRegistry.extensionInfo)}`,
72 | `}`,
73 | ], classRegistry.bottom, code, footerCode).join('\n');
74 | }
75 | }
76 |
77 | export default Compiler;
--------------------------------------------------------------------------------
/src/resources/compiler/randomNumberGen.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Chooses a random number between the min and max.
3 | * @param {number} min
4 | * @param {number} max
5 | * @returns {number}
6 | */
7 | function randomNumberGen(min, max) {
8 | // swap if min is larger
9 | if (min > max) {
10 | let _v = max;
11 | max = min;
12 | min = _v;
13 | }
14 | // math
15 | const difference = max - min;
16 | const random = Math.random() * difference;
17 | return min + random;
18 | };
--------------------------------------------------------------------------------
/src/resources/compiler/xmlToCode.js:
--------------------------------------------------------------------------------
1 | import Blockly from "blockly/core";
2 | import javascriptGenerator from '../javascriptGenerator';
3 |
4 | function xmlToCode(xml) {
5 | // this sucks but i dont know any other method
6 | // make div
7 | const tempDiv = document.createElement("div");
8 | tempDiv.style = "display:none";
9 | document.body.append(tempDiv);
10 | // inject workpace
11 | const workspace = Blockly.inject(tempDiv, {
12 | collapse: true,
13 | comments: true,
14 | scrollbars: true,
15 | disable: false
16 | });
17 |
18 | let code = "";
19 | try {
20 | const dom = Blockly.utils.xml.textToDom(xml);
21 | Blockly.Xml.domToWorkspace(dom, workspace);
22 | // yay we get to compile now
23 | code = javascriptGenerator.workspaceToCode(workspace);
24 | } catch (err) {
25 | // we do try catch so if we fail to parse
26 | // we dont leave behind an entire workspace & div in the document
27 | console.warn("could not compile xml;", err);
28 | }
29 |
30 | // gtfo
31 | workspace.dispose();
32 | tempDiv.remove();
33 |
34 | return code;
35 | }
36 |
37 | export default xmlToCode;
--------------------------------------------------------------------------------
/src/resources/events/index.js:
--------------------------------------------------------------------------------
1 | let canAccessWindow = false;
2 | class EventManager {
3 | static allowAttachment () {
4 | canAccessWindow = true;
5 | }
6 |
7 | static on (type, callback) {
8 | if (!canAccessWindow) {
9 | throw new Error('EventManager must be used after onMount allows attachment.');
10 | }
11 |
12 | window.tbevents_.push({ type, callback });
13 | }
14 |
15 | // enums
16 | static get EVENT_THEME_CHANGED () {
17 | return 'THEME';
18 | }
19 | }
20 |
21 | export default EventManager;
--------------------------------------------------------------------------------
/src/resources/fileDialog/index.js:
--------------------------------------------------------------------------------
1 | // file-dialog exists on NPM and thats what this file is
2 | // however it uses module.exports and exports a function
3 | // which vite absolutely HATES and REFUSES to build no matter what
4 | // so its reimplemented here with a few changes to work
5 |
6 | function fileDialog(...args) {
7 | const input = document.createElement('input');
8 |
9 | // Set config
10 | if (typeof args[0] === 'object') {
11 | if (args[0].multiple === true) input.setAttribute('multiple', '');
12 | if (args[0].accept !== undefined) input.setAttribute('accept', args[0].accept);
13 | }
14 | input.setAttribute('type', 'file');
15 |
16 | // IE10/11 Addition
17 | input.style.display = 'none';
18 | input.setAttribute('id', 'hidden-file');
19 | document.body.appendChild(input);
20 |
21 | // Return promise/callvack
22 | return new Promise(resolve => {
23 | input.addEventListener('change', () => {
24 | resolve(input.files);
25 | const lastArg = args[args.length - 1];
26 | if (typeof lastArg === "function") lastArg(input.files);
27 |
28 | // IE10/11 Addition
29 | document.body.removeChild(input);
30 | })
31 |
32 | // Simluate click event
33 | input.click();
34 | })
35 | }
36 |
37 | export default fileDialog;
--------------------------------------------------------------------------------
/src/resources/javascriptGenerator/index.js:
--------------------------------------------------------------------------------
1 | // vercel's build doesnt work for some reason
2 | // its related to js generator and how its imported
3 | // so lets just import it the way that it wants
4 |
5 | // we COULD modify the javascript generator here
6 | // but its much cleaner to leave this alone
7 | import pkg from 'blockly/javascript.js';
8 | const { javascriptGenerator } = pkg;
9 |
10 | export default javascriptGenerator;
--------------------------------------------------------------------------------
/src/resources/preload/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Preloads all audio files specified.
3 | * This is because the hosted version of TurboBuilder will cause a bit of a delay before playing audio
4 | * due to the host having to provide the file, not the local machine.
5 | * @param {Array} files An array full of file paths to audio files.
6 | */
7 | function preload(files) {
8 | for (const path of files) {
9 | new Audio(path);
10 | }
11 | }
12 |
13 | export default preload;
--------------------------------------------------------------------------------
/src/resources/register/index.js:
--------------------------------------------------------------------------------
1 | import Blockly from 'blockly/core';
2 | import javascriptGenerator from '../javascriptGenerator';
3 |
4 | export default (blockName, jsonData, compileFunction) => {
5 | const blockObject = {
6 | init: function () {
7 | this.jsonInit(jsonData);
8 | }
9 | };
10 |
11 | // register visual block
12 | Blockly.Blocks[blockName] = blockObject
13 |
14 | // register block compile function
15 | javascriptGenerator[blockName] = compileFunction;
16 | }
--------------------------------------------------------------------------------
/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
316 |
317 |
322 | {#if ModalState.extensionColors}
323 | {
328 | ModalState.extensionColors = false;
329 | extensionMetadata.color1 = colors.detail.color1;
330 | extensionMetadata.color2 = colors.detail.color2;
331 | extensionMetadata.color3 = colors.detail.color3;
332 | updateGeneratedCode();
333 | }}
334 | on:cancel={() => {
335 | ModalState.extensionColors = false;
336 | updateGeneratedCode();
337 | }}
338 | />
339 | {/if}
340 |
341 | File
342 | Edit
343 |
344 |
353 | {#if isExtensionIDInvalid(projectID)}
354 |
355 | Extension ID must be only letters and numbers.
356 |
357 | {/if}
358 |
359 |
367 |
368 |
369 |
514 |
515 |
516 |
748 |
--------------------------------------------------------------------------------
/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyGamer13/turbobuilder/231a2d5ed4fa2595f0affb0f2efcff614fb75418/static/favicon.png
--------------------------------------------------------------------------------
/static/favicon_any.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyGamer13/turbobuilder/231a2d5ed4fa2595f0affb0f2efcff614fb75418/static/favicon_any.png
--------------------------------------------------------------------------------
/static/favicon_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyGamer13/turbobuilder/231a2d5ed4fa2595f0affb0f2efcff614fb75418/static/favicon_dark.png
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_boolean.svg:
--------------------------------------------------------------------------------
1 | R1_ C.Procedure Editble Inputs
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_empty.svg:
--------------------------------------------------------------------------------
1 | R1_ C.Procedure Editble Inputs
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_input.svg:
--------------------------------------------------------------------------------
1 | R1_ C.Procedure Editble Inputs
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_input_angle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 | R1_ C.Procedure Editble Inputs
38 |
39 |
40 |
41 | 90
42 |
43 |
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_input_color.svg:
--------------------------------------------------------------------------------
1 | R1_ C.Procedure Editble Inputs
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_input_matrix.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 | R1_ C.Procedure Editble Inputs
38 |
39 |
40 |
41 |
42 |
44 |
45 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_input_note.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 | R1_ C.Procedure Editble Inputs
38 |
39 |
40 |
41 | 60
42 |
43 |
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_input_number.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 | R1_ C.Procedure Editble Inputs
38 |
39 |
40 |
41 | 123
42 |
43 |
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_input_text.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 | R1_ C.Procedure Editble Inputs
38 |
39 |
40 |
41 | abc
42 |
43 |
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_label.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
27 | R1_ C.Procedure Editble Inputs
28 |
29 |
30 | text
31 |
32 |
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_label_image.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
27 | R1_ C.Procedure Editble Inputs
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/static/images/blockBuilder/block_label_none.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
27 | R1_ C.Procedure Editble Inputs
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/static/images/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/static/images/dump.html:
--------------------------------------------------------------------------------
1 |
3 |
23 |
24 |
26 |
28 |
30 |
32 | 15
35 | turn
37 |
38 |
40 |
41 | degrees
44 |
45 |
46 |
--------------------------------------------------------------------------------
/static/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyGamer13/turbobuilder/231a2d5ed4fa2595f0affb0f2efcff614fb75418/static/images/icon.png
--------------------------------------------------------------------------------
/static/images/theme_switcher.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/images/turn_right.svg:
--------------------------------------------------------------------------------
1 | rotate-counter-clockwise
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-auto';
2 |
3 | /** @type {import('@sveltejs/kit').Config} */
4 | const config = {
5 | kit: {
6 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
7 | // If your environment is not supported or you settled on a specific environment, switch out the adapter.
8 | // See https://kit.svelte.dev/docs/adapters for more information about adapters.
9 | adapter: adapter()
10 | }
11 | };
12 |
13 | export default config;
14 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [sveltekit()],
6 | optimizeDeps: {
7 | include: [
8 | '@blockly/continuous-toolbox',
9 | 'file-saver',
10 | ]
11 | }
12 | });
13 |
--------------------------------------------------------------------------------