|
23 | 23 | <div class="bg-[#1c1c1c] border-t border-[#2d2d2d] px-3 py-2 flex justify-between items-center"> |
24 | 24 | <div class="text-[12px] tracking-tighter font-semibold text-zinc-500 flex items-center gap-1 w-full"> |
25 | 25 | <p>editor</p> |
26 | | - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="ml-auto w-3 h-3"> |
27 | | - <path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/> |
28 | | - <path d="M14 2v5a1 1 0 0 0 1 1h5"/> |
29 | | - </svg> |
| 26 | + <button id="run" disabled class="ml-auto flex items-center gap-1 px-2 py-0.5 bg-[#d97757] text-white rounded hover:bg-[#c16644] disabled:opacity-40 disabled:cursor-not-allowed transition"> |
| 27 | + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-3 h-3"> |
| 28 | + <polygon points="6 4 20 12 6 20"/> |
| 29 | + </svg> |
| 30 | + <span>run</span> |
| 31 | + </button> |
30 | 32 | </div> |
31 | 33 | </div> |
32 | 34 |
|
|
37 | 39 | class="no-scrollbar w-full bg-transparent p-2 outline-none resize-none text-[#c2c2c2] placeholder-[#404040] overflow-y-auto" |
38 | 40 | spellcheck="false" |
39 | 41 | placeholder="Type your Python code here..." |
40 | | - ></textarea> |
| 42 | + >print("Hello, world!")</textarea> |
41 | 43 | </div> |
42 | 44 | </div> |
43 | 45 |
|
|
46 | 48 | <div class="bg-[#1c1c1c] border-b border-[#2d2d2d] px-3 py-2 flex justify-between items-center"> |
47 | 49 | <div class="text-[12px] tracking-tighter font-semibold text-zinc-500 flex items-center gap-1 w-full"> |
48 | 50 | <p>terminal</p> |
49 | | - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="ml-auto w-3 h-3"> |
50 | | - <path d="M12 19h8"/> |
51 | | - <path d="m4 17 6-6-6-6"/> |
52 | | - </svg> |
| 51 | + <span id="status" class="ml-auto text-[#525252]">loading wasm…</span> |
53 | 52 | </div> |
54 | 53 | </div> |
55 | 54 |
|
56 | | - <div class="code-font text-sm p-3 text-[#c2c2c2] flex-1 overflow-y-auto no-scrollbar"> |
57 | | - <p>[2026-04-18T04:45:39Z INFO edge] emit: snapshot created [ops=3 consts=1]</p> |
58 | | - <p>Hello, world!</p> |
59 | | - </div> |
| 55 | + <div id="term" class="code-font text-sm p-3 text-[#c2c2c2] flex-1 overflow-y-auto no-scrollbar whitespace-pre-wrap"></div> |
60 | 56 | </div> |
61 | | - |
| 57 | + |
62 | 58 | <script> |
| 59 | + const WASM_URL = 'https://github.com/dylan-sutton-chavez/edge-python/releases/latest/download/compiler_lib.wasm'; |
| 60 | + const SZ = 1 << 20; |
| 61 | + |
63 | 62 | const ed = document.getElementById('ed'); |
64 | 63 | const ln = document.getElementById('ln'); |
| 64 | + const btn = document.getElementById('run'); |
| 65 | + const term = document.getElementById('term'); |
| 66 | + const status = document.getElementById('status'); |
| 67 | + |
| 68 | + let wasm = null; |
| 69 | + |
| 70 | + async function loadWasm() { |
| 71 | + try { |
| 72 | + status.textContent = 'fetching latest wasm…'; |
| 73 | + const res = await fetch(WASM_URL, { cache: 'no-cache' }); |
| 74 | + if (!res.ok) throw new Error(`HTTP ${res.status}`); |
| 75 | + const bytes = await res.arrayBuffer(); |
| 76 | + const { instance } = await WebAssembly.instantiate(bytes, {}); |
| 77 | + wasm = instance.exports; |
| 78 | + btn.disabled = false; |
| 79 | + status.textContent = 'ready'; |
| 80 | + status.className = 'ml-auto text-emerald-500'; |
| 81 | + } catch (e) { |
| 82 | + status.textContent = `load failed: ${e.message}`; |
| 83 | + status.className = 'ml-auto text-red-500'; |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + function runCode() { |
| 88 | + if (!wasm) return; |
| 89 | + const srcBytes = new TextEncoder().encode(ed.value); |
| 90 | + if (srcBytes.length > SZ) { |
| 91 | + term.textContent = `error: source exceeds ${SZ} bytes`; |
| 92 | + return; |
| 93 | + } |
| 94 | + const mem = new Uint8Array(wasm.memory.buffer); |
| 95 | + mem.set(srcBytes, wasm.src_ptr()); |
| 96 | + const outLen = wasm.run(srcBytes.length); |
| 97 | + const outView = new Uint8Array(wasm.memory.buffer, wasm.out_ptr(), outLen); |
| 98 | + term.textContent = new TextDecoder().decode(outView); |
| 99 | + } |
| 100 | + |
| 101 | + btn.addEventListener('click', runCode); |
65 | 102 |
|
66 | 103 | ed.addEventListener('keydown', (e) => { |
67 | | - if (e.key === 'Enter') { |
68 | | - const currentLines = ed.value.split('\n').length; |
69 | | - if (currentLines >= 99) { |
70 | | - e.preventDefault(); |
71 | | - } |
| 104 | + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { |
| 105 | + e.preventDefault(); |
| 106 | + runCode(); |
| 107 | + return; |
| 108 | + } |
| 109 | + if (e.key === 'Enter' && ed.value.split('\n').length >= 99) { |
| 110 | + e.preventDefault(); |
72 | 111 | } |
73 | 112 | }); |
74 | 113 |
|
75 | 114 | const sync = () => { |
76 | 115 | let lines = ed.value.split('\n'); |
77 | | - |
78 | 116 | if (lines.length > 99) { |
79 | 117 | ed.value = lines.slice(0, 99).join('\n'); |
80 | 118 | lines = ed.value.split('\n'); |
81 | 119 | } |
82 | | - |
83 | | - const count = lines.length; |
84 | | - |
85 | | - ln.textContent = Array.from({length: count}, (_, i) => |
| 120 | + ln.textContent = Array.from({length: lines.length}, (_, i) => |
86 | 121 | String(i + 1).padStart(2, '0') |
87 | 122 | ).join('\n'); |
88 | | - |
89 | 123 | ln.scrollTop = ed.scrollTop; |
90 | 124 | }; |
91 | 125 |
|
92 | 126 | ed.oninput = sync; |
93 | 127 | ed.onscroll = sync; |
94 | 128 |
|
95 | 129 | sync(); |
| 130 | + loadWasm(); |
96 | 131 | </script> |
97 | 132 |
|
98 | 133 | </body> |
|
0 commit comments