ready?
This commit is contained in:
		
							parent
							
								
									c6d280dfd8
								
							
						
					
					
						commit
						577557146d
					
				
					 2 changed files with 97 additions and 18 deletions
				
			
		|  | @ -30,15 +30,13 @@ | ||||||
|       display: inline-block; padding: 0.75rem 1.5rem; background: var(--primary); color: #fff; text-decoration: none; |       display: inline-block; padding: 0.75rem 1.5rem; background: var(--primary); color: #fff; text-decoration: none; | ||||||
|       border: none; border-radius: 0.375rem; font-weight: 600; cursor: pointer; |       border: none; border-radius: 0.375rem; font-weight: 600; cursor: pointer; | ||||||
|       transition: background 0.2s ease; |       transition: background 0.2s ease; | ||||||
|       margin-top: 1rem; |  | ||||||
|     } |     } | ||||||
|     .btn:hover:not([disabled]) { background: var(--primary-light); } |     .btn:hover:not([disabled]) { background: var(--primary-light); } | ||||||
|     .btn-stop { |     .btn-stop { | ||||||
|       background: #ef4444; color: #fff; font-weight: 600; cursor: pointer; |       background: #ef4444; color: #fff; font-weight: 600; cursor: pointer; | ||||||
|       transition: background 0.2s ease; |       transition: background 0.2s ease; | ||||||
|       margin-top: 1rem; padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem; |       padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem; | ||||||
|       display: inline-block; text-decoration: none; |       display: inline-block; text-decoration: none; | ||||||
|       margin-left: 1rem; |  | ||||||
|     } |     } | ||||||
|     .btn-stop:hover:not([disabled]) { background: #dc2626; } |     .btn-stop:hover:not([disabled]) { background: #dc2626; } | ||||||
|     .btn[disabled] { opacity: 0.6; cursor: not-allowed; } |     .btn[disabled] { opacity: 0.6; cursor: not-allowed; } | ||||||
|  | @ -46,13 +44,32 @@ | ||||||
|       border: 2px solid #f3f3f3; |       border: 2px solid #f3f3f3; | ||||||
|       border-top: 2px solid #fff; |       border-top: 2px solid #fff; | ||||||
|       border-radius: 50%; |       border-radius: 50%; | ||||||
|       width: 16px; |       width: 14px; | ||||||
|       height: 16px; |       height: 14px; | ||||||
|       display: inline-block; |       display: inline-block; | ||||||
|       vertical-align: middle; |       vertical-align: middle; | ||||||
|       margin-right: 0.5rem; |       margin-right: 0.5rem; | ||||||
|       animation: spin 1s linear infinite; |       animation: spin 1s linear infinite; | ||||||
|     } |     } | ||||||
|  |     .spinner::before, | ||||||
|  |     .spinner::after { | ||||||
|  |       content: ""; | ||||||
|  |       box-sizing: border-box; | ||||||
|  |       position: absolute; | ||||||
|  |       inset: 0; | ||||||
|  |       border-radius: 50%; | ||||||
|  |     } | ||||||
|  |     .spinner::before { | ||||||
|  |       border: 4px solid rgba(0,0,0,0.1); | ||||||
|  |     } | ||||||
|  |     .spinner::after { | ||||||
|  |       border: 4px solid transparent; | ||||||
|  |       border-top-color: #3498db; | ||||||
|  |       border-right-color: #8e44ad; | ||||||
|  |       box-shadow: 0 0 8px rgba(52, 152, 219, 0.6), | ||||||
|  |                   0 0 8px rgba(142, 68, 173, 0.6) inset; | ||||||
|  |       animation: spin 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite; | ||||||
|  |     } | ||||||
|     @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } |     @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } | ||||||
|     .flex { display: flex; gap: 1rem; } |     .flex { display: flex; gap: 1rem; } | ||||||
|     .flex > div { flex: 1; } |     .flex > div { flex: 1; } | ||||||
|  | @ -79,12 +96,46 @@ | ||||||
|       font-size: 0.8rem; |       font-size: 0.8rem; | ||||||
|       color: var(--text); |       color: var(--text); | ||||||
|       margin-top: 1rem; |       margin-top: 1rem; | ||||||
|       line-height: 2em; |       line-height: 1.5rem; | ||||||
|  |     } | ||||||
|  |     .styled-select { | ||||||
|  |       padding: 0.75rem; | ||||||
|  |       border: 1px solid var(--border); | ||||||
|  |       border-radius: 0.375rem; | ||||||
|  |       background-color: var(--card); | ||||||
|  |       color: var(--text); | ||||||
|  |       font-family: 'Inter', sans-serif; | ||||||
|  |       font-size: 0.9rem; | ||||||
|  |       appearance: none; | ||||||
|  |       background-image: url("data:image/svg+xml;utf8,<svg fill='%23374151' height='20' viewBox='0 0 24 24' width='20' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/></svg>"); | ||||||
|  |       background-repeat: no-repeat; | ||||||
|  |       background-position: right 0.75rem center; | ||||||
|  |       background-size: 1rem; | ||||||
|  |       padding-right: 2rem; | ||||||
|  |       padding-left: 1rem; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .styled-select:focus { | ||||||
|  |       outline: none; | ||||||
|  |       border-color: var(--primary); | ||||||
|  |       box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2); | ||||||
|  |     } | ||||||
|  |     .align-center { | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|     } |     } | ||||||
|     @media screen and (max-width: 768px) { |     @media screen and (max-width: 768px) { | ||||||
|       .flex { flex-direction: column; } |       .flex { flex-direction: column; } | ||||||
|       .flex > div { width: 100%; } |       .flex > div { width: 100%; } | ||||||
|  |       .align-center { | ||||||
|  |         align-items: normal; | ||||||
|  |       } | ||||||
|  |       .styled-select { | ||||||
|  |         width: 100%; | ||||||
|  |         max-width: 500px !important; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|   </style> |   </style> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  | @ -109,8 +160,15 @@ | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <button type="button" id="run-btn" class="btn">Run</button> |       <div class="flex align-center" style="margin-top: 1rem; flex-wrap: wrap; gap: 1rem;"> | ||||||
|       <button type="button" id="stop-btn" class="btn-stop" style="display: none;">Stop</button> |         <select id="speed" class="styled-select"> | ||||||
|  |           <option value="slllooow">pretty, pretty slow</option> | ||||||
|  |           <option value="med">medium</option> | ||||||
|  |           <option value="fast" selected>blazingly fast</option> | ||||||
|  |         </select> | ||||||
|  |         <button type="button" id="run-btn" class="btn">Run</button> | ||||||
|  |         <button type="button" id="stop-btn" class="btn-stop" style="display: none;">Stop</button> | ||||||
|  |       </div> | ||||||
|     </form> |     </form> | ||||||
| 
 | 
 | ||||||
|     <div class="card"> |     <div class="card"> | ||||||
|  | @ -129,6 +187,7 @@ | ||||||
|     let abortSignal = false; |     let abortSignal = false; | ||||||
|     const outElement = document.getElementById('output'); |     const outElement = document.getElementById('output'); | ||||||
|     const lastStateElement = document.getElementById('last_state'); |     const lastStateElement = document.getElementById('last_state'); | ||||||
|  |     const speedSelect = document.getElementById('speed'); | ||||||
| 
 | 
 | ||||||
|     class Interpreter { |     class Interpreter { | ||||||
|       constructor({ commandsPerSecond = 1_000_000_000, debug = false} = {}) { |       constructor({ commandsPerSecond = 1_000_000_000, debug = false} = {}) { | ||||||
|  | @ -146,6 +205,7 @@ | ||||||
|         this.userInput = ""; |         this.userInput = ""; | ||||||
|         this.userInputIndex = 0; |         this.userInputIndex = 0; | ||||||
|         this.savedOutput = ""; |         this.savedOutput = ""; | ||||||
|  |         this.steps = 0; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       ourI32(value) { |       ourI32(value) { | ||||||
|  | @ -277,11 +337,32 @@ | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       async execute({ codeString, userInput = '', callback } = {}) { |       async execute({ codeString, userInput = '', callback } = {}) { | ||||||
|  |         let waitTime = 0; | ||||||
|  |         let breath = 0; | ||||||
|  |         const speed = speedSelect.value; | ||||||
|  |         if (speed === 'slllooow') { | ||||||
|  |           this.commandsPerSecond = 1; | ||||||
|  |           waitTime = 1 / this.commandsPerSecond; | ||||||
|  |           breath = 1; | ||||||
|  |         } else if (speed === 'med') { | ||||||
|  |           this.commandsPerSecond = 10; | ||||||
|  |           waitTime = 1 / this.commandsPerSecond; | ||||||
|  |           breath = 1 | ||||||
|  |         } else if (speed === 'fast') { | ||||||
|  |           this.commandsPerSecond = 1_000_000_000_000; | ||||||
|  |           waitTime = 0; | ||||||
|  |           breath = 100000; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         this.loadCodeFromString(codeString); |         this.loadCodeFromString(codeString); | ||||||
|         this.userInput = userInput; |         this.userInput = userInput; | ||||||
|         let steps = 0; |         let steps = 0; | ||||||
|         let breath = 100; |  | ||||||
|         while (this.pc < this.code.length) { |         while (this.pc < this.code.length) { | ||||||
|  |           if (abortSignal) { | ||||||
|  |               console.log("Execution aborted"); | ||||||
|  |               this.savedOutput = "Execution aborted"; | ||||||
|  |               break; | ||||||
|  |             } | ||||||
|             const com = this.code[this.pc]; |             const com = this.code[this.pc]; | ||||||
|             switch (com) { |             switch (com) { | ||||||
|                 case '+': this.handlePlus(); break; |                 case '+': this.handlePlus(); break; | ||||||
|  | @ -295,16 +376,15 @@ | ||||||
|             } |             } | ||||||
|             this.pc++; |             this.pc++; | ||||||
|             steps++; |             steps++; | ||||||
|             if (steps % breath === 0) { |             this.steps = steps; | ||||||
|               await new Promise(resolve => setTimeout(resolve, 0)); |  | ||||||
|             } |  | ||||||
|             if (abortSignal) { |  | ||||||
|               console.log("Execution aborted"); |  | ||||||
|               this.savedOutput = "Execution aborted"; |  | ||||||
|               break; |  | ||||||
|             } |  | ||||||
|             outElement.textContent = this.savedOutput; |             outElement.textContent = this.savedOutput; | ||||||
|             lastStateElement.textContent = this.getState(); |             lastStateElement.textContent = this.getState(); | ||||||
|  |             // auto-scroll to the bottom | ||||||
|  |             outElement.scrollTop = outElement.scrollHeight; | ||||||
|  |             lastStateElement.scrollTop = lastStateElement.scrollHeight; | ||||||
|  |             if (steps % breath === 0) { | ||||||
|  |               await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         callback(this.savedOutput, this.getState()); |         callback(this.savedOutput, this.getState()); | ||||||
|         return this.savedOutput; |         return this.savedOutput; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| from django.template.response import TemplateResponse | from django.template.response import TemplateResponse | ||||||
| from django.http import JsonResponse as JSONResponse | from django.http import JsonResponse as JSONResponse | ||||||
| from .interpreter import Interpreter |  | ||||||
| from urllib.parse import parse_qsl, unquote_plus, unquote | from urllib.parse import parse_qsl, unquote_plus, unquote | ||||||
| from typing import Dict | from typing import Dict | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue