2025.08 / 獨立開發
純瀏覽器 Python IDE
無需安裝的純瀏覽器 Python IDE,支持代碼編輯、終端運行、互動輸入與智能提示。

第一步:讓 Python 跑起來
要讓 Python 在瀏覽器跑起來,最直接的做法是把 Python 運行時帶到前端。Pyodide 將 CPython 和常用科學計算庫編譯為 WebAssembly(WASM)。WASM 是瀏覽器可高效運行的二進制格式,因此前端可以直接加載 Pyodide,並在本機運行 Python。
這先解決了「代碼在哪裡運行」的問題,但不能直接放在主線程。Python 一旦運行較久,React 界面也會被阻塞,按鈕、編輯器和頁面更新都會停住。於是下一步是把 Pyodide 放進獨立的 Web Worker;主線程只負責渲染界面,耗時運算不會卡住用戶操作。
第二步:接上編輯器與終端
Worker 跑起來後,主線程還需要向它發送加載、運行和停止指令,並接收輸出。這裡使用 Comlink 將 Worker 的消息通信封裝成接近一般函數調用的 RPC 界面,避免手寫大量 postMessage 協議。
接著加入 Monaco Editor 作為代碼編輯器;它是 VS Code 編輯器的開源核心。終端則採用 XTerm.js,接收 Python 的 stdout/stderr 字符流。到這一步,用戶已能在瀏覽器內編寫並運行絕大部分 Python 代碼,並在終端查看結果。
還差兩件事
基本運行流程完成後,仍有兩個能力缺口:
input()無法直接使用。- 編輯器沒有自動完成和錯誤提示。
互動式 input()
Pyodide 在 Python Worker 內同步調用 stdin 回調,input() 必須在回調結束前拿到字符串。另一邊,XTerm.js 的 onData 在主線程中異步接收鍵盤輸入。若 stdin 回調只用 postMessage() 通知主線程後立刻返回,Python 只會讀到空字符串;Python Worker 也不能直接 await 主線程上、等待用戶輸入的 Promise。
項目以 SharedArrayBuffer 和 Atomics 創建同步橋接。stdin 回調先以 Atomics.wait() 阻塞 Python Worker;用戶按 Enter 後,主線程把 XTerm.js 收到的 UTF-8 bytes 與長度寫入共享 buffer,再以 Atomics.notify() 喚醒 Worker。等待只發生在 Python Worker,React 界面與終端事件仍可正常運行。
瀏覽器只有在 cross-origin isolation 下才允許使用 SharedArrayBuffer,因此部署端需要同時設置 Cross-Origin-Opener-Policy: same-origin 和 Cross-Origin-Embedder-Policy: require-corp。寫入前也會檢查 UTF-8 byte length,避免輸入超過 buffer 默認容量。
自動完成與錯誤提示
Monaco 只負責顯示和編輯代碼,並不理解 Python 語義。自動完成、屬性檢查和錯誤提示需要 Language Server Protocol(LSP):它是編輯器與語言服務之間的通信協議,讓不同編輯器可以重用同一套分析能力。
Python LSP 中較成熟的是 Pyright。它已有可在瀏覽器中運行的 WASM npm 包,因此項目啟動另一個 browser worker 加載 Pyright 與 typeshed 類型定義,再通過 monaco-languageclient 將它的 LSP 消息轉發給 Monaco。這部分幾乎沒有現成的瀏覽器接入文檔,需要自行處理 Worker 之間的 transport、文件系統和初始化順序。
接入後,編輯器可在輸入 math. 時顯示成員自動完成,也會即時標記不存在的 math.sqrrt 屬性:

完整架構
React 主線程只保留用戶可見的界面:Monaco、XTerm、輸入控制和操作按鈕。Python 運行時與 Pyright 分別在自己的 Worker 運行,前者負責運行與終端 I/O,後者只提供代碼分析。一般指令和輸出走 Comlink/消息通道;只有 input() 需要保留同步語義,才使用 SharedArrayBuffer 與 Atomics。