{"version":3,"file":"useTimelineKeyframeSegments.mjs","names":[],"sources":["../../../src/hooks/keyframes/useTimelineKeyframeSegments.ts"],"sourcesContent":["import {\n  timelineCommandFail,\n  timelineCommandInvalidInput,\n  timelineCommandOk,\n} from '@techsquidtv/canvas-timeline-core';\nimport type {\n  TimelineCommandResult,\n  TimelineKeyframeMutationOptions,\n  TimelineKeyframePropertyId,\n  TimelineKeyframeSegment,\n  TimelineKeyframeSegmentGeometryOptions,\n  TimelineKeyframeSide,\n  TimelineKeyframeSideInterpolation,\n  TimelineKeyframeTangentHandle,\n  TimelineKeyframeTangentHandleHitTestResult,\n  TimelineKeyframeTangentHitTestInput,\n  TimelineUpdateClipKeyframeSideOptions,\n  VisibleTimelineKeyframeSegment,\n} from '@techsquidtv/canvas-timeline-core';\nimport { useTimelineEngine } from '#react/hooks/core/useTimelineEngine';\nimport { useTimelineGeometryRevision } from '#react/hooks/core/useTimelineGeometryRevision';\nimport { useCallback, useMemo } from 'react';\n/** Options accepted by `useTimelineKeyframeSegments`. */\nexport interface UseTimelineKeyframeSegmentsOptions extends TimelineKeyframeSegmentGeometryOptions {\n  /** Keyframe property used to scope segment lists and commands. */\n  property: TimelineKeyframePropertyId;\n  /** Optional clip id used to scope segment lists and commands. */\n  clipId?: string;\n}\n\n/** Input for updating one keyframe tangent side. */\nexport interface TimelineKeyframeSideUpdateInput extends Omit<\n  TimelineUpdateClipKeyframeSideOptions,\n  'side'\n> {\n  /** Side to update. */\n  side: TimelineKeyframeSide;\n}\n\n/** Result returned by `useTimelineKeyframeSegments`. */\nexport interface UseTimelineKeyframeSegmentsResult {\n  /** Keyframe segments in track order. */\n  segments: TimelineKeyframeSegment<string>[];\n  /** Viewport-intersecting keyframe segments in track order. */\n  visibleSegments: VisibleTimelineKeyframeSegment<string>[];\n  /** Bezier tangent handles from all `segments`. */\n  tangentHandles: TimelineKeyframeTangentHandle<string>[];\n  /** Bezier tangent handles from all `visibleSegments`. */\n  visibleTangentHandles: TimelineKeyframeTangentHandle<string>[];\n  /** Hit-tests one viewport point against visible Bezier tangent handles. */\n  getTangentHandleAtPoint: (\n    input: TimelineKeyframeTangentHitTestInput\n  ) => TimelineKeyframeTangentHandleHitTestResult<string> | null;\n  /** Updates one keyframe side interpolation. */\n  updateKeyframeSide: (\n    input: TimelineKeyframeSideUpdateInput,\n    options?: TimelineKeyframeMutationOptions\n  ) => TimelineCommandResult<TimelineKeyframeSideInterpolation>;\n}\n\n/**\n * Reads timeline keyframe segment geometry and exposes side-aware tangent commands.\n *\n * @param options - Clip/property filters and renderer-aligned geometry settings.\n */\nexport function useTimelineKeyframeSegments(\n  options: UseTimelineKeyframeSegmentsOptions\n): UseTimelineKeyframeSegmentsResult {\n  const engine = useTimelineEngine();\n  const revision = useTimelineGeometryRevision({ redrawOnPreview: true });\n  const {\n    clipId,\n    collapsedTrackHeight,\n    edgeThreshold,\n    keyframeSize,\n    keyframeValuePadding,\n    overscanPixels,\n    property,\n    rulerHeight,\n    selectedClipOnly,\n    selectedKeyframeOnly,\n    tangentHandleSize,\n    touchEdgeThreshold,\n    trackHeight,\n    viewportHeight,\n    viewportWidth,\n  } = options;\n\n  const geometry = useMemo(\n    () => ({\n      clipId,\n      collapsedTrackHeight,\n      edgeThreshold,\n      keyframeSize,\n      keyframeValuePadding,\n      overscanPixels,\n      property,\n      rulerHeight,\n      selectedClipOnly,\n      selectedKeyframeOnly,\n      tangentHandleSize,\n      touchEdgeThreshold,\n      trackHeight,\n      viewportHeight,\n      viewportWidth,\n    }),\n    [\n      clipId,\n      collapsedTrackHeight,\n      edgeThreshold,\n      keyframeSize,\n      keyframeValuePadding,\n      overscanPixels,\n      property,\n      rulerHeight,\n      selectedClipOnly,\n      selectedKeyframeOnly,\n      tangentHandleSize,\n      touchEdgeThreshold,\n      trackHeight,\n      viewportHeight,\n      viewportWidth,\n    ]\n  );\n\n  const segments = useMemo(() => {\n    void revision;\n    return engine.keyframes\n      .getKeyframeSegments(geometry)\n      .filter((entry) => clipId === undefined || entry.clip.id === clipId);\n  }, [clipId, engine, geometry, revision]);\n\n  const visibleSegments = useMemo(() => {\n    void revision;\n    return engine.keyframes\n      .getVisibleKeyframeSegments(geometry, segments)\n      .filter((entry) => clipId === undefined || entry.clip.id === clipId);\n  }, [clipId, engine, geometry, revision, segments]);\n\n  const tangentHandles = useMemo(() => segments.flatMap((segment) => segment.handles), [segments]);\n  const visibleTangentHandles = useMemo(\n    () => visibleSegments.flatMap((segment) => segment.handles),\n    [visibleSegments]\n  );\n\n  const getTangentHandleAtPoint = useCallback(\n    (input: TimelineKeyframeTangentHitTestInput) => {\n      const hit = engine.keyframes.getKeyframeTangentHandleAtPoint(input);\n      return hit && (clipId === undefined || hit.clip.id === clipId) ? hit : null;\n    },\n    [clipId, engine]\n  );\n\n  const updateKeyframeSide = useCallback(\n    (\n      input: TimelineKeyframeSideUpdateInput,\n      mutationOptions?: TimelineKeyframeMutationOptions\n    ): TimelineCommandResult<TimelineKeyframeSideInterpolation> => {\n      const found = engine.geometry.getClip(input.clipId);\n      let keyframe: ReturnType<typeof engine.keyframes.updateClipKeyframeSide>;\n      try {\n        keyframe = engine.keyframes.updateClipKeyframeSide(input, mutationOptions);\n      } catch (updateError: unknown) {\n        return timelineCommandInvalidInput(\n          'Timeline keyframe side could not be updated from the provided input.',\n          updateError\n        );\n      }\n      const side = keyframe?.[input.side];\n      if (side) {\n        return timelineCommandOk(side);\n      }\n      if (!found) {\n        return timelineCommandFail('not-found');\n      }\n      return found.track.locked ? timelineCommandFail('locked') : timelineCommandFail('not-found');\n    },\n    [engine]\n  );\n\n  return useMemo(\n    () => ({\n      segments,\n      visibleSegments,\n      tangentHandles,\n      visibleTangentHandles,\n      getTangentHandleAtPoint,\n      updateKeyframeSide,\n    }),\n    [\n      getTangentHandleAtPoint,\n      segments,\n      tangentHandles,\n      updateKeyframeSide,\n      visibleSegments,\n      visibleTangentHandles,\n    ]\n  );\n}\n"],"mappings":";;;;;;;;;;AAiEA,SAAgB,4BACd,SACmC;CACnC,MAAM,SAAS,kBAAkB;CACjC,MAAM,WAAW,4BAA4B,EAAE,iBAAiB,KAAK,CAAC;CACtE,MAAM,EACJ,QACA,sBACA,eACA,cACA,sBACA,gBACA,UACA,aACA,kBACA,sBACA,mBACA,oBACA,aACA,gBACA,kBACE;CAEJ,MAAM,WAAW,eACR;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,IACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,MAAM,WAAW,cAAc;EAE7B,OAAO,OAAO,UACX,oBAAoB,QAAQ,CAAC,CAC7B,QAAQ,UAAU,WAAW,KAAA,KAAa,MAAM,KAAK,OAAO,MAAM;CACvE,GAAG;EAAC;EAAQ;EAAQ;EAAU;CAAQ,CAAC;CAEvC,MAAM,kBAAkB,cAAc;EAEpC,OAAO,OAAO,UACX,2BAA2B,UAAU,QAAQ,CAAC,CAC9C,QAAQ,UAAU,WAAW,KAAA,KAAa,MAAM,KAAK,OAAO,MAAM;CACvE,GAAG;EAAC;EAAQ;EAAQ;EAAU;EAAU;CAAQ,CAAC;CAEjD,MAAM,iBAAiB,cAAc,SAAS,SAAS,YAAY,QAAQ,OAAO,GAAG,CAAC,QAAQ,CAAC;CAC/F,MAAM,wBAAwB,cACtB,gBAAgB,SAAS,YAAY,QAAQ,OAAO,GAC1D,CAAC,eAAe,CAClB;CAEA,MAAM,0BAA0B,aAC7B,UAA+C;EAC9C,MAAM,MAAM,OAAO,UAAU,gCAAgC,KAAK;EAClE,OAAO,QAAQ,WAAW,KAAA,KAAa,IAAI,KAAK,OAAO,UAAU,MAAM;CACzE,GACA,CAAC,QAAQ,MAAM,CACjB;CAEA,MAAM,qBAAqB,aAEvB,OACA,oBAC6D;EAC7D,MAAM,QAAQ,OAAO,SAAS,QAAQ,MAAM,MAAM;EAClD,IAAI;EACJ,IAAI;GACF,WAAW,OAAO,UAAU,uBAAuB,OAAO,eAAe;EAC3E,SAAS,aAAsB;GAC7B,OAAO,4BACL,wEACA,WACF;EACF;EACA,MAAM,OAAO,WAAW,MAAM;EAC9B,IAAI,MACF,OAAO,kBAAkB,IAAI;EAE/B,IAAI,CAAC,OACH,OAAO,oBAAoB,WAAW;EAExC,OAAO,MAAM,MAAM,SAAS,oBAAoB,QAAQ,IAAI,oBAAoB,WAAW;CAC7F,GACA,CAAC,MAAM,CACT;CAEA,OAAO,eACE;EACL;EACA;EACA;EACA;EACA;EACA;CACF,IACA;EACE;EACA;EACA;EACA;EACA;EACA;CACF,CACF;AACF"}