appshot-core について
Appshot の動画・画像スキルが参照する基盤スキルです。プロジェクト構成に加え、アプリ情報、ブランド、機能、ストア情報、画面構造を抽出する手順を示します。
主な機能
- 01Remotion と React の構成を説明
- 02アプリ情報とブランドカラーを抽出
- 03機能とストア情報を確認
- 04ナビゲーションとページ構成を分析
- 05抽出したプロジェクト情報を保存
活用シーン
- 01動画制作向けのアプリ情報準備
- 02スクリーンショット生成向けのデザイン抽出
- 03アプリの画面遷移とページ一覧の把握
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trunghaiy/ appshot --skill appshot-coreインストール後、Claude Code で/appshot-coreを入力して読み込まれたか確認します。
詳細
Appshot Core — 基盤
すべての Appshot スキルの参照スキルです。appshot-videos または appshot-images を実行する前にお読みください。
アーキテクチャ
- Remotion 4.0 + React 18 + Tailwind CSS 3.4
- 設定ではアプリのメタデータとブランドのみを定義します。シーンは設定項目ではなく、カスタムの
.tsxファイルです - 各プロジェクトには、カスタムシーンを Remotion の
<Sequence>+<SceneWrap>で順序付けるオーケストレーターコンポーネント(例:FooPreview.tsx)があります - すべてのプリミティブは、設定の
BrandColorsから導出されるbrandプロパティを介してブランドに対応しています
プロジェクトのコンテキスト(自動挿入)
- パッケージマニフェスト: !
cat package.json 2>/dev/null || cat pubspec.yaml 2>/dev/null || cat app.json 2>/dev/null - アプリの色: !
find . -maxdepth 4 \( -name 'colors.xml' -o -name 'Colors.swift' -o -name 'colors.ts' -o -name 'theme.ts' -o -name 'Color.kt' -o -name 'tailwind.config.js' -o -name 'tailwind.config.ts' \) 2>/dev/null | head -5 | xargs head -50 2>/dev/null - アプリアイコン: !
find . -maxdepth 5 \( -name 'ic_launcher.png' -o -name 'icon.png' \) -type f 2>/dev/null | head -3 - ストアのメタデータ: !
cat fastlane/metadata/en-US/full_description.txt 2>/dev/null || cat fastlane/metadata/en-US/description.txt 2>/dev/null || cat fastlane/metadata/android/en-US/full_description.txt 2>/dev/null - README の抜粋: !
head -80 README.md 2>/dev/null
抽出とコンテキストのキャッシュ
appshot-videos と appshot-images は、どちらもこの抽出プロセスを使用します。結果は .appshot-context.json にキャッシュされるため、抽出はプロジェクトごとに一度だけ実行されます。
キャッシュされたコンテキストを確認する
スキャンの前に、プロジェクトのルートに .appshot-context.json があるか確認してください。存在する場合は:
- 読み込んで、概要をユーザーに提示します
- 「以前の抽出結果が見つかりました。この内容で問題ないか確認するか、再スキャンしますか?」と尋ねます
- 確認された場合は、抽出をスキップして、呼び出し元スキルの次のフェーズに進みます
- 再スキャンを求められた場合は、以下の抽出を実行してファイルを上書きします
抽出手順
ステップ 1:フレームワークを検出する
以下の順序でマーカーファイルを確認します。最初に一致したものが見つかった時点で終了します。
| フレームワーク | マーカーファイル |
|---|---|
| Expo / React Native | app.json、app.config.ts、app.config.js |
| Flutter | pubspec.yaml |
| iOS(Swift) | *.xcodeproj/、*.xcworkspace/、Package.swift |
| Android(Kotlin/Java) | app/src/main/AndroidManifest.xml、build.gradle.kts、build.gradle |
| Next.js | next.config.js、next.config.ts、next.config.mjs |
| SvelteKit | svelte.config.js |
| Nuxt | nuxt.config.ts、nuxt.config.js |
| Astro | astro.config.mjs、astro.config.ts |
| Remix | remix.config.js、app/root.tsx(@remix-run のインポートを含むもの) |
| Vite(React/Vue/Solid) | vite.config.ts、vite.config.js(package.json で react/vue/solid を確認) |
ls または find . -maxdepth 3 を使用して確認します。一致するものがない場合は、フレームワークを検出できなかったことを記録して次に進みます。ユーザーがアプリについて口頭で説明する場合もあります。
ステップ 2:アプリの識別情報を抽出する
Expo / React Native:
- 名前:
app.json->expo.nameまたはname - 説明/タグライン:
app.json->expo.descriptionまたはdescription - バンドル ID:
app.json->expo.ios.bundleIdentifierまたはexpo.android.package - あわせて確認:
package.json->name、description
Flutter:
- 名前:
pubspec.yaml->name: - 説明:
pubspec.yaml->description: - あわせて確認:
android/app/src/main/AndroidManifest.xml->android:label、ios/Runner/Info.plist->CFBundleDisplayName
iOS(Swift):
- 名前:
Info.plist->CFBundleDisplayNameまたはCFBundleName - バンドル ID:
Info.plist->CFBundleIdentifier - あわせて確認:
project.pbxprojで製品名を確認
Android(Kotlin/Java):
- 名前:
app/src/main/res/values/strings.xml-><string name="app_name"> - パッケージ:
AndroidManifest.xml->package属性 - あわせて確認:
build.gradle.ktsまたはbuild.gradle->applicationId
Next.js:
- 名前:
package.json→name。あわせてnext.config.*→env.APP_NAMEを確認 - 説明/タグライン:
package.json→description。あわせてapp/layout.tsx→metadata.descriptionを確認 - URL:
.envまたは.env.local→NEXT_PUBLIC_APP_URL、NEXT_PUBLIC_SITE_URLを確認
SvelteKit:
- 名前:
package.json→name - 説明:
package.json→description。src/app.html→<title>、<meta name="description">を確認
Vite(React/Vue):
- 名前:
package.json→name - 説明:
package.json→description。index.html→<title>、<meta name="description">を確認
ステップ 3:ブランドカラーを抽出する
色の定義を検索します。各フレームワークで最初に見つかったものを読み取ります。
Expo / React Native — 検索パス(順番どおり):
src/theme.tsまたはsrc/theme.jssrc/colors.tsまたはsrc/constants/colors.tstheme/colors.tsまたはtheme/index.tstailwind.config.jsまたはtailwind.config.ts->theme.extend.colorsapp.json->expo.primaryColor、expo.backgroundColor
確認する項目:名前付きの色のエクスポート(primary、secondary、background、surface、accent、success、danger/error)、16 進数文字列、rgb() 値。
Flutter — 検索パス:
lib/theme.dartまたはlib/theme/app_theme.dartlib/theme/colors.dartまたはlib/constants/colors.dartlib/main.dart->ThemeData()
確認する項目:Color(0xFF...) 定数、primaryColor、accentColor、colorScheme の定義。
iOS(Swift) — 検索パス:
*.xcassets/Colors/**/*.colorset/Contents.json—componentsを解析して RGB を確認**/Theme.swiftまたは**/Colors.swiftまたは**/ColorScheme.swift
確認する項目:静的な Color プロパティ、UIColor の 16 進数イニシャライザー、アセットカタログの色の定義。
Android — 検索パス:
app/src/main/res/values/colors.xml—<color name="primary">#RRGGBB</color>app/src/main/res/values/themes.xml—<item name="colorPrimary">**/ui/theme/Color.kt— Jetpack Compose のColor()定義
Web(すべてのフレームワーク) — 検索パス(順番どおり):
tailwind.config.jsまたはtailwind.config.ts→theme.extend.colorssrc/styles/globals.cssまたはsrc/app/globals.css→ CSS カスタムプロパティ(--primary、--backgroundなど)src/theme.*またはsrc/lib/theme.*src/styles/variables.css
ステップ 4:アプリアイコンを探す
| フレームワーク | 確認する場所 |
|---|---|
| Expo/RN | app.json の expo.icon フィールド(通常は ./assets/icon.png) |
| Flutter | android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png または ios/Runner/Assets.xcassets/AppIcon.appiconset/ |
| iOS | *.xcassets/AppIcon.appiconset/ -> 最も大きい .png を探す |
| Android | app/src/main/res/mipmap-xxxhdpi/ic_launcher.png |
| Web(すべて) | public/favicon.svg、public/favicon.ico、public/logo.svg、public/logo.png、app/favicon.ico、src/assets/logo.* |
ステップ 5:機能を抽出し、メタデータを保存する
これらのソースはすべてのフレームワークで使用できます。
| ソース | パス | 抽出する内容 |
|---|---|---|
| Fastlane(iOS) | fastlane/metadata/en-US/description.txt or full_description.txt | ストアの説明全文 |
| Fastlane(iOS) | fastlane/metadata/en-US/subtitle.txt | App Store のサブタイトル |
| Fastlane(iOS) | fastlane/metadata/en-US/keywords.txt | 検索キーワード |
| Fastlane(Android) | fastlane/metadata/android/en-US/full_description.txt | Play Store の説明 |
| Fastlane(Android) | fastlane/metadata/android/en-US/short_description.txt | 短い説明 |
| README | README.md | 最初の 80 行 — アプリの説明、機能の箇条書き |
| CLAUDE.md | CLAUDE.md | 存在する場合はプロジェクトの説明 |
ステップ 5b:ASO キーワードを抽出する
Apple と Google は現在、ビジュアルインテリジェンスを通じて、スクリーンショットや動画内のテキストを読み取ることができます。高価値の検索語句をオーバーレイテキストに自然に取り入れられるよう、キーワードリストを抽出してください。
主なソース: fastlane/metadata/en-US/keywords.txt(カンマ区切り)。これは開発者独自のキーワード戦略であり、権威あるリストとして使用してください。
代替ソース(Fastlane のキーワードがない場合):次の情報からキーワード候補を 8~12 個導き出します。
- App Store のサブタイトル(
fastlane/metadata/en-US/subtitle.txt)— 個々の語句に分割する - ストアの説明 — 繰り返し登場する名詞と動作を表す動詞を抽出する
- 抽出した機能名 — 機能名をユーザー向けの検索語句に対応付ける
- ステップ 6 のシグナル表にあるアプリカテゴリの用語
出力: 重要度の高い順に並べたキーワードリスト。「habits」「streak」のような単語と、「reading tracker」「book log」のような短いフレーズの両方を含めます。キーワードは最大 12 個にします。
ステップ 6:アプリカテゴリを推定する
抽出したキーワード、説明、機能名に基づき、アプリのカテゴリを推定します。
| カテゴリ | シグナル |
|---|---|
| 習慣トラッキング | streak, habit, daily, routine, consistency, track days |
| フィットネス | workout, exercise, gym, reps, sets, calories, steps |
| ファイナンス | budget, expense, money, savings, transactions, spending |
| 生産性 | task, todo, note, project, organize, calendar |
| ソーシャル | chat, message, friend, follow, post, share |
| 教育 | learn, quiz, study, course, flashcard, practice |
| 健康 | meditation, sleep, mood, journal, wellness, mindful |
| 旅行 | trip, itinerary, booking, destination, explore |
| 食事 | recipe, meal, restaurant, cooking, ingredients |
| SaaS | dashboard, subscription, plan, pricing, onboarding, workspace, team, analytics |
| E コマース | cart, checkout, product, shop, order, payment, inventory |
| 開発者向けツール | API, SDK, CLI, documentation, playground, sandbox, webhook |
| コンテンツ/出版 | blog, article, post, publish, editor, CMS, newsletter |
| AI/ML | model, inference, prompt, generate, embed, vector, chat, completion |
| コミュニティ | forum, discussion, thread, community, member, profile |
明確に一致するカテゴリがない場合は「カテゴリ未分類」と記載してください。ユーザーが уточします。
詳細分析
アプリの識別情報や色だけでなく、アプリが実際に何をするのかを理解してください。
画面一覧。 ナビゲーションファイルを調べ、すべての画面を特定します。
- React Native/Expo:
App.tsx、すべての*Navigator.tsx、*Router.tsx、タブバー設定、react-navigationの設定を読む - Flutter:
lib/main.dart、ルーター設定、GoRouterまたはMaterialAppのルートを読む - iOS:ストーリーボードの参照、
UITabBarControllerの設定、SwiftUI のNavigationStack/TabViewを読む - 次の項目を探します:画面名、タブのラベル、ナビゲーション構造(タブ、スタック、ドロワー) ナビゲーションのクローム抽出。 リアルなモック画面を作成するため、ユーザーが実際に目にするナビゲーションパターンを抽出します。
- ナビゲーションの種類:タブ、スタック、ドロワー、またはその組み合わせ
- タブバー:各タブのラベルテキスト、アイコンの説明(例:「家のアイコン」「虫眼鏡」「人の円形アイコン」)、デフォルト/ホームのタブ
- ナビゲーションヘッダー:タイトルのスタイル(大きなタイトルまたはインライン)、戻るボタンの有無、右側のバー項目(例:「歯車アイコン」「プラス」「ベル」)
- ステータスバーのスタイル:ライトコンテンツ(暗い背景向けの白いテキスト)またはダークコンテンツ(明るい背景向けの黒いテキスト)
- ホームインジケーター:最新の iPhone ではすべて表示(下部のバー)、古いデバイスでは非表示
確認する場所:
- React Native / Expo:ナビゲーターファイル内の
screenOptions、tabBarLabel、tabBarIcon、headerTitle、headerRight、navigationOptions - Flutter:
BottomNavigationBarの項目、AppBarのタイトル/アクション、Scaffoldの設定 - iOS:
UITabBarItemの設定、navigationItem.title、navigationItem.rightBarButtonItems、SwiftUI の.tabItem/.navigationTitle/.toolbar - Android:
BottomNavigationViewのメニュー XML、Toolbar/TopAppBarの設定、Jetpack Compose のNavigationBar項目
ページ/ルート一覧(ウェブ)。 ルーターの設定を調べて、すべてのページを見つけます。
- Next.js:
app/ディレクトリ構造を確認(page.tsxを含む各フォルダーがルート)、またはpages/ディレクトリを確認 - SvelteKit:
src/routes/ディレクトリ構造を確認(+page.svelteを含む各フォルダーがルート) - Vite/React:ルーターの設定(
react-router、tanstack-router)を確認するか、ルート定義を検索 - 確認する項目:ページ名、レイアウト構造、保護されたルートと公開ルート
ランディングページの分析。 ランディングページ/マーケティングページがある場合:
- ヒーローの見出しとサブ見出しを抽出します
- 機能紹介のセクションを抽出します
- CTA のテキスト(「始める」「無料で登録」など)を記録します
- 料金体系が表示されている場合は記録します
機能の分析。 見つかった各画面について、コンポーネントファイルを読み、以下を把握します。
- 表示されるデータ
- ユーザーが実行できる操作
- 見た目に特徴や独自性を与えている要素
UI パターンの抽出。 コンポーネントのスタイルを調べ、アプリのビジュアルデザイン言語を把握します。実際のアプリの外観と一致しないモック画面は信頼性を損ないます。この情報でその差を埋めます。
抽出する項目:
- 角丸:ボタン、カード、入力欄の角丸(例:「完全に丸みを帯びたボタン」「角が 8px のカード」「ピル形の入力欄」)
- 余白のリズム:コンテナー内や要素間に使われているパディング/マージンの値(例:「左右のパディング 16px」「セクション間の余白 24px」)
- タイポグラフィ:フォントファミリー(カスタムまたはシステム)、見出しのウェイト(例:700 と 800)、本文のサイズ、セクション見出しにすべて大文字を使っているか
- ボタンのスタイル:形状(角丸長方形、ピル形、円形)、サイズ、塗りつぶしかアウトラインか、アイコンのみのボタン
- カードのスタイル:背景色、境界線、影の深さ、ガラス/ぼかし効果
- アイコンライブラリ:使用しているアイコンセット(SF Symbols、Material Icons、Lucide、カスタム SVG、Ionicons)
- 特殊な UI 要素:波形ビジュアライザー、進捗リング、グラデーション背景、タグ/チップのスタイル、バッジ表示
確認する場所:
- React Native / Expo:
StyleSheet.create()ブロック、インラインのstyle={}、NativeWind/Tailwind クラス、コンポーネントライブラリのインポート(react-native-paper、tamagui、gluestack) - Flutter:ウィジェットファイルやテーマデータ内の
BoxDecoration、BorderRadius、TextStyle、ButtonStyle - iOS(Swift):
.cornerRadius、.font()、.padding()、UIFontディスクリプター、SwiftUI/UIKit 内の SF Symbol 名 - Android:
shapes.xml、styles.xml、Compose のRoundedCornerShape()、MaterialTheme.typography、Material アイコンのインポート
価値提案/差別化要素。 README、ストアの説明、CLAUDE.md、マーケティング文、またはランディングページから抽出します。
- このアプリはどのような問題を解決しますか?
- 競合製品との違いは何ですか?
- どのような感情的価値を約束しますか? 主要なアクションループ。 ユーザーが最も頻繁に行う単一のアクションを特定します。
- ユーザーはアプリを開くたびに何をしますか?
- タップや手順はいくつ必要ですか?
- 完了後、どのようなフィードバックが得られますか?
テーマの検出。 アプリがライトモード/ダークモードに対応しているか確認します。
- 設定内のテーマ切り替え、
useColorScheme、Appearance、ThemeProvider、darkModeを探します - デフォルトまたは主なテーマを記録します
- アプリがダークテーマのみかどうかを記録します(Spotify や映画アプリなど、一部のブランドが該当します)
コンテキストを保存する
抽出後、ユーザーの確認を得てから、結果をプロジェクトのルートにある .appshot-context.json に保存します。
{
"version": 2,
"extractedAt": "ISO timestamp",
"app": { "name": "", "tagline": "", "icon": "", "platform": "", "framework": "", "url": "string or null" },
"brand": { "primary": "", "primaryLight": "", "background": "", "surface": "", "textPrimary": "", "textSecondary": "", "success": "", "danger": "", "accent": "" },
"category": "string",
"theme": "light | dark | both",
"themeDefault": "light | dark",
"navigation": {
"type": "tabs | stack | drawer | tabs+stack",
"tabs": [{ "label": "", "iconDescription": "", "isDefault": false }],
"headerStyle": "largeTitle | inline",
"statusBarStyle": "light | dark"
},
"screens": [{ "name": "", "tab": "", "description": "" }],
"pages": [{ "route": "", "name": "", "description": "", "layout": "" }],
"landingPage": { "heroHeadline": "string or null", "heroSubheadline": "string or null", "cta": "string or null", "featureSections": ["string"] },
"uiPatterns": {
"borderRadius": { "button": "string", "card": "string", "input": "string" },
"spacing": { "horizontal": "string", "sectionGap": "string" },
"typography": { "fontFamily": "string or 'system'", "headingWeight": "number", "bodySize": "string", "capsHeaders": "boolean" },
"buttonStyle": "string — e.g., 'pill filled, 56px height' or 'rounded-lg outline'",
"cardStyle": "string — e.g., 'dark surface, 16px radius, no border, subtle shadow'",
"iconLibrary": "string — e.g., 'SF Symbols' or 'Ionicons' or 'custom SVG'",
"specialElements": ["string — e.g., 'waveform visualizer', 'gradient background', 'pill-shaped tags'"]
},
"features": ["string"],
"coreAction": "string",
"valueProps": ["string"],
"keywords": ["string — ranked ASO keywords, most important first, max 12"],
"screenshots": [{
"referenceFile": "original file path (for re-reading if needed)",
"screen": "matched screen name",
"description": "what this screenshot shows",
"visualSpec": "structured visual analysis — layout, colors, components, typography, special elements (the actual styling reference for mock UI)"
}],
"storeDescription": "string or null",
"sources": { "name": "source file", "colors": "source file", "keywords": "source file or 'derived'" }
}
スキーマ v2 に関する注意事項:
screenshots— ユーザーが自動モックを選択した場合は空の配列、実際のスクリーンショットを提供した場合は値を設定しますapp.frameworkの有効な値:"expo"|"flutter"|"ios"|"android"|"nextjs"|"sveltekit"|"nuxt"|"astro"|"remix"|"vite"|"web"app.platformの有効な値:"ios"|"android"|"both"|"web"pages— Web のルート一覧(モバイルアプリの場合は空の配列)landingPage— 抽出したランディングページのデータ(モバイルアプリの場合は null)
調査結果を提示する
見つかった内容を簡潔にまとめます。ユーザーが確認できるよう、必ず出典ファイルを明記します。
I scanned your project and found:
- **App:** [name] (from [source file])
- **Tagline:** [text] (from [source file])
- **Platform:** [iOS/Android/both] (from [evidence])
- **Colors:** primary [hex], background [hex], surface [hex], ... (from [source file])
- **Icon:** [file path]
- **Category:** [category] based on [keywords/description]
- **Store description:** [found at path / not found]
- **Theme:** [light only / dark only / both — default is X]
**Navigation:** [tabs / stack / drawer] — [header style: large title / inline]
**Tab bar:** [Tab1 (icon)] | [Tab2 (icon)] | [Tab3 (icon)] — default: [Tab1]
**Status bar:** [dark content / light content]
**Screen inventory:**
- [Tab 1]: [ScreenName] — [what it shows]
- [Tab 2]: [ScreenName] — [what it shows]
- [Modal/Detail]: [ScreenName] — [what it shows]
- ...
**Page inventory (web):**
- `/` — [Landing page] — [hero headline, feature sections]
- `/dashboard` — [Dashboard] — [what it shows]
- `/settings` — [Settings] — [configuration options]
- ...
**Landing page:** [hero headline] / [CTA text] / [number of feature sections]
**URL:** [extracted URL or "not found"]
**Key features:** [bullet list of 3-5 main features extracted from code/docs]
**Core action loop:** [what users do most — e.g., "tap Log -> enter page number -> save (3 taps, ~5 seconds)"]
**Value props:**
- [Primary differentiator]
- [Secondary differentiator]
- [Emotional promise]
**UI patterns:** [font family], [heading weight], [button style], [card style], [border radius], [icon library]
**Special elements:** [waveform, gradient bg, pill tags, ...]
**ASO keywords:** [keyword1], [keyword2], [keyword3], ... (from [fastlane/keywords.txt / derived])
**Pre-filled config:**
[Show the AppConfig object with app, brand, and video sections filled in]
Does this look right? Anything to correct before I proceed?
不足している項目があれば、その旨を伝えます。「ブランドカラーが見つかりませんでした。テーマファイルはありますか?それとも一緒に色を選びましょうか?」
確認を得てから、以下のスクリーンショット収集手順に進みます。
スクリーンショットを収集する(任意)
抽出内容の確認後、視覚的な参考資料として実際のアプリのスクリーンショットを提供するか、ユーザーに尋ねます。スクリーンショットは参考資料であり、コンテンツではありません。最終出力に埋め込まれることはありません。代わりに、視覚的な参考資料の分析によって、スクリーンショットから正確な色、形、余白、タイポグラフィを抽出します。このスキルはその情報を使い、実際のアプリに似たアニメーション付きのモック UI を作成します。
AskUserQuestion({
questions: [{
question: "Do you have screenshots of your app to use as screen content?",
header: "Screenshots",
options: [
{ label: "Yes, I'll provide screenshots", description: "Highest fidelity — your screenshots are analyzed and the mock UI is built to match exactly." },
{ label: "No, generate mock screens", description: "Skill builds UI from extracted code patterns. Good for most cases." }
],
multiSelect: false
}]
})
ユーザーがスクリーンショットを提供した場合:
- スクリーンショットのファイルパスを送るか、会話にドロップするようユーザーに依頼します
- 各スクリーンショットの画像を読み取り、何が表示されているかを把握します
- 各スクリーンショットを、抽出内容にある画面に対応付けます:
I mapped your screenshots to app screens: - recording.png → Voice Recording screen - transcription.png → Transcription Detail screen - scan-preview.png → Document Scan screen - home.png → Home / Notes List screen Missing: Settings screen (will use mock UI for this one) Does this mapping look right? - 各スクリーンショットに対して視覚的な参考資料の分析を実行します(以下を参照)
- 不足している画面、つまりスクリーンショットがない画面を記録します。これらの画面では、
uiPatternsに基づいて作成したモック UI を使用します。 - 対応付けと視覚的な分析を
.appshot-context.jsonのscreenshotsに保存します
ビジュアルリファレンスの分析
重要: スクリーンショットが提供されている場合、このステップは必須です。これを行わないと、モックシーンがありきたりな見た目になり、実際のアプリと一致しません。
提供されたスクリーンショットごとに画像を確認し、構造化されたビジュアル仕様を作成します。この仕様は次の2つの用途で使います。
- そのまま使用するシーン:スクリーンショットを
<Img>でそのまま使用し、仕様で表示内容を確認します - モックシーンと不足部分の補完:アニメーション用、スクリーンショットがない場合、またはコンテンツを変更する場合など、シーンにモックUIが必要なときに、仕様をスタイルの基準にします
スクリーンショットごとに記録する内容:
## [filename] → [Screen Name]
**Layout:**
- [top-to-bottom description of what appears: nav bar, content sections, bottom bar]
- [approximate vertical proportions: "nav bar 8%, content 78%, tab bar 8%, home indicator 6%"]
**Colors (sample from the image):**
- Background: [exact color, e.g., "#0A1628 — very dark navy, NOT pure black"]
- Cards/surfaces: [e.g., "#1A2940 — slightly lighter navy with subtle border"]
- Accent: [e.g., "#3BB8E0 — teal/cyan for CTAs and active states"]
- Text primary: [e.g., "#E8ECF1 — off-white"]
- Text secondary: [e.g., "#6B7A8D — muted blue-grey"]
- Highlight/badge: [e.g., "#E5C044 — warm yellow for 'Voice Recording' badge"]
**Components (describe exactly what you see):**
- Back button: [e.g., "48px rounded-square (#1A2940 bg), white chevron, no text"]
- Nav actions: [e.g., "three icon buttons: more (⋯), share (↑), delete (🗑), same rounded-square style"]
- Type badge: [e.g., "'Voice Recording' in yellow text on dark pill, microphone emoji prefix"]
- Content cards: [e.g., "rounded-xl (~16px), #1A2940 bg, 1px border #2A3A50, 20px padding"]
- Buttons: [e.g., "'Enhance · 2 credits' — outlined, teal border, rounded-full, 48px height"]
- Tab bar: [e.g., "3 tabs with SF Symbol icons above labels, active=teal, inactive=grey, dark bg"]
**Typography:**
- Headings: [e.g., "32px, weight 700, white — 'May 28 at 11:03 PM'"]
- Section headers: [e.g., "ALL CAPS, 13px, weight 600, letter-spacing 1px, teal — 'TRANSCRIPTION'"]
- Body: [e.g., "16px, weight 400, off-white, line-height ~1.5"]
- Labels: [e.g., "12px, weight 500, muted grey — metadata, tab labels"]
**Special elements:**
- [e.g., "Waveform: grey rounded-rect container, horizontal bars, blue play button 48px circle"]
- [e.g., "Speed pills: row of 4 (0.75x, 1x, 1.5x, 2x), active has teal bg, others have dark bg"]
- [e.g., "Coral/red horizontal divider line below title, 2px height"]
- [e.g., "Vietnamese flag emoji + 'VI' in language selector pill"]
分析時のルール:
- 漏れなく記録してください。 スクリーンショットに表示されている、個別のUI要素をすべて列挙し、数を記録します。15個の要素が見える場合は、15個すべてを記録してください。見落としやすい一般的な要素には、ナビゲーションバーのすべてのアクションボタン(戻るボタンだけではありません)、タブバーのアイコン、速度/再生コントロール、CTA、バッジ表示、クレジット数、言語セレクター、区切り線、ホームインジケーターなどがあります。
- 色を正確に記述してください。推測せず、実際の画像から色を取得します。「#0A1628」と「#000000」は大きく異なります。
- コンポーネントの形状を正確に記述してください。「角丸ボタン」ではなく、「半径12pxの48px角丸正方形」のように記述します。
- 余白を記録してください。「カード内側のパディングは20px」「要素間の間隔は12px」のように記述します。
- 存在しない要素も記録してください。「カードに影はない」「ボタンに枠線はない」「リスト項目間に区切り線はない」のように記述します。
- UIコード抽出から得た
uiPatternsと矛盾する場合、この分析を優先します。スクリーンショットが視覚的な正解です。 - ビジュアル仕様のサイズは、スマートフォンの論理ピクセル単位です。 886px幅のApp Store Previewキャンバスは、iPhone 16 Pro(393px)の2.25倍の幅です。シーンコードを書く際は、すべてのサイズを2.25倍にしてください。48pxのボタンは108px、16pxのテキストは36px、11pxのタブラベルは25pxになります。コードガイドには完全な倍率表があります。
スクリーンショットの要件(ユーザーに伝えてください):
- 解像度は問いません。これらは参照画像であり、出力には埋め込まれません。
- 余計な表示がない状態にしてください。通知やデバッグ用バナーがない状態であれば、実際のデザインを分析できます。
- 画面や機能ごとにスクリーンショットを1枚用意してください。同じ画面の複数の状態も歓迎します。
- スキルがスクリーンショットを分析し、アプリの外観に正確に一致するアニメーション付きモックUIを作成します。
ユーザーが提供を辞退した場合は、screenshots: []を設定して続行します。スキルは従来どおり、抽出したパターンからモックUIを生成します。
このステップの後、呼び出し元のスキルの次のフェーズに進みます。
プロジェクト構成
src/
app-config.ts # App metadata, brand colors, video settings
Root.tsx # Remotion entry — registers the composition
[AppName]Preview.tsx # Orchestrator — sequences scenes with <Sequence> + <SceneWrap>
scenes/
S1_[Name].tsx # Custom scene components (one per scene)
S2_[Name].tsx
...
components/ # Shared primitives library (imported from template)
PhoneFrame.tsx # Device frame with bezel, Dynamic Island, spring entrance
AmbientBackground.tsx # Animated gradient bg with floating orbs
Caption.tsx # Word-by-word animated caption with frosted pill
FadeIn.tsx # Directional spring fade-in wrapper
SceneWrap.tsx # Fade envelope for scene transitions
AppIcon.tsx # App icon with optional glow pulse
TypeWriter.tsx # Character-by-character text reveal
BrowserFrame.tsx # macOS browser chrome (light/dark)
AnimatedCursor.tsx # Bezier-interpolated cursor with click ripple
index.ts
config.ts # Type definitions, device dimensions, defaults
public/
icon.png # App icon
*.png # Screenshots, assets
設定スキーマ
設定にはアプリの識別情報、ブランドカラー、動画設定が含まれます。シーンはカスタムコンポーネントのため、設定には含まれません。
interface AppConfig {
app: {
name: string; // Display name
tagline: string; // One-line value prop
icon: string; // Filename in public/
platform: "ios" | "android" | "both";
};
brand: BrandColors;
video: {
fps: number; // 30
width: number; // 886
height: number; // 1920
device: DevicePreset;
backgroundMusic?: string; // Filename in public/
backgroundMusicVolume?: number; // 0-1
};
}
interface BrandColors {
primary: string; // Main brand color (hex)
primaryLight: string; // Tinted background
background: string; // App background
surface: string; // Card/container background
textPrimary: string; // Main text
textSecondary: string; // Secondary/muted text
success: string; // Positive states
danger: string; // Destructive/error states
accent?: string; // Optional secondary brand color
}
プリミティブライブラリ
以下のすべてのコンポーネントはsrc/components/で使用できます。"../components"からインポートしてください。
PhoneFrame
ベゼル、サイドボタン、ノッチ/Dynamic Islandを備えたデバイスのモックアップです。任意のコンテンツを囲み、実際のスマートフォンの画面のように見せます。
| プロパティ | 型 | デフォルト値 | 説明 |
|---|---|---|---|
children | ReactNode | 必須 | 画面のコンテンツ |
device | DevicePreset | "iphone-15" | デバイスモデル |
delay | number | 0 | 表示アニメーションの遅延(フレーム数) |
scale | number | 1 | 拡大縮小率 |
screenBackground | string | "#FAFAF8" | 画面内の背景色 |
使用場面: スマートフォン内にアプリUIを表示するすべてのシーン。モバイルアプリの動画の中心的な視覚要素です。
AmbientBackground
ぼかした球体が浮遊する、アニメーション付きのグラデーション背景です。各シーンの雰囲気を演出します。
| プロパティ | 型 | デフォルト値 | 説明 |
|---|---|---|---|
brand | BrandColors | 必須 | グラデーションと球体の色 |
variant | "light" | "medium" | "deep" | "dark" | "light" | 強さ/雰囲気 |
使用場面: すべてのシーンの背景に使用します。機能や解決策のシーンには"light"または"medium"、課題や対比のシーンには"dark"、感情や実績を示すシーンには"deep"を使用します。
Caption
下部に固定され、単語ごとに表示されるアニメーション付きのテキストオーバーレイです。読みやすいように、暗いピル型の背景を使用します。
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | 必須 | キャプションのテキスト(最大8単語) |
delay | number | 0 | 表示開始までの遅延(フレーム) |
fontSize | number | 44 | フォントサイズ(px) |
maxWidth | number | 820 | コンテナーの最大幅 |
使用する場面: 各シーンに1つ配置してください。音声をオフにして視聴するユーザーに、キャプションでストーリーを伝えます。
FadeIn
Remotion のスプリングを使った方向指定可能なフェードインアニメーションで、任意の要素をラップします。
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | 必須 | アニメーションを適用するコンテンツ |
delay | number | 0 | 開始フレーム |
direction | "up" | "down" | "left" | "right" | "none" | "up" | スライドの方向 |
distance | number | 40 | スライド距離(px) |
className | string | — | Tailwind クラス |
style | CSSProperties | — | インラインスタイル |
使用する場面: カードの表示、見出しの登場、アニメーションで表示したいあらゆる要素に使用します。
SceneWrap
各シーンをラップし、フェードイン/フェードアウトのトランジション(各12フレーム)を適用します。オーケストレーターで使用します。
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | 必須 | シーンのコンテンツ |
durationInFrames | number | 必須 | シーンの合計時間 |
fadeIn | boolean | true | フェードインを有効にする |
fadeOut | boolean | true | フェードアウトを有効にする |
使用する場面: オーケストレーターコンポーネントで、各シーンを <Sequence> と <SceneWrap> の内側に配置します。
AppIcon
オプションのアニメーション付きグロー効果を加えて、アプリアイコンを表示します。
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | 必須 | public/ 内のファイル名または URL |
size | number | 120 | アイコンのサイズ(px) |
glow | boolean | false | 脈動するグローを有効にする |
glowColor | string | "rgba(0,122,255,0.4)" | グローの色 |
使用する場面: CTA/クロージングシーンに使用します。ブランドの認知度向上につながります。
TypeWriter
点滅するカーソルをオプションで表示できる、文字単位のテキスト表示アニメーションです。
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | 必須 | 入力するテキスト全体 |
startFrame | number | 必須 | 入力を開始するフレーム |
charsPerFrame | number | 2 | 入力速度 |
className | string | — | Tailwind クラス |
cursor | boolean | true | 点滅するカーソルを表示する |
cursorColor | string | "#2196F3" | カーソルの色 |
使用する場面: 検索バー、テキスト入力欄、チャットメッセージなど、ユーザーの入力をシミュレートしたい場面に使用します。
BrowserFrame
信号機、アドレスバー、コンテンツ領域を備えた macOS スタイルのブラウザー枠です。ライトとダークのバリエーションがあります。
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | 必須 | ページコンテンツ |
url | string | "app.example.com" | アドレスバーのテキスト |
delay | number | 0 | 表示アニメーションの遅延(フレーム) |
scale | number | 1 | 拡大縮小率 |
variant | "light" | "dark" | "light" | ブラウザー枠のテーマ |
AnimatedCursor
ベジェ補間を使用し、キーフレーム位置間を移動するカーソルです。クリック時の波紋効果も表示します。
| Prop | Type | Default | Description |
|---|---|---|---|
keyframes | CursorKeyframe[] | 必須 | { frame, x, y, click? } の位置 |
visible | boolean | true | カーソルの表示/非表示 |
アニメーションパターン
削除されたプリミティブ(FloatingCard、StatCard、HeatMap、ProgressBar、AppStoreBadge、IconSet)は、エージェントをハードコードされたサイズに制約するシンプルなラッパーでした。代わりに、キャンバスに合わせて適切なサイズに調整し、これらのパターンをインラインで記述してください。
アニメーション付きカウンター(変更前/変更後の統計):
const counter = spring({ frame, fps, delay: 10, config: { mass: 0.6, damping: 18, stiffness: 80 } });
const value = Math.round(interpolate(counter, [0, 1], [beforeValue, afterValue]));
セル単位で塗りつぶすグリッド(ヒートマップ/貢献グリッド):
{cells.map((cell, i) => {
const cellEnter = spring({ frame, fps, delay: baseDelay + i * 0.4, config: { mass: 0.2, damping: 10, stiffness: 180 } });
return <div key={i} style={{ opacity: 0.3 + cellEnter * 0.7, transform: `scale(${0.6 + cellEnter * 0.4})` }} />;
})}
プログレスバーの塗りつぶし:
const progress = interpolate(frame, [delay, delay + 30], [0, targetPercent], { extrapolateRight: "clamp" });
<div style={{ width: `${progress}%`, height, backgroundColor: color, borderRadius }} />
スプリングインで表示するカード(FloatingCard の代替):
const entrance = spring({ frame, fps, delay, config: { mass: 0.8, damping: 14, stiffness: 120 } });
<div style={{ opacity: entrance, transform: `translateY(${(1 - entrance) * 20}px)`, borderRadius, padding, background }}>
App Store/Play Store バッジ(マーケティング CTA シーン用):
// iOS badge — black rounded rect with Apple SVG
<div style={{ background: "#000", borderRadius: 14, padding: "14px 28px", display: "flex", alignItems: "center", gap: 12 }}>
<svg width="28" height="34" viewBox="0 0 28 34" fill="white">
<path d="M23.2 17.8c0-3.6 2.9-5.3 3-5.4-1.6-2.4-4.2-2.7-5.1-2.8-2.1-.2-4.2 1.3-5.3 1.3-1.1 0-2.8-1.2-4.6-1.2C8.5 9.8 6 11.2 4.6 13.5c-2.8 4.9-.7 12.1 2 16.1 1.3 1.9 2.9 4.1 5 4 2-.1 2.8-1.3 5.2-1.3 2.4 0 3.1 1.3 5.2 1.3 2.2 0 3.5-2 4.8-3.9 1.5-2.2 2.1-4.3 2.2-4.4 0-.1-4.2-1.6-4.2-6.4zM19.3 7.5c1.1-1.3 1.8-3.2 1.6-5-1.6.1-3.4 1-4.6 2.3-1 1.2-1.9 3-1.6 4.8 1.7.2 3.5-.9 4.6-2.1z" />
</svg>
<div>
<div style={{ fontSize: 16, color: "rgba(255,255,255,0.8)" }}>Download on the</div>
<div style={{ fontSize: 28, fontWeight: 600, color: "#FFF" }}>App Store</div>
</div>
</div>
デバイスプリセット
| プリセット | 画面 | ノッチ | ストア |
|---|---|---|---|
iphone-16-pro | 393x852 | Dynamic Island | App Store |
iphone-15 | 375x812 | Dynamic Island | App Store |
ipad-pro-13 | 1024x1366 | なし | App Store(iPad) |
pixel-9 | 412x915 | パンチホール | Play Store |
ストアとデバイスの対応
| ストア | コンポジション ID のサフィックス | デバイスプリセット |
|---|---|---|
| App Store | -AppStore | iphone-16-pro |
| Play Store | -PlayStore | pixel-9 |
複数のストアに対応するプロジェクトでは、Root.tsx にストアごとに 1 つの <Composition> を登録します。シーンは共通で、デバイスフレーム(およびナビゲーションのクロームスタイル)のみが異なります。どちらもキャンバスサイズは 886×1920 です。注:App Store プレビュー動画では、CTA にストアバッジを含めません(ストアの掲載ページ内では重複するため)。マーケティング動画では、CTA にインラインのストアバッジを含めます(SVG スニペットについては、上記の「アニメーションパターン」を参照)。
出力先
AppShot は、異なるビジュアルルールを適用する 2 つの出力先に対応しています。
| 出力先 | 用途 | デバイスフレーム | オーバーレイテキスト | 画面コンテンツ |
|---|---|---|---|---|
| App Store プレビュー | App Store/Play Store の動画プレビューとスクリーンショット | デバイスフレームなし。 アプリ画面がキャンバス全体を端から端まで埋めます。 | テキストオーバーレイはアプリ画面の上に表示します(読みやすさのため、半透明のピル型背景を使用)。 | 実際のスクリーンショットのように見せるため、ナビゲーションのクローム(ステータスバー、ナビゲーションバー、タブバー)を含める必要があります。 |
| マーケティング | ソーシャルメディア、ウェブサイト、ピッチデッキ、広告 | アプリ画面を囲むデバイスフレーム(PhoneFrame)。 | テキストやカードはデバイスフレームの横または周囲に表示します。 | ナビゲーションのクロームは任意です(PhoneFrame が「デバイス」のコンテキストを示します)。 |
重要な理由: Apple は、デバイスフレームを含むプレビューや、アプリの使用状況が十分に示されていないプレビューを却下します。App Store プレビュー出力先を選ぶことで、この要件に準拠できます。マーケティング出力先では、ストア以外の用途向けに、現在の洗練された見た目を維持できます。
デフォルト: appshot-videos と appshot-images では App Store プレビューを使用します。ソーシャルメディア/ウェブ向けではマーケティングを使用します。
ユーザーが App Store プレビュー 出力先を選択した場合:
- どのシーンでも PhoneFrame を使用しないでください
- アプリ UI で、キャンバス全体(動画では 886×1920、スクリーンショットではストア指定の寸法)を埋めてください
- すべてのモック画面に、ステータスバー、ナビゲーションバー、タブバー、ホームインジケーターを含めてください
- キャプション/オーバーレイテキストには、アプリ UI の上でも読みやすいよう、半透明のピル型背景を使用してください
- 結果は、テキストオーバーレイ付きの画面録画のように見えるようにしてください
ユーザーが マーケティング 出力先を選択した場合:
- これまでどおり、PhoneFrame をスケール 1.5 で使用してください
- テキスト、カード、バッジはデバイスフレームの外側または周囲に表示してください
- スマートフォン内のナビゲーションのクロームは、あると望ましいものの必須ではありません
ストア要件
動画
| プラットフォーム | 寸法 | 長さ | 形式 |
|---|---|---|---|
| iPhone 6.7" | 886x1920 | 15-30s | H.264, MP4/MOV |
| iPhone 6.1" | 886x1920 | 15-30s | H.264, MP4/MOV |
| iPad 13" | 1200x1600 | 15-30s | H.264, MP4/MOV |
| Google Play | 886x1920 | 15-30s | H.264, MP4 |
デフォルトの出力サイズは 886×1920 です。このキャンバスは、App Store(iPhone 6.7″ のネイティブ解像度)と Google Play の両方で使用できます。appshot-videos のすべてのサイズ設定ルールは、このキャンバス幅に合わせて調整されています。
スクリーンショット
iOS App Store:
| デバイス | 寸法(縦向き) | 必須 |
|---|---|---|
| iPhone 6.9" | 1320x2868 | はい(必須のメインサイズ) |
| iPhone 6.7" | 1290x2796 | 任意(Apple が 6.9" から拡大縮小) |
| iPhone 6.5" | 1242x2688 | 任意(旧サイズ) |
| iPad 13" | 2064x2752 | アプリが iPad に対応している場合は必須 |
Google Play:
| デバイス | 寸法(縦向き) | 備考 |
|---|---|---|
| スマートフォン | 1080x1920 | 推奨される標準サイズ |
| 7" タブレット | 1200x1920 | アプリが 7" タブレットを対象とする場合 |
| 10" タブレット | 1600x2560 | アプリが 10" タブレットを対象とする場合 |
プロジェクトのセットアップ
このスキルは、フェーズ 3 のコード生成時に、対象プロジェクト内に appshot-video/ ディレクトリを生成します。生成手順の全容については、code-guide.md を参照してください。
cd appshot-video
npm run dev # Preview at localhost:3000
npm run build # Render to out/AppPreview.mp4
オーケストレーターパターン
オーケストレーターは Remotion の <Sequence> を使用してシーンを順番に並べ、各シーンを <SceneWrap> でラップしてフェード遷移を実現します。
import { Composition, Sequence } from "remotion";
import { SceneWrap } from "./components";
import { S1_Hook } from "./scenes/S1_Hook";
import { S2_Feature } from "./scenes/S2_Feature";
import { S3_CTA } from "./scenes/S3_CTA";
const scenes = [
{ component: S1_Hook, duration: 120 },
{ component: S2_Feature, duration: 150 },
{ component: S3_CTA, duration: 120 },
];
export const AppPreview: React.FC = () => {
let offset = 0;
return (
<>
{scenes.map(({ component: Scene, duration }, i) => {
const from = offset;
offset += duration;
return (
<Sequence key={i} from={from} durationInFrames={duration}>
<SceneWrap durationInFrames={duration}>
<Scene />
</SceneWrap>
</Sequence>
);
})}
</>
);
};
共有リソース
- コピーの原則:copy-principles.md
- 抽出ロジックはこのスキルに組み込まれています(上記の「抽出とコンテキストキャッシュ」を参照)