Motion · Interação
Command Palette Summon
The command palette descends — the whole screen dims and blurs, the ⌘K panel springs down with overshoot, candidate rows appear staggered, and the list narrows in real time as you type.
Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador
When to use
The "the whole product lives in one input box" narrative for efficiency-focused products; the signature entrance for a command palette/search/shortcut feature.
Intention
The signature ritual of Raycast/Linear launch films: one soft sound, the whole UI world dims to make way, the ⌘K panel drops from above center, the candidate list appears staggered; type two letters, and the list narrows in real time — "everything you need lives in this one input box." The simulated typing follows real human typing speed (R3), and the narrowing's "squeeze" feel comes from row-height collapse, not fading.
Duration & energy
- Duration: 4–5s.
- Energy: Medium (a ritual feel; the drop and the narrowing are the two small impact beats).
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
Raycast/Linear 发布片的标志性仪式:一声轻响,整个 UI 世界压暗 让路,⌘K 面板从中心上方弹落,候选列表错峰浮现;敲两个字母, 列表实时收窄——"你要的一切都在这个输入框里"。模拟交互按真人 操作速度走(R3),收窄的"挤压感"来自行高塌缩而非淡出。
动作阶段
| 阶段 | 帧段参考 | 内容 | |------|------|------| | 1 静置 | f0–12 | dashboard 初始状态 | | 2 让路 | f12–22 | 背景压暗至 45% + blur 10px | | 3 降临 | f18–33 | 面板 -20px 落入,过冲 +8px 回正(与压暗跨骑) | | 4 候选浮现 | f32–56 | 5 行每 4f 错峰上浮 | | 5 输入收窄 | ~f62–90 | 两次按键字符出现,行高塌缩收窄 5→2(塌缩完条件卸载) | | 6 选中定格 | 末 ≥40f | 首条高亮(底色+左描边),光标转常亮,真静止 |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 压暗 | 亮度 45% + blur 10px,10f | 压不够面板不突出;全黑失去上下文 | | 弹落 | 起点上方 20px,overshoot 一拍回正 | 无过冲读作淡入,仪式感没了 | | 候选错峰 | 4f/行 | 同帧出读作贴图 | | 收窄 | height→0 + overflow hidden 塌缩 | 淡出收窄没有"挤压"感;塌缩完必须卸载 | | 光标 | 闪烁周期 16f;定格后强制常亮 | 闪到结尾就永不真静止(demo 判例) |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 输入节奏按真人打字速度(R3 判例),两键间隔 ≥12f
- 组合款见 theme-switch-moves B 式(面板收缩成点引发主题涟漪)
- 实战候选行放真实功能名——这是功能清单的天然展位(P4 映射)
- 声音:唤出一声轻响(非游戏音),按键 2 声键帽拟音,
高亮一声轻 pop(S1/S4 同源)
参考实现
demos/interaction/command-palette-summon/ (CommandPaletteSummon.tsx)
Reference implementation (Remotion)
- demos/interaction/command-palette-summon/CommandPaletteSummon.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/interaction/command-palette-summon/CommandPaletteSummon.tsx
The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.
Tudo nesta página também é legível por máquina. Aponte seu agente para o JSON ou adicione o servidor MCP do gimgs e peça para buscar templates de motion.
- Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
- Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
- Registre o componente como <Composition> em um projeto Remotion e renderize com `npx remotion render`, ou reimplemente a receita no seu próprio engine.
curl -s https://gimgs.net/motion/command-palette-summon.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/interaction/command-palette-summon/CommandPaletteSummon.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("command-palette-summon")Prompt
Use the video-shotcraft shot recipe "Command Palette Summon" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/command-palette-summon.json Raw TSX: https://gimgs.net/motion/command-palette-summon/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/interaction/command-palette-summon/CommandPaletteSummon.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 5.06s 1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls). 2. Adapt it for: <describe what you want>. 3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).
// ===== demos/interaction/command-palette-summon/CommandPaletteSummon.tsx =====
// command-palette-summon —— 命令面板降临
// FakeDashboard 静置 → 整屏压暗+blur → ⌘K 面板从中心上方 20px 带 overshoot 弹落
// → 5 条候选行错峰浮现 → 模拟输入 2 字母(灰块当字符)候选 5→3→2 收窄
// → 高亮首条。f=110 后全静止(40f)。光标 f<104 闪烁、之后常亮。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, FakeDashboard } from '../../_fixtures/Fixtures';
const CL = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const;
// 时间轴
const DIM0 = 12; // 压暗开始(前 12f 初始静置)
const DIM1 = 22;
const PANEL_IN = 18; // 面板开始弹落
const ROWS_START = 32; // 候选行开始错峰浮现
const KEY1 = 62; // 第一个字母
const KEY2 = 78; // 第二个字母
const HL = 94; // 高亮首条
const BLINK_END = 104; // 光标停止闪烁(常亮)
const PANEL_W = 780;
const PANEL_X = (1920 - PANEL_W) / 2;
const PANEL_Y = 290;
const ROW_H = 72;
const ROW_GAP = 8;
const EXIT_DUR = 10;
// exitAt: 0=留到最后,1=第一次按键后退出,2=第二次按键后退出
const ROWS = [
{ titleW: 52, exitAt: 0 },
{ titleW: 38, exitAt: 0 },
{ titleW: 61, exitAt: 2 },
{ titleW: 45, exitAt: 1 },
{ titleW: 56, exitAt: 1 },
];
const PaletteRow: React.FC<{ i: number; frame: number }> = ({ i, frame }) => {
const { titleW, exitAt } = ROWS[i];
const inStart = ROWS_START + i * 4;
const exitStart = exitAt === 1 ? KEY1 + 3 : exitAt === 2 ? KEY2 + 3 : null;
if (exitStart !== null && frame >= exitStart + EXIT_DUR) return null; // 条件卸载,非 opacity 0
const inOp = interpolate(frame, [inStart, inStart + 8], [0, 1], CL);
const inY = interpolate(frame, [inStart, inStart + 8], [12, 0], {
easing: Easing.out(Easing.cubic),
...CL,
});
const exitT =
exitStart === null
? 1
: interpolate(frame, [exitStart, exitStart + EXIT_DUR], [1, 0], {
easing: Easing.inOut(Easing.cubic),
...CL,
});
// 高亮首条
const hl = i === 0 ? interpolate(frame, [HL, HL + 10], [0, 1], CL) : 0;
return (
<div
style={{
height: (ROW_H + ROW_GAP) * exitT,
opacity: inOp * exitT,
overflow: 'hidden',
}}
>
<div
style={{
height: ROW_H,
borderRadius: 12,
background: hl > 0 ? `rgba(228,228,226,${hl})` : 'transparent',
boxShadow: hl > 0 ? `inset 4px 0 0 rgba(47,47,47,${hl})` : 'none',
display: 'flex',
alignItems: 'center',
gap: 20,
padding: '0 22px',
boxSizing: 'border-box',
transform: `translateY(${inY}px)`,
}}
>
<div style={{ width: 36, height: 36, borderRadius: 9, background: G.mid }} />
<div style={{ height: 14, width: `${titleW}%`, background: G.bar, borderRadius: 7 }} />
<div style={{ marginLeft: 'auto', width: 58, height: 24, borderRadius: 6, background: G.line }} />
</div>
</div>
);
};
export const CommandPaletteSummon: React.FC = () => {
const frame = useCurrentFrame();
// 背景压暗 + blur
const dim = interpolate(frame, [DIM0, DIM1], [0, 0.45], CL);
const blur = interpolate(frame, [DIM0, DIM1], [0, 10], CL);
// 面板弹落:上方 20px → 过冲 +8px → 落回 0
const panelY =
frame < PANEL_IN + 9
? interpolate(frame, [PANEL_IN, PANEL_IN + 9], [-20, 8], {
easing: Easing.out(Easing.cubic),
...CL,
})
: interpolate(frame, [PANEL_IN + 9, PANEL_IN + 15], [8, 0], {
easing: Easing.inOut(Easing.cubic),
...CL,
});
const panelOp = interpolate(frame, [PANEL_IN, PANEL_IN + 7], [0, 1], CL);
// 模拟输入:已敲字符数
const typed = (frame >= KEY1 ? 1 : 0) + (frame >= KEY2 ? 1 : 0);
// 光标闪烁(周期 16f),BLINK_END 后常亮保证收尾静止
const cursorOn = frame >= BLINK_END ? true : (frame - PANEL_IN) % 16 < 8;
return (
<div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>
<div style={{ filter: frame < DIM0 ? undefined : `blur(${blur}px)` }}>
<FakeDashboard variant="A" />
</div>
<div style={{ position: 'absolute', inset: 0, background: `rgba(20,20,20,${dim})` }} />
{frame >= PANEL_IN && (
<div
style={{
position: 'absolute',
left: PANEL_X,
top: PANEL_Y,
width: PANEL_W,
transform: `translateY(${panelY}px)`,
opacity: panelOp,
background: G.card,
borderRadius: 18,
border: `2px solid ${G.border}`,
boxShadow: '0 28px 90px rgba(0,0,0,0.4)',
padding: 20,
boxSizing: 'border-box',
}}
>
{/* 输入框 */}
<div
style={{
height: 76,
borderBottom: `2px solid ${G.line}`,
display: 'flex',
alignItems: 'center',
gap: 14,
padding: '0 10px 14px 10px',
boxSizing: 'border-box',
}}
>
<div style={{ width: 32, height: 32, borderRadius: 8, background: G.bar }} />
{/* 已敲入的字符灰块 */}
{Array.from({ length: typed }).map((_, c) => (
<div key={c} style={{ width: 28, height: 38, borderRadius: 6, background: '#4a4a48' }} />
))}
{/* 光标 */}
{cursorOn && <div style={{ width: 4, height: 42, background: G.ink, borderRadius: 2 }} />}
{/* 占位提示条:敲第一个字母时卸载 */}
{typed === 0 && (
<div style={{ width: 260, height: 14, borderRadius: 7, background: G.line, opacity: 0.8 }} />
)}
</div>
{/* 候选行 */}
<div style={{ paddingTop: 14 }}>
{ROWS.map((_, i) => (
<PaletteRow key={i} i={i} frame={frame} />
))}
</div>
</div>
)}
</div>
);
};
- Read the recipePrompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
- Get the codetool: git
git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install - Register + rendertool: remotion
npx remotion render <CompositionId> out.mp4 # component: demos/interaction/command-palette-summon/CommandPaletteSummon.tsx - Or re-implementPort the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
| param | type | default | description |
|---|---|---|---|
| — | |||
Mais nesta categoria

Live voice waveform
A live waveform in a recording pill — 64 thin bars rise and fall with "speech," spiking tall mid-speech and shrinking to a dotted line on pauses, scrolling right to left; a full speak-pause-speak-submit performance.

Type, filter, drill in
Typing a search on a real UI, the grid converges to a single card on its own, then a click drills through into the detail page.

Theme switch: sweep and ripple
Two theme-switch moves — theme-sweep, a diagonal wipe (the skin changes wherever the boundary passes), and palette-ripple, a combo move (a ⌘K panel collapses to a point, a ripple spreads from it to re-skin).
Segmented thumb hero
A segmented control's thumb shift becomes the star of a close-up — a cursor clicks it and a new icon springs in.