Compare commits

..

1 Commits

Author SHA1 Message Date
19bdf69830 Add suggestPositionShot schema types and operation
Mirrors the backend's position-play search: SuggestPositionShotInputGQL
(pot lock + target-area polygon), PositionSuggestionGQL /
PositionShotCandidateGQL / CueStrikeGQL, the Query field, and a
SuggestPositionShot operation with a shared candidate fragment. The
regenerated hooks also provide the result types the mobile app uses to
shape local (billsim) physics results.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:28:45 -07:00
8 changed files with 5665 additions and 15238 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,6 @@ fragment VideoCardFields on VideoGQL {
stream {
id
lastIntendedSegmentBound
uploadCompletionCursor
streamSegmentType
isCompleted
lastSegmentUploadedAt

View File

@@ -33,30 +33,6 @@ query GetRecordedStreamShots($videoId: Int!, $limit: Int! = 200) {
videoId
startFrame
endFrame
serializedShotPaths {
b64EncodedBuffer
}
cueObjectFeatures {
cueObjectDistance
cueObjectAngle
cueBallSpeed
shotDirection
spinType
}
pocketingIntentionFeatures {
make
targetPocketDistance
targetPocketAngle
targetPocketAngleDirection
marginOfErrorInDegrees
intendedPocketType
difficulty
}
pocketingIntentionInfo {
ballId
pocketId
pathMetadataIndex
}
}
}
}

View File

@@ -1,51 +0,0 @@
query GetSessionCoach($videoId: Int!) {
getSessionCoach(videoId: $videoId) {
videoId
processingId
analysisVersion
state
generatedAt
processingStatus
ineligibilityReason
summary
shotCount
madeCount
missedCount
unknownOutcomeCount
analyzedShotCount
makePercentage
primaryCandidate {
...SessionCoachCandidateFields
}
candidates {
...SessionCoachCandidateFields
}
}
}
fragment SessionCoachCandidateFields on SessionCoachCandidateGQL {
id
rank
family
kind
title
body
action
supportingShotIds
drillId
limitations
evidence {
bucketKey
bucketLabel
attemptCount
madeCount
missedCount
makePercentage
comparisonAttemptCount
comparisonMadeCount
comparisonMakePercentage
patternShotCount
score
rankReason
}
}

View File

@@ -85,3 +85,53 @@ query ComputePotAim(
occludingBallIds
}
}
fragment PositionShotCandidate on PositionShotCandidateGQL {
strike {
v0
phi
theta
a
b
}
cueFinalPosition
inTargetArea
distanceToTargetArea
robustness
potted
scratched
projection {
trajectories {
ballId
points {
time
position
}
}
events {
eventType
time
ballIds
position
}
finalState {
ballId
position
}
pottedBallIds
}
}
query SuggestPositionShot($suggestionInput: SuggestPositionShotInputGQL!) {
suggestPositionShot(suggestionInput: $suggestionInput) {
achievable
evaluatedCount
successfulCount
best {
...PositionShotCandidate
}
alternates {
...PositionShotCandidate
}
}
}

View File

@@ -12,13 +12,6 @@ query getUsageStats($days: Int! = 30) {
retentionPrevActive
retentionReturned
}
cohorts {
weekStart
signups
activated7d
paid7d
activatedNotPaid7d
}
totalUsers
totalVideos
totalShots

View File

@@ -48,7 +48,6 @@ mutation GetUploadLink($videoId: Int!, $segmentIndex: Int!) {
}
}
stream {
id
uploadCompletionCursor
}
}
@@ -128,7 +127,6 @@ fragment UploadStreamWithDetails on VideoGQL {
name
startTime
stream {
id
isCompleted
lastIntendedSegmentBound
uploadCompletionCursor

View File

@@ -65,7 +65,6 @@ type Query {
limit: Int! = 500
countRespectsLimit: Boolean! = false
): GetRunsResult!
getSessionCoach(videoId: Int!): SessionCoachGQL!
videoPlayerClusters(videoId: Int!): [PlayerClusterGQL!]!
getShotAnnotationTypes(errorTypes: Boolean = false): [ShotAnnotationTypeGQL!]!
getTableState(
@@ -81,6 +80,9 @@ type Query {
targetBallId: Int!
pocket: PocketIdentifier!
): PotAimGQL!
suggestPositionShot(
suggestionInput: SuggestPositionShotInputGQL!
): PositionSuggestionGQL!
getOrderedShots(
filterInput: FilterInput!
ids: [Int!] = null
@@ -996,81 +998,6 @@ input DatetimeOrdering {
startingAt: DateTime = null
}
type SessionCoachGQL {
videoId: Int!
processingId: Int
analysisVersion: String!
state: SessionCoachStateEnum!
generatedAt: DateTime!
processingStatus: String
ineligibilityReason: SessionCoachIneligibilityReasonEnum
summary: String
shotCount: Int!
madeCount: Int!
missedCount: Int!
unknownOutcomeCount: Int!
analyzedShotCount: Int!
makePercentage: Float
primaryCandidate: SessionCoachCandidateGQL
candidates: [SessionCoachCandidateGQL!]!
}
enum SessionCoachStateEnum {
PROCESSING
READY
NO_TRACKED_SHOTS
INELIGIBLE
FAILED
}
enum SessionCoachIneligibilityReasonEnum {
MULTIPLAYER
}
type SessionCoachCandidateGQL {
id: ID!
rank: Int!
family: SessionCoachCandidateFamilyEnum!
kind: SessionCoachCandidateKindEnum!
title: String!
body: String!
action: String!
supportingShotIds: [Int!]!
drillId: String
evidence: SessionCoachEvidenceGQL!
limitations: [String!]!
}
enum SessionCoachCandidateFamilyEnum {
AIMING
CUT_ANGLE
SPIN
OBJECT_TO_POCKET_DISTANCE
CUE_TO_OBJECT_DISTANCE
BACK_CUT
}
enum SessionCoachCandidateKindEnum {
DIRECTIONAL_MISS
OUTCOME_PATTERN
USAGE_PATTERN
}
type SessionCoachEvidenceGQL {
bucketKey: String!
bucketLabel: String!
attemptCount: Int!
madeCount: Int!
missedCount: Int!
makePercentage: Float!
comparisonAttemptCount: Int!
comparisonMadeCount: Int!
comparisonMakePercentage: Float
patternShotCount: Int!
score: Float!
rankReason: String!
}
type PlayerClusterGQL {
videoId: Int!
clusterId: Int!
@@ -1219,6 +1146,44 @@ type PotAimGQL {
occludingBallIds: [Int!]!
}
type PositionSuggestionGQL {
achievable: Boolean!
best: PositionShotCandidateGQL
alternates: [PositionShotCandidateGQL!]!
evaluatedCount: Int!
successfulCount: Int!
}
type PositionShotCandidateGQL {
strike: CueStrikeGQL!
cueFinalPosition: [Float!]!
inTargetArea: Boolean!
distanceToTargetArea: Float!
robustness: Int!
potted: Boolean!
scratched: Boolean!
projection: ShotProjectionGQL
}
type CueStrikeGQL {
v0: Float!
phi: Float!
theta: Float!
a: Float!
b: Float!
}
input SuggestPositionShotInputGQL {
cueBallId: Int!
targetBallId: Int!
pocket: PocketIdentifier!
targetArea: [[Float!]!]!
balls: [SimulationBallStateInputGQL!] = null
b64Image: String = null
useHomography: HomographyInputGQL = null
tableSize: Float = null
}
type GetShotsResult {
shots: [ShotGQL!]!
count: Int
@@ -1259,7 +1224,6 @@ type UsageStatsGQL {
totalVideos: Int!
totalShots: Int!
totalRuns: Int!
cohorts: [CohortStatsGQL!]!
windows: [UsageStatsWindowGQL!]!
daily: [UsageStatsDailyGQL!]!
processingLast24h: [ProcessingStatusCountGQL!]!
@@ -1278,14 +1242,6 @@ type FunnelStatsGQL {
retentionReturned: Int!
}
type CohortStatsGQL {
weekStart: Date!
signups: Int!
activated7d: Int!
paid7d: Int!
activatedNotPaid7d: Int!
}
type UsageStatsWindowGQL {
label: String!
newUsers: Int!