Skip to content

Commit c8cf31f

Browse files
committed
refactor: consolidate gesture planning policy
1 parent e6f15a5 commit c8cf31f

8 files changed

Lines changed: 121 additions & 109 deletions

File tree

src/commands/interaction/runtime/gesture-command.ts

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import type { AgentDeviceRuntime, CommandContext } from '../../../runtime-contract.ts';
22
import type { GestureIntent, GestureSemanticInput } from '../../../contracts/gesture-plan-types.ts';
33
import { buildGesturePlan } from '../../../contracts/gesture-plan.ts';
4-
import type { NormalizedGestureInput } from '../../../contracts/gesture-normalization.ts';
5-
import { buildSwipePresetGesturePlan } from '../../../contracts/scroll-gesture.ts';
64
import type { Point, Rect } from '../../../kernel/snapshot.ts';
75
import { AppError } from '../../../kernel/errors.ts';
86
import { successText } from '../../../utils/success-text.ts';
@@ -16,7 +14,7 @@ import { assertSupportedInteractionSurface, captureInteractionSnapshot } from '.
1614
import { resolveVisibleSnapshotViewport } from './viewport.ts';
1715

1816
export type GestureCommandOptions = CommandContext & {
19-
gesture: NormalizedGestureInput;
17+
gesture: GestureSemanticInput;
2018
};
2119

2220
export type GestureCommandResult = {
@@ -36,8 +34,7 @@ export const gestureCommand: RuntimeCommand<GestureCommandOptions, GestureComman
3634
}
3735
await assertSupportedInteractionSurface(runtime, options, options.gesture.intent);
3836
const viewport = await captureGestureViewport(runtime, options);
39-
const gesture = resolvePresetGesture(options.gesture, viewport);
40-
const plan = buildGesturePlan(gesture, viewport, runtime.backend.platform);
37+
const plan = buildGesturePlan(options.gesture, viewport, runtime.backend.platform);
4138
const backendResult = await runtime.backend.performGesture(
4239
toBackendContext(runtime, options),
4340
plan,
@@ -46,13 +43,13 @@ export const gestureCommand: RuntimeCommand<GestureCommandOptions, GestureComman
4643
const from = centroidAt(plan.pointers, 0);
4744
const to = centroidAt(plan.pointers, -1);
4845
return {
49-
kind: gesture.intent,
46+
kind: plan.intent,
5047
durationMs: plan.durationMs,
5148
pointerCount: plan.topology === 'single' ? 1 : 2,
5249
from,
5350
to,
5451
...(formattedBackendResult ? { backendResult: formattedBackendResult } : {}),
55-
...successText(gestureMessage(gesture)),
52+
...successText(gestureMessage(options.gesture, from, to)),
5653
};
5754
};
5855

@@ -68,23 +65,6 @@ async function captureGestureViewport(
6865
return resolveVisibleSnapshotViewport(capture.snapshot.nodes, 'gesture');
6966
}
7067

71-
function resolvePresetGesture(input: NormalizedGestureInput, viewport: Rect): GestureSemanticInput {
72-
if (!('preset' in input)) return input;
73-
const relative = buildSwipePresetGesturePlan(input.preset, {
74-
referenceWidth: viewport.width,
75-
referenceHeight: viewport.height,
76-
});
77-
const from = { x: viewport.x + relative.x1, y: viewport.y + relative.y1 };
78-
const to = { x: viewport.x + relative.x2, y: viewport.y + relative.y2 };
79-
if (input.intent === 'fling') return { intent: 'fling', from, to };
80-
return {
81-
intent: 'pan',
82-
origin: from,
83-
delta: { x: to.x - from.x, y: to.y - from.y },
84-
durationMs: input.durationMs,
85-
};
86-
}
87-
8868
function centroidAt(
8969
pointers: readonly { samples: readonly { point: Point }[] }[],
9070
index: 0 | -1,
@@ -102,10 +82,13 @@ function centroidAt(
10282
};
10383
}
10484

105-
function gestureMessage(input: GestureSemanticInput): string {
85+
function gestureMessage(input: GestureSemanticInput, from: Point, to: Point): string {
10686
switch (input.intent) {
107-
case 'pan':
108-
return `Panned (${input.origin.x}, ${input.origin.y}) by (${input.delta.x}, ${input.delta.y})`;
87+
case 'pan': {
88+
const origin = 'preset' in input ? from : input.origin;
89+
const delta = 'preset' in input ? { x: to.x - from.x, y: to.y - from.y } : input.delta;
90+
return `Panned (${origin.x}, ${origin.y}) by (${delta.x}, ${delta.y})`;
91+
}
10992
case 'fling':
11093
return 'direction' in input ? `Flung ${input.direction}` : 'Flung';
11194
case 'pinch':

src/contracts/gesture-normalization.ts

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Point } from '../kernel/snapshot.ts';
22
import { AppError } from '../kernel/errors.ts';
3-
import type { SwipePreset } from './scroll-gesture.ts';
3+
import { gestureDirectionDelta } from './scroll-gesture.ts';
44
import { readGesturePayload, type GesturePayload } from './gesture-input.ts';
55
import { GESTURE_FLING_DEFAULT_DISTANCE } from './gesture-plan.ts';
66
import type { GestureSemanticInput } from './gesture-plan-types.ts';
@@ -12,13 +12,8 @@ export type GestureDeprecation = {
1212
replacement: string;
1313
};
1414

15-
export type NormalizedGestureInput =
16-
| GestureSemanticInput
17-
| { intent: 'fling'; preset: SwipePreset }
18-
| { intent: 'pan'; preset: SwipePreset; durationMs: number };
19-
2015
export type NormalizedPublicGesture = {
21-
gesture: NormalizedGestureInput;
16+
gesture: GestureSemanticInput;
2217
deprecations: GestureDeprecation[];
2318
};
2419

@@ -171,7 +166,7 @@ export function normalizePublicGesture(input: GesturePayload): NormalizedPublicG
171166
gesture: {
172167
intent: 'pan',
173168
origin: input.origin,
174-
delta: directionDelta(
169+
delta: gestureDirectionDelta(
175170
input.direction,
176171
input.distance ?? GESTURE_FLING_DEFAULT_DISTANCE,
177172
),
@@ -256,19 +251,6 @@ export function normalizePublicSwipeMotion(input: {
256251
};
257252
}
258253

259-
function directionDelta(direction: 'up' | 'down' | 'left' | 'right', distance: number): Point {
260-
switch (direction) {
261-
case 'up':
262-
return { x: 0, y: -distance };
263-
case 'down':
264-
return { x: 0, y: distance };
265-
case 'left':
266-
return { x: -distance, y: 0 };
267-
case 'right':
268-
return { x: distance, y: 0 };
269-
}
270-
}
271-
272254
function optionalPositionNumber(value: string | undefined): number | undefined {
273255
return value === undefined ? undefined : Number(value);
274256
}

src/contracts/gesture-plan-types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Point, Rect } from '../kernel/snapshot.ts';
2-
import type { ScrollDirection } from './scroll-gesture.ts';
2+
import type { ScrollDirection, SwipePreset } from './scroll-gesture.ts';
33

44
export type GesturePointerCount = 1 | 2;
55

@@ -10,6 +10,7 @@ export type GestureIntent = 'fling' | 'pan' | 'pinch' | 'rotate' | 'transform';
1010

1111
export type GestureSemanticInput =
1212
| { intent: 'fling'; from: Point; to: Point }
13+
| { intent: 'fling'; preset: SwipePreset }
1314
| {
1415
intent: 'fling';
1516
direction: ScrollDirection;
@@ -23,6 +24,7 @@ export type GestureSemanticInput =
2324
pointerCount?: GesturePointerCount;
2425
durationMs?: number;
2526
}
27+
| { intent: 'pan'; preset: SwipePreset; durationMs: number }
2628
| { intent: 'pinch'; origin?: Point; scale: number }
2729
| { intent: 'rotate'; origin?: Point; degrees: number }
2830
| {

0 commit comments

Comments
 (0)