Compare commits

..

6 Commits

Author SHA1 Message Date
harukin-expo-dev-env 4dd0775640 feat: add userscript generator for JR Shikoku operation info
- Implemented a script to generate a Tampermonkey userscript from ndView.tsx.
- Extracted and modified the operation page script body to include necessary postMessage functions.
- Added metadata for the userscript including name, namespace, version, and match URL.
- Ensured compatibility with Tampermonkey's GM_download for image capture.
2026-06-20 06:07:39 +00:00
harukin-expo-dev-env 4f81431ccd feat: 列車情報のキャプチャ機能を追加し、スタイルを改善 2026-06-10 05:04:09 +00:00
harukin-expo-dev-env 6371787b47 Merge commit '0ced766608b43cd9244817a0732246ca8b9c5f38' into develop 2026-06-06 08:35:26 +00:00
harukin-expo-dev-env f9b2786809 Merge commit '95f498e89418c9bb8c551987f0b3f1b693c05c1d' into develop 2026-05-20 15:29:09 +00:00
harukin-expo-dev-env a88f7bac09 Merge commit '23ef404f7b8f728749024b74e1b8742d5b75d529' into develop 2026-05-20 06:18:08 +00:00
harukin-expo-dev-env 37e147e3be Merge commit '26aa71866ae1c949f0c31c7899ef68c30d111a61' into develop 2026-05-05 10:43:58 +00:00
5 changed files with 3278 additions and 131 deletions
+6 -3
View File
@@ -1,7 +1,7 @@
import React from "react";
import { NavigationContainer, DarkTheme, DefaultTheme } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Animated, Platform, ActivityIndicator, View, StyleSheet, StatusBar } from "react-native";
import { Animated, Platform, ActivityIndicator, View, StyleSheet, StatusBar, useWindowDimensions } from "react-native";
import { useNavigationState } from "@react-navigation/native";
import { useFonts } from "expo-font";
import { LinearGradient } from "expo-linear-gradient";
@@ -38,6 +38,8 @@ const Tab = createBottomTabNavigator<RootTabParamList>();
export function AppContainer() {
const { areaInfo, areaIconBadgeText, isInfo } = useAreaInfo();
const { selectedLine } = useTrainMenu();
const { width, height } = useWindowDimensions();
const operationScreenKey = width > height ? "operation-landscape" : "operation-portrait";
const [isExtraWindowOpen, setIsExtraWindowOpen] = React.useState(false);
// フェードアニメーション用 (0=通常, 1=追加ウィンドウ青)
@@ -185,8 +187,9 @@ export function AppContainer() {
areaInfo ? areaIconBadgeText : undefined,
isInfo
)}
children={TNDView}
/>
>
{() => <TNDView key={operationScreenKey} />}
</Tab.Screen>
</Tab.Navigator>
</NavigationContainer>
);
+3 -2
View File
@@ -13,6 +13,7 @@ import { getTime, trainTimeFiltering } from "@/lib/trainTimeFiltering";
import { eachTrainDiagramType, StationProps } from "@/lib/CommonTypes";
import { useNavigation } from "@react-navigation/native";
import { useThemeColors } from "@/lib/theme";
import { stackAwareNavigate } from "@/lib/rootNavigation";
/**
*
@@ -49,7 +50,7 @@ type props = {
export const LED_vision: FC<props> = (props) => {
const { station } = props;
const { navigate, addListener, isFocused } = useNavigation();
const { navigate } = useNavigation();
const { currentTrain } = useCurrentTrain();
const [stationDiagram, setStationDiagram] = useState<{
[key: string]: string;
@@ -163,7 +164,7 @@ export const LED_vision: FC<props> = (props) => {
<AreaDescription
numberOfLines={1}
areaInfo={areaInfo}
onClick={() => alert(areaInfo)}
onClick={() => stackAwareNavigate("information")}
/>
)}
File diff suppressed because it is too large Load Diff
+1760 -126
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,101 @@
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));