mcpField
A
appshot-core

appshot-core

github.com/trunghaiy营销与增长4959 次安装21 周前更新
代码仓库

关于 appshot-core

供 Appshot 视频和截图技能使用的基础参考技能,定义项目架构、应用信息提取与品牌及界面分析流程。适用于需要基于应用上下文制作营销素材的工作流。

主要功能

  1. 01说明 Remotion 与 React 架构
  2. 02提取应用身份和品牌颜色
  3. 03识别功能与商店元数据
  4. 04分析导航和页面结构
  5. 05缓存提取的项目上下文

适用场景

  1. 01为视频制作准备应用上下文
  2. 02为截图生成提取品牌样式
  3. 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 文件,而非配置项
  • 每个项目都有一个编排组件(例如 FooPreview.tsx),通过 Remotion <Sequence> + <SceneWrap> 按顺序排列自定义场景
  • 所有基础组件都通过从配置的 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. 加载并向用户展示摘要
  2. 询问:“我找到了之前提取的内容。请确认它仍然正确,或重新扫描?”
  3. 如果用户确认,则跳过提取,继续调用技能的下一阶段
  4. 如果用户要求重新扫描,则运行下方的提取流程并覆盖该文件
提取步骤
步骤 1:检测框架

按以下顺序检查标记文件。匹配到第一个后即停止。

框架标记文件
Expo / React Nativeapp.json、app.config.ts、app.config.js
Flutterpubspec.yaml
iOS(Swift)*.xcodeproj/、*.xcworkspace/、Package.swift
Android(Kotlin/Java)app/src/main/AndroidManifest.xml、build.gradle.kts、build.gradle
Next.jsnext.config.js、next.config.ts、next.config.mjs
SvelteKitsvelte.config.js
Nuxtnuxt.config.ts、nuxt.config.js
Astroastro.config.mjs、astro.config.ts
Remixremix.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
  • Bundle 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
  • Bundle ID:Info.plist -> CFBundleIdentifier
  • 另请检查:在 project.pbxproj 中查找产品名称

Android(Kotlin/Java):

  • 名称:app/src/main/res/values/strings.xml -> <string name="app_name">
  • Package: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 — 按顺序搜索以下路径:

  1. src/theme.ts 或 src/theme.js
  2. src/colors.ts 或 src/constants/colors.ts
  3. theme/colors.ts 或 theme/index.ts
  4. tailwind.config.js 或 tailwind.config.ts -> theme.extend.colors
  5. app.json -> expo.primaryColor、expo.backgroundColor

查找:具名颜色导出(primary、secondary、background、surface、accent、success、danger/error)、十六进制字符串、rgb() 值。

Flutter — 搜索以下路径:

  1. lib/theme.dart 或 lib/theme/app_theme.dart
  2. lib/theme/colors.dart 或 lib/constants/colors.dart
  3. lib/main.dart -> ThemeData()

查找:Color(0xFF...) 常量、primaryColor、accentColor、colorScheme 定义。

iOS(Swift) — 搜索以下路径:

  1. *.xcassets/Colors/**/*.colorset/Contents.json — 解析 components 中的 RGB 值
  2. **/Theme.swift、**/Colors.swift 或 **/ColorScheme.swift

查找:静态 Color 属性、UIColor 十六进制初始化器、资源目录中的颜色定义。

Android — 搜索以下路径:

  1. app/src/main/res/values/colors.xml — <color name="primary">#RRGGBB</color>
  2. app/src/main/res/values/themes.xml — <item name="colorPrimary">
  3. **/ui/theme/Color.kt — Jetpack Compose Color() 定义

Web(所有框架) — 按顺序搜索以下路径:

  1. tailwind.config.js 或 tailwind.config.ts → theme.extend.colors
  2. src/styles/globals.css 或 src/app/globals.css → CSS 自定义属性(--primary、--background 等)
  3. src/theme.* 或 src/lib/theme.*
  4. src/styles/variables.css
步骤 4:查找应用图标
框架查找位置
Expo/RNapp.json -> expo.icon 字段(通常为 ./assets/icon.png)
Flutterandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 或 ios/Runner/Assets.xcassets/AppIcon.appiconset/
iOS*.xcassets/AppIcon.appiconset/ -> 查找最大的 .png
Androidapp/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 或 full_description.txt完整的商店描述
Fastlane(iOS)fastlane/metadata/en-US/subtitle.txtApp Store 副标题
Fastlane(iOS)fastlane/metadata/en-US/keywords.txt搜索关键词
Fastlane(Android)fastlane/metadata/android/en-US/full_description.txtPlay Store 描述
Fastlane(Android)fastlane/metadata/android/en-US/short_description.txt简短描述
READMEREADME.md前 80 行——应用描述、功能要点
CLAUDE.mdCLAUDE.md项目描述(如有)
步骤 5b:提取 ASO 关键词

Apple 和 Google 现在可以通过视觉智能读取截图和视频中的文字。提取关键词列表,以便叠加文字自然地融入高价值搜索词。

主要来源: fastlane/metadata/en-US/keywords.txt(以逗号分隔)。这是开发者自己的关键词策略,应将其作为权威列表。

备用来源(若不存在 Fastlane 关键词):从以下来源推导出 8-12 个候选关键词:

  1. App Store 副标题(fastlane/metadata/en-US/subtitle.txt)——拆分为单个词
  2. 商店描述——提取重复次数最多的名词和动作动词
  3. 提取出的功能名称——将功能名称对应到面向用户的搜索词
  4. 步骤 6 信号表中的应用类别术语

输出: 按重要性从高到低排列的关键词列表。包含单个词(“习惯”“连续记录”)和短语(“阅读追踪器”“读书记录”)。最多 12 个关键词。

步骤 6:推断应用类别

根据提取出的关键词、描述和功能名称,推断应用类别:

类别信号
习惯追踪连续记录、习惯、每日、日常、坚持、追踪天数
健身锻炼、运动、健身房、次数、组数、卡路里、步数
金融预算、支出、金钱、储蓄、交易、消费
生产力任务、待办事项、笔记、项目、整理、日历
社交聊天、消息、朋友、关注、帖子、分享
教育学习、测验、复习、课程、抽认卡、练习
健康冥想、睡眠、情绪、日记、健康、正念
旅行旅行、行程、预订、目的地、探索
美食食谱、餐食、餐厅、烹饪、食材
SaaS仪表盘、订阅、方案、定价、引导流程、工作区、团队、分析
电子商务购物车、结账、产品、购物、订单、支付、库存
开发者工具API、SDK、CLI、文档、体验场、沙盒、webhook
内容/出版博客、文章、帖子、发布、编辑器、CMS、新闻简报
AI/ML模型、推理、提示词、生成、嵌入、向量、聊天、补全
社区论坛、讨论、话题、社区、成员、个人资料

如果无法明确归类,请注明“未分类”——用户会进一步说明。

深度分析

不止了解应用的身份和颜色,还要弄清它实际能做什么。

屏幕清单。 扫描导航文件,找出所有屏幕:

  • React Native / Expo:读取 App.tsx、所有 *Navigator.tsx、*Router.tsx、标签栏配置、react-navigation 设置
  • Flutter:读取 lib/main.dart、路由配置、GoRouter 或 MaterialApp 路由
  • iOS:读取 storyboard 引用、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 项

页面/路由清单(Web)。 扫描路由配置,找出所有页面:

  • 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):SwiftUI/UIKit 中的 .cornerRadius、.font()、.padding()、UIFont 描述符、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'" }
}

架构版本 2 说明:

  • 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
  }]
})

如果用户提供截图:

  1. 请用户提供截图文件路径,或将截图拖入对话
  2. 阅读每张截图图片,了解其展示的内容
  3. 将每张截图映射到提取结果中的一个屏幕:
    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?
    
  4. 对每张截图运行视觉参考分析(见下文)
  5. 记录任何缺失项——没有截图的屏幕。这些屏幕将回退到基于 uiPatterns 构建的模拟 UI。
  6. 将映射结果和视觉分析保存到 .appshot-context.json 中的 screenshots
视觉参考分析

重要: 提供截图时,此步骤为必做步骤。否则,模拟场景会显得千篇一律,无法匹配真实应用。

对于提供的每张截图,请仔细查看图片并编写结构化视觉规范。此规范有两种用途:

  • 直接使用场景:通过 <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”相差很大。
  • 精确描述组件形状:“48px 圆角方形,圆角半径为 12px”,而不是“圆角按钮”。
  • 记录间距:“卡片内边距为 20px”“元素之间间隔 12px”。
  • 记录不存在的元素:“卡片没有阴影”“按钮没有边框”“列表项之间没有分隔线”。
  • 当视觉规范与代码提取的 uiPatterns 冲突时,以本分析为准——截图才是视觉真实依据。
  • 视觉规范中的尺寸以手机逻辑像素计。 886px 的 App Store Preview 画布宽度是 iPhone 16 Pro(393px)的 2.25 倍。编写场景代码时,所有尺寸都必须乘以 2.25:48px 的按钮变为 108px,16px 的文字变为 36px,11px 的标签文字变为 25px。代码指南中有完整的缩放表。

截图要求(告知用户):

  • 任意分辨率均可——这些是参考图片,不会嵌入输出
  • 画面干净——不要有通知或调试横幅(这样分析才能捕捉到真实设计)
  • 每种不同的屏幕/功能提供一张截图。同一屏幕的多个状态也欢迎提供。
  • 此技能会分析你的截图,并构建与应用外观精准匹配的动画模拟 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

设备模型包含边框、侧边按钮和刘海/灵动岛。可包裹任意内容,使其看起来像真实的手机屏幕。

属性类型默认值描述
childrenReactNode必填屏幕内容
deviceDevicePreset"iphone-15"设备型号
delaynumber0入场动画延迟(帧)
scalenumber1缩放倍数
screenBackgroundstring"#FAFAF8"屏幕内的背景颜色

适用场景: 在手机中展示应用 UI 的场景。是移动应用视频的核心视觉锚点。

AmbientBackground

带有浮动模糊光球的动态渐变背景,为每个场景营造氛围。

属性类型默认值描述
brandBrandColors必填渐变和光球的颜色
variant"light" | "medium" | "deep" | "dark""light"强度/氛围

适用场景: 用于每个场景的背景。在功能/解决方案场景中使用 "light" 或 "medium";在问题/对比场景中使用 "dark";在情感/证明场景中使用 "deep"。

Caption

锚定在底部的文字叠加层,带有逐词入场动画。深色胶囊背景,便于阅读。

PropTypeDefaultDescription
textstringrequired字幕文本(最多 8 个词)
delaynumber0入场延迟(帧)
fontSizenumber44字体大小(px)
maxWidthnumber820容器最大宽度

使用场景: 每个场景都应包含一个字幕。字幕能为关闭声音的观众讲述故事。

FadeIn

使用 Remotion 弹簧动画,为任意元素添加带方向的淡入动画。

PropTypeDefaultDescription
childrenReactNoderequired要添加动画的内容
delaynumber0开始帧
direction"up" | "down" | "left" | "right" | "none""up"滑入方向
distancenumber40滑动距离(px)
classNamestring—Tailwind 类
styleCSSProperties—内联样式

使用场景: 依次显示卡片、标题入场,以及任何需要播放入场动画的元素。

SceneWrap

为每个场景添加淡入和淡出过渡(各 12 帧)。由编排器使用。

PropTypeDefaultDescription
childrenReactNoderequired场景内容
durationInFramesnumberrequired场景总时长
fadeInbooleantrue启用淡入
fadeOutbooleantrue启用淡出

使用场景: 在编排器组件中,将每个场景包裹在 <Sequence> + <SceneWrap> 中。

AppIcon

渲染应用图标,并可选添加动画发光效果。

PropTypeDefaultDescription
srcstringrequiredpublic/ 中的文件名或 URL
sizenumber120图标大小(px)
glowbooleanfalse启用脉冲发光效果
glowColorstring"rgba(0,122,255,0.4)"发光颜色

使用场景: CTA/收尾场景。提升品牌辨识度。

TypeWriter

逐字符显示文本,并可选显示闪烁光标。

PropTypeDefaultDescription
textstringrequired要逐字输入的完整文本
startFramenumberrequired开始输入的帧
charsPerFramenumber2输入速度
classNamestring—Tailwind 类
cursorbooleantrue显示闪烁光标
cursorColorstring"#2196F3"光标颜色

使用场景: 搜索栏、文本输入框、聊天消息,以及任何需要模拟用户输入的场景。

BrowserFrame

macOS 风格的浏览器外框,包含红黄绿按钮、地址栏和内容区域。提供浅色和深色版本。

PropTypeDefaultDescription
childrenReactNoderequired页面内容
urlstring"app.example.com"地址栏文本
delaynumber0入场动画延迟(帧)
scalenumber1缩放倍率
variant"light" | "dark""light"浏览器外观主题
AnimatedCursor

使用贝塞尔插值在关键帧位置之间移动的光标,带有点击涟漪效果。

PropTypeDefaultDescription
keyframesCursorKeyframe[]required{ frame, x, y, click? } 位置
visiblebooleantrue显示/隐藏光标

动画模式

已移除的原语(FloatingCard、StatCard、HeatMap、ProgressBar、AppStoreBadge、IconSet)只是简单的包装器,会通过硬编码尺寸限制 Agent。请改为直接内联编写这些模式,并根据画布适当缩放:

动画计数器(前后对比数据):

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-pro393x852灵动岛App Store
iphone-15375x812灵动岛App Store
ipad-pro-131024x1366无App Store(iPad)
pixel-9412x915打孔Play Store
商店与设备的对应关系
商店Composition ID 后缀设备预设
App Store-AppStoreiphone-16-pro
Play Store-PlayStorepixel-9

对于多商店项目,Root.tsx 会为每个商店注册一个 <Composition>。场景共用,只有设备边框(以及导航栏样式)不同。两者的画布均为 886×1920。注意:App Store Preview 视频的 CTA 中不包含商店徽章(在商店列表中重复显示);营销视频的 CTA 中会内嵌商店徽章(SVG 代码片段见上文的动画模式)。

输出目标

AppShot 支持两种输出目标,视觉规则各不相同:

目标用途设备边框叠加文字屏幕内容
App Store PreviewApp Store/Play Store 视频预览和屏幕截图不带设备边框。 应用屏幕铺满整个画布,延伸至边缘。文字叠加在应用屏幕上方(使用半透明胶囊背景以便阅读)。必须包含导航栏(状态栏、导航栏、标签栏),看起来像真实截图。
Marketing社交媒体、网站、演示文稿、广告使用 PhoneFrame 包裹应用屏幕的设备边框。文字和卡片显示在设备边框的旁边或周围。导航栏可选(PhoneFrame 已提供“设备”场景)。

**为何这很重要:**如果 App Store 预览中包含设备边框,或未充分展示应用的实际使用情况,Apple 会拒绝该预览。App Store Preview 目标可确保符合要求。Marketing 目标则保留了当前精致的外观,适用于非商店场景。

默认值:appshot-videos 和 appshot-images 使用 App Store Preview。社交媒体/网页场景使用 Marketing。

用户选择 App Store Preview 目标时:

  • 不要在任何场景中使用 PhoneFrame
  • 应用 UI 填满 886×1920 画布(视频)或商店要求的尺寸(屏幕截图)
  • 每个模拟屏幕都应包含状态栏、导航栏、标签栏和主屏幕指示条
  • 字幕/叠加文字使用半透明胶囊背景,以便在应用 UI 上清晰可读
  • 最终效果应类似带有文字叠加的屏幕录制

用户选择 Marketing 目标时:

  • 像之前一样,以 1.5 的缩放比例使用 PhoneFrame
  • 文字、卡片和徽章显示在设备边框的外部/周围
  • 手机内的导航栏有则更好,但不是必需的

商店要求

视频
平台尺寸时长格式
iPhone 6.7"886x192015-30sH.264, MP4/MOV
iPhone 6.1"886x192015-30sH.264, MP4/MOV
iPad 13"1200x160015-30sH.264, MP4/MOV
Google Play886x192015-30sH.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
  • 提取逻辑已内置于此技能中(请参阅上文的 Extraction & Context Caching)

评论

支持 Markdown