generated at
Scrapbox UserScriptからSocket.IOを使
Socket.IOcdnjs.cloudflare.comから読み込める

script.js
export function importSocketIO(version = "4.2.0") { const url = `https://cdnjs.cloudflare.com/ajax/libs/socket.io/${version}/socket.io.min.js`; if (document.querySelector(`script[src="${url}"]`)) { return Promise.resolve().then(() => globalThis.io); } const script = document.createElement("script"); script.src = url; return new Promise((resolve, reject) => { script.onload = () => resolve(globalThis.io); script.onerror = (e) => reject(e); document.head.append(script); }); }

テスト
test.js
import {importSocketIO} from "https://scrapbox.io/api/code/takker/Scrapbox_UserScriptからSocket.IOを使/script.js"; const io = await importSocketIO("4.2.0");
cf.
test.js
const socket = io(location.origin, {reconnectionDelay: 5000, transports: ["websocket"]});

コードに直接埋め込む場合は、ESmodule版としてhttps://esm.sh/socket.io-client@4.2.0?bundle も使える
minifyしたコードサイズはcdnjs版の2倍くらいある
?bundle を付けないとさらに数倍に膨れ上がってしまうので注意

特定のprojectに接続する
test2.js
import {importSocketIO} from "https://scrapbox.io/api/code/takker/Scrapbox_UserScriptからSocket.IOを使/script.js"; const io = await importSocketIO("4.2.0"); const socket = io(location.origin, {reconnectionDelay: 5000, transports: ["websocket"]}); const result = await new Promise(res => socket.emit("socket.io-request",{ method: "room:join", data: { pageId: null, projectId: "5f2f02f3c4a48d00237e1534", projectUpdatesStream: false, }, }, (e) => res(e))); console.info(result); socket.disconnect();

特定のprojectのstreamを購読する
stream.js
import {importSocketIO} from "https://scrapbox.io/api/code/takker/Scrapbox_UserScriptからSocket.IOを使/script.js"; const project = "villagepump"; const PROJECT_ID = "5e2455255664e000177a46fc"; const io = await importSocketIO("4.2.0"); const socket = io(location.origin, {reconnectionDelay: 5000, transports: ["websocket"]}); const result = await new Promise(res => socket.emit("socket.io-request",{ method: "room:join", data: { pageId: null, projectId: PROJECT_ID, projectUpdatesStream: true, }, }, (e) => res(e))); console.info("Start subscribing stream");
左下に表示する
stream.js
const div = document.createElement("div"); const shadowRoot = div.attachShadow({mode: "open"}); shadowRoot.innerHTML = ` <style> :host { position: fixed; left: 0; bottom: 0; z-index: 9999; text-decoration: none; background-color: var(--page-bg); color: var(--page-text-color); border: solid 1px var(--telomere-unread); border-radius: 4px; padding: 8px; margin: 10px; } button { display: inline-block; } </style> <div> <button id="button">x</button> <span id="title">caption</span> </div> <table> <thead> <tr> <th>type</th> <th>id</th> <th>lines</th> </tr> </thead> <tbody id="table"> </tbody> </table> <pre><code id="code"> </code></pre> `; document.body.append(div); socket.on("projectUpdatesStream:commit", (data) => { const {parentId, changes, pageId, userId, projectId, id} = data; const title = pageId; const commitMsg = `${title}@${project}\n${parentId.slice(0, 8)} -> ${id.slice(0, 8)}`; shadowRoot.getElementById("title").innerText = commitMsg; shadowRoot.getElementById("table").textContent = ""; shadowRoot.getElementById("table").append(...changes.map(change => { const {_update, _insert, _delete, lines} = change; const tr = document.createElement("tr"); const type = document.createElement("td"); type.textContent = _update ? "~" : _insert ? "+" : _delete ? "-" : "unknown"; const commitId = document.createElement("td"); commitId.textContent = (_update ?? _insert ?? _delete ?? "unknown").slice(0, 8); const linesTd = document.createElement("td"); linesTd.textContent = lines.text ?? lines.origText; tr.append(type, commitId, linesTd); return tr; })); });
閉じる
stream.js
shadowRoot.getElementById("button").onclick = () => { console.info("End subscribing stream"); socket.disconnect(); div.remove(); };

#2021-10-13 13:40:48