一键导入
rn-manifest-wiring
Content manifest wiring: how to connect content.json data to React Native data hooks and screen components
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Content manifest wiring: how to connect content.json data to React Native data hooks and screen components
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Template repo file structure: where screens, navigation, theme, data, and platform entries live in the monorepo
Android TV build and D-pad QA using Android CLI first, with bounded Gradle and ADB fallbacks
Expo Application Services cloud build configuration for TV app APK and IPA generation
Expo TV configuration: app.json plugins, prebuild settings, platform-specific config for react-native-tvos
Fire TV leanback manifest configuration: banner icons, LEANBACK_LAUNCHER intent filter, TV-specific Android settings
Gradle build commands: assembleDebug, assembleRelease, APK output paths, and emulator installation
| name | rn-manifest-wiring |
| description | Content manifest wiring: how to connect content.json data to React Native data hooks and screen components |
| applies_to | ["phase_manifest","phase_screens"] |
| load_when | injecting `content.json`, or wiring data into screens |
Content flows:
content.json→ typed parse → hooks → screens. The template ships hooks for the default shape. The agent's job is matching the user's manifest to those hooks, or generating new hooks when the shape diverges.
packages/shared-ui/data/
├── content.json # The user's manifest (overwrite on inject)
├── content.schema.ts # Type definitions
└── seed.json # Original template seed — keep as fallback example
inject_content copies the user's manifest to content.json and validates against content.schema.ts. Validate before screens get wired, not after.
type Manifest = {
title: string;
description: string;
categories: { id: string; name: string; items: string[] }[]; // items = video IDs
videos: Video[];
featured: string[]; // video IDs to put in the hero
};
type Video = {
id: string;
title: string;
description: string;
duration_sec: number;
thumbnail_url: string;
stream_url: string;
stream_type: "hls" | "dash" | "mp4";
tags?: string[];
};
| Hook | Returns | Used by |
|---|---|---|
useFeatured() | Video[] | Home hero |
useCategories() | Category[] | Home rails, drawer items |
useCategory(id) | Video[] | Category grid screen |
useVideo(id) | Video | Detail, Player screens |
useSearch(q) | Video[] | Search screen |
useRelated(id) | Video[] | Detail "related" rail |
These hooks read directly from content.json. Swap the underlying source for an API later — same signature.
Exact match. Just inject and move on. The screens are already wired.
Match with extra fields (e.g. user has cast, year, rating). Inject the manifest, and:
Different shape but mappable. Add a translation step in inject_content:
transformManifest() function that maps the user's shape to the template's shape.data/source.json for debugging.content.json.Fundamentally different model (e.g. live TV channels with EPG, not VOD videos). The template's hooks won't fit:
useChannels, useNowPlaying, useEPG) instead of forcing the data into the VOD shape.These catch ~80% of "the app rendered blank" bugs:
categories[].items and featured exists in videos. Missing = silent empty screens.thumbnail_url and stream_url parse as URLs. Relative paths break on TV (no document base).stream_type is one of hls | dash | mp4. Misspellings (HLS, m3u8) lead to player init failures.duration_sec is a number, not a string. Common JSON drift.featured length ≥ 1 and ≤ 10. A hero with 0 items breaks the home screen. More than 10 is a UX smell.Fail loud on (1) and (4). Warn and continue on others.
Past validation, the manifest often needs gentle reshaping to look right:
featured is empty, pick the first 5 videos. If it has 20, take the first 5. The hero is not a backlog.[] not undefined on no data. Screens then show their built-in empty state.thumbnail_url 404s, the template's <Tile> shows a placeholder. Make sure theme.color.surface looks reasonable — it's the placeholder background.content.json, loaded synchronously. Fast.useEffect to avoid linear scans in useVideo.content.json.