owenkaplinsky commited on
Commit
df3419a
·
0 Parent(s):

Initial commit

Browse files
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .env
2
+ node_modules
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 owenkaplinsky
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.
README.md ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # agent-blockly
2
+
hello-world/.npmignore ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+
8
+ # Dependency directories
9
+ node_modules/
10
+
11
+ # Generated files
12
+ dist/
13
+ build/
14
+ .DS_Store
15
+
16
+ # Optional npm cache directory
17
+ .npm
18
+
19
+ # IDEs and editors
20
+ .idea
21
+ *.sublime-workspace
22
+ .vscode/*
hello-world/app.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ import gradio as gr
4
+ import uvicorn
5
+ import asyncio
6
+
7
+ app = FastAPI()
8
+
9
+ # Allow Blockly (running on localhost:8080) to connect
10
+ app.add_middleware(
11
+ CORSMiddleware,
12
+ allow_origins=["http://localhost:8080", "http://127.0.0.1:8080"],
13
+ allow_credentials=True,
14
+ allow_methods=["*"],
15
+ allow_headers=["*"],
16
+ )
17
+
18
+ # Authoritative chat history (the one true state)
19
+ history = []
20
+
21
+ # Live Python code string generated from Blockly
22
+ latest_blockly_code = ""
23
+
24
+ # A queue for assistant replies (so Gradio waits until Blockly has responded)
25
+ assistant_queue = asyncio.Queue()
26
+
27
+
28
+ @app.post("/update_code")
29
+ async def update_code(request: Request):
30
+ """Receives live Python code generated by Blockly."""
31
+ global latest_blockly_code
32
+ data = await request.json()
33
+ latest_blockly_code = data.get("code", "")
34
+ print("\n[FASTAPI] Updated Blockly code:\n", latest_blockly_code)
35
+ return {"ok": True}
36
+
37
+
38
+ def execute_blockly_logic(user_message: str) -> str:
39
+ """
40
+ Executes the latest Blockly-generated Python code in a controlled environment.
41
+ Expects the Blockly code to define a function called on_user_send(user_message)
42
+ that returns or prints the assistant reply.
43
+ """
44
+ global latest_blockly_code
45
+
46
+ if not latest_blockly_code.strip():
47
+ print("[EXECUTION] No Blockly code yet.")
48
+ return "(no logic yet)"
49
+
50
+ try:
51
+ local_vars = {}
52
+ # Execute the code safely (creates function definitions, etc.)
53
+ exec(latest_blockly_code, {}, local_vars)
54
+ # Call the defined handler function, if it exists
55
+ if "on_user_send" in local_vars:
56
+ result = local_vars["on_user_send"](user_message)
57
+ if result is None:
58
+ result = "(no reply)"
59
+ return str(result)
60
+ else:
61
+ print("[EXECUTION] No on_user_send() found in Blockly code.")
62
+ return "(missing on_user_send)"
63
+ except Exception as e:
64
+ print("[EXECUTION ERROR]", e)
65
+ return f"(error: {e})"
66
+
67
+
68
+ def build_interface():
69
+ with gr.Blocks() as demo:
70
+ chatbot = gr.Chatbot(type="messages", label="Assistant")
71
+ msg = gr.Textbox(placeholder="Type a message and press Enter")
72
+
73
+ async def process_message(message):
74
+ global history
75
+
76
+ # 1. Add user message
77
+ history.append({"role": "user", "content": message})
78
+ print(f"[USER] {message!r}")
79
+ yield "", history
80
+
81
+ # 2. Compute assistant response using current Blockly logic
82
+ assistant_text = execute_blockly_logic(message)
83
+ print(f"[ASSISTANT] {assistant_text!r}")
84
+
85
+ # 3. Add assistant message
86
+ history.append({"role": "assistant", "content": assistant_text})
87
+ yield "", history
88
+
89
+ msg.submit(process_message, [msg], [msg, chatbot], queue=True)
90
+
91
+ clear_btn = gr.Button("Reset chat")
92
+ clear_btn.click(lambda: ([], ""), None, [chatbot, msg], queue=False)
93
+
94
+ return demo
95
+
96
+
97
+ demo = build_interface()
98
+ app = gr.mount_gradio_app(app, demo, path="/")
99
+
100
+ if __name__ == "__main__":
101
+ print("[BOOT] Running Gradio+FastAPI combo on http://127.0.0.1:7860")
102
+ uvicorn.run(app, host="0.0.0.0", port=7860)
hello-world/package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
hello-world/package.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "hello-world",
3
+ "version": "1.0.0",
4
+ "description": "A sample app using Blockly",
5
+ "main": "index.js",
6
+ "private": true,
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "build": "webpack --mode production",
10
+ "start": "concurrently \"python app.py\" \"webpack serve --open --mode development\""
11
+ },
12
+ "keywords": [
13
+ "blockly"
14
+ ],
15
+ "author": "",
16
+ "license": "Apache-2.0",
17
+ "devDependencies": {
18
+ "css-loader": "^6.7.1",
19
+ "html-webpack-plugin": "^5.5.0",
20
+ "source-map-loader": "^4.0.1",
21
+ "style-loader": "^3.3.1",
22
+ "webpack": "^5.76.0",
23
+ "webpack-cli": "^4.10.0",
24
+ "webpack-dev-server": "^4.11.1"
25
+ },
26
+ "dependencies": {
27
+ "blockly": "^11.0.0"
28
+ }
29
+ }
hello-world/src/blocks/text.js ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as Blockly from 'blockly/core';
8
+
9
+ const whenUserSends = {
10
+ type: 'when_user_sends',
11
+ message0: 'When user sends msg do %1 %2',
12
+ args0: [
13
+ {
14
+ "type": "input_dummy"
15
+ },
16
+ {
17
+ "type": "input_statement",
18
+ "name": "code"
19
+ }
20
+ ],
21
+ inputsInline: true,
22
+ colour: 230,
23
+ tooltip: 'Triggered when the user sends a chat message.',
24
+ helpUrl: '',
25
+ };
26
+
27
+ const assistantReply = {
28
+ type: 'assistant_reply',
29
+ message0: 'Reply with %1',
30
+ args0: [
31
+ {
32
+ type: 'input_value',
33
+ name: 'INPUT',
34
+ check: 'String',
35
+ },
36
+ ],
37
+ previousStatement: null,
38
+ nextStatement: null,
39
+ colour: 65,
40
+ tooltip: 'Send a message as the assistant.',
41
+ helpUrl: '',
42
+ };
43
+
44
+ export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([
45
+ whenUserSends,
46
+ assistantReply,
47
+ ]);
hello-world/src/generators/python.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Order } from 'blockly/python';
2
+
3
+ export const forBlock = Object.create(null);
4
+
5
+ // Generates a Python function that runs when the user sends a message
6
+ forBlock['when_user_sends'] = function (block, generator) {
7
+ const body = generator.statementToCode(block, 'code') || "";
8
+ const code = `def on_user_send(user_message):\n${body}\n`;
9
+ return code;
10
+ };
11
+
12
+ // Generates a Python 'return' statement for the assistant's reply
13
+ forBlock['assistant_reply'] = function (block, generator) {
14
+ const reply = generator.valueToCode(block, 'INPUT', Order.NONE) || "''";
15
+ const code = `return ${reply}\n`;
16
+ return code;
17
+ };
hello-world/src/index.css ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html,
2
+ body {
3
+ height: 100%;
4
+ margin: 0;
5
+ padding: 0;
6
+ max-width: 100vw;
7
+ }
8
+
9
+ #pageContainer {
10
+ display: flex;
11
+ width: 100%;
12
+ height: 100%;
13
+ overflow: hidden;
14
+ }
15
+
16
+ #blocklyDiv {
17
+ flex: 1;
18
+ min-width: 600px;
19
+ height: 100%;
20
+ }
21
+
22
+ #outputPane {
23
+ display: flex;
24
+ flex-direction: column;
25
+ width: 400px;
26
+ flex-shrink: 0;
27
+ height: 100%;
28
+ overflow: hidden;
29
+ padding: 1rem;
30
+ box-sizing: border-box;
31
+ }
32
+
33
+ #chatContainer {
34
+ flex: 0 0 400px;
35
+ /* fixed height for iframe area */
36
+ width: 100%;
37
+ }
38
+
39
+ #chatContainer iframe {
40
+ width: 100%;
41
+ height: 100%;
42
+ border: none;
43
+ display: block;
44
+ }
45
+
46
+ #generatedCode {
47
+ flex: 1;
48
+ background: #111;
49
+ color: #0f0;
50
+ padding: 10px;
51
+ font-family: monospace;
52
+ white-space: pre-wrap;
53
+ overflow-y: auto;
54
+ border: 1px solid #333;
55
+ margin-top: 10px;
56
+ }
hello-world/src/index.html ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <title>Blockly Sample App</title>
7
+ </head>
8
+
9
+ <body>
10
+ <div id="pageContainer">
11
+ <div id="outputPane">
12
+ <div id="chatContainer">
13
+ <iframe src="http://127.0.0.1:7860" style="width: 100%; height: 600px; border: none;"></iframe>
14
+ </div>
15
+
16
+ <pre id="generatedCode"><code></code></pre>
17
+ </div>
18
+ <div id="blocklyDiv"></div>
19
+ </div>
20
+ </body>
21
+
22
+ </html>
hello-world/src/index.js ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as Blockly from 'blockly';
8
+ import { blocks } from './blocks/text';
9
+ import { forBlock } from './generators/python';
10
+ import { pythonGenerator } from 'blockly/python';
11
+ import { save, load } from './serialization';
12
+ import { toolbox } from './toolbox';
13
+ import './index.css';
14
+
15
+ // Register the blocks and generator with Blockly
16
+ Blockly.common.defineBlocks(blocks);
17
+ Object.assign(pythonGenerator.forBlock, forBlock);
18
+
19
+ // Set up UI elements and inject Blockly
20
+ const blocklyDiv = document.getElementById('blocklyDiv');
21
+
22
+ // Create a custom theme (Scratch-like colors and hats)
23
+ const myTheme = Blockly.Theme.defineTheme('myScratchTheme', {
24
+ base: Blockly.Themes.Classic,
25
+ startHats: true,
26
+ blockStyles: {
27
+ logic_blocks: {
28
+ colourPrimary: '#5C81A6',
29
+ colourSecondary: '#4A6D8B',
30
+ colourTertiary: '#3B5572',
31
+ },
32
+ loop_blocks: {
33
+ colourPrimary: '#5CA65C',
34
+ colourSecondary: '#498949',
35
+ colourTertiary: '#3B723B',
36
+ },
37
+ },
38
+ categoryStyles: {
39
+ logic_category: { colour: '#5C81A6' },
40
+ loop_category: { colour: '#5CA65C' },
41
+ },
42
+ componentStyles: {
43
+ workspaceBackgroundColour: '#f0f0f0',
44
+ toolboxBackgroundColour: '#ffffff',
45
+ },
46
+ });
47
+
48
+ // Inject Blockly with theme + renderer
49
+ const ws = Blockly.inject(blocklyDiv, {
50
+ toolbox,
51
+ renderer: 'zelos',
52
+ theme: myTheme,
53
+ });
54
+
55
+ ws.updateUserMessage = (message) => {
56
+ let variable = ws.getVariable('user_message');
57
+ if (!variable) ws.createVariable('user_message');
58
+ ws.variableValues = ws.variableValues || {};
59
+ ws.variableValues['user_message'] = message;
60
+ };
61
+
62
+ const runCode = () => {
63
+ const code = pythonGenerator.workspaceToCode(ws);
64
+ const codeEl = document.querySelector('#generatedCode code');
65
+
66
+ if (codeEl) {
67
+ codeEl.textContent = code;
68
+ }
69
+
70
+ // Send generated Python code to backend
71
+ fetch("http://127.0.0.1:7860/update_code", {
72
+ method: "POST",
73
+ headers: { "Content-Type": "application/json" },
74
+ body: JSON.stringify({ code }),
75
+ })
76
+ .then(() => {
77
+ console.log("[Blockly] Sent updated Python code to backend");
78
+ })
79
+ .catch((err) => {
80
+ console.error("[Blockly] Error sending Python code:", err);
81
+ });
82
+ };
83
+
84
+ try {
85
+ load(ws);
86
+ } catch (e) {
87
+ console.warn('Workspace load failed, clearing storage:', e);
88
+ localStorage.clear();
89
+ }
90
+ runCode();
91
+
92
+ ws.addChangeListener((e) => {
93
+ if (e.isUiEvent) return;
94
+ save(ws);
95
+ });
96
+
97
+ ws.addChangeListener((e) => {
98
+ if (e.isUiEvent || e.type == Blockly.Events.FINISHED_LOADING || ws.isDragging()) {
99
+ return;
100
+ }
101
+ runCode();
102
+ });
103
+
104
+ window.addEventListener("message", (event) => {
105
+ if (event.data?.type === "user_message") {
106
+ handleUserMessage(event.data.text);
107
+ }
108
+ });
109
+
110
+ function handleUserMessage(message) {
111
+ if (window.Blockly && window.ws) {
112
+ let v = ws.getVariable('user_message') || ws.createVariable('user_message');
113
+ ws.variableValues = ws.variableValues || {};
114
+ ws.variableValues['user_message'] = message;
115
+ }
116
+
117
+ if (typeof window.userSendHandler === "function") {
118
+ console.log("[PARENT] userSendHandler exists, invoking it");
119
+ window.userSendHandler(message);
120
+ } else {
121
+ console.warn("[PARENT] userSendHandler is undefined!");
122
+ }
123
+ }
hello-world/src/serialization.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as Blockly from 'blockly/core';
8
+
9
+ const storageKey = 'mainWorkspace';
10
+
11
+ /**
12
+ * Saves the state of the workspace to browser's local storage.
13
+ * @param {Blockly.Workspace} workspace Blockly workspace to save.
14
+ */
15
+ export const save = function (workspace) {
16
+ const data = Blockly.serialization.workspaces.save(workspace);
17
+ window.localStorage?.setItem(storageKey, JSON.stringify(data));
18
+ };
19
+
20
+ /**
21
+ * Loads saved state from local storage into the given workspace.
22
+ * @param {Blockly.Workspace} workspace Blockly workspace to load into.
23
+ */
24
+ export const load = function (workspace) {
25
+ const data = window.localStorage?.getItem(storageKey);
26
+ if (!data) return;
27
+
28
+ // Don't emit events during loading.
29
+ Blockly.Events.disable();
30
+ Blockly.serialization.workspaces.load(JSON.parse(data), workspace, false);
31
+ Blockly.Events.enable();
32
+ };
hello-world/src/toolbox.js ADDED
@@ -0,0 +1,608 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /*
8
+ This toolbox contains nearly every single built-in block that Blockly offers,
9
+ in addition to the custom block 'add_text' this sample app adds.
10
+ You probably don't need every single block, and should consider either rewriting
11
+ your toolbox from scratch, or carefully choosing whether you need each block
12
+ listed here.
13
+ */
14
+
15
+ export const toolbox = {
16
+ kind: 'categoryToolbox',
17
+ contents: [
18
+ {
19
+ kind: 'category',
20
+ name: 'Custom',
21
+ categorystyle: 'logic_category',
22
+ contents: [
23
+ {
24
+ kind: 'block',
25
+ type: 'when_user_sends',
26
+ },
27
+ {
28
+ kind: 'block',
29
+ type: 'assistant_reply',
30
+ },
31
+ ]
32
+ },
33
+ {
34
+ kind: 'category',
35
+ name: 'Logic',
36
+ categorystyle: 'logic_category',
37
+ contents: [
38
+ {
39
+ kind: 'block',
40
+ type: 'controls_if',
41
+ },
42
+ {
43
+ kind: 'block',
44
+ type: 'logic_compare',
45
+ },
46
+ {
47
+ kind: 'block',
48
+ type: 'logic_operation',
49
+ },
50
+ {
51
+ kind: 'block',
52
+ type: 'logic_negate',
53
+ },
54
+ {
55
+ kind: 'block',
56
+ type: 'logic_boolean',
57
+ },
58
+ {
59
+ kind: 'block',
60
+ type: 'logic_null',
61
+ },
62
+ {
63
+ kind: 'block',
64
+ type: 'logic_ternary',
65
+ },
66
+ ],
67
+ },
68
+ {
69
+ kind: 'category',
70
+ name: 'Loops',
71
+ categorystyle: 'loop_category',
72
+ contents: [
73
+ {
74
+ kind: 'block',
75
+ type: 'controls_repeat_ext',
76
+ inputs: {
77
+ TIMES: {
78
+ shadow: {
79
+ type: 'math_number',
80
+ fields: {
81
+ NUM: 10,
82
+ },
83
+ },
84
+ },
85
+ },
86
+ },
87
+ {
88
+ kind: 'block',
89
+ type: 'controls_whileUntil',
90
+ },
91
+ {
92
+ kind: 'block',
93
+ type: 'controls_for',
94
+ inputs: {
95
+ FROM: {
96
+ shadow: {
97
+ type: 'math_number',
98
+ fields: {
99
+ NUM: 1,
100
+ },
101
+ },
102
+ },
103
+ TO: {
104
+ shadow: {
105
+ type: 'math_number',
106
+ fields: {
107
+ NUM: 10,
108
+ },
109
+ },
110
+ },
111
+ BY: {
112
+ shadow: {
113
+ type: 'math_number',
114
+ fields: {
115
+ NUM: 1,
116
+ },
117
+ },
118
+ },
119
+ },
120
+ },
121
+ {
122
+ kind: 'block',
123
+ type: 'controls_forEach',
124
+ },
125
+ {
126
+ kind: 'block',
127
+ type: 'controls_flow_statements',
128
+ },
129
+ ],
130
+ },
131
+ {
132
+ kind: 'category',
133
+ name: 'Math',
134
+ categorystyle: 'math_category',
135
+ contents: [
136
+ {
137
+ kind: 'block',
138
+ type: 'math_number',
139
+ fields: {
140
+ NUM: 123,
141
+ },
142
+ },
143
+ {
144
+ kind: 'block',
145
+ type: 'math_arithmetic',
146
+ inputs: {
147
+ A: {
148
+ shadow: {
149
+ type: 'math_number',
150
+ fields: {
151
+ NUM: 1,
152
+ },
153
+ },
154
+ },
155
+ B: {
156
+ shadow: {
157
+ type: 'math_number',
158
+ fields: {
159
+ NUM: 1,
160
+ },
161
+ },
162
+ },
163
+ },
164
+ },
165
+ {
166
+ kind: 'block',
167
+ type: 'math_single',
168
+ inputs: {
169
+ NUM: {
170
+ shadow: {
171
+ type: 'math_number',
172
+ fields: {
173
+ NUM: 9,
174
+ },
175
+ },
176
+ },
177
+ },
178
+ },
179
+ {
180
+ kind: 'block',
181
+ type: 'math_trig',
182
+ inputs: {
183
+ NUM: {
184
+ shadow: {
185
+ type: 'math_number',
186
+ fields: {
187
+ NUM: 45,
188
+ },
189
+ },
190
+ },
191
+ },
192
+ },
193
+ {
194
+ kind: 'block',
195
+ type: 'math_constant',
196
+ },
197
+ {
198
+ kind: 'block',
199
+ type: 'math_number_property',
200
+ inputs: {
201
+ NUMBER_TO_CHECK: {
202
+ shadow: {
203
+ type: 'math_number',
204
+ fields: {
205
+ NUM: 0,
206
+ },
207
+ },
208
+ },
209
+ },
210
+ },
211
+ {
212
+ kind: 'block',
213
+ type: 'math_round',
214
+ fields: {
215
+ OP: 'ROUND',
216
+ },
217
+ inputs: {
218
+ NUM: {
219
+ shadow: {
220
+ type: 'math_number',
221
+ fields: {
222
+ NUM: 3.1,
223
+ },
224
+ },
225
+ },
226
+ },
227
+ },
228
+ {
229
+ kind: 'block',
230
+ type: 'math_on_list',
231
+ fields: {
232
+ OP: 'SUM',
233
+ },
234
+ },
235
+ {
236
+ kind: 'block',
237
+ type: 'math_modulo',
238
+ inputs: {
239
+ DIVIDEND: {
240
+ shadow: {
241
+ type: 'math_number',
242
+ fields: {
243
+ NUM: 64,
244
+ },
245
+ },
246
+ },
247
+ DIVISOR: {
248
+ shadow: {
249
+ type: 'math_number',
250
+ fields: {
251
+ NUM: 10,
252
+ },
253
+ },
254
+ },
255
+ },
256
+ },
257
+ {
258
+ kind: 'block',
259
+ type: 'math_constrain',
260
+ inputs: {
261
+ VALUE: {
262
+ shadow: {
263
+ type: 'math_number',
264
+ fields: {
265
+ NUM: 50,
266
+ },
267
+ },
268
+ },
269
+ LOW: {
270
+ shadow: {
271
+ type: 'math_number',
272
+ fields: {
273
+ NUM: 1,
274
+ },
275
+ },
276
+ },
277
+ HIGH: {
278
+ shadow: {
279
+ type: 'math_number',
280
+ fields: {
281
+ NUM: 100,
282
+ },
283
+ },
284
+ },
285
+ },
286
+ },
287
+ {
288
+ kind: 'block',
289
+ type: 'math_random_int',
290
+ inputs: {
291
+ FROM: {
292
+ shadow: {
293
+ type: 'math_number',
294
+ fields: {
295
+ NUM: 1,
296
+ },
297
+ },
298
+ },
299
+ TO: {
300
+ shadow: {
301
+ type: 'math_number',
302
+ fields: {
303
+ NUM: 100,
304
+ },
305
+ },
306
+ },
307
+ },
308
+ },
309
+ {
310
+ kind: 'block',
311
+ type: 'math_random_float',
312
+ },
313
+ ],
314
+ },
315
+ {
316
+ kind: 'category',
317
+ name: 'Text',
318
+ categorystyle: 'text_category',
319
+ contents: [
320
+ {
321
+ kind: 'block',
322
+ type: 'text',
323
+ },
324
+ {
325
+ kind: 'block',
326
+ type: 'text_join',
327
+ },
328
+ {
329
+ kind: 'block',
330
+ type: 'text_append',
331
+ inputs: {
332
+ TEXT: {
333
+ shadow: {
334
+ type: 'text',
335
+ fields: {
336
+ TEXT: '',
337
+ },
338
+ },
339
+ },
340
+ },
341
+ },
342
+ {
343
+ kind: 'block',
344
+ type: 'text_length',
345
+ inputs: {
346
+ VALUE: {
347
+ shadow: {
348
+ type: 'text',
349
+ fields: {
350
+ TEXT: 'abc',
351
+ },
352
+ },
353
+ },
354
+ },
355
+ },
356
+ {
357
+ kind: 'block',
358
+ type: 'text_isEmpty',
359
+ inputs: {
360
+ VALUE: {
361
+ shadow: {
362
+ type: 'text',
363
+ fields: {
364
+ TEXT: '',
365
+ },
366
+ },
367
+ },
368
+ },
369
+ },
370
+ {
371
+ kind: 'block',
372
+ type: 'text_indexOf',
373
+ inputs: {
374
+ VALUE: {
375
+ block: {
376
+ type: 'variables_get',
377
+ },
378
+ },
379
+ FIND: {
380
+ shadow: {
381
+ type: 'text',
382
+ fields: {
383
+ TEXT: 'abc',
384
+ },
385
+ },
386
+ },
387
+ },
388
+ },
389
+ {
390
+ kind: 'block',
391
+ type: 'text_charAt',
392
+ inputs: {
393
+ VALUE: {
394
+ block: {
395
+ type: 'variables_get',
396
+ },
397
+ },
398
+ },
399
+ },
400
+ {
401
+ kind: 'block',
402
+ type: 'text_getSubstring',
403
+ inputs: {
404
+ STRING: {
405
+ block: {
406
+ type: 'variables_get',
407
+ },
408
+ },
409
+ },
410
+ },
411
+ {
412
+ kind: 'block',
413
+ type: 'text_changeCase',
414
+ inputs: {
415
+ TEXT: {
416
+ shadow: {
417
+ type: 'text',
418
+ fields: {
419
+ TEXT: 'abc',
420
+ },
421
+ },
422
+ },
423
+ },
424
+ },
425
+ {
426
+ kind: 'block',
427
+ type: 'text_trim',
428
+ inputs: {
429
+ TEXT: {
430
+ shadow: {
431
+ type: 'text',
432
+ fields: {
433
+ TEXT: 'abc',
434
+ },
435
+ },
436
+ },
437
+ },
438
+ },
439
+ {
440
+ kind: 'block',
441
+ type: 'text_count',
442
+ inputs: {
443
+ SUB: {
444
+ shadow: {
445
+ type: 'text',
446
+ },
447
+ },
448
+ TEXT: {
449
+ shadow: {
450
+ type: 'text',
451
+ },
452
+ },
453
+ },
454
+ },
455
+ {
456
+ kind: 'block',
457
+ type: 'text_replace',
458
+ inputs: {
459
+ FROM: {
460
+ shadow: {
461
+ type: 'text',
462
+ },
463
+ },
464
+ TO: {
465
+ shadow: {
466
+ type: 'text',
467
+ },
468
+ },
469
+ TEXT: {
470
+ shadow: {
471
+ type: 'text',
472
+ },
473
+ },
474
+ },
475
+ },
476
+ {
477
+ kind: 'block',
478
+ type: 'text_reverse',
479
+ inputs: {
480
+ TEXT: {
481
+ shadow: {
482
+ type: 'text',
483
+ },
484
+ },
485
+ },
486
+ },
487
+ ],
488
+ },
489
+ {
490
+ kind: 'category',
491
+ name: 'Lists',
492
+ categorystyle: 'list_category',
493
+ contents: [
494
+ {
495
+ kind: 'block',
496
+ type: 'lists_create_with',
497
+ },
498
+ {
499
+ kind: 'block',
500
+ type: 'lists_create_with',
501
+ },
502
+ {
503
+ kind: 'block',
504
+ type: 'lists_repeat',
505
+ inputs: {
506
+ NUM: {
507
+ shadow: {
508
+ type: 'math_number',
509
+ fields: {
510
+ NUM: 5,
511
+ },
512
+ },
513
+ },
514
+ },
515
+ },
516
+ {
517
+ kind: 'block',
518
+ type: 'lists_length',
519
+ },
520
+ {
521
+ kind: 'block',
522
+ type: 'lists_isEmpty',
523
+ },
524
+ {
525
+ kind: 'block',
526
+ type: 'lists_indexOf',
527
+ inputs: {
528
+ VALUE: {
529
+ block: {
530
+ type: 'variables_get',
531
+ },
532
+ },
533
+ },
534
+ },
535
+ {
536
+ kind: 'block',
537
+ type: 'lists_getIndex',
538
+ inputs: {
539
+ VALUE: {
540
+ block: {
541
+ type: 'variables_get',
542
+ },
543
+ },
544
+ },
545
+ },
546
+ {
547
+ kind: 'block',
548
+ type: 'lists_setIndex',
549
+ inputs: {
550
+ LIST: {
551
+ block: {
552
+ type: 'variables_get',
553
+ },
554
+ },
555
+ },
556
+ },
557
+ {
558
+ kind: 'block',
559
+ type: 'lists_getSublist',
560
+ inputs: {
561
+ LIST: {
562
+ block: {
563
+ type: 'variables_get',
564
+ },
565
+ },
566
+ },
567
+ },
568
+ {
569
+ kind: 'block',
570
+ type: 'lists_split',
571
+ inputs: {
572
+ DELIM: {
573
+ shadow: {
574
+ type: 'text',
575
+ fields: {
576
+ TEXT: ',',
577
+ },
578
+ },
579
+ },
580
+ },
581
+ },
582
+ {
583
+ kind: 'block',
584
+ type: 'lists_sort',
585
+ },
586
+ {
587
+ kind: 'block',
588
+ type: 'lists_reverse',
589
+ },
590
+ ],
591
+ },
592
+ {
593
+ kind: 'sep',
594
+ },
595
+ {
596
+ kind: 'category',
597
+ name: 'Variables',
598
+ categorystyle: 'variable_category',
599
+ custom: 'VARIABLE',
600
+ },
601
+ {
602
+ kind: 'category',
603
+ name: 'Functions',
604
+ categorystyle: 'procedure_category',
605
+ custom: 'PROCEDURE',
606
+ },
607
+ ],
608
+ };
hello-world/webpack.config.js ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require('path');
2
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
3
+
4
+ // Base config that applies to either development or production mode.
5
+ const config = {
6
+ entry: './src/index.js',
7
+ output: {
8
+ // Compile the source files into a bundle.
9
+ filename: 'bundle.js',
10
+ path: path.resolve(__dirname, 'dist'),
11
+ clean: true,
12
+ },
13
+ // Enable webpack-dev-server to get hot refresh of the app.
14
+ devServer: {
15
+ static: './build',
16
+ },
17
+ module: {
18
+ rules: [
19
+ {
20
+ // Load CSS files. They can be imported into JS files.
21
+ test: /\.css$/i,
22
+ use: ['style-loader', 'css-loader'],
23
+ },
24
+ ],
25
+ },
26
+ plugins: [
27
+ // Generate the HTML index page based on our template.
28
+ // This will output the same index page with the bundle we
29
+ // created above added in a script tag.
30
+ new HtmlWebpackPlugin({
31
+ template: 'src/index.html',
32
+ }),
33
+ ],
34
+ };
35
+
36
+ module.exports = (env, argv) => {
37
+ if (argv.mode === 'development') {
38
+ // Set the output path to the `build` directory
39
+ // so we don't clobber production builds.
40
+ config.output.path = path.resolve(__dirname, 'build');
41
+
42
+ // Generate source maps for our code for easier debugging.
43
+ // Not suitable for production builds. If you want source maps in
44
+ // production, choose a different one from https://webpack.js.org/configuration/devtool
45
+ config.devtool = 'eval-cheap-module-source-map';
46
+
47
+ // Include the source maps for Blockly for easier debugging Blockly code.
48
+ config.module.rules.push({
49
+ test: /(blockly\/.*\.js)$/,
50
+ use: [require.resolve('source-map-loader')],
51
+ enforce: 'pre',
52
+ });
53
+
54
+ // Ignore spurious warnings from source-map-loader
55
+ // It can't find source maps for some Closure modules and that is expected
56
+ config.ignoreWarnings = [/Failed to parse source map/];
57
+ }
58
+ return config;
59
+ };
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ gradio