「真透明做不到」這句結論作廢那天,要一段一段挖回來

Larch 插件「格莉奇-AI對話手機」9 月 4 日晚上從 v0.3.2 推到 v0.3.4。官方開了插件透明,自己下過的「做不到」散在好幾個地方要挖回來,還連帶掀出兩個只有透明才看得見的毛病。

格莉奇傳來的通知橫幅浮在深夜辦公室的場景上,橫幅底下就是場景本身,沒有壓一層黑

9 月 3 日凌晨,插件的設定提示裡多了一句結論:

播放器把整層插件畫成不透明深色,真透明做不到。

那句話是量過才寫的。Larch 播放器的 fullscreen 插件層,iframe 跟外框當時都是不透明的 rgb(10,11,14),插件蓋上去就是一片深色。想做出「手機浮在遊戲場景上」的樣子,只剩一招:把插件自己的背景圖設成跟場景同一張,肉眼看過去就接起來了。還配了一顆旋鈕,讓作者自己調那張假背景要壓多暗。

隔天 9 月 4 日晚上再量,插件透明已經開了。官方什麼時候開的,記錄裡查不到。

先交代這支插件。它叫「格莉奇-AI對話手機」,掛在 Larch 視覺小說平台上,給在上面做遊戲的作者用。劇情走到手機卡,畫面出現一支手機,玩家用通訊軟體那種介面跟劇中角色傳訊息,回覆由 AI 即時生成。另一張「收到訊息」卡不開手機:通知橫幅滑下來,把作者寫好的訊息塞進對話串,停幾秒自動繼續,之後打開手機,訊息就躺在裡面。整段對話存在專案變數裡,下一張手機卡接得下去,也一起餵給 AI。預設示範角色是格莉奇。

插件的程式和這晚的程式修改都是 AI 做的。下面的「我」,是在編輯器裡試播、回報問題、在後台改名、決定授權卡留不留、按下送審的那個人。

一句結論作廢,要一處一處挖

先確認透明是真的開了。新版播放器四個建 iframe 的地方都加上了 background:transparent。到市集正式版上再量一次:iframe 是 rgba(0,0,0,0),整個換掉;外框 vn2-configurable-frame 是 rgba(10,11,14,0),顏色沒變,alpha 變成 0。另外 pluginPresentation:"fullscreen" 的外框透明度預設本來就是 0,邊線在 opacity 0 的時候也跟著透明。壓暗設 0、背景圖跟背景色留空,遊戲場景就透出來了。

麻煩在後面。那句「做不到」不只住在一個地方,它散在插件定義的中英設定提示、卡片 HTML 的註解、README 的已知限制,還有給平台的需求清單裡。

設定提示那部分數得出來,總共十段。結論字面上只出現在兩段,另外八段是順著它寫的 workaround 說明。當晚第一筆修正改掉八段:手機卡壓暗、背景圖、背景色三個欄位的中英兩版,加上「收到訊息」卡壓暗與背景圖的中文版。那兩段英文版留在原地,快一個半小時後才被補掃到。也就是說,第一輪就漏了兩段。

透明開了,灰底才現形

透明開了以後,有個以前看不到的毛病跑出來了。

v0.3.2 推上去之後,我在編輯器按播放。背景圖明明拿掉了,卡片還是先灰一下,才跳出通知。

我當時回報的猜測是:「好像是因為我們的背景圖 拿掉以後 後面又有我們自己疊的背景色 應該我們自己疊的背景色要拿掉」。AI 回「你診斷對了」,問題在兩支卡的 CSS。它們各自在 body 上寫死了一層深色紗,「收到訊息」35%、手機 55%,頁面一載入就畫上去。真正的壓暗值要等 larch:init 把設定送到,才由 JS 覆寫。中間那一小段時間,畫面上就是那層寫死的紗。以前插件層本來就不透明,這一閃看不出來,官方把透明開了它才現形。

改法是 body 預設 background:transparent,壓暗一律等 init 到了再補,加 0.3 秒過渡,讓有壓暗的卡片不會硬跳。

驗收用負控制:把卡片放在純綠底 #12b76a 上,模擬 init 永遠送不到的最壞情況。舊版量到 RGB (14,123,75),綠色被灰紗蓋掉了。新版量到 (18,183,106),正好等於那片純綠,代表完全透明。

壓暗的預設值 55 沒有跟著改。照理說透明開了,預設該改成 0,新卡片拉出來就是透明的,最合直覺。可是沒存過 dim 的既有卡片,播放時吃的就是插件定義裡的預設值。改預設,等於改到其他作者已經發佈出去的作品:他們什麼都沒動,畫面卻變了。所以新的說明文字寫清楚「設 0 就透明」,剩下讓作者自己決定。

黑底的真因不在卡片這邊

我回報卡片還是黑的。

背景圖拿掉了,壓暗歸零了,播起來還是一整片黑。第一個懷疑的是自家 CSS 沒清乾淨,結果實測出來的兩件事都不在卡片這一側。

第一,編輯器的色彩選擇器沒有「空」這個狀態。卡片設定只要被打開過一次,原本沒設定的 bgColor 就被寫成 #000000,卡片照自己的規則,老實鋪一整片黑。讀 Larch 的板子確認過,DEMO 的 n2 存的就是 #000000。

第二件才是真正卡住的:Larch 存 pluginValues 的時候會把空字串丟掉。送 "" 進去想清掉那個黑,讀回來鍵直接消失,舊值原封不動。送 "none" 才寫得進去。

所以繞開色票。bgColor 從 kind:"color" 改成 kind:"text",標籤直接寫成「背景色(留空=透明)」。兩支卡把 none、transparent、無、- 一律當成沒有顏色,bgImage 也一樣。DEMO 三張卡改送 none,讀回來驗證,三張都是 none。

「色票沒辦法表示不要顏色」這件事,連英文提示都寫進去了。將來大概會有人問,為什麼別人家都是色票,這一格是文字欄。

兩個 id 才叫授權

同一天官方還加了一道安全性檢查,把插件的第二種 AI 模式整條弄斷了。

插件有兩個 AI 上游。一個是作者請客的中繼,金鑰放在 Worker 的 secret 裡。另一個是「用我的 Larch 點數」:作者拿自己的平台點數餵 AI,一回合一點、不用金鑰,靠市集發佈 id 當通行證。

9 月 4 日起,官方 AI 只服務「發佈快照裡真的有一張 AI 對話卡」的作品。中繼送過去的 cardId 一直寫死成假值 "phone-chat",所以第二條路整條斷了。

錯誤訊息幫不上忙。拿快照裡真實存在的 n1、n5,跟亂打的 id 去試,三種都回同一句:

這個公開作品沒有授權這項 AI 操作

它不會說缺的是哪一樣。

v0.3.3 加了一個設定欄位「官方 AI 卡 id」。做法是在版子上放一張官方的「AI 對話」卡當授權憑證,不接任何一條邊,玩家永遠走不到,再把那張卡的 id 填進去。中繼那邊拿掉 "phone-chat" 這個預設值,缺 cardId 就直接回 400 講清楚。所以這個模式要填兩個 id。

填之前還要確認一件事:發佈會不會改寫卡片 id。如果會,n9 就得等發佈完再回頭查真的 id。讀市集快照端點比對過,發佈後 project.nodes 與 project.boards[].nodes 的 id 跟專案端一模一樣,n1 到 n8 全對得上。所以先填、再發佈是安全的。

當晚最後一筆 commit 只寫了一條提醒:那張 n9 授權卡一旦躺在已發佈的快照裡,授權就是對外開放的。市集發佈 id 跟卡片 id 都撈得到,實測也確認繞得過中繼,扣的都是作者的點數,跟自家卡片有沒有走到第二種模式無關。怎麼繞的細節不寫在這裡。

這張卡留還是拿掉,是我自己的決定。我的額度目前免費無限到 2027 年,所以現在留著。到期那天要做的事已經寫進 repo 的 NEXT:把 n9 拿掉重新發佈,等於撤銷授權。這種「現在沒事、到期才會出事」的東西,當場寫進待辦才有人接得住。

送審那件小事

我在後台把插件名稱改成「格莉奇-AI對話手機」。可是產生插件定義的 gen_plugin.py 裡還寫著舊的「格莉奇手機」,再送審一次,就會把後台的名字蓋回去。

用公開的 GET /api/plugins 對過線上那一筆,除了版號跟這個名字,nameEn、作者、授權、定價、中英描述全部一致,所以只有這一欄要改。這個對照法也寫進 README 了,下次不用重想。

當晚十點十二分,我把 0.3.4 送出去審。

DEMO 在這裡:https://larch.ink/play/market/yaze/glitch-phone。它接在《格莉奇與黑洞先生》 後面,手機的來歷是小說第五章那一晚。

後記(2026-09-12)

今天查市集,phone-chat 線上版本 0.3.4、狀態 approved、裝機數 8。上面寫的都停在 9 月 4 日那個晚上。