Token导航 LogoToken导航

Agent Skill · 前端设计

basic-custom-app-build-w-video(带视频的基本自定义应用程序构建)

用于辅助视频生成、动画合成、脚本化剪辑或 Remotion 等视频项目开发。它适合让 Agent 组织镜头、生成素材说明、维护合成代码或排查渲染问题。使用时需要确认分辨率、时长、素材路径和导出格式;涉及外部素材、人物肖像或商业发布时,应先核对版权授权和内容审核要求。

basic-custom-app-build-w-video 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为执行命令;许可证信息还不明确,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 6,192。

总安装 541周安装 23GitHub Stars 14下载 190核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:basic-custom-app-build-w-video(带视频的基本自定义应用程序构建)
来源仓库:https://github.com/stahura/domo-ai-vibe-rules
仓库路径:skills/basic-custom-app-build-w-video
安装命令:
npx skills add https://github.com/stahura/domo-ai-vibe-rules --skill basic-custom-app-build-w-video
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/stahura/domo-ai-vibe-rules --skill basic-custom-app-build-w-video

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

basic-custom-app-build-w-video 为标准构建添加视频演示制作能力,强化 Remotion 兼容性。在 Phase 3A 提供示例数据生成,确保演示 UI 显示真实内容。适用于需要营销材料或文档演示的新建或改造项目。保持与标准构建流程的一致性,仅在必要时增加视频相关步骤。

适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

来源仓库内路径:skills/basic-custom-app-build-w-video

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/stahura/domo-ai-vibe-rules --skill basic-custo…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要执行命令,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

basic-custom-app-build-w-video 怎么安装?

在终端执行 npx skills add https://github.com/stahura/domo-ai-vibe-rules --skill basic-custom-app-buil…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

basic-custom-app-build-w-video 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

basic-custom-app-build-w-video 需要什么权限?

权限级别是执行命令。安装流程涉及命令执行,可能通过 npx skills add https://github.com/stahura/domo-ai-vibe-rules --skill basic-custom-app-build-w-video 联网下载 Skill 或依赖。用户安装前应确认命令来源、仓库内容和执行环境。

basic-custom-app-build-w-video 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

basic-custom-app-build-w-video 用的人多吗?

公开数据是累计安装 541 次,近一周安装 23 次,下载 190 次,GitHub 14 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

basic-custom-app-build-w-video 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 需要执行命令,装到常用环境前要先评估影响面。
  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 不能接受 Skill 在你机器上执行命令的人。
  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
basic-custom-app-build-w-video

带视频的基本自定义应用程序构建 · 本页

54114执行命令未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Domo App + Demo Video Build Playbook

When to use

  • Building a new Domo custom app that needs a demo video for stakeholders, documentation, or marketing.
  • Taking over an existing app and producing a visual demo alongside normalization.

How this differs from basic-custom-app-build

This playbook wraps the standard Domo app build with three additions:

  1. Remotion-compatible styling constraints are enforced from Phase 0 so the app's UI can be captured and animated in Remotion without rework.
  2. Sample data generation (Phase 3A) produces realistic mock data so the demo renders a populated UI without needing live Domo datasets or credentials.
  3. Demo video creation (Phase 10) uses Remotion to produce a 10-15 second polished video of the finished app.

Progress checklist

Copy this checklist and update it as you work.

Demo-Ready Build Progress:
- [ ] Phase 0 : Rules loaded (platform + Remotion styling constraints)
- [ ] Phase 1 : Manifest & contracts
- [ ] Phase 2 : App shell (domo.js)
- [ ] Phase 3 : Data access
- [ ] Phase 3A: Sample data generation
- [ ] Phase 4 : App storage (if needed)
- [ ] Phase 5 : Toolkit clients (if needed)
- [ ] Phase 6 : Feature skills (AI / Code Engine / Workflow — as needed)
- [ ] Phase 7 : UI build (Remotion-safe styling)
- [ ] Phase 8 : Performance review
- [ ] Phase 9 : Build & publish
- [ ] Phase 10: Demo video (Remotion)
- [ ] Phase 11: Verification

Phase 0 — Load rules (always-on)

Apply before writing any code:

  • rules/core-custom-apps-rule.md
  • rules/custom-app-gotchas.md

Remotion styling constraints (enforce throughout all phases)

These rules ensure every UI component you build can be screenshot-captured or directly rendered inside a Remotion composition without rework.

RuleWhy
**No CSS transition-* or animation-* properties**Remotion cannot capture CSS-driven motion. All animation in the demo will be driven by useCurrentFrame(). Static styling (colors, borders, shadows, transforms at rest) is fine.
**No Tailwind transition-* or animate-* utility classes**Same reason. All other Tailwind utilities are fine and encouraged.
Use inline style objects or Tailwind for layoutRemotion renders React components to frames; CSS modules and scoped styles work, but CSS-in-JS runtime libraries (styled-components, Emotion) may cause hydration issues in the Remotion render pipeline. Prefer inline styles or Tailwind.
Use <img> normally in the Domo appIn the *app* code, use standard <img> tags. The Remotion demo will use its own <Img> component when composing scenes. This constraint is about not relying on lazy-loading or intersection-observer patterns that won't fire in a Remotion frame capture.
Keep visual state deterministicEvery UI state the demo will show must be reproducible from props/data alone, not from user interaction history or timers. Build components so that passing in sample data produces the exact visual you want in the demo.
Design for 1920x1080Ensure the app layout looks good at 1920x1080, since this is the demo video's native resolution. Responsive design is still fine; just verify the 1080p breakpoint.

Phase 1 — Manifest & contracts

Use manifest.

Define all external resource mappings first: datasets, collections, workflows, Code Engine packages. Everything else depends on this.

Use alias-safe names only (^[A-Za-z][A-Za-z0-9]*$), no spaces or special characters.

Check root folder for an existing thumbnail.png to copy into the app's public/ folder.

Existing-app takeover? Audit the current manifest.json against actual code usage before changing anything.


Phase 2 — App shell

Use domo-js.

Set up the baseline: ryuu.js import, navigation via domo.navigate(), event listeners, environment info.

DA CLI users: Also use da-cli for scaffolding if the user explicitly requests it.


Phase 3 — Data access

Use dataset-query (primary) and data-api (routing overview).

Build queries with @domoinc/query. Before writing UI/data-mapping logic, fetch the real schema for every dataset in manifest.json datasetsMapping and create an explicit field map.

Need raw SQL? Use sql-query, but know that SQL ignores page filters.


Phase 3A — Sample data generation

This is the critical addition for demo-readiness. Generate realistic sample data that:

  1. Populates every view the demo will show (dashboard, detail panels, charts, tables, chat messages, etc.)
  2. Contains no real customer/user data — all names, emails, IDs, and metrics are synthetic
  3. Is deterministic — the same seed produces the same data so screenshots and Remotion captures are reproducible

How to implement

Create a src/sample-data/ directory in the app with:

src/sample-data/
  index.ts          # Re-exports all sample datasets; single import point
  datasets/
    <alias>.ts      # One file per manifest dataset alias, exporting typed arrays
  README.md         # Documents the shape and purpose of each sample dataset
For each dataset alias in manifest.json:
  1. Create a TypeScript file exporting a typed array matching the dataset's field map from Phase 3.
  2. Generate 20-100 rows of realistic data (enough to fill charts and tables convincingly).
  3. Use the actual field names from the dataset schema — not guesses.
  4. Include a range of values that produces interesting visual patterns (trends, outliers, category distribution).
// src/sample-data/datasets/sales.ts
export type SalesRow = {
  Date: string;
  Region: string;
  Product: string;
  Revenue: number;
  Units: number;
};

export const salesData: SalesRow[] = [
  { Date: "2026-01-15", Region: "West", Product: "Widget A", Revenue: 12400, Units: 62 },
  { Date: "2026-01-16", Region: "East", Product: "Widget B", Revenue: 8750, Units: 35 },
  // ... 20-100 rows with realistic distribution
];
Wire sample data into the app

Create a data-access layer that switches between live Domo queries and sample data:

// src/data/use-dataset.ts
import { salesData } from "../sample-data/datasets/sales";

const USE_SAMPLE_DATA = !window.__DOMO_ENV__; // true outside Domo runtime

export async function fetchSalesData(): Promise<SalesRow[]> {
  if (USE_SAMPLE_DATA) {
    return salesData;
  }
  // Real query via @domoinc/query
  return new Query().select([...]).fetch("sales");
}

This ensures:

  • In Domo: the app queries real datasets as normal.
  • Locally / in Remotion: the app renders with sample data, no credentials needed.
  • Security: no real data is ever baked into the demo video.
Sample data for non-dataset sources
SourceSample strategy
AppDB collectionsExport sample document arrays in src/sample-data/collections/<name>.ts
AI responsesExport canned response strings in src/sample-data/ai-responses.ts
User identityExport a mock user object: {displayName: "Alex Demo", email: "alex@example.com", role: "Admin"}
Workflow resultsExport sample status objects in src/sample-data/workflows/<name>.ts

Phase 4 — App storage (if needed)

Use appdb and appdb-collection-create when storage must be created.

Skip if the app only reads datasets.


Phase 5 — Toolkit clients (if needed)

Use toolkit.

Use typed @domoinc/toolkit clients where they add value.


Phase 6 — Feature skills (as needed)

Only load the skills your app actually requires:

Feature neededSkill
AI text generation or text-to-SQLai-service-layer
Server-side functions (secrets, external APIs)code-engine + code-engine-create + code-engine-update
Triggering automation workflowsworkflow

Skip this phase if none of these features are needed.


Phase 7 — UI build (Remotion-safe styling)

Build the app's UI components. This is standard React development with the Remotion constraints from Phase 0 enforced:

  • Use Tailwind utilities or inline style objects for all styling.
  • No transition-*, animate-*, or CSS keyframe animations.
  • All visual states must be reproducible from props + sample data (Phase 3A).
  • Verify the layout at 1920x1080.
  • Every "scene" the demo will show (loading state, populated dashboard, detail view, etc.) should be reachable by passing different sample data, not by simulating user clicks.

Component design for demo-ability

Structure components so each visual state is a function of data:

// Good: demo can render any state by passing props
<ChatPanel messages={sampleMessages} isTyping={false} />
<ChatPanel messages={sampleMessages} isTyping={true} />

// Bad: state is internal and requires user interaction to reach
<ChatPanel /> // internally manages messages via useState

You don't need to refactor the entire app this way — just the views you want in the demo. Expose the 3-5 key visual states as prop-driven variants.


Phase 8 — Performance review

Use performance.

Review all queries. Check for full-dataset fetches, missing aggregations, unnecessary columns.


Phase 9 — Build & publish

Use publish.

npm run build -> cd dist -> domo publish. Copy generated id back to source manifest on first publish.


Phase 10 — Demo video (Remotion)

The demo video renders the actual app components directly inside Remotion — not screenshots, not title cards, not marketing slides. The viewer should see the real UI populated with sample data, with animated scrolling or view changes to show all parts of the app.

10.1 — Scaffold the Remotion project

Create a demo/ directory alongside the app source:

my-app/
  src/            # Domo app source
  demo/           # Remotion demo project
    src/
      Root.tsx
      DemoVideo.tsx
      index.ts
    remotion.config.ts  # Webpack alias to import app components
    package.json
package.json

The demo project must include the same UI dependencies the app uses (e.g., recharts) so Remotion can render the actual components:

{
  "dependencies": {
    "@remotion/cli": "^4.0.0",
    "@remotion/transitions": "^4.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "recharts": "^2.12.0",
    "remotion": "^4.0.0"
  }
}

Add any other UI library the app uses (chart libraries, UI component kits, etc.).

remotion.config.ts — Webpack alias for app imports

This is critical. Configure a @app alias so demo code can import directly from the app's src/:

import { Config } from '@remotion/cli/config';
import path from 'path';

Config.overrideWebpackConfig((currentConfiguration) => ({
  ...currentConfiguration,
  resolve: {
    ...currentConfiguration.resolve,
    alias: {
      ...currentConfiguration.resolve?.alias,
      '@app': path.resolve(process.cwd(), '..', 'src'),
    },
  },
}));
index.ts — Remotion entry point
import { registerRoot } from 'remotion';
import { RemotionRoot } from './Root';
registerRoot(RemotionRoot);
tsconfig.json — Path mapping
{
  "compilerOptions": {
    "paths": { "@app/*": ["../src/*"] },
    "baseUrl": "."
  },
  "include": ["src", "../src"]
}

10.2 — AnimatedCursor component

Create a reusable cursor that moves between positions and shows click pulses. This sells the illusion of someone using the app.

Place in demo/src/components/AnimatedCursor.tsx:

import React from 'react';
import { useCurrentFrame, useVideoConfig, interpolate, spring, Easing } from 'remotion';

export type CursorWaypoint = {
  x: number;       // Pixel position from left
  y: number;       // Pixel position from top
  atFrame: number; // Frame at which cursor should arrive here
  click?: boolean; // Show a click pulse when arriving
};

type Props = {
  waypoints: CursorWaypoint[];
  size?: number;  // Default 20
};

export const AnimatedCursor: React.FC<Props> = ({ waypoints, size = 20 }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  if (waypoints.length === 0) return null;

  // Find which two waypoints we're interpolating between
  let fromWp = waypoints[0];
  let toWp = waypoints[0];
  for (let i = 0; i < waypoints.length - 1; i++) {
    if (frame >= waypoints[i].atFrame) {
      fromWp = waypoints[i];
      toWp = waypoints[i + 1];
    }
  }
  if (frame >= waypoints[waypoints.length - 1].atFrame) {
    fromWp = waypoints[waypoints.length - 1];
    toWp = fromWp;
  }

  const easing = Easing.inOut(Easing.quad);
  const x = fromWp === toWp ? fromWp.x
    : interpolate(frame, [fromWp.atFrame, toWp.atFrame], [fromWp.x, toWp.x],
        { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing });
  const y = fromWp === toWp ? fromWp.y
    : interpolate(frame, [fromWp.atFrame, toWp.atFrame], [fromWp.y, toWp.y],
        { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing });

  // Click pulse at waypoints marked click: true
  let pulseOpacity = 0;
  let pulseScale = 0;
  for (const wp of waypoints) {
    if (wp.click && frame >= wp.atFrame && frame < wp.atFrame + 20) {
      const p = spring({ frame: frame - wp.atFrame, fps, config: { damping: 20, stiffness: 200 } });
      pulseScale = interpolate(p, [0, 1], [0.5, 1.5]);
      pulseOpacity = interpolate(frame - wp.atFrame, [0, 20], [0.5, 0], { extrapolateRight: 'clamp' });
    }
  }

  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 9999 }}>
      {pulseOpacity > 0 && (
        <div style={{
          position: 'absolute', left: x - 15, top: y - 15,
          width: 30, height: 30, borderRadius: '50%',
          border: '2px solid rgba(255,255,255,0.8)',
          transform: `scale(${pulseScale})`, opacity: pulseOpacity,
        }} />
      )}
      <svg style={{ position: 'absolute', left: x, top: y, width: size, height: size,
        filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.5))' }} viewBox="0 0 24 24" fill="none">
        <path d="M5 3L19 12L12 12L8 21L5 3Z" fill="white" stroke="#1e293b"
          strokeWidth="1.5" strokeLinejoin="round" />
      </svg>
    </div>
  );
};

10.3 — Root.tsx

import { Composition } from 'remotion';
import { DemoVideo } from './DemoVideo';

export const RemotionRoot = () => (
  <Composition
    id="AppDemo"
    component={DemoVideo}
    durationInFrames={300}  // 10s at 30fps
    fps={30}
    width={1920}
    height={1080}
  />
);

10.4 — Demo patterns by app type

Pick the pattern that matches your app and combine them if needed. All patterns import the real app components via @app/.

Pattern A: Single-view scroll (dashboards, tables, long forms)

Render the component and animate translateY to scroll through it.

import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate, Easing } from 'remotion';
import { Dashboard } from '@app/App';
import { sampleData } from '@app/sample-data';
import { AnimatedCursor } from './components/AnimatedCursor';

export const DemoVideo: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  const scrollY = interpolate(frame, [2 * fps, 6 * fps], [0, -500], {
    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
    easing: Easing.inOut(Easing.quad),
  });

  return (
    <AbsoluteFill style={{ backgroundColor: '#0f172a', overflow: 'hidden' }}>
      <div style={{ transform: `translateY(${scrollY}px)`, width: '100%' }}>
        <Dashboard data={sampleData} />
      </div>
      <AnimatedCursor waypoints={[
        { x: 700, y: 400, atFrame: 0 },
        { x: 700, y: 500, atFrame: Math.round(3 * fps) },
        { x: 500, y: 450, atFrame: Math.round(6 * fps) },
      ]} />
    </AbsoluteFill>
  );
};
Pattern B: Tabbed / multi-view apps (most common)

Use <Sequence> to switch between views. The cursor moves to the tab/button, a click pulse fires, and the next Sequence renders the new view.

import { AbsoluteFill, Sequence, useCurrentFrame, useVideoConfig, interpolate, Easing } from 'remotion';
import { Dashboard } from '@app/App';
import { sampleData } from '@app/sample-data';
import { AnimatedCursor } from './components/AnimatedCursor';

const OverviewScene: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  const scrollY = interpolate(frame, [2 * fps, 4 * fps], [0, -350], {
    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
    easing: Easing.inOut(Easing.quad),
  });

  return (
    <AbsoluteFill style={{ overflow: 'hidden' }}>
      <div style={{ transform: `translateY(${scrollY}px)`, width: '100%' }}>
        <Dashboard data={sampleData} activeTab="overview" />
      </div>
      <AnimatedCursor waypoints={[
        { x: 700, y: 400, atFrame: 0 },
        { x: 900, y: 450, atFrame: Math.round(3 * fps) },
        // End near the target tab button
        { x: 1195, y: 48, atFrame: Math.round(4.5 * fps) },
      ]} />
    </AbsoluteFill>
  );
};

const DetailScene: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  const scrollY = interpolate(frame, [2 * fps, 4 * fps], [0, -200], {
    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
    easing: Easing.inOut(Easing.quad),
  });

  return (
    <AbsoluteFill style={{ overflow: 'hidden' }}>
      <div style={{ transform: `translateY(${scrollY}px)`, width: '100%' }}>
        <Dashboard data={sampleData} activeTab="detail" selectedItem={sampleData.items[0]} />
      </div>
      <AnimatedCursor waypoints={[
        { x: 1195, y: 48, atFrame: 0, click: true },  // Click the tab
        { x: 600, y: 300, atFrame: Math.round(0.8 * fps) },
        { x: 500, y: 400, atFrame: Math.round(3 * fps) },
      ]} />
    </AbsoluteFill>
  );
};

export const DemoVideo: React.FC = () => {
  const { fps } = useVideoConfig();
  return (
    <AbsoluteFill>
      <Sequence from={0} durationInFrames={5 * fps} premountFor={fps}>
        <OverviewScene />
      </Sequence>
      <Sequence from={5 * fps} durationInFrames={5 * fps} premountFor={fps}>
        <DetailScene />
      </Sequence>
    </AbsoluteFill>
  );
};

Key: The cursor's last waypoint in Scene 1 and first waypoint in Scene 2 should be at the same position (the tab button) to create a seamless "click" effect across the cut.

Pattern C: AI chat / conversational apps

Render the chat component with progressively more messages using frame-based array slicing. This shows messages "arriving" without needing actual interaction.

import { AbsoluteFill, useCurrentFrame, useVideoConfig } from 'remotion';
import { ChatPanel } from '@app/components/ChatPanel';
import { sampleMessages } from '@app/sample-data';
import { AnimatedCursor } from './components/AnimatedCursor';

// Pre-define which messages are visible at which frame
const messageTimeline = [
  { visibleCount: 2, atFrame: 0 },    // Start with 2 messages already visible
  { visibleCount: 3, atFrame: 60 },   // User message appears at 2s
  { visibleCount: 3, typingIndicator: true, atFrame: 90 },  // AI typing at 3s
  { visibleCount: 4, atFrame: 120 },  // AI response appears at 4s
  { visibleCount: 5, atFrame: 180 },  // Another user message at 6s
  { visibleCount: 5, typingIndicator: true, atFrame: 210 },
  { visibleCount: 6, atFrame: 240 },  // Final AI response at 8s
];

export const DemoVideo: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  // Find current state based on frame
  let currentState = messageTimeline[0];
  for (const state of messageTimeline) {
    if (frame >= state.atFrame) currentState = state;
  }

  const visibleMessages = sampleMessages.slice(0, currentState.visibleCount);

  return (
    <AbsoluteFill style={{ overflow: 'hidden' }}>
      <ChatPanel
        messages={visibleMessages}
        isTyping={currentState.typingIndicator ?? false}
      />
      <AnimatedCursor waypoints={[
        { x: 960, y: 800, atFrame: 0 },
        // Cursor moves to input area before "sending" a message
        { x: 700, y: 950, atFrame: 50, click: true },
        { x: 960, y: 600, atFrame: 90 },   // Watches AI type
        { x: 700, y: 950, atFrame: 170, click: true },  // Sends another
        { x: 960, y: 500, atFrame: 240 },  // Reads response
      ]} />
    </AbsoluteFill>
  );
};

Key for chat apps: The ChatPanel component must accept messages and isTyping as props (Phase 7 enforces this). The sample data in Phase 3A should include a realistic conversation thread.

Pattern D: Button / interaction-heavy apps (dispatch boards, forms)

Render different prop states in sequence to simulate button clicks. Use the cursor to point at the button, fire a click pulse, and switch to the "after" state.

// Scene 1: Empty form state, cursor fills in a field
<Sequence from={0} durationInFrames={4 * fps}>
  <FormView formData={emptyForm} />
  <AnimatedCursor waypoints={[
    { x: 500, y: 300, atFrame: 0 },
    { x: 500, y: 300, atFrame: 30, click: true },
  ]} />
</Sequence>

// Scene 2: Filled form, cursor clicks submit
<Sequence from={4 * fps} durationInFrames={3 * fps}>
  <FormView formData={filledForm} />
  <AnimatedCursor waypoints={[
    { x: 500, y: 600, atFrame: 0 },
    { x: 500, y: 600, atFrame: 30, click: true },  // Click "Submit"
  ]} />
</Sequence>

// Scene 3: Success state
<Sequence from={7 * fps} durationInFrames={3 * fps}>
  <FormView formData={filledForm} submitSuccess={true} />
</Sequence>

10.5 — Cursor waypoint positioning tips

  • Finding coordinates: Run npx remotion studio, hover over the preview, and note the pixel coordinates where buttons/tabs/elements are rendered.
  • Match across Sequences: When the cursor "clicks" a tab at the end of Scene 1, its first position in Scene 2 should be the same coordinates — this creates a seamless transition.
  • Movement speed: Keep moves to 30-60 frames (1-2s). Faster looks frantic, slower looks boring.
  • Don't over-animate: 2-3 cursor moves per scene is enough. The cursor should guide attention, not distract.

10.6 — Preview and render

cd demo
npx remotion studio          # Preview in browser
npx remotion render AppDemo  # Render to out/AppDemo.mp4

Phase 11 — Verification

After publishing, confirm:

  • App loads without console errors in Domo
  • All dataset aliases resolve (no 404s on data calls)
  • AppDB collections are wired in the card UI (if used)
  • Page filters propagate correctly (if app is embedded in a dashboard)
  • Navigation uses domo.navigate(), not <a href>
  • Thumbnail has been copied into the public/ folder
  • Sample data renders correctly when running outside Domo (no undefined/null errors)
  • Demo video renders without blank frames or missing assets (npx remotion render AppDemo)
  • Demo video shows populated UI — not empty states or loading spinners

Build-time guardrails

  • Client-side only: no SSR/server routes/server components.
  • Use Vite base: './'.
  • Prefer HashRouter unless rewrites are intentionally handled.
  • Treat domo.env.* as convenience only; use verified identity for trust decisions.
  • No CSS transitions or animations in any component the demo will capture.
  • Sample data must contain zero real customer data.
  • All demo visual states must be prop-driven, not interaction-driven.

平台分布

Codex

37.95%
按下载量换算72

Claude

28.15%
按下载量换算53

Cursor

18.23%
按下载量换算35

Gemini CLI

9.86%
按下载量换算19

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

执行命令

安装流程涉及命令执行,可能通过 npx skills add https://github.com/stahura/domo-ai-vibe-rules --skill basic-custom-app-build-w-video 联网下载 Skill 或依赖。用户安装前应确认命令来源、仓库内容和执行环境。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills