{"version":3,"file":"useTimelineClipboard.mjs","names":[],"sources":["../../../src/hooks/selection/useTimelineClipboard.ts"],"sourcesContent":["import { runTimelineCommand } from '#react/hooks/core/runTimelineCommand';\nimport { timelineCommandFail, timelineCommandOk } from '@techsquidtv/canvas-timeline-core';\nimport type { TimelineCommandResult } from '@techsquidtv/canvas-timeline-core';\nimport { useTimelineEngine } from '#react/hooks/core/useTimelineEngine';\nimport { useTimelineExternalStore } from '#react/hooks/core/useTimelineExternalStore';\nimport { useTimelineSelection } from '#react/hooks/selection/useTimelineSelection';\nimport type { RationalTime } from '@techsquidtv/canvas-timeline-utils';\nimport { useCallback, useMemo } from 'react';\nconst clipboardEvents = ['clipboard:change'] as const;\n\n/** Result returned by `useTimelineClipboard`. */\nexport interface UseTimelineClipboardResult {\n  /** Whether the current selection can be copied. */\n  canCopy: boolean;\n  /** Whether the current selection can be cut. */\n  canCut: boolean;\n  /** Whether clipboard contents are available to paste. */\n  canPaste: boolean;\n  /** Number of clips currently stored in the timeline clipboard. */\n  clipboardCount: number;\n  /** Copies the current clip selection to the timeline clipboard. */\n  copySelection: () => TimelineCommandResult;\n  /** Cuts the current clip selection to the timeline clipboard. */\n  cutSelection: () => TimelineCommandResult;\n  /** Pastes clipboard clips at a time and optional target track. */\n  pasteSelection: (time: RationalTime, targetTrackId?: string) => TimelineCommandResult;\n}\n\n/**\n * Provides access to clipboard operations for copying, cutting, and pasting clips.\n *\n * @returns Commands for manipulating the timeline clip clipboard.\n */\nexport function useTimelineClipboard(): UseTimelineClipboardResult {\n  const engine = useTimelineEngine();\n  const { selectedClip } = useTimelineSelection();\n  const clipboardCount = useTimelineExternalStore(\n    clipboardEvents,\n    (engine) => engine.clipboardCount\n  );\n  const clipboardState = useMemo(\n    () => ({\n      canCopy: selectedClip !== null,\n      canCut: selectedClip !== null,\n      canPaste: clipboardCount > 0,\n      clipboardCount,\n    }),\n    [clipboardCount, selectedClip]\n  );\n\n  const copySelection = useCallback(\n    () =>\n      runTimelineCommand(() => {\n        if (engine.getSelectedClipIds().length === 0) {\n          return timelineCommandFail('empty-selection');\n        }\n        engine.copySelection();\n        return timelineCommandOk();\n      }),\n    [engine]\n  );\n\n  const cutSelection = useCallback(\n    () =>\n      runTimelineCommand(() => {\n        if (engine.getSelectedClipIds().length === 0) {\n          return timelineCommandFail('empty-selection');\n        }\n        const result = engine.cutSelection();\n        return result.committed\n          ? timelineCommandOk()\n          : timelineCommandFail(result.preview.reason ?? 'unsupported', result.preview.message);\n      }),\n    [engine]\n  );\n\n  const pasteSelection = useCallback(\n    (time: RationalTime, targetTrackId?: string) =>\n      runTimelineCommand(() => {\n        if (!engine.canPasteSelection) {\n          return timelineCommandFail('empty-clipboard');\n        }\n        if (\n          targetTrackId !== undefined &&\n          !engine.tracks.some((track) => track.id === targetTrackId)\n        ) {\n          return timelineCommandFail('not-found');\n        }\n        const results = engine.pasteSelection(time, targetTrackId);\n        if (!results?.length) {\n          return timelineCommandFail('not-found');\n        }\n        // Earlier valid edits are also uncommitted after rollback; report the rejecting edit.\n        const failed = results.find((result) => !result.preview.valid);\n        return failed\n          ? timelineCommandFail(failed.preview.reason ?? 'unsupported', failed.preview.message)\n          : timelineCommandOk();\n      }),\n    [engine]\n  );\n\n  return useMemo(\n    () => ({\n      ...clipboardState,\n      copySelection,\n      cutSelection,\n      pasteSelection,\n    }),\n    [clipboardState, copySelection, cutSelection, pasteSelection]\n  );\n}\n"],"mappings":";;;;;;;AAQA,MAAM,kBAAkB,CAAC,kBAAkB;;;;;;AAyB3C,SAAgB,uBAAmD;CACjE,MAAM,SAAS,kBAAkB;CACjC,MAAM,EAAE,iBAAiB,qBAAqB;CAC9C,MAAM,iBAAiB,yBACrB,kBACC,WAAW,OAAO,cACrB;CACA,MAAM,iBAAiB,eACd;EACL,SAAS,iBAAiB;EAC1B,QAAQ,iBAAiB;EACzB,UAAU,iBAAiB;EAC3B;CACF,IACA,CAAC,gBAAgB,YAAY,CAC/B;CAEA,MAAM,gBAAgB,kBAElB,yBAAyB;EACvB,IAAI,OAAO,mBAAmB,CAAC,CAAC,WAAW,GACzC,OAAO,oBAAoB,iBAAiB;EAE9C,OAAO,cAAc;EACrB,OAAO,kBAAkB;CAC3B,CAAC,GACH,CAAC,MAAM,CACT;CAEA,MAAM,eAAe,kBAEjB,yBAAyB;EACvB,IAAI,OAAO,mBAAmB,CAAC,CAAC,WAAW,GACzC,OAAO,oBAAoB,iBAAiB;EAE9C,MAAM,SAAS,OAAO,aAAa;EACnC,OAAO,OAAO,YACV,kBAAkB,IAClB,oBAAoB,OAAO,QAAQ,UAAU,eAAe,OAAO,QAAQ,OAAO;CACxF,CAAC,GACH,CAAC,MAAM,CACT;CAEA,MAAM,iBAAiB,aACpB,MAAoB,kBACnB,yBAAyB;EACvB,IAAI,CAAC,OAAO,mBACV,OAAO,oBAAoB,iBAAiB;EAE9C,IACE,kBAAkB,KAAA,KAClB,CAAC,OAAO,OAAO,MAAM,UAAU,MAAM,OAAO,aAAa,GAEzD,OAAO,oBAAoB,WAAW;EAExC,MAAM,UAAU,OAAO,eAAe,MAAM,aAAa;EACzD,IAAI,CAAC,SAAS,QACZ,OAAO,oBAAoB,WAAW;EAGxC,MAAM,SAAS,QAAQ,MAAM,WAAW,CAAC,OAAO,QAAQ,KAAK;EAC7D,OAAO,SACH,oBAAoB,OAAO,QAAQ,UAAU,eAAe,OAAO,QAAQ,OAAO,IAClF,kBAAkB;CACxB,CAAC,GACH,CAAC,MAAM,CACT;CAEA,OAAO,eACE;EACL,GAAG;EACH;EACA;EACA;CACF,IACA;EAAC;EAAgB;EAAe;EAAc;CAAc,CAC9D;AACF"}