[{"content":"第一个 post！博客跑喺 Cloudflare Pages 上面，全球 CDN 直出。\n测试标题 支持 Markdown 代码块： 呢篇系示范文章，之后直接喺 content/posts/ 加 .md 文件再跑 deploy.sh 就得更新。\n1 console.log(\u0026#34;hello again\u0026#34;); ","permalink":"/posts/hello-world/","summary":"\u003cp\u003e第一个 post！博客跑喺 Cloudflare Pages 上面，全球 CDN 直出。\u003c/p\u003e\n\u003ch2 id=\"测试标题\"\u003e测试标题\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e支持 Markdown\u003c/li\u003e\n\u003cli\u003e代码块：\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e呢篇系示范文章，之后直接喺 \u003ccode\u003econtent/posts/\u003c/code\u003e 加 \u003ccode\u003e.md\u003c/code\u003e 文件再跑 \u003ccode\u003edeploy.sh\u003c/code\u003e 就得更新。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econsole.log(\u003cspan style=\"color:#f1fa8c\"\u003e\u0026#34;hello again\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e","title":"Hello World"},{"content":"用 Cloudflare Workers + Durable Objects 寫咗個多人實時電子白板，開個房間、把連結發出去，大家就可以一齊畫。\n源碼開放喺 GitHub：crazysarah/cf-whiteboard，wrangler deploy 一分鐘就開到自己嗰塊板。用法：房間名放喺 hash（https://\u0026lt;你的域\u0026gt;/#房間名），同一個 hash 就係同一塊板。\n點解要自己寫一個 市面嘅白板工具（Miro、Excalidrawlive 嗰啲）要麼要註冊、要麼有牆。自己寫一個：\n零註冊：開連結就畫，房間名即開即用 一個房間 = 一個 Durable Object：DO 係全球單實例強一致，天生就啱做房間狀態 WebSocket 用 Hibernation API：空房間唔佔 CPU 時間，hibernated 狀態免費——唔使驚養個白板燒錢 筆畫歷史存 DO 內置 SQLite：每 3 筆落盤，重新打開仲喺度（上限 5000 筆，舊嘅自動清） 截圖直接貼上牆：方便貼參考圖 一邊傾一邊畫：WebRTC 語音 P2P，DO 只轉發 SDP/ICE 信令，媒體流唔過 CF——零成本 架構一句話 /room/\u0026lt;name\u0026gt; 路由到同名 Durable Object；WebSocket 常駐但 hibernated（唔使 CPU 時間）；歷史入 SQLite；語音走 P2P 信令通道。\n已知取捨 清空係廣播式：任何人按清空，全房清掉（undo 栈留返做後續） 坐標係視口坐標，唔同屏幕比例睇到嘅畫面比例可能唔同（縮放適配後續加） 有興趣可以直接開個房試下，或者攞源碼去 wrangler deploy 一分鐘開埋自己嗰塊板。\n","permalink":"/posts/2026-11-09-cf-whiteboard/","summary":"\u003cp\u003e用 Cloudflare Workers + Durable Objects 寫咗個\u003cstrong\u003e多人實時電子白板\u003c/strong\u003e，開個房間、把連結發出去，大家就可以一齊畫。\u003c/p\u003e\n\u003cp\u003e源碼開放喺 GitHub：\u003ca href=\"https://github.com/crazysarah/cf-whiteboard\"\u003ecrazysarah/cf-whiteboard\u003c/a\u003e，\u003ccode\u003ewrangler deploy\u003c/code\u003e 一分鐘就開到自己嗰塊板。用法：房間名放喺 hash（\u003ccode\u003ehttps://\u0026lt;你的域\u0026gt;/#房間名\u003c/code\u003e），同一個 hash 就係同一塊板。\u003c/p\u003e\n\u003ch2 id=\"點解要自己寫一個\"\u003e點解要自己寫一個\u003c/h2\u003e\n\u003cp\u003e市面嘅白板工具（Miro、Excalidrawlive 嗰啲）要麼要註冊、要麼有牆。自己寫一個：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e零註冊\u003c/strong\u003e：開連結就畫，房間名即開即用\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e一個房間 = 一個 Durable Object\u003c/strong\u003e：DO 係全球單實例強一致，天生就啱做房間狀態\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWebSocket 用 Hibernation API\u003c/strong\u003e：空房間唔佔 CPU 時間，hibernated 狀態免費——唔使驚養個白板燒錢\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e筆畫歷史存 DO 內置 SQLite\u003c/strong\u003e：每 3 筆落盤，重新打開仲喺度（上限 5000 筆，舊嘅自動清）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e截圖直接貼上牆\u003c/strong\u003e：方便貼參考圖\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e一邊傾一邊畫\u003c/strong\u003e：WebRTC 語音 P2P，DO 只轉發 SDP/ICE 信令，媒體流唔過 CF——\u003cstrong\u003e零成本\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"架構一句話\"\u003e架構一句話\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e/room/\u0026lt;name\u0026gt;\u003c/code\u003e 路由到同名 Durable Object；WebSocket 常駐但 hibernated（唔使 CPU 時間）；歷史入 SQLite；語音走 P2P 信令通道。\u003c/p\u003e\n\u003ch2 id=\"已知取捨\"\u003e已知取捨\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e清空係廣播式：任何人按清空，全房清掉（undo 栈留返做後續）\u003c/li\u003e\n\u003cli\u003e坐標係視口坐標，唔同屏幕比例睇到嘅畫面比例可能唔同（縮放適配後續加）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e有興趣可以直接開個房試下，或者攞源碼去 \u003ccode\u003ewrangler deploy\u003c/code\u003e 一分鐘開埋自己嗰塊板。\u003c/p\u003e","title":"整咗個 Cloudflare 多人電子白板"}]