55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 "/>
66 < title > Blockly JavaScript Builder</ title >
77 < script src ="https://unpkg.com/blockly/blockly.min.js "> </ script >
8+ < script src ="https://unpkg.com/blockly/blocks_compressed.js "> </ script >
9+ < script src ="https://unpkg.com/blockly/msg/en.js "> </ script >
10+ < script src ="https://unpkg.com/blockly/zelos.js "> </ script >
811 < style >
912 body {
1013 font-family : sans-serif;
4548 background : # eee ;
4649 padding : 10px ;
4750 border-radius : 5px ;
48- height : 100% ;
4951 margin : 0 ;
5052 white-space : pre-wrap;
5153 word-wrap : break-word;
6163 cursor : pointer;
6264 }
6365 # output {
64- margin-top : 10px ;
66+ margin-top : 5px ;
67+ font-family : monospace;
68+ white-space : pre-wrap;
69+ }
70+ # errorMessage {
71+ margin-top : 5px ;
72+ color : red;
6573 font-family : monospace;
6674 }
75+ # themeToggle {
76+ position : absolute;
77+ top : 10px ;
78+ right : 10px ;
79+ z-index : 1000 ;
80+ background : # eee ;
81+ border : 1px solid # 999 ;
82+ border-radius : 4px ;
83+ padding : 5px 10px ;
84+ }
85+ # loopControls {
86+ display : flex;
87+ align-items : center;
88+ gap : 10px ;
89+ }
90+ # loopInterval {
91+ width : 80px ;
92+ padding : 4px ;
93+ }
6794 </ style >
6895</ head >
6996< body >
97+ < button id ="themeToggle "> 🎨 Toggle Theme</ button >
7098 < h2 style ="margin: 10px; "> Blockly JavaScript Builder</ h2 >
99+
71100 < div id ="main ">
72101 < div id ="blocklyDiv "> </ div >
73102 < div id ="rightPanel ">
74103 < div id ="codeHeader ">
75104 < h4 style ="margin: 0; "> 📄 JavaScript Code</ h4 >
76- < button onclick ="runCode() "> ▶ Run</ button >
105+ < div id ="loopControls ">
106+ < input id ="loopInterval " type ="number " value ="1000 " min ="100 " /> ms
107+ < button onclick ="runCode() "> ▶ Run</ button >
108+ < button onclick ="startLoop() "> 🔁 Loop Run</ button >
109+ < button onclick ="stopLoop() "> ⏹ Stop</ button >
110+ </ div >
77111 </ div >
78112 < div id ="codeArea ">
79113 < pre id ="code "> </ pre >
@@ -82,11 +116,11 @@ <h4 style="margin: 0;">📄 JavaScript Code</h4>
82116 < button onclick ="updateCode() "> 🔄 Update</ button >
83117 < button onclick ="copyCode() "> 📋 Copy Code</ button >
84118 < div id ="output "> </ div >
119+ < div id ="errorMessage "> </ div >
85120 </ div >
86121 </ div >
87122 </ div >
88123
89- <!-- Toolbox -->
90124 < xml id ="toolbox " style ="display: none ">
91125 < category name ="Logic " colour ="%{BKY_LOGIC_HUE} ">
92126 < block type ="controls_if "> </ block >
@@ -96,14 +130,8 @@ <h4 style="margin: 0;">📄 JavaScript Code</h4>
96130 < block type ="logic_negate "> </ block >
97131 < block type ="logic_null "> </ block >
98132 </ category >
99- < category name ="Loops " colour ="%{BKY_LOOPS_HUE} ">
100- < block type ="controls_repeat_ext "> </ block >
101- < block type ="controls_whileUntil "> </ block >
102- </ category >
103133 < category name ="Math " colour ="%{BKY_MATH_HUE} ">
104- < block type ="math_number ">
105- < field name ="NUM "> 0</ field >
106- </ block >
134+ < block type ="math_number "> < field name ="NUM "> 0</ field > </ block >
107135 < block type ="math_arithmetic "> </ block >
108136 < block type ="math_round "> </ block >
109137 < block type ="math_single "> </ block >
@@ -131,21 +159,64 @@ <h4 style="margin: 0;">📄 JavaScript Code</h4>
131159 < block type ="text_isEmpty "> </ block >
132160 < block type ="text_join "> </ block >
133161 </ category >
134- < category name ="Variables " custom ="VARIABLE " colour ="%{BKY_VARIABLES_HUE} ">
135- < block type ="variables_get "> </ block >
136- < block type ="variables_set "> </ block >
137- </ category >
138- < category name ="Functions " colour ="%{BKY_PROCEDURES_HUE} ">
139- < block type ="procedures_defnoreturn "> </ block >
140- < block type ="procedures_callnoreturn "> </ block >
141- </ category >
162+ < category name ="Variables " custom ="VARIABLE " colour ="%{BKY_VARIABLES_HUE} "> </ category >
163+ < category name ="Functions " custom ="PROCEDURE " colour ="%{BKY_PROCEDURES_HUE} "> </ category >
142164 </ xml >
143165
144- <!-- Blockly + JavaScript -->
145166 < script >
146- const workspace = Blockly . inject ( 'blocklyDiv' , {
147- toolbox : document . getElementById ( 'toolbox' ) ,
148- scrollbars : true ,
167+ Blockly . Themes . ScratchBright = Blockly . Theme . defineTheme ( 'scratchTheme' , {
168+ base : Blockly . Themes . Classic ,
169+ blockStyles : {
170+ logic_blocks : { colourPrimary : '#5C81A6' } ,
171+ math_blocks : { colourPrimary : '#5C68A6' } ,
172+ text_blocks : { colourPrimary : '#5CA68D' } ,
173+ list_blocks : { colourPrimary : '#745CA6' } ,
174+ variable_blocks : { colourPrimary : '#A65C81' } ,
175+ procedure_blocks : { colourPrimary : '#8000ff' }
176+ } ,
177+ fontStyle : {
178+ family : 'Helvetica, Arial, sans-serif' ,
179+ size : 10
180+ } ,
181+ startHats : true
182+ } ) ;
183+
184+ let usingScratchTheme = true ;
185+ let workspace ;
186+
187+ function createWorkspace ( theme , renderer ) {
188+ if ( workspace ) workspace . dispose ( ) ;
189+ workspace = Blockly . inject ( 'blocklyDiv' , {
190+ toolbox : document . getElementById ( 'toolbox' ) ,
191+ scrollbars : true ,
192+ theme,
193+ renderer,
194+ zoom : {
195+ controls : true ,
196+ wheel : true ,
197+ startScale : 1.0 ,
198+ maxScale : 3 ,
199+ minScale : 0.3 ,
200+ scaleSpeed : 1.2 ,
201+ pinch : true
202+ } ,
203+ trashcan : true
204+ } ) ;
205+ workspace . addChangeListener ( updateCode ) ;
206+ updateCode ( ) ;
207+ }
208+
209+ createWorkspace ( Blockly . Themes . ScratchBright , 'zelos' ) ;
210+
211+ document . getElementById ( 'themeToggle' ) . addEventListener ( 'click' , ( ) => {
212+ const confirmChange = window . confirm ( "Are you sure you want to change themes? This will delete your code." ) ;
213+ if ( confirmChange ) {
214+ usingScratchTheme = ! usingScratchTheme ;
215+ createWorkspace (
216+ usingScratchTheme ? Blockly . Themes . ScratchBright : Blockly . Themes . Classic ,
217+ usingScratchTheme ? 'zelos' : 'geras'
218+ ) ;
219+ }
149220 } ) ;
150221
151222 function updateCode ( ) {
@@ -156,11 +227,14 @@ <h4 style="margin: 0;">📄 JavaScript Code</h4>
156227 function runCode ( ) {
157228 updateCode ( ) ;
158229 const code = document . getElementById ( 'code' ) . textContent ;
230+ const outputDiv = document . getElementById ( 'output' ) ;
231+ const errorDiv = document . getElementById ( 'errorMessage' ) ;
232+ outputDiv . textContent = "" ;
233+ errorDiv . textContent = "" ;
159234 try {
160- const result = eval ( code ) ;
161- document . getElementById ( 'output' ) . innerText = "Output: " + ( result !== undefined ? result : "(no output)" ) ;
235+ ( async ( ) => { await eval ( `(async () => {\n${ code } \n})()` ) ; } ) ( ) ;
162236 } catch ( e ) {
163- document . getElementById ( 'output' ) . innerText = "Error: " + e . message ;
237+ errorDiv . innerText = "❌ Error: " + e . message ;
164238 }
165239 }
166240
@@ -171,32 +245,41 @@ <h4 style="margin: 0;">📄 JavaScript Code</h4>
171245 . catch ( err => alert ( "Failed to copy code: " + err ) ) ;
172246 }
173247
174- Blockly . defineBlocksWithJsonArray ( [
175- {
176- "type" : "text_prompt" ,
177- "message0" : "prompt with message %1" ,
178- "args0" : [
179- {
180- "type" : "input_value" ,
181- "name" : "TEXT" ,
182- "check" : "String"
248+ let loopInterval = null ;
249+
250+ function startLoop ( ) {
251+ stopLoop ( ) ;
252+ updateCode ( ) ;
253+ const code = document . getElementById ( 'code' ) . textContent ;
254+ const outputDiv = document . getElementById ( 'output' ) ;
255+ const errorDiv = document . getElementById ( 'errorMessage' ) ;
256+ const interval = parseInt ( document . getElementById ( 'loopInterval' ) . value ) || 1000 ;
257+ outputDiv . textContent = "" ;
258+ errorDiv . textContent = "" ;
259+ try {
260+ loopInterval = setInterval ( ( ) => {
261+ try {
262+ errorDiv . textContent = "" ;
263+ ( async ( ) => { await eval ( `(async () => {\n${ code } \n})()` ) ; } ) ( ) ;
264+ } catch ( e ) {
265+ outputDiv . textContent = "" ;
266+ errorDiv . textContent = "❌ Loop Error: " + e . message ;
267+ stopLoop ( ) ;
183268 }
184- ] ,
185- "output" : "String" ,
186- "colour" : 160 ,
187- "tooltip" : "Prompts the user for input." ,
188- "helpUrl" : ""
269+ } , interval ) ;
270+ } catch ( e ) {
271+ errorDiv . textContent = "❌ Setup Error: " + e . message ;
189272 }
190- ] ) ;
191-
192- Blockly . JavaScript [ 'text_prompt' ] = function ( block ) {
193- const msg = Blockly . JavaScript . valueToCode ( block , 'TEXT' , Blockly . JavaScript . ORDER_NONE ) || '""' ;
194- const code = `prompt(${ msg } )` ;
195- return [ code , Blockly . JavaScript . ORDER_FUNCTION_CALL ] ;
196- } ;
273+ }
197274
198- workspace . addChangeListener ( updateCode ) ;
199- updateCode ( ) ;
275+ function stopLoop ( ) {
276+ if ( loopInterval ) {
277+ clearInterval ( loopInterval ) ;
278+ loopInterval = null ;
279+ document . getElementById ( 'output' ) . textContent += "\n⏹ Loop stopped." ;
280+ document . getElementById ( 'errorMessage' ) . textContent = "" ;
281+ }
282+ }
200283 </ script >
201284</ body >
202285</ html >
0 commit comments