Pull to refresh

第一次寫 Omarchy plugin 就上手:用 oma.spectra 當例子,從 clone 內建時鐘到上架 marketplace

我把 Spectra 做成 Omarchy 的 bar 外掛 oma.spectra,從第一個 commit 到可以送 marketplace 的 v0.3.2 是同一個下午的兩個半小時,整包 41 個 commit。這篇把過程寫成別人也走得通的路:Omarchy plugin 有六種 kind、每種對應一個 entryPoints 鍵與一個固定檔名,官方建議的起手式不是從空資料夾開始而是 omarchy plugin clone omarchy.clock --edit 把內建時鐘複製成自己的,因為時鐘就是「一個 bar 圖示加一個面板」這個最常見的形狀。manifest.json 只有六個必填欄位,但 barWidget 區塊裡的 schema 陣列才是讓使用者能在 shell 設定裡改參數的關鍵。驗證分兩層:omarchy plugin validate 看 manifest 與資料夾結構,qmllint -I $OMARCHY_PATH/shell 看 QML 對不對得上已安裝的 shell imports,而且 validate 不吃 symlink,要指到 ~/.config/omarchy/plugins 底下的真實路徑。上架只有三步:公開 repo、根目錄放合格 manifest、開 issue 表單送審,自動檢查會綁定一個 commit 快照。後半是 oma.spectra 本身:面板讀 Spectra CLI 的 list 與 status JSON、渲染 proposal/design/specs/tasks、可改 .spectra.yaml 五個鍵,其餘一律唯讀,new/apply/archive/park 留在終端機。踩到的坑有四個是有紀錄的:mise/nvm 裝的 spxa 只在互動 shell 的 PATH 上、登入 profile 印任何東西都會污染 CLI 回來的 JSON、validator 拒絕 symlink、Markdown 裡的圖片與角括號要先消毒再丟給 Qt 的渲染器。

| 3,265 words | 9 min read | 5 views |
Text size
Line height

oma.spectra 是我寫的第一個 Omarchy 外掛,現在掛在官方 marketplace 上,id 就是 oma.spectra,MIT,分類 DEVELOPER TOOLS。它做的事很單純:在 bar 上放一個圖示,點開是一個面板,把這台機器上所有 Spectra 專案、每個專案開著的 change、每個 change 的 proposal / design / specs / tasks 都攤在同一個畫面裡。

這篇不是使用說明,是把「第一次寫 Omarchy plugin」的路線寫下來,讓下一個人不用重新摸。內容來自官方的 Develop / Publish 兩份文件、losehrt/oma.spectra 這個公開 repo 的 README 與 commit 紀錄,以及 marketplace 的上架頁。

2026-09-17-oma-spectra-panel.jpg

一、先看時間軸:兩個半小時到可上架

整包 41 個 commit。以下是台北時間:

時間 版本 做到哪
2026-09-13 16:37 第一個 commit
2026-09-13 16:53 v0.1.0 補 author、LICENSE、安裝說明,準備公開
2026-09-13 17:28 v0.2.0 支援多個專案根目錄,把 Spectra 自己的工作檔排除在 repo 外
2026-09-13 18:00 v0.3.0 面板內可直接編輯專案根目錄,含一層資料夾瀏覽器
2026-09-13 18:48 v0.3.1 CLI 改走登入 shell,hero 顯示 plugin id 與 manifest 版本
2026-09-13 19:12 v0.3.2 補 Uninstall 與 Dependencies 兩節、加 preview.png,對齊上架要求
2026-09-13 19:41 README 開頭放桌面實拍截圖
2026-09-14 08:11 v0.3.3 純粹為了讓 marketplace 驗證的 commit 有對應的 tag
2026-09-16 21:32 v0.3.4 渲染前先消毒 artifact 的 Markdown

從第一個 commit 到「這包可以送審了」是 16:37 到 19:12,兩小時三十五分。這個速度不是因為 QML 好寫,是因為起手式不是空資料夾。

二、六種 kind,先挑對形狀

Omarchy plugin 的第一個決定是 kinds。文件列了六種,每種對應一個 entryPoints 的鍵和一個約定檔名:

kind entryPoints 鍵 載入的檔 用途
bar-widget barWidget BarWidget.qml bar 上的一個項目
panel panel Panel.qml 浮動面板
overlay overlay Overlay.qml 全螢幕覆蓋層
menu menu Menu.qml 被叫出來的選單
service service Service.qml 無畫面的常駐單例
bar bar Bar.qml 整條 bar 換掉

容易搞混的是 bar-widgetpanel。文件講得很清楚:如果面板是這個 bar 圖示點開的東西,那它就是 bar-widget 的一部分,kinds 只寫 ["bar-widget"],entry point 指向 bar 的那支 QML,面板由它內部用 Loader 載進來,不要為了這個面板再多宣告一個 panel kind。

oma.spectra 走的是這條,但把 entry point 直接命名成 Panel.qml

"kinds": ["bar-widget"],
"entryPoints": { "barWidget": "Panel.qml" }

檔名不是強制的,entryPoints 寫什麼就載什麼,只要大小寫跟磁碟上一致。

三、起手式:clone 內建時鐘,不要從零開始

官方 Develop 文件的第一步不是「建一個資料夾」,是:

omarchy plugin clone omarchy.clock --edit

理由很實際:內建時鐘就是「一個 bar 圖示加一個詳細面板」這個最常見的形狀,clone 出來的是一份可以跑的完整骨架。指令跑完會印出新的 plugin id、建好資料夾、用你設定的編輯器打開它,而且它會直接接手 bar 上原本時鐘的位置,所以你改一行存檔就看得到結果。

~/.config/omarchy/plugins/yourname.clock/
├── manifest.json
├── BarWidget.qml
├── Panel.qml
└── Model.js

開發期間就用它印出來的那個 id,不要急著改名。存檔會自動重載;只有在它沒發現檔案變動時才需要手動掃一次:

omarchy-shell shell rescanPlugins

正式的命名空間 id(例如 oma.spectra)留到要公開前才換,同時把 clone 產生的 omarchy.clonedFrom 欄位刪掉。那個欄位在開發期間要留著,因為停用或移除 clone 時它負責把內建時鐘還原回去。

第三方 id 不能用 omarchy.* 開頭,這是 validator 會擋的硬規則。

四、manifest.json:必填六欄,真正有用的是 schema

上架文件給的最小 manifest 只有六個必填欄位:schemaVersionidnameversionkindsentryPointsauthordescriptionlicense 實務上一定要寫)。

但如果你的外掛需要使用者設定參數,關鍵在 barWidget 區塊裡的 schema 陣列。oma.spectra 只有一個設定,長這樣:

"barWidget": {
  "displayName": "Spectra",
  "category": "AI",
  "aliases": ["spectra", "spxa"],
  "allowMultiple": false,
  "defaultSection": "right",
  "defaults": { "projectsRoot": "~/projects" },
  "schema": [
    {
      "key": "projectsRoot",
      "type": "string",
      "label": "Projects folders",
      "defaultValue": "~/projects",
      "description": "One or more folders separated by ':'; their direct subfolders containing a .spectra.yaml are listed."
    }
  ]
}

宣告了 schema,這個設定就會出現在 shell 的 widget 設定介面裡,值寫進 ~/.config/omarchy/shell.json 的這個 widget 條目。使用者也可以直接在 bar 版面設定裡內嵌:

{ "id": "oma.spectra", "projectsRoot": "~/code:~/work" }

另外三個欄位值得記:activationon-demand(面板關著就什麼都不跑)、defaultSection 決定圖示預設落在 bar 的哪一段、allowMultiple 設 false 表示同一個 bar 不能放兩個。

PLUGIN_ID="yourname.clock"
PLUGIN_DIR="$HOME/.config/omarchy/plugins/$PLUGIN_ID"
omarchy plugin validate "$PLUGIN_DIR"
qmllint -I "$OMARCHY_PATH/shell" "$PLUGIN_DIR/BarWidget.qml" "$PLUGIN_DIR/Panel.qml"

omarchy plugin validate 檢查四件事:manifest 是合法 JSON 且六個必填欄位都在、kind 與 entryPoints 對得上、entryPoints 指到的檔案真的存在而且是安全的相對路徑、id 沒有用保留的 omarchy.* 前綴且資料夾裡沒有 symlink。

qmllint 檢查的是另一件事:你的 QML 對不對得上已安裝的那套 shell imports。兩個都要跑,過不了的話錯誤訊息是可以直接動手的,例如 entry point file not found: 'BarWidget.qml'

這裡有一個實際踩到的坑,README 也特別寫了一句:validate 跟其他 omarchy plugin 指令要指在 ~/.config/omarchy/plugins/<id> 這個真實路徑上,validator 拒絕從 symlink 出發。如果你跟我一樣習慣把 plugin 原始碼放在 ~/projects 底下再 symlink 過去,這條會直接擋住你。

裝好之後確認 shell 真的看得到它:

omarchy plugin list --json | jq --arg id "$PLUGIN_ID" '.[] | select(.id == $id)'

面板生命週期可以直接從指令列打:

omarchy-shell shell summon "$PLUGIN_ID" '{}'
omarchy-shell shell hide "$PLUGIN_ID"

送出去之前,文件要求測過這七種情況:點擊、Esc、shell 開、shell 關、停用、重新啟用、shell 重啟、移除。

六、上架只有三步

  1. 公開的 GitHub repo,根目錄放合格的 manifest.json,README 與 license 都要有,安裝與移除都要是安全的。preview.png 選配,上傳後會自動壓縮。
  2. manifest 先在本機 validate 過
  3. 開 issue 表單送審:填 repo 連結、分類、標籤。自動檢查會跑在當下那個 commit 上,然後由維護者核准上架。

marketplace 頁面上會顯示驗證狀態,oma.spectra 目前是:Compatibility PASSED、Verified snapshot dc30eaa(2026-09-17)、Branch main、Upstream changes 無。

要注意驗證的語意:marketplace 驗證的是那一個快照 commit,不是你的 HEAD。而安裝指令 omarchy plugin add <repo>.git --enable clone 的是 repo 當下的 HEAD,兩者可能不同。頁面上的 Security Notice 就是在講這件事,順帶提醒第三方 plugin 是不沙箱、以你的使用者權限執行的。這一點在官方文件裡重複了三次,寫自己的 plugin 時值得放在心上。

所以 v0.3.3 那個「純粹為了讓驗證的 commit 有對應 tag」的 bump 不是形式主義,它是讓「marketplace 驗過的東西」和「使用者裝到的東西」對得起來的唯一辦法。

七、oma.spectra 實際上在做什麼

載入鏈是四層:

flowchart TD
    A["manifest.json<br/>entryPoints.barWidget"] --> B["Panel.qml<br/>bar 按鈕、彈出面板、所有渲染"]
    B --> C["Projects.qml<br/>掃描 projectsRoot"]
    C --> D["Project.qml<br/>單一專案的 .spectra.yaml、list、status、specs"]
    D --> E["bash -l 執行 &lt;cli&gt;<br/>spectra / specx / spxa"]
    E --> F["JSON 回傳<br/>change 清單與進度"]
    B --> G["Spectra.js<br/>純函式:設定解析、JSON 形狀、CLI 白名單"]
    G -.被測試.-> H["Spectra.test.js"]

面板上有的東西:hero(logo 加目前專案名)、專案 chip、change 列(名稱、completed/total、進度條、狀態)、parked 的 change 以淡色排在後面、ARCHIVED (N) 折疊區、artifact 分頁(proposal / design / specs / tasks,CLI 沒回報 done 的那個分頁是淡的)、內容區用 Qt 的 Markdown 引擎渲染、SPECS (N) 折疊區、SETTINGS 折疊區。

鍵盤游標會依視覺順序走過每一個可點的項目,j/k 上下、h/l 左右、Enter 啟用、Tab 切分頁、r 重掃、Esc 逐層關閉。IPC 也開得很滿,omarchy-shell oma.spectra <fn> 支援 show / hide / toggle / refresh / next / project / select / tab / spec / terminal / cursor / setSetting / update / state 等等,state 會回一包 JSON 描述現在什麼開著、選到哪、游標在哪。

滑鼠三鍵各有分工:左鍵開關面板、中鍵切下一個專案、右鍵在選到的專案根目錄開一個終端機然後把面板關掉。

八、邊界寫在 README 裡,而不是留給使用者猜

這是我覺得寫 plugin 最該學起來的一段。README 有一節叫 Non-goals,另有一段明講「這個 plugin 會往磁碟寫什麼,除此之外什麼都不寫」:

  • 只會改專案 .spectra.yaml 裡的五個鍵:localetddauditexperiencecli_command,而且只在你於面板上改它的時候。
  • 只會執行 <cli> update 去重新產生該專案 .claude/ 底下的檔案,而且要你按按鈕。
  • 其餘全部唯讀,畫面上的東西都是從檔案和 <cli> list / <cli> status 讀來的。
  • newapplyarchiveparktask 一律留在 Claude Code 跟終端機裡,面板不做。

不做的還有:改 spec_dirtools、程式碼區塊的語法高亮、Mermaid 圖渲染、檔案變動的即時監看、巢狀專案探索(只掃每個根目錄的直接子目錄)、封存 change 的任務進度條。

移除也寫清楚了。omarchy plugin remove oma.spectra 會刪掉 plugin 資料夾跟 bar 上的圖示,但有兩樣它不碰,因為那是你自己手動加的:~/.config/hypr/bindings.lua 裡那行快捷鍵綁定,和 bar 在你設定專案資料夾時寫進 ~/.config/omarchy/shell.json 的那個條目。

一個外掛在別人機器上跑,講清楚它碰什麼、不碰什麼、移除後留下什麼,比多做一個功能重要。

九、有紀錄的四個坑

一、版本管理器裝的 CLI 在 shell 裡找不到。 面板要執行專案指定的 cli_command,但 Quickshell 這個長時間跑的程序拿到的 PATH 是登入階段的 PATH,不是你 .bashrc 互動區段裡 mise / nvm / asdf 加上去的那份。修法是把 CLI 改成透過 bash -l 執行(v0.3.1 那個 commit)。使用者端的檢查指令:

bash -lc 'command -v spxa'

沒印東西就代表那個 PATH 只存在於互動 shell。解法是把工具啟用搬到 ~/.profile,或用 ~/.config/environment.d/mise.conf 塞進 session PATH 再重新登入。

二、登入 profile 印任何東西都會弄壞 JSON。 因為面板是把 CLI 的 stdout 當 JSON 解析的,登入 profile 印的歡迎訊息會混進去。README 直接寫了一句「Keep the login profile quiet」。這個坑很難自己想到,因為在終端機裡手動跑同一個指令是看不出問題的。

三、validator 不吃 symlink,前面第五節講過。

四、外部 Markdown 要先消毒再丟給渲染器。 最後一個 commit(v0.3.4)做的就是這件事:把 artifact 裡的圖片轉成連結、把 < 轉成 &lt;,再交給 Qt 的 Markdown 引擎。面板渲染的是專案裡的檔案,那些檔案的內容不受 plugin 控制。

十、這篇沒有的東西

  • 沒有 QML 程式碼的逐行解說。 Panel.qml 有 75 KB,這篇只講它在架構上的位置。
  • 沒有效能數字。 沒量過面板開啟時間、掃描大量專案的耗時、記憶體佔用。
  • 沒有多機器驗證。 以上是單一 Omarchy Quattro 環境的經驗。
  • 上架審核的實際等待時間沒記錄。 只知道送出後由維護者核准。
  • marketplace 目前的數字是 21 次瀏覽、0 次指令複製(查詢時間 2026-09-17)。也就是說,還沒有可觀察到的第三方安裝。

十一、為什麼這件事值得花一個下午

三個理由。

第一,Spectra 的紀律本來就要求一件事先 discuss 再 propose 再 apply 再 archive,這條流程的狀態原本散在終端機的滾動輸出裡。把它變成 bar 上一個隨時可開的面板,等於把「我現在在哪一個 change 的哪一步」這個問題從記憶搬到畫面上。

第二,這是一次完整的 Omarchy 外掛端到端演練:manifest 契約、QML 元件、validate、qmllint、IPC、marketplace 上架流程全部走過一遍。我之前寫的 Omarchy 醫院工作站架構草案裡,第一層的「院內殼」就是要靠 Quickshell plugin 去做的,那一層要成立的前提是我自己得先能寫得出一個能上架、能被別人裝、能被驗證的 plugin。現在這個前提成立了。

第三,Spectra.test.js 那支測試檔存在本身就是重點。純函式的部分(設定解析、JSON 形狀、CLI 白名單)抽成 Spectra.js 再單獨測,讓「錯誤降級」那條路徑也能被單元測試蓋到。一個個人下午專案帶測試,看起來過頭,但那正是讓兩個半小時之內連發五個版本還不翻車的原因。

相關頁面

出處

tech Public omarchy omarchy-plugin plugin marketplace quickshell quattro qml hyprland wayland bar-widget panel overlay menu service manifest entrypoints schemaversion ipc omarchy-shell qmllint plugin-validate spectra spxa sdd spec-driven-development cli json login-shell path mise nvm asdf symlink markdown渲染 消毒 sanitize 開源 mit github 上架流程 外掛開發 桌面殼 開發環境 積木化 可交接 可外推 dhh 802 kafgh