Skip to content

開発者オーバレイのUIと長押しメニュー切り替えを整備#5578

Merged
TinyKitten merged 6 commits intodevfrom
codex/dev-overlay-menu-toggle
Mar 11, 2026
Merged

開発者オーバレイのUIと長押しメニュー切り替えを整備#5578
TinyKitten merged 6 commits intodevfrom
codex/dev-overlay-menu-toggle

Conversation

@TinyKitten
Copy link
Member

@TinyKitten TinyKitten commented Mar 11, 2026

目的

開発者オーバレイの使い勝手と見た目を改善し、長押しメニューから表示/非表示を切り替えられるようにします。

主な変更

  • 開発者オーバレイのデザインを再構成し、横画面レイアウトを最適化
  • オーバレイをドラッグ移動できるように変更
  • 長押しメニューに開発者オーバレイの表示/非表示トグルを追加
  • オーバレイ表示状態の永続化と文言追加
  • DevOverlay のテストを表示実装に合わせて更新

リスク

  • 開発者向けオーバレイ周辺の UI と入力挙動に影響します
  • 長押しメニューの項目追加により dev app のみ選択肢が増えます

確認コマンド

  • npm run lint
  • npm run typecheck
  • npm test -- DevOverlay.test.tsx --runInBand

Summary by CodeRabbit

  • 新機能
    • 開発者オーバーレイをドラッグ可能なインタラクティブパネルに刷新。ランドスケープ対応や新しいメトリック表示、状態バッジを追加。
    • 長押しメニューからオーバーレイの表示/非表示を切替え、設定を永続化。
  • 多言語対応
    • 英語・日本語の表示文言を追加(オーバーレイ表示切替)。
  • テスト
    • 新UI構造と境界ケースに合わせて表示テストを更新。

@github-actions github-actions bot added the react label Mar 11, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

開発者オーバーレイがドラッグ可能な対話型パネルに差し替えられ、表示/非表示トグル用の翻訳キーと長押しメニュー経由のトグルおよび AsyncStorage 永続化が追加されました。

変更内容

コホート / ファイル(s) 概要
翻訳ファイル
assets/translations/en.json, assets/translations/ja.json
開発者オーバーレイ表示/非表示用の新規翻訳キー(showDevOverlayhideDevOverlay)を追加。
DevOverlay コンポーネント
src/components/DevOverlay.tsx
静的パネルを Animated.View + PanResponder ベースのドラッグ可能パネルへ全面的に書き換え。レスポンシブレイアウト、位置管理、StatusPill/MetricCard の内部コンポーネント追加、精度履歴・速度・次駅メタ等の表示ロジックを導入。型(StatusPillProps, MetricCardProps)と内部コンポーネントをエクスポート。
DevOverlay テスト
src/components/DevOverlay.test.tsx
atom モックを集中管理する setup helper を導入。Dimensions.get のモック追加、テストIDとアサーションを更新/拡張し、レイアウト(ランドスケープ)やエッジケース(null/undefined)を検証。
Permitted コンポーネント
src/components/Permitted.tsx
長押しメニューをデータ駆動化し、isDevApp 時に DevOverlay の表示/非表示トグルを追加。トグルは tuningState.devOverlayEnabled を更新し、ASYNC_STORAGE_KEYS.DEV_OVERLAY_ENABLED に保存。
AsyncStorage 定数
src/constants/asyncStorage.ts
ASYNC_STORAGE_KEYSDEV_OVERLAY_ENABLED: '@TrainLCD:devOverlayEnabled' を追加。

シーケンス図

sequenceDiagram
    participant User as User
    participant Permitted as Permitted\n(menu)
    participant Tuning as TuningState
    participant Storage as AsyncStorage
    participant DevOverlay as DevOverlay

    User->>Permitted: 長押し → Show/Hide Dev Overlay を選択
    Permitted->>Tuning: devOverlayEnabled を更新
    Permitted->>Storage: setItem('@TrainLCD:devOverlayEnabled', value)
    Storage-->>Permitted: 保存完了
    Tuning-->>DevOverlay: devOverlayEnabled の変化を通知
    User->>DevOverlay: パネルをドラッグ
    DevOverlay->>DevOverlay: 位置を計算・クランプして保持
Loading

推定コードレビュー工数

🎯 4 (Complex) | ⏱️ ~60 minutes

関連する可能性のある PR

ポエム

🐰 ぴょんと動くパネル、風にのって、
透明な光が角をつつむ、
トグルで隠して、また呼び戻し、
保存された場所で静かに待つ、
今日も開発の野でぴょん!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed プルリクエストのタイトルはセットの変更内容を的確に説明しており、開発者オーバレイのUIと長押しメニュー切り替え機能という主要な変更を明確に表現している。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/dev-overlay-menu-toggle

Comment @coderabbitai help to get the list of available commands and usage tips.

@TinyKitten TinyKitten self-assigned this Mar 11, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/components/DevOverlay.test.tsx (1)

84-95: 未対応の atom は即失敗させた方が安全です。

未知の atomundefined で返すと、DevOverlay が新しい useAtomValue 依存を増やしてもセットアップ漏れを見逃しやすいです。ここは throw で落としておく方が原因を追いやすいです。

💡 修正例
       if (atom === backgroundLocationTrackingAtom) {
         return backgroundLocationTracking as never;
       }
-      return undefined as never;
+      throw new Error('Unhandled atom in DevOverlay test');
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.test.tsx` around lines 84 - 95, The
mockUseAtomValue implementation in the test should fail fast for unknown atoms:
update the mock inside DevOverlay.test.tsx (the mockUseAtomValue function) so
that after checking locationAtom, accuracyHistoryAtom, and
backgroundLocationTrackingAtom it throws an error (including the atom value in
the message) instead of returning undefined; this ensures any new useAtomValue
dependency in DevOverlay will cause the test to fail and force adding the
required mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/DevOverlay.tsx`:
- Around line 511-528: The accuracy placeholder '---' is being rendered twice
when accuracyChartBlocks.length === 0 (once in the first Typography and again in
the second with testID "dev-overlay-accuracy-history"); update DevOverlay.tsx so
only one Typography renders the placeholder: remove the first conditional
placeholder (keep the first Typography for spacing/styling but render null when
empty) and let the second Typography handle either the mapped blocks or the
single '---' placeholder (also apply the same change for the other occurrence
around the 583-600 block). Ensure you reference accuracyChartBlocks,
chartValueStyle, styles.chartValue and the testID "dev-overlay-accuracy-history"
when making the change.

In `@src/components/Permitted.tsx`:
- Around line 241-250: handler 内で AsyncStorage.setItem
の失敗をハンドルするため、現在の楽観的更新を保持する前の値を退避し、setTuning で UI を更新した後に try/catch で await
AsyncStorage.setItem(ASYNC_STORAGE_KEYS.DEV_OVERLAY_ENABLED, String(nextValue))
を実行してください。catch ブロックでは保存に失敗した場合に元の値へロールバックするために setTuning(prev => ({ ...prev,
devOverlayEnabled: prevValue })) を呼び出し(必要に応じてエラーログ/通知も出す)、handler は async
のまま内部で例外を消化して未処理の rejection が残らないようにしてください(参照する識別子: devOverlayEnabled,
setTuning, ASYNC_STORAGE_KEYS.DEV_OVERLAY_ENABLED, handler)。

---

Nitpick comments:
In `@src/components/DevOverlay.test.tsx`:
- Around line 84-95: The mockUseAtomValue implementation in the test should fail
fast for unknown atoms: update the mock inside DevOverlay.test.tsx (the
mockUseAtomValue function) so that after checking locationAtom,
accuracyHistoryAtom, and backgroundLocationTrackingAtom it throws an error
(including the atom value in the message) instead of returning undefined; this
ensures any new useAtomValue dependency in DevOverlay will cause the test to
fail and force adding the required mock.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a2f05197-5b32-44be-bfa7-0552d121698f

📥 Commits

Reviewing files that changed from the base of the PR and between 264529e and cb9dbe9.

📒 Files selected for processing (6)
  • assets/translations/en.json
  • assets/translations/ja.json
  • src/components/DevOverlay.test.tsx
  • src/components/DevOverlay.tsx
  • src/components/Permitted.tsx
  • src/constants/asyncStorage.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/DevOverlay.tsx (1)

519-540: ⚠️ Potential issue | 🟡 Minor

空の精度履歴プレースホルダが二重描画されています。

accuracyChartBlocks.length === 0 のとき、上段の TypographytestID="dev-overlay-accuracy-history" 側の両方が --- を出しているので、空状態だけ 2 行になります。プレースホルダは 1 箇所に寄せた方が自然です。

修正例
-                    <Typography style={[styles.chartValue, chartValueStyle]}>
-                      {accuracyChartBlocks.length === 0 ? '---' : null}
-                    </Typography>
                     <Typography
                       style={[styles.chartValue, chartValueStyle]}
                       testID="dev-overlay-accuracy-history"
                     >
@@
-                <Typography style={[styles.chartValue, chartValueStyle]}>
-                  {accuracyChartBlocks.length === 0 ? '---' : null}
-                </Typography>
                 <Typography
                   style={[styles.chartValue, chartValueStyle]}
                   testID="dev-overlay-accuracy-history"
                 >

Also applies to: 591-612

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.tsx` around lines 519 - 540, The empty
accuracy-history placeholder is rendered twice: once in the first Typography
(style={styles.chartValue, chartValueStyle}) and again inside the Typography
with testID="dev-overlay-accuracy-history"; remove the duplicate by rendering
the '---' placeholder only in the testID="dev-overlay-accuracy-history" block
and make the earlier Typography return null when accuracyChartBlocks.length ===
0 (or simply omit the placeholder there); apply the same change to the parallel
accuracy/history block handled later in DevOverlay (the other accuracy chart
rendering).
🧹 Nitpick comments (1)
src/components/DevOverlay.test.tsx (1)

84-95: 未知の atom を黙って undefined にしない方がいいです。

ここがフォールバックで通ってしまうと、DevOverlay の atom 依存が増えた時にテストが誤って緑のまま残ります。unexpected atom は throw して fail-fast にしておく方が安全です。

修正例
     if (atom === backgroundLocationTrackingAtom) {
       return backgroundLocationTracking as never;
     }
-    return undefined as never;
+    throw new Error('Unexpected atom passed to useAtomValue mock');
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.test.tsx` around lines 84 - 95, The
mockUseAtomValue implementation used in DevOverlay.test.tsx currently falls back
to returning undefined for unknown atoms, which can hide missing dependencies;
update the mockImplementation for mockUseAtomValue so it throws an error for any
atom that is not one of the expected atoms (locationAtom, accuracyHistoryAtom,
backgroundLocationTrackingAtom) — e.g., detect the unexpected atom in the final
branch and throw a descriptive Error including the atom identity to fail fast
and surface new atom requirements.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/DevOverlay.tsx`:
- Around line 36-51: The root style currently mixes shadow props and overflow:
'hidden' (root) which on iOS clips shadows; modify the DevOverlay component to
split this into two Views: an outer wrapper that keeps the shadow-related styles
(shadowColor, shadowOpacity, shadowRadius, shadowOffset, elevation, zIndex) and
does NOT set overflow or borderRadius clipping, and an inner wrapper that
applies borderRadius, overflow: 'hidden', borderWidth, borderColor and
backgroundColor to handle rounded corners and content clipping; update any
references to the root style accordingly and verify shadows on iOS
devices/simulator.

---

Duplicate comments:
In `@src/components/DevOverlay.tsx`:
- Around line 519-540: The empty accuracy-history placeholder is rendered twice:
once in the first Typography (style={styles.chartValue, chartValueStyle}) and
again inside the Typography with testID="dev-overlay-accuracy-history"; remove
the duplicate by rendering the '---' placeholder only in the
testID="dev-overlay-accuracy-history" block and make the earlier Typography
return null when accuracyChartBlocks.length === 0 (or simply omit the
placeholder there); apply the same change to the parallel accuracy/history block
handled later in DevOverlay (the other accuracy chart rendering).

---

Nitpick comments:
In `@src/components/DevOverlay.test.tsx`:
- Around line 84-95: The mockUseAtomValue implementation used in
DevOverlay.test.tsx currently falls back to returning undefined for unknown
atoms, which can hide missing dependencies; update the mockImplementation for
mockUseAtomValue so it throws an error for any atom that is not one of the
expected atoms (locationAtom, accuracyHistoryAtom,
backgroundLocationTrackingAtom) — e.g., detect the unexpected atom in the final
branch and throw a descriptive Error including the atom identity to fail fast
and surface new atom requirements.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 50091b64-189c-4554-9a82-05878fa84814

📥 Commits

Reviewing files that changed from the base of the PR and between cb9dbe9 and 356ba1c.

📒 Files selected for processing (2)
  • src/components/DevOverlay.test.tsx
  • src/components/DevOverlay.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/DevOverlay.tsx (1)

36-51: ⚠️ Potential issue | 🟡 Minor

overflow: 'hidden' と shadow を同じレイヤーに置くと、iOS で影が欠けます。

styles.root はまだ shadow 描画と内容クリップを同居させているので、iOS ではカード外側の影が切れます。shadow 用の外側ラッパーと、borderRadius / overflow: 'hidden' 用の内側ラッパーを分けた方が安全です。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.tsx` around lines 36 - 51, styles.root currently
mixes shadow styles and overflow: 'hidden', which causes clipped shadows on iOS;
update the DevOverlay JSX/CSS by splitting the wrapper into two: an outer
wrapper (e.g., DevOverlayShadowWrapper) that contains the shadow props
(shadowColor, shadowOpacity, shadowRadius, shadowOffset, elevation, zIndex) and
the positioning, and an inner wrapper (e.g., DevOverlayClipWrapper) that
contains borderRadius and overflow: 'hidden' plus the PANEL_BORDER/PANEL_BG
styles and children; remove overflow: 'hidden' and borderRadius from styles.root
so shadows render on the outer layer while content is clipped by the inner
layer.
🧹 Nitpick comments (1)
src/components/DevOverlay.test.tsx (1)

117-192: 横画面分岐を固定したケースも 1 本ほしいです。

DevOverlay は縦横で JSX が大きく分岐しますが、今の assertions はどちらの分岐でも通るものが中心なので、今回の landscape レイアウト変更を実質固定できていません。寸法を明示的に切り替えたケースを追加し、必要なら分岐コンテナに軽い testID を付けておくと、この PR の主変更点を守りやすいです。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.test.tsx` around lines 117 - 192, Tests currently
don't assert the landscape-specific JSX branch of DevOverlay; add a new test
(e.g., "横画面レイアウトを表示する") that forces landscape dimensions before calling
render(<DevOverlay />) (mock the window dimensions or use a helper like
setWindowSize/override Dimensions) and then assert a landscape-only element is
present; if the branch lacks a stable selector, add a lightweight testID to the
landscape container (e.g., testID="dev-overlay-landscape") in the DevOverlay
component and assert getByTestId('dev-overlay-landscape') exists and/or its
specific text/layout differences to lock the landscape branch behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/DevOverlay.tsx`:
- Around line 428-451: The two pan responder handlers are asymmetric:
onPanResponderRelease sets hasDraggedRef.current = true but
onPanResponderTerminate does not, causing a false "not dragged" state when
dragTranslation triggers the effect; update the onPanResponderTerminate handler
(the block using dragTranslation.stopAnimation and computing clampedPosition via
clampPosition with basePositionRef, panelWidth and panelHeight) to also set
hasDraggedRef.current = true before calling setBasePosition and
dragTranslation.setValue({ x: 0, y: 0 }) so both handlers behave identically.

---

Duplicate comments:
In `@src/components/DevOverlay.tsx`:
- Around line 36-51: styles.root currently mixes shadow styles and overflow:
'hidden', which causes clipped shadows on iOS; update the DevOverlay JSX/CSS by
splitting the wrapper into two: an outer wrapper (e.g., DevOverlayShadowWrapper)
that contains the shadow props (shadowColor, shadowOpacity, shadowRadius,
shadowOffset, elevation, zIndex) and the positioning, and an inner wrapper
(e.g., DevOverlayClipWrapper) that contains borderRadius and overflow: 'hidden'
plus the PANEL_BORDER/PANEL_BG styles and children; remove overflow: 'hidden'
and borderRadius from styles.root so shadows render on the outer layer while
content is clipped by the inner layer.

---

Nitpick comments:
In `@src/components/DevOverlay.test.tsx`:
- Around line 117-192: Tests currently don't assert the landscape-specific JSX
branch of DevOverlay; add a new test (e.g., "横画面レイアウトを表示する") that forces
landscape dimensions before calling render(<DevOverlay />) (mock the window
dimensions or use a helper like setWindowSize/override Dimensions) and then
assert a landscape-only element is present; if the branch lacks a stable
selector, add a lightweight testID to the landscape container (e.g.,
testID="dev-overlay-landscape") in the DevOverlay component and assert
getByTestId('dev-overlay-landscape') exists and/or its specific text/layout
differences to lock the landscape branch behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1be44186-3aae-481d-b9f0-cdaeae0a2683

📥 Commits

Reviewing files that changed from the base of the PR and between 356ba1c and 0a1e657.

📒 Files selected for processing (3)
  • src/components/DevOverlay.test.tsx
  • src/components/DevOverlay.tsx
  • src/components/Permitted.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/components/DevOverlay.tsx (3)

286-295: 速度計算のロジックは正しいですが、若干複雑です。

coordsSpeed を計算した後、speedKMH で再度 speed をチェックしています。意図通り動作しますが、将来のメンテナンスのため、coordsSpeed のみを使用する方がシンプルです。

♻️ 簡略化の提案
  const speedKMH = useMemo(
    () =>
-     (
-       (speed && Math.round((coordsSpeed * 3600) / 1000)) ??
-       0
-     ).toLocaleString(),
-   [coordsSpeed, speed]
+     Math.round((coordsSpeed * 3600) / 1000).toLocaleString(),
+   [coordsSpeed]
  );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.tsx` around lines 286 - 295, The speedKMH
computation currently re-checks speed even after coordsSpeed was derived;
simplify by computing kilometers-per-hour solely from coordsSpeed inside the
useMemo for speedKMH (remove the secondary speed null/undefined checks), convert
coordsSpeed to km/h via Math.round((coordsSpeed * 3600) / 1000) (or 0 when
coordsSpeed is falsy), call .toLocaleString() on that result, and update the
useMemo dependency array to only [coordsSpeed] so speedKMH depends exclusively
on coordsSpeed.

305-307: ?? undefined は冗長です。

オプショナルチェーンが失敗した場合、既に undefined が返されます。

♻️ 冗長なコードの削除
  const nextStationNumber =
    nextStation?.stationNumbers?.find((item) => !!item?.stationNumber)
-     ?.stationNumber ?? undefined;
+     ?.stationNumber;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.tsx` around lines 305 - 307, The expression
assigning nextStationNumber uses a redundant nullish coalescing "?? undefined";
remove the "?? undefined" so nextStationNumber is assigned directly from
nextStation?.stationNumbers?.find(item => !!item?.stationNumber)?.stationNumber,
relying on optional chaining to yield undefined when absent; update the
assignment of nextStationNumber accordingly (reference symbols:
nextStationNumber, nextStation, stationNumbers, stationNumber).

529-671: 横画面・縦画面のレイアウトが条件分岐で実装されています。

両レイアウト間でコード重複がありますが、レイアウト固有の調整が必要なため、現在の実装は許容範囲です。将来的にメトリックカード群を共通コンポーネントとして抽出することで、保守性が向上する可能性があります。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DevOverlay.tsx` around lines 529 - 671, There is duplicated
JSX between the isLandscape true/false branches (accuracy chart + three
MetricCard instances) — extract a shared presentational component (e.g.,
MetricCardsGroup) that renders the three MetricCard elements using props:
accuracyChartBlocks, speedKMH, distanceToNextStation, nextStationMeta,
metricCardStyle, metricLabelStyle, metricValueStyle, metricMetaStyle and allow
layout overrides (props for container width/gap e.g., metricWidth,
leftMetricWidth, nextCardWidth, metricsGap, nextTargetCardStyle); keep the
existing accuracy chart rendering (accuracyChartBlocks, chartShellStyle,
chartValueStyle) and conditional container wrappers (isLandscape branch uses
styles.landscapeTopRow/landscapeSubGrid, portrait uses styles.metricsGrid) but
replace the duplicated MetricCard blocks with the new MetricCardsGroup component
so layout-specific widths/styles are passed in from each branch and
functionality of MetricCard, accuracy, speedKMH, and nextStationMeta remains
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/DevOverlay.tsx`:
- Around line 286-295: The speedKMH computation currently re-checks speed even
after coordsSpeed was derived; simplify by computing kilometers-per-hour solely
from coordsSpeed inside the useMemo for speedKMH (remove the secondary speed
null/undefined checks), convert coordsSpeed to km/h via Math.round((coordsSpeed
* 3600) / 1000) (or 0 when coordsSpeed is falsy), call .toLocaleString() on that
result, and update the useMemo dependency array to only [coordsSpeed] so
speedKMH depends exclusively on coordsSpeed.
- Around line 305-307: The expression assigning nextStationNumber uses a
redundant nullish coalescing "?? undefined"; remove the "?? undefined" so
nextStationNumber is assigned directly from
nextStation?.stationNumbers?.find(item => !!item?.stationNumber)?.stationNumber,
relying on optional chaining to yield undefined when absent; update the
assignment of nextStationNumber accordingly (reference symbols:
nextStationNumber, nextStation, stationNumbers, stationNumber).
- Around line 529-671: There is duplicated JSX between the isLandscape
true/false branches (accuracy chart + three MetricCard instances) — extract a
shared presentational component (e.g., MetricCardsGroup) that renders the three
MetricCard elements using props: accuracyChartBlocks, speedKMH,
distanceToNextStation, nextStationMeta, metricCardStyle, metricLabelStyle,
metricValueStyle, metricMetaStyle and allow layout overrides (props for
container width/gap e.g., metricWidth, leftMetricWidth, nextCardWidth,
metricsGap, nextTargetCardStyle); keep the existing accuracy chart rendering
(accuracyChartBlocks, chartShellStyle, chartValueStyle) and conditional
container wrappers (isLandscape branch uses
styles.landscapeTopRow/landscapeSubGrid, portrait uses styles.metricsGrid) but
replace the duplicated MetricCard blocks with the new MetricCardsGroup component
so layout-specific widths/styles are passed in from each branch and
functionality of MetricCard, accuracy, speedKMH, and nextStationMeta remains
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ba7855f4-1065-49ab-999e-6de0f1aca18f

📥 Commits

Reviewing files that changed from the base of the PR and between 0a1e657 and 4d9f1cb.

📒 Files selected for processing (2)
  • src/components/DevOverlay.test.tsx
  • src/components/DevOverlay.tsx

@TinyKitten TinyKitten merged commit 9d2e99c into dev Mar 11, 2026
6 checks passed
@TinyKitten TinyKitten deleted the codex/dev-overlay-menu-toggle branch March 11, 2026 01:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant