Skip to content

Commit cc42e85

Browse files
committed
docs(demo): more realistic scroll, dismiss and plan interactions
Intervene scene: multi-scroll through expanded 13-item plan with sub-descriptions, real Claude Code prompt format, ↓ to select "bypass permissions" then ⏎ to accept. Phase duration 135β†’195f. AITools scene: both popups dismissed by tapping q toolbar button (realistic tmux behaviour) before reopening drawer. Critique diff extended with more auth.ts + security.ts content for meaningful scroll.
1 parent 445fb2e commit cc42e85

5 files changed

Lines changed: 462 additions & 83 deletions

File tree

β€Ždemo/src/DemoVideo.tsxβ€Ž

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,26 @@ import { colours } from './theme'
2121
export const FPS = 30
2222
export const TRANSITION_FRAMES = 20
2323
/** Phase durations in frames β€” desktop intro, then persistent phone with content switches */
24-
const PHASE = [75, 45, 120, 135, 280] as const
24+
const PHASE = [75, 45, 120, 195, 280] as const
2525
const PHASE_START = [
2626
0,
2727
PHASE[0],
2828
PHASE[0] + PHASE[1],
2929
PHASE[0] + PHASE[1] + PHASE[2],
3030
PHASE[0] + PHASE[1] + PHASE[2] + PHASE[3],
3131
] as const
32-
const PHONE_TOTAL = PHASE[0] + PHASE[1] + PHASE[2] + PHASE[3] + PHASE[4] // 470
32+
const PHONE_TOTAL = PHASE[0] + PHASE[1] + PHASE[2] + PHASE[3] + PHASE[4]
3333

3434
// Guard against silent arithmetic drift when phase durations change
35-
if (PHONE_TOTAL !== 655) {
36-
throw new Error(`PHONE_TOTAL is ${PHONE_TOTAL}, expected 655 β€” update PHASE or this assertion`)
35+
if (PHONE_TOTAL !== 715) {
36+
throw new Error(`PHONE_TOTAL is ${PHONE_TOTAL}, expected 715 β€” update PHASE or this assertion`)
3737
}
3838

3939
const INTRO_DURATION = 75
4040
const TAGLINE_DURATION = 70
4141

4242
/** Total frames: intro + phone section + tagline - two fade overlaps */
43-
export const TOTAL_FRAMES = INTRO_DURATION + PHONE_TOTAL + TAGLINE_DURATION - 2 * TRANSITION_FRAMES // 760
43+
export const TOTAL_FRAMES = INTRO_DURATION + PHONE_TOTAL + TAGLINE_DURATION - 2 * TRANSITION_FRAMES
4444

4545
/**
4646
* Main composition (~25s): intro branding β†’ desktop β†’ phone β†’ interactions β†’ tagline.
@@ -56,7 +56,7 @@ export const TOTAL_FRAMES = INTRO_DURATION + PHONE_TOTAL + TAGLINE_DURATION - 2
5656
* | 1: Desktop | 2.5s (75f) | Full-bleed terminal + macOS |
5757
* | 2: Handoff | 1.5s (45f) | Phone materialises around it |
5858
* | 3: Swipe | 4.0s (120f) | Claude β†’ OpenCode β†’ Claude |
59-
* | 4: Intervene| 4.5s (135f) | Scroll + approve plan |
59+
* | 4: Intervene| 6.5s (195f) | Multi-scroll + approve plan |
6060
* | 5: Drawer | 9.3s (280f) | Drawer β†’ AI Usage β†’ Critique |
6161
* | [fade] | | |
6262
* | 6: Tagline | 2.3s (70f) | "Your terminal. Everywhere." |

β€Ždemo/src/layout.tsβ€Ž

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ export const TOUCH = {
1111
scrollStart: [195, 500] as const,
1212
/** Horizontal centre, upper-mid β€” scroll gesture end */
1313
scrollEnd: [195, 350] as const,
14+
/** Second scroll β€” slight X offset for realism */
15+
scrollUp2Start: [200, 510] as const,
16+
scrollUp2End: [200, 360] as const,
17+
/** ↓ button on toolbar (row 1, index 6) */
18+
toolbarDown: [252, 715] as const,
1419
/** Enter button on toolbar (row 1, rightmost) */
15-
toolbarEnter: [350, 775] as const,
20+
toolbarEnter: [365, 715] as const,
21+
/** q button on toolbar (row 2, leftmost) */
22+
toolbarQ: [30, 775] as const,
1623
/** "More" button on toolbar (row 2) */
1724
toolbarMore: [175, 775] as const,
1825
/** Centre of remobi app icon on home screen */

β€Ždemo/src/scenes/AITools.tsxβ€Ž

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import {
1919
* Scene 4: DRAWER + POPUPS β€” the payoff (280f)
2020
*
2121
* After the agent starts implementing, the user pulls up the drawer to
22-
* check AI usage stats, then views the git diff via critique. Completes
23-
* the narrative: see work β†’ approve β†’ monitor β†’ review results.
22+
* check AI usage stats, then views the git diff via critique. Both popups
23+
* are dismissed by tapping the `q` toolbar button (realistic tmux behaviour).
2424
*
2525
* Timeline:
2626
* 0-25: Terminal stagger (static carry-over + new content)
@@ -30,31 +30,37 @@ import {
3030
* 48: Tap "AI Usage" (highlight)
3131
* 52: Drawer #1 closes
3232
* 56: AI Usage popup opens
33-
* 56-110: View AI usage (~1.8s)
34-
* 110: AI Usage popup closes
35-
* 120: Tap ☰ More again
36-
* 128: Drawer #2 opens
37-
* 128-143: Dwell on drawer
38-
* 143: Tap "Critique" (highlight)
39-
* 147: Drawer #2 closes
40-
* 151: Critique popup opens
41-
* 151-195: View diff (static)
42-
* 195-225: Scroll gesture on diff (scrollOffset 0β†’60px)
43-
* 225-245: Hold after scroll
44-
* 245: Critique popup closes
45-
* 250-280: Hold for transition to tagline
33+
* 56-98: View AI usage (~1.4s)
34+
* 98: Tap q (highlight q, TapFinger)
35+
* 102: AI Usage popup closes
36+
* 110: Tap ☰ More again
37+
* 118: Drawer #2 opens
38+
* 118-133: Dwell on drawer
39+
* 133: Tap "Critique" (highlight)
40+
* 137: Drawer #2 closes
41+
* 141: Critique popup opens
42+
* 141-183: View diff (static)
43+
* 183-213: Scroll gesture on diff (scrollOffset 0β†’120px)
44+
* 213-230: Hold after scroll
45+
* 230: Tap q (highlight q, TapFinger)
46+
* 234: Critique popup closes
47+
* 240-280: Hold for transition to tagline
4648
*/
4749
export const AITools: React.FC = () => {
4850
const frame = useCurrentFrame()
4951

50-
// ── Toolbar highlights ──
51-
const toolbarHighlight1 = frame >= 25 && frame < 29 ? '\u2630 More' : undefined
52-
const toolbarHighlight2 = frame >= 120 && frame < 124 ? '\u2630 More' : undefined
53-
const toolbarHighlight = toolbarHighlight1 ?? toolbarHighlight2
54-
const toolbarHighlightAt = toolbarHighlight1 ? 25 : toolbarHighlight2 ? 120 : 0
52+
// ── Toolbar highlights (☰ More x2 + q x2) ──
53+
const getToolbarHighlight = (): { button: string; at: number } | undefined => {
54+
if (frame >= 25 && frame < 29) return { button: '\u2630 More', at: 25 }
55+
if (frame >= 98 && frame < 102) return { button: 'q', at: 98 }
56+
if (frame >= 110 && frame < 114) return { button: '\u2630 More', at: 110 }
57+
if (frame >= 230 && frame < 234) return { button: 'q', at: 230 }
58+
return undefined
59+
}
60+
const highlight = getToolbarHighlight()
5561

5662
// ── Critique scroll ──
57-
const critiqueScroll = interpolate(frame, [195, 225], [0, 60], {
63+
const critiqueScroll = interpolate(frame, [183, 213], [0, 120], {
5864
extrapolateLeft: 'clamp',
5965
extrapolateRight: 'clamp',
6066
})
@@ -64,7 +70,7 @@ export const AITools: React.FC = () => {
6470
<AgentHeader agent="claude" />
6571
<Terminal screen={claudeCodeDenseScreen} />
6672
<TmuxStatusBar {...claudeCodeDenseStatus} />
67-
<RemobiToolbar highlightButton={toolbarHighlight} highlightAt={toolbarHighlightAt} />
73+
<RemobiToolbar highlightButton={highlight?.button} highlightAt={highlight?.at ?? 0} />
6874

6975
{/* ── Drawer #1: AI Usage ── */}
7076
<TapFinger tapFrame={25} position={TOUCH.toolbarMore} />
@@ -77,33 +83,39 @@ export const AITools: React.FC = () => {
7783
<TapFinger tapFrame={48} position={TOUCH.drawerAIUsage} />
7884

7985
{/* ── AI Usage popup ── */}
80-
<TmuxPopup showFrame={56} hideFrame={110} title="AI Usage">
86+
<TmuxPopup showFrame={56} hideFrame={102} title="AI Usage">
8187
<Terminal screen={aiUsageScreen} fontSize={12} padding={4} background="transparent" />
8288
</TmuxPopup>
8389

90+
{/* Tap q to close AI Usage popup */}
91+
<TapFinger tapFrame={98} position={TOUCH.toolbarQ} />
92+
8493
{/* ── Drawer #2: Critique ── */}
85-
<TapFinger tapFrame={120} position={TOUCH.toolbarMore} />
94+
<TapFinger tapFrame={110} position={TOUCH.toolbarMore} />
8695
<DrawerOverlay
87-
showFrame={128}
88-
hideFrame={147}
96+
showFrame={118}
97+
hideFrame={137}
8998
highlightButton="Critique"
90-
highlightAt={143}
99+
highlightAt={133}
91100
/>
92-
<TapFinger tapFrame={143} position={TOUCH.drawerCritique} />
101+
<TapFinger tapFrame={133} position={TOUCH.drawerCritique} />
93102

94103
{/* ── Critique diff popup ── */}
95-
<TmuxPopup showFrame={151} hideFrame={245} title="critique" scrollOffset={critiqueScroll}>
104+
<TmuxPopup showFrame={141} hideFrame={234} title="critique" scrollOffset={critiqueScroll}>
96105
<Terminal screen={critiqueDiffScreen} fontSize={11} padding={4} background="transparent" />
97106
</TmuxPopup>
98107

99108
{/* Scroll gesture on critique popup */}
100109
<TouchFinger
101-
startFrame={190}
102-
endFrame={225}
110+
startFrame={178}
111+
endFrame={213}
103112
from={[195, 450]}
104-
to={[195, 350]}
113+
to={[195, 330]}
105114
holdFrames={5}
106115
/>
116+
117+
{/* Tap q to close critique popup */}
118+
<TapFinger tapFrame={230} position={TOUCH.toolbarQ} />
107119
</>
108120
)
109121
}

β€Ždemo/src/scenes/Intervene.tsxβ€Ž

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,60 +9,91 @@ import { TOUCH } from '../layout'
99
import {
1010
claudeCodeAcceptedScreen,
1111
claudeCodePlanScreen,
12+
claudeCodePlanScreen2,
1213
claudeCodePlanStatus,
1314
} from '../screens'
1415

1516
/**
1617
* Scene 3: THE INTERVENTION β€” proves you can control, not just monitor.
17-
* User scrolls through Claude's plan on phone, then taps to approve.
18+
* User scrolls down through a long plan to review it, then selects
19+
* option 2 ("Yes, and bypass permissions") with ↓ and ⏎.
1820
*
19-
* Timeline (145f):
20-
* 0-5: Claude Code terminal with plan visible (top portion)
21-
* 5-10: Touch finger appears mid-screen
22-
* 10-40: Finger drags upward (scroll gesture), scrollOffset 0β†’120px
23-
* 40-60: Hold β€” viewer reads the full plan + "exit plan mode" prompt
24-
* 60-70: Tap finger on ⏎ Enter (toolbar)
25-
* 70-80: Terminal switches to accepted screen
26-
* 80-120: Checkmarks stagger in
27-
* 120-145: Hold for transition
21+
* Timeline (195f / 6.5s):
22+
* 0-5: Plan visible, top portion
23+
* 5-28: First scroll gesture (scrollOffset 0β†’150)
24+
* 33-55: Second scroll gesture (scrollOffset 150β†’280)
25+
* 55-95: Pause β€” reader reviews plan + prompt options
26+
* 98: Tap ↓ (move cursor to option 2)
27+
* 112: Tap ⏎ Enter to accept
28+
* 125: ACCEPT_FRAME β€” switch screen
29+
* 125-175: Checkmarks stagger in
30+
* 175-195: Hold for transition
2831
*/
2932
export const Intervene: React.FC = () => {
3033
const frame = useCurrentFrame()
3134

32-
// Scroll: finger drags upward frames 10-40, scrollOffset 0β†’120px
33-
const scrollOffset = interpolate(frame, [10, 40], [0, 120], {
35+
// Additive scrollOffset: two scroll gestures, content moves up
36+
const scroll1 = interpolate(frame, [10, 28], [0, 150], {
3437
extrapolateLeft: 'clamp',
3538
extrapolateRight: 'clamp',
3639
})
40+
const scroll2 = interpolate(frame, [38, 55], [0, 170], {
41+
extrapolateLeft: 'clamp',
42+
extrapolateRight: 'clamp',
43+
})
44+
const scrollOffset = scroll1 + scroll2
45+
46+
const DOWN_FRAME = 98
47+
const ACCEPT_FRAME = 125
48+
49+
// Switch plan screen to show cursor on option 2 after ↓ press
50+
const planScreen = frame >= DOWN_FRAME ? claudeCodePlanScreen2 : claudeCodePlanScreen
3751

38-
// Switch to accepted screen at frame 70 β€” use Sequence to reset
39-
// useCurrentFrame() so the accepted screen's appearAt staggers work
40-
const ACCEPT_FRAME = 70
52+
// Toolbar highlights: ↓ then ⏎
53+
const highlightButton =
54+
frame >= 98 && frame < 102
55+
? '\u2193'
56+
: frame >= 112 && frame < 116
57+
? '\u23CE'
58+
: undefined
59+
const highlightAt = frame >= 112 && frame < 116 ? 112 : 98
4160

4261
return (
4362
<>
4463
<AgentHeader agent="claude" />
4564
{frame < ACCEPT_FRAME ? (
46-
<Terminal screen={claudeCodePlanScreen} scrollOffset={scrollOffset} />
65+
<Terminal screen={planScreen} scrollOffset={scrollOffset} />
4766
) : (
4867
<Sequence from={ACCEPT_FRAME} layout="none">
4968
<Terminal screen={claudeCodeAcceptedScreen} />
5069
</Sequence>
5170
)}
5271
<TmuxStatusBar {...claudeCodePlanStatus} />
53-
<RemobiToolbar />
72+
<RemobiToolbar highlightButton={highlightButton} highlightAt={highlightAt} />
5473

55-
{/* Scroll gesture: finger drags upward */}
74+
{/* First scroll gesture */}
5675
<TouchFinger
5776
startFrame={5}
58-
endFrame={40}
77+
endFrame={28}
5978
from={TOUCH.scrollStart}
6079
to={TOUCH.scrollEnd}
6180
holdFrames={5}
6281
/>
6382

64-
{/* Tap ⏎ Enter to approve */}
65-
<TapFinger tapFrame={60} position={TOUCH.toolbarEnter} />
83+
{/* Second scroll gesture β€” slight X offset for realism */}
84+
<TouchFinger
85+
startFrame={33}
86+
endFrame={55}
87+
from={TOUCH.scrollUp2Start}
88+
to={TOUCH.scrollUp2End}
89+
holdFrames={5}
90+
/>
91+
92+
{/* Tap ↓ to move cursor to option 2 */}
93+
<TapFinger tapFrame={98} position={TOUCH.toolbarDown} />
94+
95+
{/* Tap ⏎ Enter to accept */}
96+
<TapFinger tapFrame={112} position={TOUCH.toolbarEnter} />
6697
</>
6798
)
6899
}

0 commit comments

Comments
Β (0)