Skip to content

feat: CustomExitScreen - Debug Inputs UI改善と全Exit Reason対応 - #61

Merged
akriaueno merged 15 commits into
developfrom
feature/custom-access-denied-plugin
Feb 4, 2026
Merged

akriaueno merged 15 commits into
developfrom
feature/custom-access-denied-plugin

Conversation

@akriaueno

@akriaueno akriaueno commented Jan 28, 2026

Copy link
Copy Markdown
Member

Summary

  • 全7種類のExit Reasonに対応(exited, left, closed, denied, kicked, connectError, sceneError)
  • Debug InputsのUI改善(コントロールをヘッダーに配置、Logo URL入力追加)
  • Deniedカードをクリックして Signed-in / Anonymous を切り替え可能に
  • action フィールドを削除(プラグインインターフェースでは不要)
  • READMEを日本語化

Test plan

  • npm run dev でプレビュー画面を確認
  • 各Reasonを選択して表示が切り替わることを確認
  • Deniedの場合、カードクリックでSigned-in/Anonymousが切り替わることを確認
  • Logo URLを変更してロゴが反映されることを確認

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 新機能

    • CustomExitScreenプラグインテンプレートを追加。退出理由ごと/サインイン状態別に文言・ボタンを編集できるエディタとライブプレビュー、ロゴ表示、主要アクションを提供。
  • ドキュメント

    • 利用方法、開発/ビルド手順、出力先やprops契約、対応する退出理由等を含むREADMEを追加。
  • Chores

    • プロジェクト設定、ビルド設定、パッケージ定義、UUID/メタ生成および配布用スクリプト、.gitignoreを追加。

✏️ Tip: You can customize this high-level summary in your review settings.

akriaueno and others added 7 commits January 26, 2026 16:01
- Add all 7 exit reasons with Japanese labels
- Improve Debug Inputs layout with controls in header
- Add Logo URL input field
- Make Denied cards clickable to toggle Signed-in state
- Remove action field (not needed for plugin interface)
- Update README with Japanese documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jan 28, 2026

Copy link
Copy Markdown

Walkthrough

React/TypeScript/ViteベースのCustomExitScreenプラグインテンプレートを追加。カスタムエグジット画面コンポーネント、編集用UIとライブプレビュー、Module Federation設定、ビルド/メタデータ生成およびアーカイブスクリプトを含む。

変更内容

Cohort / File(s) 変更の概要
プロジェクト設定ファイル群
CustomExitScreen/.gitignore, CustomExitScreen/package.json, CustomExitScreen/tsconfig.json, CustomExitScreen/tsconfig.node.json, CustomExitScreen/vite.config.ts
新規追加。ツール設定、依存、TypeScript/Vite設定、Module Federation(exposes/shared/manifest)とVolta指定
ドキュメント・エントリーポイント群
CustomExitScreen/README.md, CustomExitScreen/index.html, CustomExitScreen/src/main.tsx, README.md
新規追加/更新。プラグイン説明、HTMLエントリ、Reactブートストラップ、テンプレート一覧更新
ビルド/メタデータ/アーカイブ スクリプト群
CustomExitScreen/scripts/set_uuid.js, CustomExitScreen/scripts/add_metadata.js, CustomExitScreen/scripts/archive_dist.js
新規追加。VERSION_ID生成、dist/metadata.json 作成、dist を plugin.zip に圧縮するユーティリティ
コアコンポーネント群
CustomExitScreen/src/components/CustomExitScreen/CustomExitScreen.tsx, CustomExitScreen/src/components/CustomExitScreen/CustomExitScreen.module.css, CustomExitScreen/src/components/CustomExitScreen/index.ts, CustomExitScreen/src/index.ts
新規追加。ExitReason 型定義、ExitScreenContent 型、mfMetaCustomExitScreen コンポーネント(ロゴ・タイトル・メッセージ・ボタン)と再エクスポート
エディタUI群
CustomExitScreen/src/App.tsx, CustomExitScreen/src/App.module.css
新規追加。理由別編集パネル、サインイン切替、ロゴURL入力、リアルタイムプレビューを備えたエディタUIとスタイル

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant EditorApp as Editor App
  participant ExitComp as CustomExitScreen
  participant Host as ブラウザ/ホスト

  User->>EditorApp: 理由選択・コンテンツ編集・サインイン切替
  EditorApp->>ExitComp: props(reason, contentByReason, logoUrl, isSignedIn, onPrimaryAction)
  ExitComp-->>EditorApp: プレビュー描画
  User->>ExitComp: プライマリアクションをクリック
  ExitComp->>Host: buttonUrl へ遷移 または location.reload()
Loading

推定レビュー工数

🎯 4 (Complex) | ⏱️ ~45 minutes

🐇 新しい画面を作ったよ、ぴょん
理由でことばを分けたよ、ぴょん
ロゴのせてプレビューで見るよ、ぴょん
メタ作って圧縮もしたよ、ぴょん
さあ放つ準備はできたよ、ぴょん 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR タイトルは CustomExitScreen の追加と Debug Inputs UI 改善、および全 Exit Reason 対応という実装内容を正確に反映しており、変更セットの主要な内容を適切に要約している。

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

✨ Finishing touches
  • 📝 Generate docstrings

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

akriaueno and others added 3 commits January 28, 2026 23:10
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename to custom-exit-screen-template
- Update description for exit screen
- Set initial version to 0.1.0
- Add engines field for Node >=24

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove legacy entries from React/Hubs project

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@CustomExitScreen/package.json`:
- Around line 1-4: The package.json currently has mismatched metadata; update
the "name" and "description" fields in package.json to match the renamed
template "CustomExitScreen" (e.g., set "name" to something like
"custom-exit-screen-template" or "CustomExitScreen" and update "description" to
reference CustomExitScreen) so the package metadata aligns with the PR/README;
modify the "name" and "description" keys in package.json accordingly.

In `@CustomExitScreen/src/App.tsx`:
- Around line 137-148: The onKeyDown handler inside interactiveProps can fire
when typing in child inputs (causing Enter/Space in Title/Message/Button Text to
trigger onSelect); update the onKeyDown in interactiveProps to only handle keys
when the container itself is focused by adding a guard that checks
e.currentTarget === e.target before preventing default and calling onSelect,
leaving onClick and other props as-is so only direct container key events toggle
the group.
🧹 Nitpick comments (2)
CustomExitScreen/vite.config.ts (1)

16-25: 共有依存関係のバージョン管理について

requiredVersion: "18.3.1" がハードコードされています。package.json のバージョンと同期を維持する必要があるため、将来の更新時に不整合が生じる可能性があります。

♻️ 提案: package.json からバージョンを読み込む
 import { defineConfig } from "vite";
 import react from "@vitejs/plugin-react";
 import { federation } from "@module-federation/vite";
+import pkg from "./package.json";

 const versionId = process.env.VERSION_ID || "custom-exit-screen";
+const reactVersion = pkg.dependencies.react;

 export default defineConfig({
   plugins: [
     react(),
     federation({
       name: versionId,
       filename: "remoteEntry.js",
       exposes: {
         "./CustomExitScreen": "./src/components/CustomExitScreen"
       },
       shared: {
         react: {
           singleton: true,
-          requiredVersion: "18.3.1"
+          requiredVersion: reactVersion
         },
         "react-dom": {
           singleton: true,
-          requiredVersion: "18.3.1"
+          requiredVersion: reactVersion
         }
       },
       manifest: true
     })
   ],
CustomExitScreen/src/components/CustomExitScreen/CustomExitScreen.module.css (1)

8-18: 単語の途中改行が強すぎるため調整を検討。
word-break: break-all; は英単語や URL が不自然に分断されやすいので、読みやすさ重視なら overflow-wrap へ寄せるのが無難です。

♻️ 変更案
 .container {
 	padding: 36px 28px;
 	min-width: 280px;
 	display: flex;
 	flex-direction: column;
 	align-items: center;
 	text-align: center;
-	word-break: break-all;
+	overflow-wrap: anywhere;
+	word-break: normal;
 	background-color: `#ffffff`;
 	border-radius: 12px;
 }

Comment thread CustomExitScreen/package.json Outdated
Comment thread CustomExitScreen/src/App.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@CustomExitScreen/README.md`:
- Around line 28-30: The fenced code block containing "CustomExitScreen" lacks a
language label; update that block by changing the opening triple backticks from
``` to ```text (or ```plaintext) so it becomes ```text\nCustomExitScreen\n```,
ensuring markdownlint no longer flags the missing language.

In `@CustomExitScreen/src/App.tsx`:
- Around line 202-215: The CustomExitScreen prop isSignedIn is declared but
unused; remove it from the component’s public API and call sites: delete
isSignedIn from CustomExitScreenProps and from the function/component parameter
list inside CustomExitScreen, and remove the isSignedIn prop when rendering
CustomExitScreen in App.tsx (and any other callers). If the sign-in state is
actually required, instead wire it into the component internals (e.g., read from
context or use a prop inside the component) and ensure the prop is destructured
and used; otherwise remove all references to isSignedIn to keep types and
implementation consistent.

Comment on lines +28 to +30
```
CustomExitScreen
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

コードブロックに言語指定を追加してください。

静的解析ツール(markdownlint)がこのコードブロックに言語指定がないことを検出しました。textまたはplaintextを指定することで修正できます。

修正案
-```
+```text
 CustomExitScreen
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

28-28: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@CustomExitScreen/README.md` around lines 28 - 30, The fenced code block
containing "CustomExitScreen" lacks a language label; update that block by
changing the opening triple backticks from ``` to ```text (or ```plaintext) so
it becomes ```text\nCustomExitScreen\n```, ensuring markdownlint no longer flags
the missing language.

Comment on lines +202 to +215
<CustomExitScreen
reason={reason}
isSignedIn={isSignedIn}
contentByReason={contentByReason}
logoUrl={logoUrl}
onPrimaryAction={() => {
const content = contentByReason[reason];
if (content.buttonUrl) {
window.location.href = content.buttonUrl;
return;
}
window.location.reload();
}}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for CustomExitScreenProps type definition
rg "type CustomExitScreenProps|interface CustomExitScreenProps" -A 10

Repository: urth-inc/metatell-plugin-template

Length of output: 1613


🏁 Script executed:

#!/bin/bash
# Also search for CustomExitScreen component definition to see all props it accepts
rg "export.*CustomExitScreen|function CustomExitScreen" -A 20

Repository: urth-inc/metatell-plugin-template

Length of output: 5095


🏁 Script executed:

#!/bin/bash
# Check the exact file mentioned - CustomExitScreen.tsx
fd "CustomExitScreen.tsx$" --exec head -100 {}

Repository: urth-inc/metatell-plugin-template

Length of output: 1678


isSignedInプロップが定義されていますが、コンポーネント内で使用されていません。

CustomExitScreenコンポーネントの型定義にはisSignedIn: booleanが含まれており、App.tsx内で渡されていますが、コンポーネント実装ではこのプロップが分割代入されておらず、コンポーネント内のどこでも使用されていません。CustomExitScreenPropsの型定義からisSignedInを削除するか、コンポーネント内で実際に使用する必要があります。

🤖 Prompt for AI Agents
In `@CustomExitScreen/src/App.tsx` around lines 202 - 215, The CustomExitScreen
prop isSignedIn is declared but unused; remove it from the component’s public
API and call sites: delete isSignedIn from CustomExitScreenProps and from the
function/component parameter list inside CustomExitScreen, and remove the
isSignedIn prop when rendering CustomExitScreen in App.tsx (and any other
callers). If the sign-in state is actually required, instead wire it into the
component internals (e.g., read from context or use a prop inside the component)
and ensure the prop is destructured and used; otherwise remove all references to
isSignedIn to keep types and implementation consistent.

akriaueno and others added 4 commits January 29, 2026 00:29
- Add add_metadata.js to generate metadata.json
- Add archive_dist.js to create plugin.zip
- Add rimraf and adm-zip dependencies
- Fix biome version (remove caret)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Set base to "./" for build, "/" for dev
- Set publicPath to "auto" by default (or from env vars)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@CustomExitScreen/src/App.tsx`:
- Around line 209-215: The onPrimaryAction handler uses
contentByReason[reason].buttonUrl and assigns it directly to
window.location.href; add scheme validation to reject unsafe schemes (e.g.,
javascript:, data:) by parsing the URL with the URL constructor or a safe-check
and only allowing a whitelist such as http and https (and other approved schemes
if needed); if the URL is invalid or the scheme is not allowed, fall back to
window.location.reload() (or no-op) instead of navigating. Ensure the check is
applied where content.buttonUrl is read in the onPrimaryAction callback so
unsafe values are never assigned to window.location.href.

Comment on lines +209 to +215
onPrimaryAction={() => {
const content = contentByReason[reason];
if (content.buttonUrl) {
window.location.href = content.buttonUrl;
return;
}
window.location.reload();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

遷移先 URL のスキーム検証を追加してください。

window.location.href への直接代入は javascript: / data: などの不正スキームを許す余地があります。テンプレートであっても安全なスキームに限定するのが無難です。

修正案
onPrimaryAction={() => {
	const content = contentByReason[reason];
-	if (content.buttonUrl) {
-		window.location.href = content.buttonUrl;
-		return;
-	}
+	const rawUrl = content.buttonUrl?.trim();
+	if (rawUrl) {
+		try {
+			const parsed = new URL(rawUrl, window.location.origin);
+			if (parsed.protocol === "http:" || parsed.protocol === "https:") {
+				window.location.href = parsed.toString();
+				return;
+			}
+		} catch {
+			// fall through to reload
+		}
+	}
	window.location.reload();
}}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onPrimaryAction={() => {
const content = contentByReason[reason];
if (content.buttonUrl) {
window.location.href = content.buttonUrl;
return;
}
window.location.reload();
onPrimaryAction={() => {
const content = contentByReason[reason];
const rawUrl = content.buttonUrl?.trim();
if (rawUrl) {
try {
const parsed = new URL(rawUrl, window.location.origin);
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
window.location.href = parsed.toString();
return;
}
} catch {
// fall through to reload
}
}
window.location.reload();
🤖 Prompt for AI Agents
In `@CustomExitScreen/src/App.tsx` around lines 209 - 215, The onPrimaryAction
handler uses contentByReason[reason].buttonUrl and assigns it directly to
window.location.href; add scheme validation to reject unsafe schemes (e.g.,
javascript:, data:) by parsing the URL with the URL constructor or a safe-check
and only allowing a whitelist such as http and https (and other approved schemes
if needed); if the URL is invalid or the scheme is not allowed, fall back to
window.location.reload() (or no-op) instead of navigating. Ensure the check is
applied where content.buttonUrl is read in the onPrimaryAction callback so
unsafe values are never assigned to window.location.href.

@akriaueno
akriaueno merged commit 465a4eb into develop Feb 4, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant