framejs
framejs 在瀏覽器中執行 ES6 JavaScript 模組。一個 frame 就是一個應用程式:它位於一個穩定、可分享的 framejs.app/j/<uuid> URL,其內容是可變的 — 你可以透過 POST 其雜湊參數來原地更新應用程式。你的任務是:將使用者的請求轉換成該瀏覽器 JavaScript,並以適合你環境的正確方式交付。
選擇你的交付模式
根據你能做什麼來選擇 — 而不是根據請求:
- 自動化模式 — 你可以執行 shell /
node: 生成 JavaScript,然後建立或更新該 session 的 framejs.app frame,印出它的/j/<uuid>URL,並在瀏覽器中打開。每當有 shell 可用時,這是預設模式。 - 程式碼區塊模式 — 僅限聊天 / API,沒有 shell: 僅回覆一個被圍欄的 JavaScript 程式碼區塊(使用
javascript資訊字串開啟),沒有其他內容 — 無前後文,無檔案,無 URL。使用者將其貼到 framejs.io 的編輯器中。
兩種模式中你撰寫的 JavaScript 遵循相同的規則 — 請閱讀 references/coding-guide.md。
請求的類型
- 從提示詞建立 — 「一個彈跳球動畫」、「繪製 y = sin(x)」。
- 修改現有應用程式 — 請求包含一個 frame URL。它可能是
https://framejs.app/j/<uuid>或者 一個https://framejs.io/j/<uuid>?token=…URL(「複製 frame 供 AI 使用」動作會複製 framejs.io 執行時期表單)、一個裸的 32 字元十六進位 ID,或是舊式的https://framejs.io/j/<sha256>/ 64 字元 ID 快照。每當你看到一個 framejs URL,從其/j/<uuid>路徑中提取 uuid,並鎖定該 frame(將 URL 直接傳給--id— 即使對於 framejs.io URL,輔助工具也會將更新路由到應用層)。你必須先擷取現有程式碼再修改 — 請見 references/short-url-api.md(§ 修改)。如果該 URL 帶有來自應用程式「複製 frame 供 AI 使用」動作的?token=<key>查詢參數,請將整個 URL 傳給--id(或將 key 傳給--token):輔助工具會儲存它,並將其作為 bearer 憑證發送,這樣即使在 frame 的擁有者已聲明它之後,你的更新仍能繼續運作。傳遞完整 URL 也會鎖定該 URL 命名的後端 — 因此一個開發或自架 frame URL(例如https://framejs-app.localhost:13747/j/<uuid>?token=<key>)會更新其自己的堆疊,無需預先配置任何環境變數。使用--app-origin <url>/--io-origin <url>明確設定後端。請見 references/short-url-api.md(§ 本機/開發來源)。 - 將本機檔案視覺化 — 請求引用了檔案路徑(
./data.csv、/tmp/results.json)。上傳它們並作為輸入傳遞 — 請見 references/file-inputs.md。
自動化模式 — 如何交付
生成程式碼,然後使用捆綁的輔助工具(首選)或 references/short-url-api.md 中的 inline-node 備用方案:
cat << 'JSCODE' | node scripts/framejs.mjs create --state "${SCRATCHPAD}/framejs-frame.json" --title "<短標題>" --description "<一句話摘要>" --screenshot
// 你生成的瀏覽器 JS 在這裡 — $vars、反引號、所有特殊字元在 heredoc 內部都是安全的
JSCODE
輔助工具會在 stdout 上印出兩個 URL — 主要的 https://framejs.app/j/<uuid> 頁面 URL,以及一個不可變的 snapshot: https://framejs.io/j/<sha256> URL。它還會在 stderr 上印出一個你必須轉達給使用者的分享連結生命週期通知:
/j/<uuid>頁面 是可編輯且即時更新的,但一個匿名(未聲明)的 frame 是暫時的,將會過期 — 告訴使用者打開它並聲明它(免費帳戶)以永久保留;/j/<sha256>快照 是目前應用程式的不可變副本,在最後一次被打開後約 30 天過期 — 這是一個穩定的分享/備份連結,但它永遠不會反映後續的編輯。
對於經典 script 加上 --module <url>,對於輸入加上 --input name=value。
每個 session 一個 frame,原地更新。 總是傳遞 --state "<你的 scratchpad 路徑>/framejs-frame.json"(任何可寫入的檔案路徑)。第一次 create 會鑄造 frame;使用相同的 --state 重新執行 create 會更新同一個 frame — /j/<uuid> URL 保持不變,任何已打開的 framejs.app 頁面都會即時更新,所以只需給使用者一次 URL。僅當使用者想在同一個 session 中有一個分開的應用程式時,才加上 --new 來啟動一個新的 frame(後續的更新將鎖定這個新 frame)。要更新一個特定的 frame(不論 state 為何),請傳遞 --id <uuid>。在這種原地更新中,當你沒有傳遞 --og/--title/--description 時,輔助工具會自動保留 frame 現有的 Open Graph 資料,因此一個裸的重新執行永遠不會丟失標題/描述(而保留的 og.image 則跳過多餘的重新截圖)。僅當你想變更預覽文案時才再次傳遞 --title/--description。
瀏覽器僅在 frame 首次被鑄造時自動打開(加上 --no-open 可連這也跳過)。後續對同一個 frame 的更新 — 相同的 --state,或明確的 --id — 不會重新打開瀏覽器:已打開的頁面透過同一個 frame 的訂閱即時獲取更新,再次打開只會產生一個多餘的新分頁。
如果存在本機開發 checkout,輔助工具會自動載入其 .env 並鎖定開發堆疊(FRAMEJS_APP_ORIGIN / FRAMEJS_IO_ORIGIN)而非生產環境 — 你無需採取任何行動。
如果輔助工具印出了一個 out of date 更新通知(在 stderr 上,最多每小時一次),請逐字轉達給使用者一次 — 他們安裝的技能已落後於最新版本,通知中會告訴他們更新所需的命令。
總是傳遞 --screenshot:輔助工具會渲染完成的應用程式,並將擷取畫面儲存為 og:image 預覽。它具有自我保護機制 — 僅在應用程式還沒有 og.image 時才擷取,因此絕不會覆蓋先前執行(或使用者)已設定的圖片,並且如果沒有可用的渲染器,它會靜默地回退到不含圖片的 URL。擷取偏好使用 Playwright(當可匯入時 — 真正的 network-idle 等待 — 最適合會擷取輸入的應用程式),否則使用系統無頭 Chrome。Playwright 是可選的:安裝它(npm i -g playwright && npx playwright install chromium)以獲得更可靠的路徑,或將 $FRAMEJS_PLAYWRIGHT 指向一個其 node_modules 中有它的目錄。使用 --screenshot-wait <ms>(預設 6000 — 對於載入緩慢的應用程式請調高)和 --screenshot-size <w,h>(預設 1200,630)進行調整。使用 $CHROME_PATH 覆蓋 Chrome 二進位檔。
scripts/framejs.mjs 是相對於此技能的目錄解析的,而非你當前的 working directory — 從技能資料夾執行它,或使用其絕對路徑(Claude Code 將該目錄暴露為 ${CLAUDE_SKILL_DIR},因此 ${CLAUDE_SKILL_DIR}/scripts/framejs.mjs 永遠有效)。如果你無法找到或執行捆綁的輔助工具,請使用 references/short-url-api.md 中的 inline-node 備用方案 — 它不需要任何 script 檔案。
總是包含 Open Graph 預覽標籤,以便連結在分享時能漂亮地展開 — 請見 references/short-url-api.md 中的 OG 規則:
- 新應用程式: 使用
--title/--description衍生新的文案,並傳遞--screenshot來擷取預覽圖片。 - 修改現有應用程式: 擷取的應用程式已帶有
og(fetch命令會回傳它)。不要重新計算它 — 透過--og '<the fetched og JSON>'直接將擷取的物件傳回,這會保留所有欄位(包括image)。使用--id <uuid>(或相同的--state)更新同一個 frame。僅當使用者明確要求變更預覽文案時才設定新的--title/--description。你仍然可以傳遞--screenshot:如果擷取的og已有image,則保持不變;如果沒有,則會加入新的擷取畫面。
絕對規則(兩種模式)
- 僅限瀏覽器 JavaScript — 它在 iframe 中執行,不是 Node.js。
- 必須使用 ES6 模組語法:
export function onInputs(inputs) {}。 - 絕不建立 HTML 檔案。絕不寫入本機
.js檔案。絕不使用你自己的視覺化/渲染/元件工具來渲染結果。 - 絕不修改
root.style.position、root.style.height或root.style.width。 - 重要:視覺化效果必須在行動裝置上看起來良好,並適應其螢幕尺寸 — 使用響應式尺寸(讀取
root的尺寸 / 監聽 resize 事件),避免固定的像素寬度導致溢出,確保小螢幕上的文字和觸控目標清晰可讀。 - 在自動化模式中,絕不輸出供使用者複製的程式碼區塊,也絕不手動建立長雜湊 URL 作為交付物 — 總是透過 frame API 來 POST(使用輔助工具的
create)。給使用者/j/<uuid>頁面 URL。 - 在程式碼區塊模式中,僅輸出單一被圍欄的 JavaScript 程式碼區塊 — 無其他內容。
參考資料
- references/coding-guide.md — 全域變數、匯出、模式、CDN 函式庫、常見錯誤。
- references/short-url-api.md — 建立/更新一個 frame(
POST /j/<uuid>.json)、每個 session 一個 frame 的狀態、Open Graph 標籤、API token(在 frame 被聲明後仍可持續更新)、inline 備用方案。 - references/file-inputs.md — 上傳本機檔案並將其作為輸入接入。
scripts/framejs.mjs— Node 輔助工具:create(stdin JS → framejs.app frame)、fetch <id>、upload <path>。來源:FRAMEJS_APP_ORIGIN/FRAMEJS_IO_ORIGIN(在本機開發中會從附近的.env自動載入),或透過--app-origin/--io-origin或傳遞給--id/fetch的完整 frame URL 的來源進行每次執行時指定。