file

作為軟體工程部落格,這邊貼出程式碼主要還是仰賴 highlight.js 的語法標示能力,這樣做的好處是既能夠產生顏色、又能夠讓訪客可以複製程式碼回去(假如想要的話);不過缺點是整篇文章沒有圖片,在轉貼到社群網站的時候頂多只能顯示網站預設的插圖。為了解決這個問題,從某個時期開始,對於整篇文章中沒有圖片的文章我都會像上面這樣放一段我覺得該篇最重要的程式碼的快照圖片。原本這種圖片我是用 Carbon 這個線上工具來產生的,但是這一兩天我決定把這些圖片全部改成用 VS Code 上的外掛產生的圖片取代,原因一方面是 VS Code 在色彩標示的部份因為有融入語意、當然做得比 Carbon 或 highlight.js 這類用簡單的 regex 來標示的程式更好,而且另一方面也是因為這是我平常編輯時看習慣的色彩。

VS Code 上頭做這件事情的外掛很多,其中最元祖的應該是 Polacode(而它本身也是受到 Carbon 啟發的),但是這個已經快兩年沒維護了,所以後來從這個外掛開始 fork 出去的外掛多到講不完,除了一堆 Polacode XXX 外掛和一個 Snapcode 分支之外,一個做得最好的分支就是這篇的主角: CodeSnap

CodeSnap 提供了恰到好處的選項,像是固定的 container padding、可選擇列號顯示和標題、OS X 控制項等等。另外重點就是它可以選擇按下拍照時是要另存圖片、或是直接複製到剪貼簿中。由於我只要直接把剪貼簿中的圖片貼到 WordPress 編輯器裡面就可以直接上傳,我當然懶得再多一道存檔的手續。

然而,正當我滿懷期待把 CodeSnap v1.3.2 安裝好並且設定了複製模式之後,我這才發現:按下拍照根本沒有複製東西到剪貼簿上。

當然,這種時候首先就是先去看看它的儲存庫那邊有沒有人回報 issue,而果然是有(這篇),且有人說如果倒退回到 v1.2.1 版、複製功能就可以正常使用。但當然我的強迫症沒辦法接受在工具欄那邊一直有個提示說「有延伸模組可以更新」,所以既然在所有的類似外掛裡面(除了這個問題之外)我最看中這一套,我便決定幫一把、來看看能不能幫作者修好這個問題。

很快地我就發現,因為 VS Code 本身提供的 API 似乎並沒有複製圖片的功能,這個外掛裡面的複製機制用的是另外寫一些外部腳本或程式去針對不同的作業系統來達成複製的目的,而該外掛的作者還把這些整理成了另一個 NPM 套件 img-clipboard 並且讓 CodeSnap 相依在它上面。然而,自從 img-clipboard 更新到了 v1.1.0 之後,它在 Windows 系統上就失效了;如果我把 CodeSnap 裡面相依的 img-clipboard 降版到 v1.0.4 之後,一切就運作正常了。

可是 img-clipboard 裡面撰寫的那些外部腳本和程式離我熟悉的領域太遠了,那些我可能比較幫不上忙,而我又沒耐心等作者去修正那個套件,所以我就試著找突破點,看看有沒有可能完全改採用別的辦法。事實上我的確曾經寫過跟複製圖片有關的東西——我的 BPS 裡面就有這種功能可以把設計出來的摺紙展開圖以 PNG 格式複製到剪貼簿中,所以我對於相關的網頁 API 是非常熟悉的。但是,雖然 VS Code 其實說穿了是一個透過 Electron 框架跑起來的網頁,但是裡頭的延伸模組並沒有能力直接取用網頁的 API,只能使用 VS Code 開放給外掛用的那些 API……

除了一個例外,那就是當外掛開了一個 WebView 起來、載入了一個自己提供的網頁的時候;而這正巧就是 CodeSnap 做的事!它的預覽畫面就是一個 WebView,所以它裡面的腳本是有能力取得所有的網頁 API 的、包括 navigator.clipboard 等等都能用。於是我很快地就把裡面最關鍵的 snap.js 做了改寫:

// 這一段產生出來的是一個 data url
const url = await domtoimage.toPng(target, { /* 省略 */ });

// 底下是改寫的部份;data 是裡面的資料段本身,是 base64 格式的
const data = url.slice(url.indexOf(',') + 1);
if(config.shutterAction === 'copy') {
    // 複製的情況中,先把 base64 轉換成 Blob 物件,這一段很制式
    const binary = atob(data);
    const array = new Uint8Array(binary.length);
    for(let i = 0; i < binary.length; i++) array[i] = binary.charCodeAt(i);
    const blob = new Blob([array], { type: 'image/png' });

    // 然後呼叫 Clipboard API 完成複製
    navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);

    // 最後仿照原本的程式,呼叫拍照的特效動畫
    cameraFlashAnimation();
} else {
    // 其它的情況就跟原本一樣把 base64 資料傳送給外掛的主程式
    vscode.postMessage({ type: config.shutterAction, data });
}

改好了以後馬上送出 PR,而這顯然讓這個兩個多月沒更新的專案突然又興奮了起來,他們馬上就確認了我這個程式碼真的在不同的平台都能運作,而且甚至還(在我意料之外地)克服了該外掛原本在開啟遠端工作區的時候怎樣都無法複製圖片的弱點(似乎因為在該情況中跑外部腳本程式沒有用),所以他們火速就合併了 PR 並且上架了新版本的外掛。勝利的滋味 ✌

總之,當然也是因為我有參與到,這邊強烈推薦大家來用 CodeSnap 這款外掛來產生程式碼快照圖片!


分享此頁至:
最後修改日期: 2021/07/03

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。