專案 GitHub:storytold/wordcraft
重點
- 從零寫的 Word 相容文書處理器:約 85,600 行 Rust,拆成 15 個 crate,從幾何、文件模型、字型、排版到 DOCX 讀寫與 PDF 輸出都自己做。
- 一切都是命令:功能區按鈕、快捷鍵、命令搜尋、CLI、控制通道與 MCP 都用同一份命令表,用 id 呼叫同一段程式,AI 代理能做的事和人一樣多。
- 規定不准當機:禁用
unwrap、panic!等寫法,所有外來數字都當成惡意輸入,還有專門測試餵亂數參數給每個命令。 - 大部分程式碼由 AI 撰寫:專案的貢獻者紀錄裡,Claude Opus 模型掛名 45 個 commit 中的 34 個。
- 還是 alpha:專案自估約完成 60%,十月七日才建立,版本號 v0.6.1。
以 2026 年 10 月 13 日從 GitHub API 查到的資料,Wordcraft 在 2026 年 10 月 7 日(台北時間)建立,約 9,214 顆星、2,425 個 fork,最新版本是 10 月 13 日凌晨發布的 v0.6.1。GitHub 判讀的授權是 Apache-2.0,README 則寫明採 MIT 或 Apache-2.0 雙授權。
以下根據 README、AGENTS.md、docs/architecture.md 和幾個原始碼檔案整理。
想解決什麼
開源文書處理器並不少,但要開啟、編輯 Word 檔又不跑版,難度很高。Wordcraft 的目標是自己做出一個和 Word 行為接近的編輯器,介面用 egui(Rust 的即時模式 GUI 函式庫)。它有一個比較少見的出發點:從設計第一天就假設除了人,AI 代理也會來操作這個編輯器。
分層架構
15 個 crate 分成 L0 到 L6 七層,下層不准依賴上層,由 cargo xtask layers 在 CI 檢查。最重要的一條規則是 L6 以下的程式都不能依賴 egui、eframe、winit、rfd 這些介面套件,所以整個編輯核心可以不開視窗執行。
| 層級 | 內容(節選) |
|---|---|
| 底層 | geom 幾何、doc 文件模型、fonts 字型 |
| 中層 | layout 排版、docx/formats 檔案格式、render 點陣繪製、pdf 輸出 |
engine | 編輯工作階段與命令表 |
| 外層 | mcp 伺服器、ui-egui 介面,以及 3 個應用程式 |
一份文件從開啟到儲存的流程:
flowchart LR
open["開啟 DOCX"] --> doc["文件模型"]
doc --> run["Session::run 執行命令"]
run --> layout["增量排版"]
layout --> render["vello_cpu 繪製頁面"]
layout --> pdf["krilla 輸出 PDF"]
run --> save["儲存"]
每個命令都透過 Session::run 執行。執行前先拍一份快照供復原使用,文件的段落區塊以 Arc(共享所有權的指標)共用,所以快照不必整份複製,成本很低。萬一命令裡還是發生 panic,工作階段會攔下來並把文件還原。排版是增量的,只重排受影響的部分;架構文件記載,在一份 188 頁的文件上增量排版約 1.4 毫秒(專案自述,2026 年 10 月 6 日量測,本文未實測)。頁面用 vello_cpu 繪成點陣並逐頁快取,PDF 則由 krilla 產生。
一切都是命令
AGENTS.md 把這條列為核心原則。每個功能都是一筆 CommandSpec,裡面有 id、顯示名稱、在功能區的位置、快捷鍵、參數說明、目前能不能用,以及實際執行的函式。engine 的 cmd/ 目錄裡目前約有 422 筆。
功能區按鈕、快捷鍵、命令搜尋框、CLI、控制通道和 MCP 伺服器,全部是拿 id 去查這份表再執行,沒有哪個入口偷偷走自己的程式路徑。規範也要求透過程式呼叫時絕不彈出對話框,所需的值一律從參數傳入。
一般桌面軟體的做法,是先把功能寫在按鈕的事件處理裡,之後要支援巨集或外掛時再另外開 API,兩邊的功能常常對不上。Wordcraft 把命令表當成唯一入口,介面上有的東西自動就能用程式呼叫。cargo xtask parity 會比對 catalog.rs 裡的功能清單,涵蓋數只能往上,不能倒退。
給 AI 代理用的 MCP 介面
MCP(Model Context Protocol)是讓 AI 模型呼叫外部工具的開放協定。Wordcraft 的 MCP 伺服器提供這些工具:
list_commands、execute、batch:列出命令、執行一個或一批命令;type_text、select_text:輸入與選取文字;inspect_document:以結構化資料回報文件狀態;render_page、save_document:繪出頁面、儲存。
搭配圖形介面執行時還多了截圖、點擊與按鍵。README 特別提到 inspect_document 讓代理不必看截圖也能確認編輯結果,這比讓模型辨識畫面省事也可靠。
不准當機的規範
文書處理器當掉會讓人丟掉工作成果,Wordcraft 把「不當機」寫成硬性規定:
- 程式裡不准出現
unwrap、expect、panic!、unreachable!、todo!、unimplemented!,各 crate 的根部用#![deny(clippy::unwrap_used, ...)]讓編譯檢查擋下來; unsafe_code設為forbid,完全不允許不安全的 Rust 程式碼;- 所有從外部進來的數字都視為惡意:取陣列元素用
get()不用[i]、算術用有溢位檢查的版本、數值先夾在合理範圍、配置記憶體設上限; crates/engine/src/tests.rs裡的hostile_params_never_panic測試會對命令餵各種不合理的參數,確認都不會 panic。
專案共有 821 個 #[test] 與 10 組 property test(以隨機產生的輸入檢查性質是否成立的測試),README 說 cargo xtask ci 會跑約 800 個測試。
排版細節:禁則與繞排
禁則(kinsoku)是中日文排版規定某些標點不能出現在行首或行尾,例如「、。,)」」不放行首,「(「【」不放行尾。kinsoku.rs 在 Unicode 斷行演算法(UAX #14)找出的可斷行位置上再套用這兩份清單。關掉禁則時,東亞文字之間任何地方都可斷行;關掉「自動換行」時,拉丁文字也會依字素任意斷開。
圖文繞排是文字沿著圖片外框排列。contour.rs 依 ECMA-376(Office Open XML 標準)§20.4.2.17 與 §20.4.2.18 實作 wrapThrough 和 wrapTight 兩種模式:
- 每個物件只建一次輪廓,排版時以
occupied(top, bottom)查某段高度內被佔用的水平範圍,沿著邊走過、加上留白、合併重疊的區段; - Tight 只回傳最左到最右的一整段,Through 則保留中間的空隙,讓文字可以穿進圖片內部的空白;
- 非有限值的點直接丟掉,點數有上限
MAX_WRAP_POINTS,留白夾在 0 到 1584 之間; - 輪廓的 id 是點與留白的雜湊,直接當排版快取的鍵。
圖片本身沒有輪廓資料時,會從透明度產生:把圖片縮到一個網格,alpha 值大於等於 128 的格子算實心。邊長上限 8192、記憶體上限 512 MiB,JPEG 沒有透明度就跳過。
怎麼寫出來的
AGENTS.md 裡有一份「自主運作規範」要代理遵守,還有乾淨室規則:只能把 Word 當黑盒子觀察行為、參考 ECMA-376 規格,不得使用 GPL 程式碼。contributors/contributors.json 的紀錄(截至 10 月 9 日、共 45 個 commit)把模型也列為貢獻者:Claude Opus 5.5 掛名 34 個 commit、新增約 61,120 行,Claude Opus 4.8 有 1 個;人類維護者掛名 26 個 commit、約 57,353 行(同一個 commit 可能同時掛名人與模型,所以加總超過 45)。從這些紀錄來看,大部分程式碼由 AI 撰寫,人負責規範與審核。
這也說明了前面那些規範的用意。命令表、分層檢查、不准 unwrap、功能涵蓋數只能上升,都是可以用工具自動檢查的規則,讓大量由模型產生的程式碼仍能維持一致。
限制與成熟度
- alpha 階段:README 說功能區的功能已涵蓋 88%,但專案自估整體只完成約 60%。
- 非常新:建立不到一週,星數成長很快,但 API 與檔案格式支援都還在變動。
- 相容性未經驗證:DOCX 相容程度要看實際文件,本文沒有測試。
- 文件小出入:
proofcrate 在docs/architecture.md列在 L1,AGENTS.md寫 L3。 - 本文沒有實測:以上根據文件與原始碼整理,沒有編譯或執行;效能數字都是專案自述。