import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const repoRoot = path.resolve(__dirname, ".."); const sourcePath = path.join(repoRoot, "ndView.tsx"); const outputDir = path.join(repoRoot, "docs", "generated"); const outputPath = path.join(outputDir, "jrshikoku-operation-info.user.js"); const source = fs.readFileSync(sourcePath, "utf8"); const match = source.match(/return String\.raw`([\s\S]*?)`;\n};/); if (!match) { throw new Error("Failed to locate buildOperationPageScript body in ndView.tsx"); } let scriptBody = match[1]; scriptBody = scriptBody.replace( " window.__jrsNativeOperationLayout = ${nativeLayout};", [ " window.__jrsNativeOperationLayout = Object.assign({", " forceSignage: true,", " safeAreaLeft: 0,", " safeAreaRight: 0,", " lineBadges: []", " }, window.__TM_OPERATION_INFO_LAYOUT || {});" ].join("\n") ); const nativePostMessageBlock = [ " function postMessage(payload) {", " if (!window.ReactNativeWebView) return;", " payload.type = 'operationInfoCapture';", " window.ReactNativeWebView.postMessage(JSON.stringify(payload));", " }" ].join("\n"); const tampermonkeyPostMessageBlock = [ " function downloadCapture(dataUrl, fileName) {", " if (!dataUrl) return;", " if (typeof GM_download === 'function') {", " try {", " GM_download({ url: dataUrl, name: fileName, saveAs: true });", " return;", " } catch (error) {", " console.warn('[JRShikoku TM] GM_download failed, falling back to anchor download.', error);", " }", " }", "", " var link = document.createElement('a');", " link.href = dataUrl;", " link.download = fileName || ('operation-info-' + Date.now() + '.png');", " link.rel = 'noopener';", " document.body.appendChild(link);", " link.click();", " link.remove();", " }", "", " function postMessage(payload) {", " if (!payload) return;", " if (payload.error) {", " console.error('[JRShikoku TM] capture failed', payload);", " window.alert('運行情報の画像生成に失敗しました。');", " return;", " }", " downloadCapture(payload.dataUrl, payload.fileName);", " }" ].join("\n"); if (!scriptBody.includes(nativePostMessageBlock)) { throw new Error("Failed to locate React Native postMessage block in ndView.tsx"); } scriptBody = scriptBody.replace(nativePostMessageBlock, tampermonkeyPostMessageBlock); const header = [ "// ==UserScript==", "// @name JR四国 運行情報 Inject", "// @namespace https://github.com/harukin/jrshikoku", "// @version 0.1.0", "// @description Run the JR Shikoku operation-info injection on desktop browsers via Tampermonkey.", "// @match https://www.jr-shikoku.co.jp/info/*", "// @run-at document-idle", "// @grant GM_download", "// ==/UserScript==", "", "/* Generated from ndView.tsx by scripts/generate-operation-info-userscript.mjs. */", "", "window.__TM_OPERATION_INFO_LAYOUT = Object.assign({", " forceSignage: true,", " safeAreaLeft: 0,", " safeAreaRight: 0,", " lineBadges: []", "}, window.__TM_OPERATION_INFO_LAYOUT || {});", "" ].join("\n"); fs.mkdirSync(outputDir, { recursive: true }); fs.writeFileSync(outputPath, header + scriptBody.trimStart() + "\n", "utf8"); console.log(path.relative(repoRoot, outputPath));