@adminide-stack/yantra-mobile 12.0.121 → 12.0.123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/lib/features/apps/appArt.js +1 -1
  2. package/lib/features/apps/appArt.js.map +1 -1
  3. package/lib/features/apps/mobileAppCatalog.js +15 -11
  4. package/lib/features/apps/mobileAppCatalog.js.map +1 -1
  5. package/lib/features/canvas/CanvasAgentChatPane.js +82 -20
  6. package/lib/features/canvas/CanvasAgentChatPane.js.map +1 -1
  7. package/lib/features/canvas/CanvasBoardsGrid.js +291 -100
  8. package/lib/features/canvas/CanvasBoardsGrid.js.map +1 -1
  9. package/lib/features/canvas/CanvasEditToolsPanel.js +105 -12
  10. package/lib/features/canvas/CanvasEditToolsPanel.js.map +1 -1
  11. package/lib/features/canvas/CanvasExperience.js +266 -68
  12. package/lib/features/canvas/CanvasExperience.js.map +1 -1
  13. package/lib/features/canvas/CanvasGeneratePanel.js +204 -66
  14. package/lib/features/canvas/CanvasGeneratePanel.js.map +1 -1
  15. package/lib/features/canvas/CanvasLibrarySheet.js +31 -13
  16. package/lib/features/canvas/CanvasLibrarySheet.js.map +1 -1
  17. package/lib/features/canvas/CanvasModelPicker.js +176 -0
  18. package/lib/features/canvas/CanvasModelPicker.js.map +1 -0
  19. package/lib/features/canvas/CanvasTasksPanel.js +325 -29
  20. package/lib/features/canvas/CanvasTasksPanel.js.map +1 -1
  21. package/lib/features/canvas/CanvasTemplatesPanel.js +5 -4
  22. package/lib/features/canvas/CanvasTemplatesPanel.js.map +1 -1
  23. package/lib/features/canvas/CanvasToolDock.js +137 -61
  24. package/lib/features/canvas/CanvasToolDock.js.map +1 -1
  25. package/lib/features/canvas/FrameExtractorExperience.js +33 -2
  26. package/lib/features/canvas/FrameExtractorExperience.js.map +1 -1
  27. package/lib/features/canvas/GeneratorReferenceSlots.js +197 -0
  28. package/lib/features/canvas/GeneratorReferenceSlots.js.map +1 -0
  29. package/lib/features/canvas/NativeCanvasViewer.js +89 -11
  30. package/lib/features/canvas/NativeCanvasViewer.js.map +1 -1
  31. package/lib/features/canvas/PromoContextFields.js +55 -2
  32. package/lib/features/canvas/PromoContextFields.js.map +1 -1
  33. package/lib/features/canvas/SetStudioExperience.js +323 -13
  34. package/lib/features/canvas/SetStudioExperience.js.map +1 -1
  35. package/lib/features/canvas/VideoEditExperience.js +1 -53
  36. package/lib/features/canvas/VideoEditExperience.js.map +1 -1
  37. package/lib/features/canvas/VideoEditorExperience.js +697 -506
  38. package/lib/features/canvas/VideoEditorExperience.js.map +1 -1
  39. package/lib/features/canvas/VideoGeneratorExperience.js +22 -5
  40. package/lib/features/canvas/VideoGeneratorExperience.js.map +1 -1
  41. package/lib/features/canvas/canvasAgentChat.js +125 -11
  42. package/lib/features/canvas/canvasAgentChat.js.map +1 -1
  43. package/lib/features/canvas/canvasCore.js +117 -12
  44. package/lib/features/canvas/canvasCore.js.map +1 -1
  45. package/lib/features/canvas/canvasEditOps.js +148 -111
  46. package/lib/features/canvas/canvasEditOps.js.map +1 -1
  47. package/lib/features/canvas/canvasTaskSync.js +235 -0
  48. package/lib/features/canvas/canvasTaskSync.js.map +1 -0
  49. package/lib/features/canvas/canvasTasks.js +98 -11
  50. package/lib/features/canvas/canvasTasks.js.map +1 -1
  51. package/lib/features/canvas/createTiles.js +247 -0
  52. package/lib/features/canvas/createTiles.js.map +1 -0
  53. package/lib/features/canvas/deepLinks.js +83 -0
  54. package/lib/features/canvas/deepLinks.js.map +1 -0
  55. package/lib/features/canvas/frameExtract.js +13 -1
  56. package/lib/features/canvas/frameExtract.js.map +1 -1
  57. package/lib/features/canvas/generationModels.js +36 -8
  58. package/lib/features/canvas/generationModels.js.map +1 -1
  59. package/lib/features/canvas/generatorCommands.js +106 -0
  60. package/lib/features/canvas/generatorCommands.js.map +1 -0
  61. package/lib/features/canvas/generatorReferences.js +82 -0
  62. package/lib/features/canvas/generatorReferences.js.map +1 -0
  63. package/lib/features/canvas/homeWork.js +74 -0
  64. package/lib/features/canvas/homeWork.js.map +1 -0
  65. package/lib/features/canvas/linksRpc.js +130 -0
  66. package/lib/features/canvas/linksRpc.js.map +1 -0
  67. package/lib/features/canvas/mediaRecheck.js +176 -0
  68. package/lib/features/canvas/mediaRecheck.js.map +1 -0
  69. package/lib/features/canvas/mediaUrls.js +258 -0
  70. package/lib/features/canvas/mediaUrls.js.map +1 -0
  71. package/lib/features/canvas/nativeViewerRegistry.js.map +1 -1
  72. package/lib/features/canvas/promoProjectImport.js +80 -13
  73. package/lib/features/canvas/promoProjectImport.js.map +1 -1
  74. package/lib/features/canvas/promoVideo.js +106 -15
  75. package/lib/features/canvas/promoVideo.js.map +1 -1
  76. package/lib/features/canvas/reframe.js +228 -0
  77. package/lib/features/canvas/reframe.js.map +1 -0
  78. package/lib/features/canvas/serverTasks.js +697 -0
  79. package/lib/features/canvas/serverTasks.js.map +1 -0
  80. package/lib/features/canvas/set/SetViewportWebView.js +207 -28
  81. package/lib/features/canvas/set/SetViewportWebView.js.map +1 -1
  82. package/lib/features/canvas/set/agent/prompt.js +66 -0
  83. package/lib/features/canvas/set/agent/prompt.js.map +1 -0
  84. package/lib/features/canvas/set/agent/setOps.js +203 -0
  85. package/lib/features/canvas/set/agent/setOps.js.map +1 -0
  86. package/lib/features/canvas/set/agent/setTurn.js +128 -0
  87. package/lib/features/canvas/set/agent/setTurn.js.map +1 -0
  88. package/lib/features/canvas/set/agent/snapshot.js +43 -0
  89. package/lib/features/canvas/set/agent/snapshot.js.map +1 -0
  90. package/lib/features/canvas/set/agent/tools.js +580 -0
  91. package/lib/features/canvas/set/agent/tools.js.map +1 -0
  92. package/lib/features/canvas/set/setProject.js +43 -4
  93. package/lib/features/canvas/set/setProject.js.map +1 -1
  94. package/lib/features/canvas/set/supplyLibrary.js +397 -0
  95. package/lib/features/canvas/set/supplyLibrary.js.map +1 -0
  96. package/lib/features/canvas/storageLibrary.js +113 -52
  97. package/lib/features/canvas/storageLibrary.js.map +1 -1
  98. package/lib/features/canvas/studioCatalog.js +15 -2
  99. package/lib/features/canvas/studioCatalog.js.map +1 -1
  100. package/lib/features/canvas/studioLooks.js +1 -8
  101. package/lib/features/canvas/studioLooks.js.map +1 -1
  102. package/lib/features/canvas/useBoardMediaRefresh.js +66 -0
  103. package/lib/features/canvas/useBoardMediaRefresh.js.map +1 -0
  104. package/lib/features/canvas/useMediaSource.js +84 -0
  105. package/lib/features/canvas/useMediaSource.js.map +1 -0
  106. package/lib/features/canvas/video3d.js +11 -10
  107. package/lib/features/canvas/video3d.js.map +1 -1
  108. package/lib/features/canvas/videoEditor/BlurBoxOverlay.js +46 -0
  109. package/lib/features/canvas/videoEditor/BlurBoxOverlay.js.map +1 -0
  110. package/lib/features/canvas/videoEditor/ExportTaskCard.js +134 -0
  111. package/lib/features/canvas/videoEditor/ExportTaskCard.js.map +1 -0
  112. package/lib/features/canvas/videoEditor/agent/AgentCutPane.js +45 -7
  113. package/lib/features/canvas/videoEditor/agent/AgentCutPane.js.map +1 -1
  114. package/lib/features/canvas/videoEditor/agent/agentLoop.js +4 -2
  115. package/lib/features/canvas/videoEditor/agent/agentLoop.js.map +1 -1
  116. package/lib/features/canvas/videoEditor/agent/attachments.js +304 -0
  117. package/lib/features/canvas/videoEditor/agent/attachments.js.map +1 -0
  118. package/lib/features/canvas/videoEditor/agent/brainClient.js +3 -2
  119. package/lib/features/canvas/videoEditor/agent/brainClient.js.map +1 -1
  120. package/lib/features/canvas/videoEditor/agent/cutTools.js +207 -7
  121. package/lib/features/canvas/videoEditor/agent/cutTools.js.map +1 -1
  122. package/lib/features/canvas/videoEditor/blurBox.js +34 -0
  123. package/lib/features/canvas/videoEditor/blurBox.js.map +1 -0
  124. package/lib/features/canvas/videoEditor/compileTimeline.js +668 -0
  125. package/lib/features/canvas/videoEditor/compileTimeline.js.map +1 -0
  126. package/lib/features/canvas/videoEditor/editorModel.js +56 -22
  127. package/lib/features/canvas/videoEditor/editorModel.js.map +1 -1
  128. package/lib/features/canvas/videoEditor/exportPipeline.js +77 -0
  129. package/lib/features/canvas/videoEditor/exportPipeline.js.map +1 -0
  130. package/lib/features/canvas/videoEditor/exportRenderer.js +19 -12
  131. package/lib/features/canvas/videoEditor/exportRenderer.js.map +1 -1
  132. package/lib/features/canvas/videoEditor/exportTask.js +216 -0
  133. package/lib/features/canvas/videoEditor/exportTask.js.map +1 -0
  134. package/lib/features/canvas/videoEditor/formWidgets.js +142 -0
  135. package/lib/features/canvas/videoEditor/formWidgets.js.map +1 -0
  136. package/lib/features/canvas/videoEditor/produce/ProduceVideoSheet.js +195 -0
  137. package/lib/features/canvas/videoEditor/produce/ProduceVideoSheet.js.map +1 -0
  138. package/lib/features/canvas/videoEditor/produce/produceScenes.js +158 -0
  139. package/lib/features/canvas/videoEditor/produce/produceScenes.js.map +1 -0
  140. package/lib/features/canvas/videoEditor/remoteMediaClient.js +17 -5
  141. package/lib/features/canvas/videoEditor/remoteMediaClient.js.map +1 -1
  142. package/lib/features/canvas/videoEditor/renderTimeline.js +207 -0
  143. package/lib/features/canvas/videoEditor/renderTimeline.js.map +1 -0
  144. package/lib/features/canvas/videoEditor/renewLinks.js +64 -0
  145. package/lib/features/canvas/videoEditor/renewLinks.js.map +1 -0
  146. package/lib/features/canvas/videoEditor/sceneLayers.js +101 -0
  147. package/lib/features/canvas/videoEditor/sceneLayers.js.map +1 -0
  148. package/lib/features/canvas/videoEditor/tools/ToolsDrawer.js +283 -0
  149. package/lib/features/canvas/videoEditor/tools/ToolsDrawer.js.map +1 -0
  150. package/lib/features/canvas/videoEditor/tools/catalog.js +576 -0
  151. package/lib/features/canvas/videoEditor/tools/catalog.js.map +1 -0
  152. package/lib/features/canvas/videoEditor/videoProjectsStore.js +27 -3
  153. package/lib/features/canvas/videoEditor/videoProjectsStore.js.map +1 -1
  154. package/lib/features/webbuilder/WebBuilderExperience.js +29 -1
  155. package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -1
  156. package/lib/features/webbuilder/buildFragments.js +8 -4
  157. package/lib/features/webbuilder/buildFragments.js.map +1 -1
  158. package/lib/features/webbuilder/codezenHandoff.js +108 -0
  159. package/lib/features/webbuilder/codezenHandoff.js.map +1 -0
  160. package/lib/features/webbuilder/workflow.js.map +1 -1
  161. package/lib/hooks/useChatApi.js +1 -2
  162. package/lib/hooks/useChatApi.js.map +1 -1
  163. package/lib/screens/CanvasBoard/index.js +12 -1
  164. package/lib/screens/CanvasBoard/index.js.map +1 -1
  165. package/lib/screens/Home/HomeScreen.js.map +1 -1
  166. package/lib/screens/Home/components/HomeSuggestions.js +3 -3
  167. package/lib/screens/Home/components/HomeSuggestions.js.map +1 -1
  168. package/lib/screens/WebBuilder/index.js +4 -2
  169. package/lib/screens/WebBuilder/index.js.map +1 -1
  170. package/lib/screens/WebBuilderNative/index.js +29 -2
  171. package/lib/screens/WebBuilderNative/index.js.map +1 -1
  172. package/lib/screens/WebBuilderNative/useWebBuilderHost.js +30 -6
  173. package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -1
  174. package/package.json +3 -3
  175. /package/lib/assets/apps/{paper2poster.webp → poster.webp} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"canvasEditOps.js","sources":["../../../src/features/canvas/canvasEditOps.ts"],"sourcesContent":["/**\n * Native runners for canvas edit tools — twin of web reframe/upscale/editOps\n * + lipsyncRpc, driven by RunMediaAction.\n */\nimport { generationMediaUrl, resultUrl, type RunMediaAction } from './videoEditor/mediaActions';\nimport { buildVideoGenerateParams } from './generationModels';\nimport { unwrapProxyUrl } from './videoEditor/remoteMediaClient';\nimport { continuationPrompt } from './extendVideo';\n\nexport const REFRAME_RATIOS = ['9:16', '16:9', '4:3', '3:4', '21:9', '1:1'] as const;\nexport type ReframeRatio = (typeof REFRAME_RATIOS)[number];\nexport type ReframeFit = 'crop' | 'fit';\n\nexport interface Dims {\n width: number;\n height: number;\n}\n\nexport const UPSCALE_TARGETS = [\n { id: '1080p', label: '1080p', longEdge: 1920 },\n { id: '2k', label: '2K', longEdge: 2560 },\n { id: '4k', label: '4K', longEdge: 3840 },\n] as const;\n\nexport type UpscaleTarget = (typeof UPSCALE_TARGETS)[number]['id'];\n\nconst evenDown = (n: number) => Math.max(2, Math.floor(n / 2) * 2);\nconst even = (n: number) => Math.max(2, Math.round(n / 2) * 2);\n\nexport function parseRatio(ratio: string): number | null {\n const [w, h] = ratio.split(':').map(Number);\n if (!Number.isFinite(w) || !Number.isFinite(h) || !w || !h) return null;\n return w / h;\n}\n\nexport type ReframePlan =\n | { fit: 'crop'; width: number; height: number; x: number; y: number }\n | { fit: 'fit'; width: number; height: number };\n\nexport function reframePlan(src: Dims, ratio: string, fit: ReframeFit): ReframePlan | null {\n const target = parseRatio(ratio);\n if (target === null || !(src.width > 0) || !(src.height > 0)) return null;\n const current = src.width / src.height;\n if (fit === 'crop') {\n let width = src.width;\n let height = src.height;\n if (current > target) width = src.height * target;\n else height = src.width / target;\n width = Math.min(evenDown(Math.round(width)), evenDown(src.width));\n height = Math.min(evenDown(Math.round(height)), evenDown(src.height));\n return {\n fit: 'crop',\n width,\n height,\n x: Math.floor((src.width - width) / 2),\n y: Math.floor((src.height - height) / 2),\n };\n }\n let width = src.width;\n let height = src.height;\n if (current > target) height = even(src.width / target);\n else width = even(src.height * target);\n return { fit: 'fit', width, height };\n}\n\nexport function upscalePlan(src: Dims, target: UpscaleTarget): Dims | null {\n const t = UPSCALE_TARGETS.find((u) => u.id === target);\n if (!t || !(src.width > 0) || !(src.height > 0)) return null;\n const long = Math.max(src.width, src.height);\n if (long >= t.longEdge) return null;\n const scale = t.longEdge / long;\n return { width: even(src.width * scale), height: even(src.height * scale) };\n}\n\nasync function runImage(run: RunMediaAction, action: string, params: Record<string, unknown>): Promise<string> {\n const result = await run('yantra-image-editing', action, params);\n return resultUrl(result, action);\n}\n\nasync function runVideo(run: RunMediaAction, action: string, params: Record<string, unknown>): Promise<string> {\n const result = await run('yantra-video-editing', action, params);\n return resultUrl(result, action);\n}\n\nexport async function runReframe(\n run: RunMediaAction,\n kind: 'image' | 'video',\n src: string,\n plan: ReframePlan,\n): Promise<string> {\n const url = unwrapProxyUrl(src);\n if (kind === 'image') {\n if (plan.fit === 'crop') {\n return runImage(run, 'YANTRA_IMAGE_EDIT_CROP', {\n input_url: url,\n x: plan.x,\n y: plan.y,\n width: plan.width,\n height: plan.height,\n });\n }\n return runImage(run, 'YANTRA_IMAGE_EDIT_PAD', {\n input_url: url,\n width: plan.width,\n height: plan.height,\n });\n }\n if (plan.fit === 'crop') {\n return runVideo(run, 'YANTRA_VIDEO_EDIT_CROP', {\n input_url: url,\n x: plan.x,\n y: plan.y,\n width: plan.width,\n height: plan.height,\n });\n }\n return runVideo(run, 'YANTRA_VIDEO_EDIT_TRANSCODE', {\n input_url: url,\n resolution: `${plan.width}x${plan.height}`,\n });\n}\n\nexport async function runRemoveBackground(run: RunMediaAction, kind: 'image' | 'video', src: string): Promise<string> {\n const url = unwrapProxyUrl(src);\n if (kind === 'image') {\n return runImage(run, 'YANTRA_IMAGE_EDIT_REMOVE_BACKGROUND', { input_url: url });\n }\n return runVideo(run, 'YANTRA_VIDEO_EDIT_CHROMAKEY', {\n input_url: url,\n color: 'auto',\n similarity: 0.2,\n blend: 0.1,\n });\n}\n\nexport async function runUpscale(\n run: RunMediaAction,\n kind: 'image' | 'video',\n src: string,\n out: Dims,\n): Promise<string> {\n const url = unwrapProxyUrl(src);\n if (kind === 'image') {\n return runImage(run, 'YANTRA_IMAGE_EDIT_RESIZE', {\n input_url: url,\n width: out.width,\n height: out.height,\n fit: 'fill',\n });\n }\n return runVideo(run, 'YANTRA_VIDEO_EDIT_TRANSCODE', {\n input_url: url,\n resolution: `${out.width}x${out.height}`,\n });\n}\n\n/** Simple grade: boost contrast/saturation slightly toward a cinematic look. */\nexport async function runMatchLook(run: RunMediaAction, kind: 'image' | 'video', src: string): Promise<string> {\n const url = unwrapProxyUrl(src);\n if (kind === 'image') {\n return runImage(run, 'YANTRA_IMAGE_EDIT_FINETUNE', {\n input_url: url,\n brightness: 0.02,\n contrast: 1.15,\n saturation: 1.1,\n });\n }\n return runVideo(run, 'YANTRA_VIDEO_EDIT_COLOR_ADJUST', {\n input_url: url,\n brightness: 0.02,\n contrast: 1.15,\n saturation: 1.1,\n });\n}\n\nexport async function runLipSync(\n run: RunMediaAction,\n videoSrc: string,\n opts: {\n text?: string;\n audioUrl?: string;\n model?: string;\n syncMode?: string;\n voiceId?: string;\n },\n): Promise<string> {\n const videoUrl = unwrapProxyUrl(videoSrc);\n const submit = await run('yantra-audio', 'YANTRA_AUDIO_LIPSYNC_SUBMIT', {\n video_url: videoUrl,\n ...(opts.audioUrl ? { audio_url: unwrapProxyUrl(opts.audioUrl) } : {}),\n ...(opts.text?.trim() && !opts.audioUrl ? { text: opts.text.trim() } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n ...(opts.syncMode ? { sync_mode: opts.syncMode } : {}),\n ...(opts.voiceId ? { voice_id: opts.voiceId } : {}),\n });\n const body = ((submit.data && typeof submit.data === 'object' ? submit.data : submit) ?? {}) as Record<\n string,\n unknown\n >;\n const requestId = typeof body.requestId === 'string' ? body.requestId : '';\n const statusUrl = typeof body.statusUrl === 'string' ? body.statusUrl : '';\n if (!requestId) throw new Error('Lip sync did not return a queue handle');\n\n const deadline = Date.now() + 12 * 60_000;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, 5000));\n const poll = await run('yantra-audio', 'YANTRA_AUDIO_LIPSYNC_STATUS', {\n requestId,\n ...(statusUrl ? { statusUrl } : {}),\n });\n const p = ((poll.data && typeof poll.data === 'object' ? poll.data : poll) ?? {}) as Record<string, unknown>;\n const state = typeof p.state === 'string' ? p.state : typeof p.status === 'string' ? p.status : '';\n if (state === 'succeeded' || state === 'completed') {\n const url =\n (typeof p.videoUrl === 'string' && p.videoUrl) ||\n (typeof p.url === 'string' && p.url) ||\n resultUrl(poll, 'YANTRA_AUDIO_LIPSYNC_STATUS');\n if (url) return url;\n throw new Error('Lip sync finished without a video URL');\n }\n if (state === 'failed' || state === 'error') {\n throw new Error(typeof p.error === 'string' ? p.error : 'Lip sync failed');\n }\n }\n throw new Error('Lip sync is taking longer than expected');\n}\n\n/**\n * Extend: last-frame thumbnail → generate continuation → concat.\n */\nexport async function runExtendVideo(run: RunMediaAction, videoSrc: string, prompt: string): Promise<string> {\n const url = unwrapProxyUrl(videoSrc);\n const thumb = await run('yantra-video-editing', 'YANTRA_VIDEO_EDIT_THUMBNAIL', {\n input_url: url,\n at_end: true,\n backoff_secs: 0.08,\n });\n const frameUrl = resultUrl(thumb, 'YANTRA_VIDEO_EDIT_THUMBNAIL');\n const generated = await run(\n 'yantra-video-generation',\n 'YANTRA_VIDEO_GENERATION_GENERATE',\n buildVideoGenerateParams({\n prompt: continuationPrompt(prompt),\n aspectRatio: '16:9',\n referenceImageUrl: frameUrl,\n durationSeconds: 4,\n }),\n );\n const nextUrl = generationMediaUrl(generated);\n const concat = await run('yantra-video-editing', 'YANTRA_VIDEO_EDIT_CONCAT', {\n input_urls: [url, nextUrl],\n });\n return resultUrl(concat, 'YANTRA_VIDEO_EDIT_CONCAT');\n}\n"],"names":["width","height"],"mappings":"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"}
1
+ {"version":3,"file":"canvasEditOps.js","sources":["../../../src/features/canvas/canvasEditOps.ts"],"sourcesContent":["/**\n * Native runners for canvas edit tools — twin of web reframe/upscale/editOps\n * + lipsyncRpc, driven by RunMediaAction.\n */\nimport { generationMediaUrl, resultUrl, type RunMediaAction } from './videoEditor/mediaActions';\nimport { buildVideoGenerateParams } from './generationModels';\nimport { runActionAcceptsJob, unwrapProxyUrl, type RunMediaActionWithBody } from './videoEditor/remoteMediaClient';\nimport { lipSyncVideoFromVerdict, trackLipSync, type JobRecord, type SurfaceJobRef } from './serverTasks';\nimport { continuationPrompt } from './extendVideo';\nimport {\n reframeClipParams,\n reframeOutput,\n reframePlan as platformReframePlan,\n reframeStillCall,\n stillReframeNote,\n type ReframeChoice,\n} from './reframe';\n\nexport const REFRAME_RATIOS = ['9:16', '16:9', '4:3', '3:4', '21:9', '1:1'] as const;\nexport type ReframeRatio = (typeof REFRAME_RATIOS)[number];\nexport type ReframeFit = 'crop' | 'fit';\n\nexport interface Dims {\n width: number;\n height: number;\n}\n\nexport const UPSCALE_TARGETS = [\n { id: '1080p', label: '1080p', longEdge: 1920 },\n { id: '2k', label: '2K', longEdge: 2560 },\n { id: '4k', label: '4K', longEdge: 3840 },\n] as const;\n\nexport type UpscaleTarget = (typeof UPSCALE_TARGETS)[number]['id'];\n\nconst evenDown = (n: number) => Math.max(2, Math.floor(n / 2) * 2);\nconst even = (n: number) => Math.max(2, Math.round(n / 2) * 2);\n\nexport function parseRatio(ratio: string): number | null {\n const [w, h] = ratio.split(':').map(Number);\n if (!Number.isFinite(w) || !Number.isFinite(h) || !w || !h) return null;\n return w / h;\n}\n\nexport type ReframePlan =\n | { fit: 'crop'; width: number; height: number; x: number; y: number }\n | { fit: 'fit'; width: number; height: number };\n\nexport function reframePlan(src: Dims, ratio: string, fit: ReframeFit): ReframePlan | null {\n const target = parseRatio(ratio);\n if (target === null || !(src.width > 0) || !(src.height > 0)) return null;\n const current = src.width / src.height;\n if (fit === 'crop') {\n let width = src.width;\n let height = src.height;\n if (current > target) width = src.height * target;\n else height = src.width / target;\n width = Math.min(evenDown(Math.round(width)), evenDown(src.width));\n height = Math.min(evenDown(Math.round(height)), evenDown(src.height));\n return {\n fit: 'crop',\n width,\n height,\n x: Math.floor((src.width - width) / 2),\n y: Math.floor((src.height - height) / 2),\n };\n }\n let width = src.width;\n let height = src.height;\n if (current > target) height = even(src.width / target);\n else width = even(src.height * target);\n return { fit: 'fit', width, height };\n}\n\nexport function upscalePlan(src: Dims, target: UpscaleTarget): Dims | null {\n const t = UPSCALE_TARGETS.find((u) => u.id === target);\n if (!t || !(src.width > 0) || !(src.height > 0)) return null;\n const long = Math.max(src.width, src.height);\n if (long >= t.longEdge) return null;\n const scale = t.longEdge / long;\n return { width: even(src.width * scale), height: even(src.height * scale) };\n}\n\nasync function runImage(run: RunMediaAction, action: string, params: Record<string, unknown>): Promise<string> {\n const result = await run('yantra-image-editing', action, params);\n return resultUrl(result, action);\n}\n\nasync function runVideo(run: RunMediaAction, action: string, params: Record<string, unknown>): Promise<string> {\n const result = await run('yantra-video-editing', action, params);\n return resultUrl(result, action);\n}\n\nexport async function runReframe(\n run: RunMediaAction,\n kind: 'image' | 'video',\n src: string,\n plan: ReframePlan,\n): Promise<string> {\n const url = unwrapProxyUrl(src);\n if (kind === 'image') {\n if (plan.fit === 'crop') {\n return runImage(run, 'YANTRA_IMAGE_EDIT_CROP', {\n input_url: url,\n x: plan.x,\n y: plan.y,\n width: plan.width,\n height: plan.height,\n });\n }\n return runImage(run, 'YANTRA_IMAGE_EDIT_PAD', {\n input_url: url,\n width: plan.width,\n height: plan.height,\n });\n }\n // One REFRAME pass (web `reframeClipParams`): TRANSCODE to WxH squashed the\n // picture. A fit spreads the clip's own backdrop into the bars ('auto' =\n // web's default Match), picked by the connector from frames across the clip.\n return runVideo(run, 'YANTRA_VIDEO_EDIT_REFRAME', {\n input_url: url,\n width: plan.width,\n height: plan.height,\n ...(plan.fit === 'crop' ? { fit: 'cover' } : { fit: 'contain', background: 'auto' }),\n });\n}\n\n/**\n * Platform reframe (web `reframe.ts`): a clip renders in ONE REFRAME pass at\n * the platform size; a still maps onto CROP / PAD (see `reframeStillCall`).\n * Returns the url and, for a still whose bars could not be honoured, a note.\n */\nexport async function runReframeChoice(\n run: RunMediaAction,\n kind: 'image' | 'video',\n src: string,\n choice: ReframeChoice,\n dims: Dims,\n): Promise<{ url: string; note: string | null }> {\n const url = unwrapProxyUrl(src);\n if (kind === 'video') {\n const plan = platformReframePlan(dims, choice.format.ratio, choice.fit, choice.focus);\n if (!plan) throw new Error('Could not plan that reframe');\n const { params } = reframeClipParams(choice, dims, reframeOutput(choice.format, plan));\n return { url: await runVideo(run, 'YANTRA_VIDEO_EDIT_REFRAME', { input_url: url, ...params }), note: null };\n }\n const call = reframeStillCall(choice, dims);\n if (!call) throw new Error('Could not plan that reframe');\n return {\n url: await runImage(run, call.action, { input_url: url, ...call.params }),\n note: stillReframeNote(choice),\n };\n}\n\n/** Key colour choice (web EditToolPanel): border-sampled, green/blue screen, or a hex. */\nexport type KeyColorChoice = 'auto' | 'green' | 'blue' | 'custom';\n\nexport const KEY_COLOR_CHOICES: readonly { id: KeyColorChoice; label: string; hex?: string }[] = [\n { id: 'auto', label: 'Auto' },\n { id: 'green', label: 'Green', hex: '#00ff00' },\n { id: 'blue', label: 'Blue', hex: '#0000ff' },\n { id: 'custom', label: 'Custom' },\n];\n\nexport interface RemoveBackgroundOptions {\n key?: KeyColorChoice;\n /** '#rrggbb' for `custom`. */\n customHex?: string;\n similarity?: number;\n blend?: number;\n}\n\n/** Connector defaults differ per kind; start where each one does (web). */\nexport function removeBackgroundDefaults(kind: 'image' | 'video'): { similarity: number; blend: number } {\n return kind === 'video' ? { similarity: 0.1, blend: 0 } : { similarity: 0.12, blend: 0.08 };\n}\n\n/** A '#rgb' / '#rrggbb' / 'rrggbb' string as '#rrggbb', or null. */\nexport function normalizeHex(raw: string | undefined): string | null {\n const t = (raw ?? '').trim().replace(/^#/, '').replace(/^0x/i, '');\n if (/^[0-9a-f]{3}$/i.test(t))\n return `#${t\n .split('')\n .map((c) => c + c)\n .join('')\n .toLowerCase()}`;\n if (/^[0-9a-f]{6}$/i.test(t)) return `#${t.toLowerCase()}`;\n return null;\n}\n\n/**\n * The key colour param per kind: stills take 'auto' or a '#hex'; clips need\n * an ffmpeg hex ('auto' is a green screen there, as on web).\n */\nexport function keyColorParam(\n kind: 'image' | 'video',\n opts: Pick<RemoveBackgroundOptions, 'key' | 'customHex'>,\n): string {\n const key = opts.key ?? 'auto';\n const hex =\n key === 'custom' ? normalizeHex(opts.customHex) : (KEY_COLOR_CHOICES.find((c) => c.id === key)?.hex ?? null);\n if (kind === 'image') return hex ?? 'auto';\n return `0x${(hex ?? '#00ff00').slice(1).toUpperCase()}`;\n}\n\nexport async function runRemoveBackground(\n run: RunMediaAction,\n kind: 'image' | 'video',\n src: string,\n opts: RemoveBackgroundOptions = {},\n): Promise<string> {\n const url = unwrapProxyUrl(src);\n const defaults = removeBackgroundDefaults(kind);\n const params = {\n input_url: url,\n color: keyColorParam(kind, opts),\n similarity: opts.similarity ?? defaults.similarity,\n blend: opts.blend ?? defaults.blend,\n };\n if (kind === 'image') return runImage(run, 'YANTRA_IMAGE_EDIT_REMOVE_BACKGROUND', params);\n return runVideo(run, 'YANTRA_VIDEO_EDIT_CHROMA_KEY', params);\n}\n\nexport async function runUpscale(\n run: RunMediaAction,\n kind: 'image' | 'video',\n src: string,\n out: Dims,\n): Promise<string> {\n const url = unwrapProxyUrl(src);\n if (kind === 'image') {\n return runImage(run, 'YANTRA_IMAGE_EDIT_RESIZE', {\n input_url: url,\n width: out.width,\n height: out.height,\n fit: 'fill',\n });\n }\n return runVideo(run, 'YANTRA_VIDEO_EDIT_TRANSCODE', {\n input_url: url,\n resolution: `${out.width}x${out.height}`,\n });\n}\n\n/** Simple grade: boost contrast/saturation slightly toward a cinematic look. */\nexport async function runMatchLook(run: RunMediaAction, kind: 'image' | 'video', src: string): Promise<string> {\n const url = unwrapProxyUrl(src);\n if (kind === 'image') {\n return runImage(run, 'YANTRA_IMAGE_EDIT_FINETUNE', {\n input_url: url,\n brightness: 0.02,\n contrast: 1.15,\n saturation: 1.1,\n });\n }\n return runVideo(run, 'YANTRA_VIDEO_EDIT_COLOR_ADJUST', {\n input_url: url,\n brightness: 0.02,\n contrast: 1.15,\n saturation: 1.1,\n });\n}\n\n/**\n * Lip sync over the `yantra-audio` queue (web `rpc/lipsyncRpc.ts`): SUBMIT takes\n * camelCase params and returns `{statusUrl, responseUrl}`; STATUS takes both and\n * answers `status` = completed | failed | (anything else: pending).\n */\nexport async function runLipSync(\n run: RunMediaAction,\n videoSrc: string,\n opts: {\n text?: string;\n audioUrl?: string;\n model?: string;\n syncMode?: string;\n voiceId?: string;\n },\n onProgress?: (note: string) => void,\n /**\n * The run's server job (./serverTasks `trackLipSync`): the SUBMIT carries it\n * with the follow spec, and the phone waits on the agent's verdict instead of\n * polling the queue beside it - polling only when the agent never took it.\n */\n tracking: { job?: SurfaceJobRef; awaitAgent?: (startedAt: number) => Promise<JobRecord | null> } = {},\n): Promise<string> {\n const audioUrl = opts.audioUrl ? unwrapProxyUrl(opts.audioUrl) : '';\n const text = opts.text?.trim();\n const params = {\n videoUrl: unwrapProxyUrl(videoSrc),\n ...(audioUrl ? { audioUrl } : {}),\n ...(text && !audioUrl ? { text } : {}),\n ...(opts.voiceId && !audioUrl ? { voiceId: opts.voiceId } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n ...(opts.syncMode ? { syncMode: opts.syncMode } : {}),\n };\n const handed = Boolean(tracking.job && runActionAcceptsJob(run));\n const submit = handed\n ? await (run as RunMediaActionWithBody)('yantra-audio', 'YANTRA_AUDIO_LIPSYNC_SUBMIT', params, {\n job: tracking.job,\n })\n : await run('yantra-audio', 'YANTRA_AUDIO_LIPSYNC_SUBMIT', params);\n const body = unwrapData(submit);\n const statusUrl = typeof body.statusUrl === 'string' ? body.statusUrl : '';\n const responseUrl = typeof body.responseUrl === 'string' ? body.responseUrl : '';\n if (!statusUrl || !responseUrl) throw new Error('Lip sync did not start: the connector returned no queue handle.');\n onProgress?.(\n body.synthesizedSpeech === true ? 'Spoke the line. Syncing the mouth…' : 'Syncing the mouth to your audio…',\n );\n\n const startedAt = Date.now();\n if (handed && tracking.awaitAgent) {\n const verdict = await tracking.awaitAgent(startedAt);\n if (verdict?.state === 'failed') throw new Error(verdict.error || 'Lip sync failed.');\n if (verdict?.state === 'succeeded') {\n const videoUrl = lipSyncVideoFromVerdict(verdict);\n if (!videoUrl) throw new Error('Lip sync finished but returned no video.');\n return videoUrl;\n }\n // No verdict: the agent never took the job. Follow the queue from here.\n }\n let lastPollError = '';\n for (;;) {\n await new Promise((r) => setTimeout(r, LIPSYNC_POLL_MS));\n const elapsed = Date.now() - startedAt;\n if (elapsed > LIPSYNC_MAX_WAIT_MS) {\n const why = lastPollError ? ` Last status check said: ${lastPollError}` : '';\n throw new Error(\n `Lip sync did not finish within ${Math.round(LIPSYNC_MAX_WAIT_MS / 60_000)} minutes.${why}`,\n );\n }\n let p: Record<string, unknown>;\n try {\n p = unwrapData(await run('yantra-audio', 'YANTRA_AUDIO_LIPSYNC_STATUS', { statusUrl, responseUrl }));\n } catch (err) {\n // An unreachable queue says nothing about the job - keep following it.\n lastPollError = err instanceof Error ? err.message : String(err);\n continue;\n }\n if (p.status === 'completed') {\n if (typeof p.videoUrl === 'string' && p.videoUrl) return p.videoUrl;\n throw new Error('Lip sync finished but returned no video.');\n }\n if (p.status === 'failed') {\n throw new Error(typeof p.error === 'string' && p.error ? p.error : 'Lip sync failed.');\n }\n onProgress?.(`Syncing… ${Math.round(elapsed / 1000)}s elapsed`);\n }\n}\n\n/**\n * {@link runLipSync} as a canvas job (web canvas lip sync lane): recorded on the\n * Task API first, so it shows in every Tasks panel and the agent can finish it\n * if the phone goes away. Without a backend it runs exactly as runLipSync.\n */\nexport async function runTrackedLipSync(\n run: RunMediaAction,\n videoSrc: string,\n opts: Parameters<typeof runLipSync>[2],\n onProgress?: (note: string) => void,\n boardId?: string | null,\n): Promise<string> {\n const tracked = await trackLipSync({ videoUrl: unwrapProxyUrl(videoSrc), text: opts.text, boardId }).catch(\n () => null,\n );\n try {\n const url = await runLipSync(run, videoSrc, opts, onProgress, tracked ?? {});\n tracked?.settle({ videoUrl: url });\n return url;\n } catch (err) {\n tracked?.settle({ error: err instanceof Error ? err.message : String(err) });\n throw err;\n }\n}\n\nconst LIPSYNC_POLL_MS = 5_000;\n/** Past this something is wrong upstream; a measured run took ~4 minutes. */\nconst LIPSYNC_MAX_WAIT_MS = 12 * 60_000;\n\n/** Connector results sometimes arrive wrapped in `data`; tolerate both. */\nfunction unwrapData(raw: unknown): Record<string, unknown> {\n const outer = (raw ?? {}) as Record<string, unknown>;\n const inner = outer.data;\n return (inner && typeof inner === 'object' ? inner : outer) as Record<string, unknown>;\n}\n\n/**\n * Extend: last-frame thumbnail → generate continuation → concat.\n */\nexport async function runExtendVideo(run: RunMediaAction, videoSrc: string, prompt: string): Promise<string> {\n const url = unwrapProxyUrl(videoSrc);\n const thumb = await run('yantra-video-editing', 'YANTRA_VIDEO_EDIT_THUMBNAIL', {\n input_url: url,\n at_end: true,\n backoff_secs: 0.08,\n });\n const frameUrl = resultUrl(thumb, 'YANTRA_VIDEO_EDIT_THUMBNAIL');\n const generated = await run(\n 'yantra-video-generation',\n 'YANTRA_VIDEO_GENERATION_GENERATE',\n buildVideoGenerateParams({\n prompt: continuationPrompt(prompt),\n aspectRatio: '16:9',\n referenceImageUrl: frameUrl,\n durationSeconds: 4,\n }),\n );\n const nextUrl = generationMediaUrl(generated);\n const concat = await run('yantra-video-editing', 'YANTRA_VIDEO_EDIT_CONCAT', {\n input_urls: [url, nextUrl],\n });\n return resultUrl(concat, 'YANTRA_VIDEO_EDIT_CONCAT');\n}\n"],"names":["platformReframePlan"],"mappings":";;;;;;;;;;;;;;;;AAiBO,MAAM,kBAAkB,CAAC;AAAA,EAC9B,EAAI,EAAA,OAAA;AAAA,EACJ,KAAO,EAAA,OAAA;AAAA,EACP,QAAU,EAAA;AACZ,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,IAAA;AAAA,EACJ,KAAO,EAAA,IAAA;AAAA,EACP,QAAU,EAAA;AACZ,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,IAAA;AAAA,EACJ,KAAO,EAAA,IAAA;AAAA,EACP,QAAU,EAAA;AACZ,CAAC;AAGD,MAAM,IAAA,GAAO,CAAC,CAAA,KAAc,IAAK,CAAA,GAAA,CAAI,CAAG,EAAA,IAAA,CAAK,KAAM,CAAA,CAAA,GAAI,CAAC,CAAA,GAAI,CAAC,CAAA;AA4C7C,SAAA,WAAA,CAAY,KAAW,MAAoC,EAAA;AACzE,EAAA,MAAM,IAAI,eAAgB,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,CAAE,OAAO,MAAM,CAAA;AACnD,EAAI,IAAA,CAAC,CAAK,IAAA,EAAE,GAAI,CAAA,KAAA,GAAQ,MAAM,EAAE,GAAA,CAAI,MAAS,GAAA,CAAA,CAAA,EAAW,OAAA,IAAA;AACxD,EAAA,MAAM,OAAO,IAAK,CAAA,GAAA,CAAI,GAAI,CAAA,KAAA,EAAO,IAAI,MAAM,CAAA;AAC3C,EAAI,IAAA,IAAA,IAAQ,CAAE,CAAA,QAAA,EAAiB,OAAA,IAAA;AAC/B,EAAM,MAAA,KAAA,GAAQ,EAAE,QAAW,GAAA,IAAA;AAC3B,EAAO,OAAA;AAAA,IACL,KAAO,EAAA,IAAA,CAAK,GAAI,CAAA,KAAA,GAAQ,KAAK,CAAA;AAAA,IAC7B,MAAQ,EAAA,IAAA,CAAK,GAAI,CAAA,MAAA,GAAS,KAAK;AAAA,GACjC;AACF;AACA,eAAe,QAAA,CAAS,GAAqB,EAAA,MAAA,EAAgB,MAAkD,EAAA;AAC7G,EAAA,MAAM,MAAS,GAAA,MAAM,GAAI,CAAA,sBAAA,EAAwB,QAAQ,MAAM,CAAA;AAC/D,EAAO,OAAA,SAAA,CAAU,QAAQ,MAAM,CAAA;AACjC;AACA,eAAe,QAAA,CAAS,GAAqB,EAAA,MAAA,EAAgB,MAAkD,EAAA;AAC7G,EAAA,MAAM,MAAS,GAAA,MAAM,GAAI,CAAA,sBAAA,EAAwB,QAAQ,MAAM,CAAA;AAC/D,EAAO,OAAA,SAAA,CAAU,QAAQ,MAAM,CAAA;AACjC;AAwCA,eAAsB,gBAAiB,CAAA,GAAA,EAAqB,IAAyB,EAAA,GAAA,EAAa,QAAuB,IAGtH,EAAA;AACD,EAAM,MAAA,GAAA,GAAM,eAAe,GAAG,CAAA;AAC9B,EAAA,IAAI,SAAS,OAAS,EAAA;AACpB,IAAM,MAAA,IAAA,GAAOA,YAAoB,IAAM,EAAA,MAAA,CAAO,OAAO,KAAO,EAAA,MAAA,CAAO,GAAK,EAAA,MAAA,CAAO,KAAK,CAAA;AACpF,IAAA,IAAI,CAAC,IAAA,EAAY,MAAA,IAAI,MAAM,6BAA6B,CAAA;AACxD,IAAM,MAAA;AAAA,MACJ;AAAA,KACF,GAAI,kBAAkB,MAAQ,EAAA,IAAA,EAAM,cAAc,MAAO,CAAA,MAAA,EAAQ,IAAI,CAAC,CAAA;AACtE,IAAO,OAAA;AAAA,MACL,GAAK,EAAA,MAAM,QAAS,CAAA,GAAA,EAAK,2BAA6B,EAAA,cAAA,CAAA;AAAA,QACpD,SAAW,EAAA;AAAA,OAAA,EACR,MACJ,CAAA,CAAA;AAAA,MACD,IAAM,EAAA;AAAA,KACR;AAAA;AAEF,EAAM,MAAA,IAAA,GAAO,gBAAiB,CAAA,MAAA,EAAQ,IAAI,CAAA;AAC1C,EAAA,IAAI,CAAC,IAAA,EAAY,MAAA,IAAI,MAAM,6BAA6B,CAAA;AACxD,EAAO,OAAA;AAAA,IACL,GAAK,EAAA,MAAM,QAAS,CAAA,GAAA,EAAK,KAAK,MAAQ,EAAA,cAAA,CAAA;AAAA,MACpC,SAAW,EAAA;AAAA,KAAA,EACR,KAAK,MACT,CAAA,CAAA;AAAA,IACD,IAAA,EAAM,iBAAiB,MAAM;AAAA,GAC/B;AACF;AAIO,MAAM,oBAIP,CAAC;AAAA,EACL,EAAI,EAAA,MAAA;AAAA,EACJ,KAAO,EAAA;AACT,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,OAAA;AAAA,EACJ,KAAO,EAAA,OAAA;AAAA,EACP,GAAK,EAAA;AACP,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,MAAA;AAAA,EACJ,KAAO,EAAA,MAAA;AAAA,EACP,GAAK,EAAA;AACP,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,QAAA;AAAA,EACJ,KAAO,EAAA;AACT,CAAC;AAUM,SAAS,yBAAyB,IAGvC,EAAA;AACA,EAAA,OAAO,SAAS,OAAU,GAAA;AAAA,IACxB,UAAY,EAAA,GAAA;AAAA,IACZ,KAAO,EAAA;AAAA,GACL,GAAA;AAAA,IACF,UAAY,EAAA,IAAA;AAAA,IACZ,KAAO,EAAA;AAAA,GACT;AACF;AAGO,SAAS,aAAa,GAAwC,EAAA;AACnE,EAAM,MAAA,CAAA,GAAA,CAAK,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,EAAA,EAAI,IAAK,EAAA,CAAE,OAAQ,CAAA,IAAA,EAAM,EAAE,CAAA,CAAE,OAAQ,CAAA,MAAA,EAAQ,EAAE,CAAA;AACjE,EAAA,IAAI,iBAAiB,IAAK,CAAA,CAAC,GAAU,OAAA,CAAA,CAAA,EAAI,EAAE,KAAM,CAAA,EAAE,EAAE,GAAI,CAAA,CAAA,CAAA,KAAK,IAAI,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA,CAAE,aAAa,CAAA,CAAA;AAC3F,EAAI,IAAA,gBAAA,CAAiB,KAAK,CAAC,CAAA,SAAU,CAAI,CAAA,EAAA,CAAA,CAAE,aAAa,CAAA,CAAA;AACxD,EAAO,OAAA,IAAA;AACT;AAMgB,SAAA,aAAA,CAAc,MAAyB,IAAkE,EAAA;AA3NzH,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA4NE,EAAM,MAAA,GAAA,GAAA,CAAM,EAAK,GAAA,IAAA,CAAA,GAAA,KAAL,IAAY,GAAA,EAAA,GAAA,MAAA;AACxB,EAAA,MAAM,MAAM,GAAQ,KAAA,QAAA,GAAW,YAAa,CAAA,IAAA,CAAK,SAAS,CAAI,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,iBAAA,CAAkB,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,EAAO,KAAA,GAAG,CAAxC,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAA2C,QAA3C,IAAkD,GAAA,EAAA,GAAA,IAAA;AAChH,EAAI,IAAA,IAAA,KAAS,OAAS,EAAA,OAAO,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,MAAA;AACpC,EAAA,OAAO,MAAM,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,SAAA,EAAW,MAAM,CAAC,CAAA,CAAE,aAAa,CAAA,CAAA;AACvD;AACA,eAAsB,oBAAoB,GAAqB,EAAA,IAAA,EAAyB,GAAa,EAAA,IAAA,GAAgC,EAAqB,EAAA;AAjO1J,EAAA,IAAA,EAAA,EAAA,EAAA;AAkOE,EAAM,MAAA,GAAA,GAAM,eAAe,GAAG,CAAA;AAC9B,EAAM,MAAA,QAAA,GAAW,yBAAyB,IAAI,CAAA;AAC9C,EAAA,MAAM,MAAS,GAAA;AAAA,IACb,SAAW,EAAA,GAAA;AAAA,IACX,KAAA,EAAO,aAAc,CAAA,IAAA,EAAM,IAAI,CAAA;AAAA,IAC/B,UAAY,EAAA,CAAA,EAAA,GAAA,IAAA,CAAK,UAAL,KAAA,IAAA,GAAA,EAAA,GAAmB,QAAS,CAAA,UAAA;AAAA,IACxC,KAAO,EAAA,CAAA,EAAA,GAAA,IAAA,CAAK,KAAL,KAAA,IAAA,GAAA,EAAA,GAAc,QAAS,CAAA;AAAA,GAChC;AACA,EAAA,IAAI,SAAS,OAAS,EAAA,OAAO,QAAS,CAAA,GAAA,EAAK,uCAAuC,MAAM,CAAA;AACxF,EAAO,OAAA,QAAA,CAAS,GAAK,EAAA,8BAAA,EAAgC,MAAM,CAAA;AAC7D;AACA,eAAsB,UAAW,CAAA,GAAA,EAAqB,IAAyB,EAAA,GAAA,EAAa,GAA4B,EAAA;AACtH,EAAM,MAAA,GAAA,GAAM,eAAe,GAAG,CAAA;AAC9B,EAAA,IAAI,SAAS,OAAS,EAAA;AACpB,IAAO,OAAA,QAAA,CAAS,KAAK,0BAA4B,EAAA;AAAA,MAC/C,SAAW,EAAA,GAAA;AAAA,MACX,OAAO,GAAI,CAAA,KAAA;AAAA,MACX,QAAQ,GAAI,CAAA,MAAA;AAAA,MACZ,GAAK,EAAA;AAAA,KACN,CAAA;AAAA;AAEH,EAAO,OAAA,QAAA,CAAS,KAAK,6BAA+B,EAAA;AAAA,IAClD,SAAW,EAAA,GAAA;AAAA,IACX,YAAY,CAAG,EAAA,GAAA,CAAI,KAAK,CAAA,CAAA,EAAI,IAAI,MAAM,CAAA;AAAA,GACvC,CAAA;AACH;AAGsB,eAAA,YAAA,CAAa,GAAqB,EAAA,IAAA,EAAyB,GAA8B,EAAA;AAC7G,EAAM,MAAA,GAAA,GAAM,eAAe,GAAG,CAAA;AAC9B,EAAA,IAAI,SAAS,OAAS,EAAA;AACpB,IAAO,OAAA,QAAA,CAAS,KAAK,4BAA8B,EAAA;AAAA,MACjD,SAAW,EAAA,GAAA;AAAA,MACX,UAAY,EAAA,IAAA;AAAA,MACZ,QAAU,EAAA,IAAA;AAAA,MACV,UAAY,EAAA;AAAA,KACb,CAAA;AAAA;AAEH,EAAO,OAAA,QAAA,CAAS,KAAK,gCAAkC,EAAA;AAAA,IACrD,SAAW,EAAA,GAAA;AAAA,IACX,UAAY,EAAA,IAAA;AAAA,IACZ,QAAU,EAAA,IAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACb,CAAA;AACH;AAOA,eAAsB,WAAW,GAAqB,EAAA,QAAA,EAAkB,MAMrE,UAMH,EAAA,QAAA,GAGI,EAAqB,EAAA;AApSzB,EAAA,IAAA,EAAA;AAqSE,EAAA,MAAM,WAAW,IAAK,CAAA,QAAA,GAAW,cAAe,CAAA,IAAA,CAAK,QAAQ,CAAI,GAAA,EAAA;AACjE,EAAM,MAAA,IAAA,GAAA,CAAO,EAAK,GAAA,IAAA,CAAA,IAAA,KAAL,IAAW,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AACxB,EAAA,MAAM,MAAS,GAAA,cAAA,CAAA,cAAA,CAAA,cAAA,CAAA,cAAA,CAAA,cAAA,CAAA;AAAA,IACb,QAAA,EAAU,eAAe,QAAQ;AAAA,GAAA,EAC7B,QAAW,GAAA;AAAA,IACb;AAAA,GACE,GAAA,EACA,CAAA,EAAA,IAAA,IAAQ,CAAC,QAAW,GAAA;AAAA,IACtB;AAAA,MACE,EAAC,CAAA,EACD,IAAK,CAAA,OAAA,IAAW,CAAC,QAAW,GAAA;AAAA,IAC9B,SAAS,IAAK,CAAA;AAAA,GACZ,GAAA,EACA,CAAA,EAAA,IAAA,CAAK,KAAQ,GAAA;AAAA,IACf,OAAO,IAAK,CAAA;AAAA,GACV,GAAA,EACA,CAAA,EAAA,IAAA,CAAK,QAAW,GAAA;AAAA,IAClB,UAAU,IAAK,CAAA;AAAA,MACb,EAAC,CAAA;AAEP,EAAA,MAAM,SAAS,OAAQ,CAAA,QAAA,CAAS,GAAO,IAAA,mBAAA,CAAoB,GAAG,CAAC,CAAA;AAC/D,EAAA,MAAM,SAAS,MAAS,GAAA,MAAO,GAA+B,CAAA,cAAA,EAAgB,+BAA+B,MAAQ,EAAA;AAAA,IACnH,KAAK,QAAS,CAAA;AAAA,GACf,CAAI,GAAA,MAAM,GAAI,CAAA,cAAA,EAAgB,+BAA+B,MAAM,CAAA;AACpE,EAAM,MAAA,IAAA,GAAO,WAAW,MAAM,CAAA;AAC9B,EAAA,MAAM,YAAY,OAAO,IAAA,CAAK,SAAc,KAAA,QAAA,GAAW,KAAK,SAAY,GAAA,EAAA;AACxE,EAAA,MAAM,cAAc,OAAO,IAAA,CAAK,WAAgB,KAAA,QAAA,GAAW,KAAK,WAAc,GAAA,EAAA;AAC9E,EAAA,IAAI,CAAC,SAAa,IAAA,CAAC,aAAmB,MAAA,IAAI,MAAM,iEAAiE,CAAA;AAEjH,EAAM,MAAA,SAAA,GAAY,KAAK,GAAI,EAAA;AAC3B,EAAI,IAAA,MAAA,IAAU,SAAS,UAAY,EAAA;AACjC,IAAA,MAAM,OAAU,GAAA,MAAM,QAAS,CAAA,UAAA,CAAW,SAAS,CAAA;AACnD,IAAI,IAAA,CAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,WAAU,QAAU,EAAA,MAAM,IAAI,KAAM,CAAA,OAAA,CAAQ,SAAS,kBAAkB,CAAA;AACpF,IAAI,IAAA,CAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,WAAU,WAAa,EAAA;AAClC,MAAM,MAAA,QAAA,GAAW,wBAAwB,OAAO,CAAA;AAChD,MAAA,IAAI,CAAC,QAAA,EAAgB,MAAA,IAAI,MAAM,0CAA0C,CAAA;AACzE,MAAO,OAAA,QAAA;AAAA;AACT;AAGF,EAAA,IAAI,aAAgB,GAAA,EAAA;AACpB,EAAS,WAAA;AACP,IAAA,MAAM,IAAI,OAAQ,CAAA,CAAA,CAAA,KAAK,UAAW,CAAA,CAAA,EAAG,eAAe,CAAC,CAAA;AACrD,IAAM,MAAA,OAAA,GAAU,IAAK,CAAA,GAAA,EAAQ,GAAA,SAAA;AAC7B,IAAA,IAAI,UAAU,mBAAqB,EAAA;AACjC,MAAA,MAAM,GAAM,GAAA,aAAA,GAAgB,CAA4B,yBAAA,EAAA,aAAa,CAAK,CAAA,GAAA,EAAA;AAC1E,MAAM,MAAA,IAAI,KAAM,CAAA,CAAA,+BAAA,EAAkC,IAAK,CAAA,KAAA,CAAM,sBAAsB,GAAM,CAAC,CAAY,SAAA,EAAA,GAAG,CAAE,CAAA,CAAA;AAAA;AAE7G,IAAI,IAAA,CAAA;AACJ,IAAI,IAAA;AACF,MAAA,CAAA,GAAI,UAAW,CAAA,MAAM,GAAI,CAAA,cAAA,EAAgB,6BAA+B,EAAA;AAAA,QACtE,SAAA;AAAA,QACA;AAAA,OACD,CAAC,CAAA;AAAA,aACK,GAAK,EAAA;AAEZ,MAAA,aAAA,GAAgB,GAAe,YAAA,KAAA,GAAQ,GAAI,CAAA,OAAA,GAAU,OAAO,GAAG,CAAA;AAC/D,MAAA;AAAA;AAEF,IAAI,IAAA,CAAA,CAAE,WAAW,WAAa,EAAA;AAC5B,MAAA,IAAI,OAAO,CAAE,CAAA,QAAA,KAAa,YAAY,CAAE,CAAA,QAAA,SAAiB,CAAE,CAAA,QAAA;AAC3D,MAAM,MAAA,IAAI,MAAM,0CAA0C,CAAA;AAAA;AAE5D,IAAI,IAAA,CAAA,CAAE,WAAW,QAAU,EAAA;AACzB,MAAM,MAAA,IAAI,KAAM,CAAA,OAAO,CAAE,CAAA,KAAA,KAAU,YAAY,CAAE,CAAA,KAAA,GAAQ,CAAE,CAAA,KAAA,GAAQ,kBAAkB,CAAA;AAAA;AAEpC;AAEvD;AAOA,eAAsB,iBAAkB,CAAA,GAAA,EAAqB,QAAkB,EAAA,IAAA,EAAwC,YAAqC,OAA0C,EAAA;AACpM,EAAM,MAAA,OAAA,GAAU,MAAM,YAAa,CAAA;AAAA,IACjC,QAAA,EAAU,eAAe,QAAQ,CAAA;AAAA,IACjC,MAAM,IAAK,CAAA,IAEb,CAAC,CAAA,CAAE,KAAM,CAAA,MAAM,IAAI,CAAA;AACnB,EAAI,IAAA;AACF,IAAM,MAAA,GAAA,GAAM,MAAM,UAAW,CAAA,GAAA,EAAK,UAAU,IAAM,EAAA,UAAA,EAAY,OAAW,IAAA,IAAA,GAAA,OAAA,GAAA,EAAE,CAAA;AAC3E,IAAA,OAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,OAAA,CAAS,MAAO,CAAA;AAAA,MACd,QAAU,EAAA;AAAA,KACZ,CAAA;AACA,IAAO,OAAA,GAAA;AAAA,WACA,GAAK,EAAA;AACZ,IAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,MAAO,CAAA;AAAA,MACd,OAAO,GAAe,YAAA,KAAA,GAAQ,GAAI,CAAA,OAAA,GAAU,OAAO,GAAG;AAAA,KACxD,CAAA;AACA,IAAM,MAAA,GAAA;AAAA;AAEV;AACA,MAAM,eAAkB,GAAA,GAAA;AAExB,MAAM,sBAAsB,EAAK,GAAA,GAAA;AAGjC,SAAS,WAAW,GAAuC,EAAA;AACzD,EAAM,MAAA,KAAA,GAAS,oBAAO,EAAC;AACvB,EAAA,MAAM,QAAQ,KAAM,CAAA,IAAA;AACpB,EAAA,OAAQ,KAAS,IAAA,OAAO,KAAU,KAAA,QAAA,GAAW,KAAQ,GAAA,KAAA;AACvD;AAKsB,eAAA,cAAA,CAAe,GAAqB,EAAA,QAAA,EAAkB,MAAiC,EAAA;AAC3G,EAAM,MAAA,GAAA,GAAM,eAAe,QAAQ,CAAA;AACnC,EAAA,MAAM,KAAQ,GAAA,MAAM,GAAI,CAAA,sBAAA,EAAwB,6BAA+B,EAAA;AAAA,IAC7E,SAAW,EAAA,GAAA;AAAA,IACX,MAAQ,EAAA,IAAA;AAAA,IACR,YAAc,EAAA;AAAA,GACf,CAAA;AACD,EAAM,MAAA,QAAA,GAAW,SAAU,CAAA,KAAA,EAAO,6BAA6B,CAAA;AAC/D,EAAA,MAAM,SAAY,GAAA,MAAM,GAAI,CAAA,yBAAA,EAA2B,oCAAoC,wBAAyB,CAAA;AAAA,IAClH,MAAA,EAAQ,mBAAmB,MAAM,CAAA;AAAA,IACjC,WAAa,EAAA,MAAA;AAAA,IACb,iBAAmB,EAAA,QAAA;AAAA,IACnB,eAAiB,EAAA;AAAA,GAClB,CAAC,CAAA;AACF,EAAM,MAAA,OAAA,GAAU,mBAAmB,SAAS,CAAA;AAC5C,EAAA,MAAM,MAAS,GAAA,MAAM,GAAI,CAAA,sBAAA,EAAwB,0BAA4B,EAAA;AAAA,IAC3E,UAAA,EAAY,CAAC,GAAA,EAAK,OAAO;AAAA,GAC1B,CAAA;AACD,EAAO,OAAA,SAAA,CAAU,QAAQ,0BAA0B,CAAA;AACrD"}
@@ -0,0 +1,235 @@
1
+ import {useState,useEffect}from'react';import {applyServerPatch,isTerminal,listCanvasTasks}from'./canvasTasks.js';import {taskApiTransport,listRemoteJobs,canvasTaskToJob,recordSurfaceJob,jobFingerprint,jobKey,sortJobs,CANVAS_SURFACE,promoVideoFromVerdict,serverProgressLine,updateSurfaceJob,surfaceJobPatch,isStaleJob,removeSurfaceJob,claimDelivery}from'./serverTasks.js';var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+ function buildTaskRows(local, remote, hidden) {
33
+ const byKey = new Map(remote.map((job) => [jobKey(job), job]));
34
+ const rows = [];
35
+ const held = /* @__PURE__ */ new Set();
36
+ for (const task of local) {
37
+ const job = canvasTaskToJob(task);
38
+ const key = jobKey(job);
39
+ held.add(key);
40
+ const row = byKey.get(key);
41
+ rows.push(__spreadProps(__spreadValues({
42
+ kind: "task",
43
+ key,
44
+ task
45
+ }, row ? {
46
+ remote: row
47
+ } : {}), {
48
+ state: job.state,
49
+ createdAt: job.createdAt
50
+ }));
51
+ }
52
+ for (const job of remote) {
53
+ const key = jobKey(job);
54
+ if (held.has(key) || (hidden == null ? void 0 : hidden.has(key))) continue;
55
+ rows.push({
56
+ kind: "job",
57
+ key,
58
+ job,
59
+ state: job.state,
60
+ createdAt: job.createdAt
61
+ });
62
+ }
63
+ return sortJobs(rows).map((_a) => {
64
+ var _b = _a, {
65
+ state: _s,
66
+ createdAt: _c
67
+ } = _b, row = __objRest(_b, [
68
+ "state",
69
+ "createdAt"
70
+ ]);
71
+ return row;
72
+ });
73
+ }
74
+ function isRowStaleAt(row, now) {
75
+ if (row.kind === "job") return isStaleJob(row.job, now);
76
+ const {
77
+ task
78
+ } = row;
79
+ return !isTerminal(task.state) && isStaleJob({
80
+ state: "running",
81
+ deadlineAt: task.deadlineAt,
82
+ createdAt: task.createdAt
83
+ }, now);
84
+ }
85
+ const isRowRunningAt = (row, now) => (row.kind === "task" ? !isTerminal(row.task.state) : row.job.state === "running") && !isRowStaleAt(row, now);
86
+ function formatWorked(ms) {
87
+ const s = Math.max(0, Math.round(ms / 1e3));
88
+ if (s < 60) return `${s}s`;
89
+ const m = Math.floor(s / 60);
90
+ if (m < 60) return `${m}m ${String(s % 60).padStart(2, "0")}s`;
91
+ const h = Math.floor(m / 60);
92
+ return `${h}h ${String(m % 60).padStart(2, "0")}m`;
93
+ }
94
+ function renderProgressPct(step, projectedMs, elapsedMs) {
95
+ const byStep = step && step.total > 0 ? Math.min(100, (step.index - 1) / step.total * 100) : 0;
96
+ const byTime = projectedMs ? Math.min(95, elapsedMs / projectedMs * 100) : 0;
97
+ return Math.max(0, byStep, byTime);
98
+ }
99
+ function renderTimeLeft(projectedMs, elapsedMs) {
100
+ const left = projectedMs - elapsedMs;
101
+ if (left <= 0) return "Taking longer than estimated";
102
+ if (left < 6e4) return "Less than a minute left";
103
+ return `About ${Math.max(1, Math.round(left / 6e4))} min left`;
104
+ }
105
+ function reconcilePatches(local, remote, now = Date.now()) {
106
+ var _a, _b, _c, _d;
107
+ const byId = new Map(remote.filter((j) => j.surface === CANVAS_SURFACE).map((j) => [j.id, j]));
108
+ const out = [];
109
+ for (const task of local) {
110
+ if (task.kind !== "promo") continue;
111
+ const job = byId.get(task.id);
112
+ if (!job) continue;
113
+ const browserFailure = task.state === "failed" && task.verdictBy !== "agent" && !task.delivered;
114
+ if (job.state !== "running" && job.verdictBy === "agent" && (!isTerminal(task.state) || browserFailure)) {
115
+ const video = job.state === "succeeded" ? promoVideoFromVerdict(job) : void 0;
116
+ out.push({
117
+ id: task.id,
118
+ patch: video ? {
119
+ state: "succeeded",
120
+ result: {
121
+ url: video.url,
122
+ title: video.title || "Marketing video",
123
+ projectId: (_b = (_a = task.handle) == null ? void 0 : _a.projectId) != null ? _b : "",
124
+ preset: task.request.preset,
125
+ pipeline: ""
126
+ },
127
+ lastLine: "Ready",
128
+ error: void 0,
129
+ delivered: false,
130
+ endedAt: (_c = job.endedAt) != null ? _c : now,
131
+ verdictBy: "agent"
132
+ } : {
133
+ state: "failed",
134
+ error: job.error || (job.state === "succeeded" ? "Production finished but returned no video" : "Production failed"),
135
+ endedAt: (_d = job.endedAt) != null ? _d : now,
136
+ verdictBy: "agent"
137
+ }
138
+ });
139
+ continue;
140
+ }
141
+ if (!isTerminal(task.state) && job.state === "running" && job.agentOwned) {
142
+ const line = serverProgressLine(job);
143
+ if (line && line !== task.lastLine) out.push({
144
+ id: task.id,
145
+ patch: {
146
+ lastLine: line
147
+ }
148
+ });
149
+ }
150
+ }
151
+ return out;
152
+ }
153
+ function mirrorPlan(local, remote, sent = /* @__PURE__ */ new Map()) {
154
+ const byId = new Map(remote.filter((j) => j.surface === CANVAS_SURFACE).map((j) => [j.id, j]));
155
+ const plan = {
156
+ create: [],
157
+ update: []
158
+ };
159
+ for (const task of local) {
160
+ const job = canvasTaskToJob(task);
161
+ const print = jobFingerprint(job);
162
+ if (sent.get(task.id) === print) continue;
163
+ const row = byId.get(task.id);
164
+ if (!row) {
165
+ if (task.state !== "submitting") plan.create.push(task);
166
+ continue;
167
+ }
168
+ if (!row.remoteId || row.verdictBy === "agent") continue;
169
+ if (task.agentReports && !isTerminal(task.state)) continue;
170
+ if (jobFingerprint(row) === print) continue;
171
+ plan.update.push({
172
+ remoteId: row.remoteId,
173
+ task
174
+ });
175
+ }
176
+ return plan;
177
+ }
178
+ const sentPrints = /* @__PURE__ */ new Map();
179
+ async function syncCanvasTasks(t = taskApiTransport()) {
180
+ if (!t) return [];
181
+ const remote = await listRemoteJobs(t, CANVAS_SURFACE);
182
+ for (const {
183
+ id,
184
+ patch
185
+ } of reconcilePatches(listCanvasTasks(), remote)) applyServerPatch(id, patch);
186
+ const plan = mirrorPlan(listCanvasTasks(), remote, sentPrints);
187
+ await Promise.all([...plan.create.map(async (task) => {
188
+ const job = canvasTaskToJob(task);
189
+ if (await recordSurfaceJob(t, job).catch(() => null)) sentPrints.set(task.id, jobFingerprint(job));
190
+ }), ...plan.update.map(async ({
191
+ remoteId,
192
+ task
193
+ }) => {
194
+ const job = canvasTaskToJob(task);
195
+ await updateSurfaceJobSafe(t, remoteId, job);
196
+ sentPrints.set(task.id, jobFingerprint(job));
197
+ })]);
198
+ return remote;
199
+ }
200
+ async function updateSurfaceJobSafe(t, remoteId, job) {
201
+ await updateSurfaceJob(t, remoteId, surfaceJobPatch(job)).catch(() => void 0);
202
+ }
203
+ async function removeRemoteRow(remoteId) {
204
+ const t = taskApiTransport();
205
+ if (!t || !remoteId) return;
206
+ await removeSurfaceJob(t, remoteId).catch(() => void 0);
207
+ }
208
+ async function claimRow(remoteId) {
209
+ const t = taskApiTransport();
210
+ if (!t || !remoteId) return true;
211
+ const claimed = await claimDelivery(t, remoteId).catch(() => void 0);
212
+ return claimed !== false;
213
+ }
214
+ let host = {
215
+ openBoardId: null
216
+ };
217
+ const hostListeners = /* @__PURE__ */ new Set();
218
+ function setCanvasTasksHost(next) {
219
+ host = __spreadValues(__spreadValues({}, host), next);
220
+ hostListeners.forEach((l) => l());
221
+ }
222
+ function useCanvasTasksHost() {
223
+ const [value, setValue] = useState(host);
224
+ useEffect(() => {
225
+ const listener = () => setValue(host);
226
+ hostListeners.add(listener);
227
+ listener();
228
+ return () => {
229
+ hostListeners.delete(listener);
230
+ };
231
+ }, []);
232
+ return value;
233
+ }
234
+ const SYNC_RUNNING_MS = 8e3;
235
+ const SYNC_IDLE_MS = 3e4;export{SYNC_IDLE_MS,SYNC_RUNNING_MS,buildTaskRows,claimRow,formatWorked,isRowRunningAt,isRowStaleAt,mirrorPlan,reconcilePatches,removeRemoteRow,renderProgressPct,renderTimeLeft,setCanvasTasksHost,syncCanvasTasks,useCanvasTasksHost};//# sourceMappingURL=canvasTaskSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvasTaskSync.js","sources":["../../../src/features/canvas/canvasTaskSync.ts"],"sourcesContent":["/**\n * The Tasks sheet's list - native twin of canvas `tasks/rows`, `tasks/reconcile`\n * and the store half of `tasks/remote` (the mirror).\n *\n * One list: this phone's productions (live, from ./canvasTasks) and the\n * caller's canvas jobs on the Task API that no local task holds - a run\n * started on the web, on another phone, or a lip sync. The server rows are\n * also how a production the agent followed settles here (reconcile), and how\n * a production this phone settled itself reaches every other panel (mirror).\n *\n * The pure halves (`buildTaskRows`, `reconcilePatches`, `mirrorPlan`) carry\n * the rules and are tested; `syncCanvasTasks` applies them.\n */\nimport { useEffect, useState } from 'react';\nimport { applyServerPatch, isTerminal, listCanvasTasks, type CanvasTask } from './canvasTasks';\nimport {\n CANVAS_SURFACE,\n canvasTaskToJob,\n claimDelivery,\n isStaleJob,\n jobFingerprint,\n jobKey,\n listRemoteJobs,\n promoVideoFromVerdict,\n recordSurfaceJob,\n removeSurfaceJob,\n serverProgressLine,\n sortJobs,\n surfaceJobPatch,\n taskApiTransport,\n updateSurfaceJob,\n type JobRecord,\n type TaskApiTransport,\n} from './serverTasks';\nimport type { RunMediaAction } from './videoEditor/mediaActions';\n\n// ── Rows ─────────────────────────────────────────────────────────────────────\n\nexport type TaskRow =\n | { kind: 'task'; key: string; task: CanvasTask; remote?: JobRecord }\n | { kind: 'job'; key: string; job: JobRecord };\n\n/** Local tasks first-class, then every server row no local task holds; running first, newest first. */\nexport function buildTaskRows(local: CanvasTask[], remote: JobRecord[], hidden?: ReadonlySet<string>): TaskRow[] {\n const byKey = new Map(remote.map((job) => [jobKey(job), job]));\n const rows: (TaskRow & { state: JobRecord['state']; createdAt: number })[] = [];\n const held = new Set<string>();\n for (const task of local) {\n const job = canvasTaskToJob(task);\n const key = jobKey(job);\n held.add(key);\n const row = byKey.get(key);\n rows.push({\n kind: 'task',\n key,\n task,\n ...(row ? { remote: row } : {}),\n state: job.state,\n createdAt: job.createdAt,\n });\n }\n for (const job of remote) {\n const key = jobKey(job);\n if (held.has(key) || hidden?.has(key)) continue;\n rows.push({ kind: 'job', key, job, state: job.state, createdAt: job.createdAt });\n }\n return sortJobs(rows).map(({ state: _s, createdAt: _c, ...row }) => row as TaskRow);\n}\n\n/** Still marked running past its deadline: shown as \"Stopped responding\", never with a growing clock. */\nexport function isRowStaleAt(row: TaskRow, now: number): boolean {\n if (row.kind === 'job') return isStaleJob(row.job, now);\n const { task } = row;\n return (\n !isTerminal(task.state) &&\n isStaleJob({ state: 'running', deadlineAt: task.deadlineAt, createdAt: task.createdAt }, now)\n );\n}\n\nexport const isRowRunningAt = (row: TaskRow, now: number): boolean =>\n (row.kind === 'task' ? !isTerminal(row.task.state) : row.job.state === 'running') && !isRowStaleAt(row, now);\n\n// ── The card's numbers ───────────────────────────────────────────────────────\n\n/** \"1m 05s\" - how long a run has worked (web `formatWorked`). */\nexport function formatWorked(ms: number): string {\n const s = Math.max(0, Math.round(ms / 1000));\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${String(s % 60).padStart(2, '0')}s`;\n const h = Math.floor(m / 60);\n return `${h}h ${String(m % 60).padStart(2, '0')}m`;\n}\n\n/** An export's bar: steps done, or time against the projection - never the finish on time alone. */\nexport function renderProgressPct(\n step: { index: number; total: number } | undefined,\n projectedMs: number | undefined,\n elapsedMs: number,\n): number {\n const byStep = step && step.total > 0 ? Math.min(100, ((step.index - 1) / step.total) * 100) : 0;\n const byTime = projectedMs ? Math.min(95, (elapsedMs / projectedMs) * 100) : 0;\n return Math.max(0, byStep, byTime);\n}\n\nexport function renderTimeLeft(projectedMs: number, elapsedMs: number): string {\n const left = projectedMs - elapsedMs;\n if (left <= 0) return 'Taking longer than estimated';\n if (left < 60_000) return 'Less than a minute left';\n return `About ${Math.max(1, Math.round(left / 60_000))} min left`;\n}\n\n// ── Reconcile: the rows' word on local tasks ─────────────────────────────────\n\nexport interface TaskPatch {\n id: string;\n patch: Partial<CanvasTask>;\n}\n\n/**\n * What the server rows change on this phone's tasks: the agent's verdict\n * (final - also over a failure this phone decided while the agent still had\n * the run, as long as nothing was placed), and the progress line while the\n * agent works. Productions only; an export's row is the export lane's.\n */\nexport function reconcilePatches(local: CanvasTask[], remote: JobRecord[], now = Date.now()): TaskPatch[] {\n const byId = new Map(remote.filter((j) => j.surface === CANVAS_SURFACE).map((j) => [j.id, j]));\n const out: TaskPatch[] = [];\n for (const task of local) {\n if (task.kind !== 'promo') continue;\n const job = byId.get(task.id);\n if (!job) continue;\n const browserFailure = task.state === 'failed' && task.verdictBy !== 'agent' && !task.delivered;\n if (job.state !== 'running' && job.verdictBy === 'agent' && (!isTerminal(task.state) || browserFailure)) {\n const video = job.state === 'succeeded' ? promoVideoFromVerdict(job) : undefined;\n out.push({\n id: task.id,\n patch: video\n ? {\n state: 'succeeded',\n result: {\n url: video.url,\n title: video.title || 'Marketing video',\n projectId: task.handle?.projectId ?? '',\n preset: task.request.preset,\n pipeline: '',\n },\n lastLine: 'Ready',\n error: undefined,\n delivered: false,\n endedAt: job.endedAt ?? now,\n verdictBy: 'agent',\n }\n : {\n state: 'failed',\n error:\n job.error ||\n (job.state === 'succeeded'\n ? 'Production finished but returned no video'\n : 'Production failed'),\n endedAt: job.endedAt ?? now,\n verdictBy: 'agent',\n },\n });\n continue;\n }\n if (!isTerminal(task.state) && job.state === 'running' && job.agentOwned) {\n const line = serverProgressLine(job);\n if (line && line !== task.lastLine) out.push({ id: task.id, patch: { lastLine: line } });\n }\n }\n return out;\n}\n\n// ── Mirror: this phone's word on its own rows ────────────────────────────────\n\nexport interface MirrorPlan {\n /** Local tasks with no row yet (recorded before a backend answered, or by an older build). */\n create: CanvasTask[];\n /** Rows behind what this phone knows: a production it followed and settled itself. */\n update: { remoteId: string; task: CanvasTask }[];\n}\n\n/**\n * What to write so every panel reads what this phone knows. A run the agent\n * follows is the agent's to report; an agent verdict is never written over.\n * `sent` holds fingerprints already written this session.\n */\nexport function mirrorPlan(\n local: CanvasTask[],\n remote: JobRecord[],\n sent: ReadonlyMap<string, string> = new Map(),\n): MirrorPlan {\n const byId = new Map(remote.filter((j) => j.surface === CANVAS_SURFACE).map((j) => [j.id, j]));\n const plan: MirrorPlan = { create: [], update: [] };\n for (const task of local) {\n const job = canvasTaskToJob(task);\n const print = jobFingerprint(job);\n if (sent.get(task.id) === print) continue;\n const row = byId.get(task.id);\n if (!row) {\n if (task.state !== 'submitting') plan.create.push(task);\n continue;\n }\n if (!row.remoteId || row.verdictBy === 'agent') continue;\n if (task.agentReports && !isTerminal(task.state)) continue;\n if (jobFingerprint(row) === print) continue;\n plan.update.push({ remoteId: row.remoteId, task });\n }\n return plan;\n}\n\nconst sentPrints = new Map<string, string>();\n\n/** One pass: list the rows, settle local tasks from them, write back what the rows lack. */\nexport async function syncCanvasTasks(t: TaskApiTransport | null = taskApiTransport()): Promise<JobRecord[]> {\n if (!t) return [];\n const remote = await listRemoteJobs(t, CANVAS_SURFACE);\n for (const { id, patch } of reconcilePatches(listCanvasTasks(), remote)) applyServerPatch(id, patch);\n const plan = mirrorPlan(listCanvasTasks(), remote, sentPrints);\n await Promise.all([\n ...plan.create.map(async (task) => {\n const job = canvasTaskToJob(task);\n if (await recordSurfaceJob(t, job).catch(() => null)) sentPrints.set(task.id, jobFingerprint(job));\n }),\n ...plan.update.map(async ({ remoteId, task }) => {\n const job = canvasTaskToJob(task);\n await updateSurfaceJobSafe(t, remoteId, job);\n sentPrints.set(task.id, jobFingerprint(job));\n }),\n ]);\n return remote;\n}\n\nasync function updateSurfaceJobSafe(t: TaskApiTransport, remoteId: string, job: JobRecord): Promise<void> {\n await updateSurfaceJob(t, remoteId, surfaceJobPatch(job)).catch(() => undefined);\n}\n\n/** Take a row down (a dismissed run). Best effort. */\nexport async function removeRemoteRow(remoteId: string | undefined): Promise<void> {\n const t = taskApiTransport();\n if (!t || !remoteId) return;\n await removeSurfaceJob(t, remoteId).catch(() => undefined);\n}\n\n/**\n * Claim a finished row before placing its result: true for exactly one\n * device. No row, no backend, or a backend without claims: place as before.\n */\nexport async function claimRow(remoteId: string | undefined): Promise<boolean> {\n const t = taskApiTransport();\n if (!t || !remoteId) return true;\n const claimed = await claimDelivery(t, remoteId).catch(() => undefined);\n return claimed !== false;\n}\n\n// ── The host: what the sheet cannot learn from its own props ────────────────\n\nexport interface CanvasTasksHost {\n /** The board on screen, so a ready task offers \"Place on board\" or \"Open board\". */\n openBoardId: string | null;\n openBoard?: (boardId: string) => void;\n boardLabel?: (boardId: string) => string | undefined;\n /** For \"Run again\". */\n runAction?: RunMediaAction;\n}\n\nlet host: CanvasTasksHost = { openBoardId: null };\nconst hostListeners = new Set<() => void>();\n\n/** CanvasExperience keeps this current; the sheet reads it. */\nexport function setCanvasTasksHost(next: Partial<CanvasTasksHost>): void {\n host = { ...host, ...next };\n hostListeners.forEach((l) => l());\n}\n\nexport function useCanvasTasksHost(): CanvasTasksHost {\n const [value, setValue] = useState(host);\n useEffect(() => {\n const listener = () => setValue(host);\n hostListeners.add(listener);\n listener();\n return () => {\n hostListeners.delete(listener);\n };\n }, []);\n return value;\n}\n\n/** How often the sheet re-reads the rows: briskly while one is running, rarely when all are done. */\nexport const SYNC_RUNNING_MS = 8_000;\nexport const SYNC_IDLE_MS = 30_000;\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import AsyncStorage from'@react-native-async-storage/async-storage';import {PROMO_POLL_MAX_MS,PROMO_CONNECTOR,PRODUCE_STATUS_ACTION,parsePollResult,PROMO_POLL_EVERY_MS,PRODUCE_SUBMIT_ACTION,buildPromoRunParams,parseSubmitResult}from'./promoVideo.js';var __defProp = Object.defineProperty;
1
+ import AsyncStorage from'@react-native-async-storage/async-storage';import {promoPollMaxMs,PROMO_CONNECTOR,PRODUCE_STATUS_ACTION,parsePollResult,PROMO_POLL_EVERY_MS,promoRunTimeoutMs,PRODUCE_SUBMIT_ACTION,buildPromoRunParams,parseSubmitResult}from'./promoVideo.js';import {resumeExportTasks}from'./videoEditor/exportTask.js';import {serverJobId,lookupRemoteJobId,awaitAgentVerdict,serverProgressLine,promoVideoFromVerdict,forgetServerJob,canvasJobKey,jobDeadlineAt,PROMO_FOLLOW_SLACK_MS,registerPromoJob,runWithJob}from'./serverTasks.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -33,12 +33,12 @@ function persist() {
33
33
  function coerceTask(value) {
34
34
  if (!value || typeof value !== "object") return null;
35
35
  const t = value;
36
- if (typeof t.id !== "string" || !t.id || t.kind !== "promo") return null;
36
+ if (typeof t.id !== "string" || !t.id || t.kind !== "promo" && t.kind !== "export") return null;
37
37
  if (typeof t.state !== "string") return null;
38
38
  if (!["submitting", "running", "succeeded", "failed"].includes(t.state)) return null;
39
39
  if (!t.request || typeof t.request !== "object" || typeof t.boardId !== "string") return null;
40
40
  return __spreadProps(__spreadValues({}, t), {
41
- title: typeof t.title === "string" ? t.title : "Marketing video",
41
+ title: typeof t.title === "string" ? t.title : t.kind === "export" ? "Video export" : "Marketing video",
42
42
  brief: typeof t.brief === "string" ? t.brief : "",
43
43
  createdAt: typeof t.createdAt === "number" ? t.createdAt : 0
44
44
  });
@@ -56,6 +56,18 @@ async function hydrateCanvasTasks() {
56
56
  tasks = [];
57
57
  }
58
58
  }
59
+ function getCanvasTask(id) {
60
+ return tasks.find((t) => t.id === id);
61
+ }
62
+ function patchCanvasTask(id, patch) {
63
+ if (!tasks.some((t) => t.id === id)) return;
64
+ patchTask(id, patch);
65
+ }
66
+ function addCanvasTask(task) {
67
+ tasks = [task, ...tasks.filter((t) => t.id !== task.id)];
68
+ persist();
69
+ emit();
70
+ }
59
71
  function listCanvasTasks() {
60
72
  return tasks;
61
73
  }
@@ -86,11 +98,75 @@ function markTaskDelivered(id) {
86
98
  delivered: true
87
99
  });
88
100
  }
101
+ function applyServerPatch(id, patch) {
102
+ if (!tasks.some((t) => t.id === id)) return;
103
+ patchTask(id, patch);
104
+ }
89
105
  let seq = 0;
90
106
  const newId = () => `task-${Date.now().toString(36)}-${(seq++).toString(36)}`;
107
+ async function awaitAgentFor(id) {
108
+ var _a, _b, _c, _d, _e, _f;
109
+ const task = tasks.find((t) => t.id === id);
110
+ if (!(task == null ? void 0 : task.agentReports)) return false;
111
+ const remoteId = (_a = serverJobId(canvasJobKey(id))) != null ? _a : await lookupRemoteJobId(id).catch(() => void 0);
112
+ if (!remoteId) return false;
113
+ try {
114
+ const verdict = await awaitAgentVerdict(remoteId, {
115
+ submittedAt: (_b = task.startedAt) != null ? _b : task.createdAt,
116
+ deadlineAt: (_c = task.deadlineAt) != null ? _c : Date.now() + promoPollMaxMs(task.request),
117
+ onProgress: (row) => {
118
+ const line = serverProgressLine(row);
119
+ if (line) patchTask(id, {
120
+ state: "running",
121
+ lastLine: line
122
+ });
123
+ }
124
+ });
125
+ if (!verdict) {
126
+ patchTask(id, {
127
+ agentReports: false
128
+ });
129
+ return false;
130
+ }
131
+ const video = verdict.state === "succeeded" ? promoVideoFromVerdict(verdict) : void 0;
132
+ patchTask(id, __spreadValues(__spreadProps(__spreadValues({}, video ? {
133
+ state: "succeeded",
134
+ result: {
135
+ url: video.url,
136
+ title: video.title || "Marketing video",
137
+ projectId: (_e = (_d = task.handle) == null ? void 0 : _d.projectId) != null ? _e : "",
138
+ preset: task.request.preset,
139
+ pipeline: ""
140
+ },
141
+ lastLine: "Ready"
142
+ } : {
143
+ state: "failed",
144
+ error: verdict.error || (verdict.state === "succeeded" ? "Production finished but returned no video" : "Production failed")
145
+ }), {
146
+ endedAt: (_f = verdict.endedAt) != null ? _f : Date.now()
147
+ }), verdict.verdictBy ? {
148
+ verdictBy: verdict.verdictBy
149
+ } : {}));
150
+ forgetServerJob(canvasJobKey(id));
151
+ return true;
152
+ } catch (err) {
153
+ patchTask(id, {
154
+ state: "failed",
155
+ endedAt: Date.now(),
156
+ error: err instanceof Error ? err.message : String(err)
157
+ });
158
+ return true;
159
+ }
160
+ }
91
161
  async function pollTask(run, id) {
162
+ var _a;
163
+ if (await awaitAgentFor(id)) return;
92
164
  const started = Date.now();
93
- while (Date.now() - started < PROMO_POLL_MAX_MS) {
165
+ const request = (_a = tasks.find((t) => t.id === id)) == null ? void 0 : _a.request;
166
+ const pollMax = request ? promoPollMaxMs(request) : promoPollMaxMs({
167
+ preset: ""
168
+ });
169
+ while (Date.now() - started < pollMax) {
94
170
  const task = tasks.find((t) => t.id === id);
95
171
  if (!(task == null ? void 0 : task.handle)) return;
96
172
  try {
@@ -137,6 +213,7 @@ async function pollTask(run, id) {
137
213
  async function startPromoTask(run, input) {
138
214
  await hydrateCanvasTasks();
139
215
  const id = newId();
216
+ const createdAt = Date.now();
140
217
  const task = {
141
218
  id,
142
219
  kind: "promo",
@@ -144,7 +221,9 @@ async function startPromoTask(run, input) {
144
221
  brief: input.brief,
145
222
  boardId: input.boardId,
146
223
  state: "submitting",
147
- createdAt: Date.now(),
224
+ createdAt,
225
+ // The run's own budget, the follow's slack past it, then the grace.
226
+ deadlineAt: jobDeadlineAt(createdAt, promoRunTimeoutMs(input.request) + PROMO_FOLLOW_SLACK_MS),
148
227
  request: input.request,
149
228
  lastLine: "Submitting\u2026"
150
229
  };
@@ -152,18 +231,24 @@ async function startPromoTask(run, input) {
152
231
  persist();
153
232
  emit();
154
233
  try {
155
- const raw = await run(PROMO_CONNECTOR, PRODUCE_SUBMIT_ACTION, buildPromoRunParams(input.request));
234
+ const job = await registerPromoJob(task, () => tasks.some((t) => t.id === id)).catch(() => void 0);
235
+ const {
236
+ result: raw,
237
+ handed
238
+ } = await runWithJob(run, PROMO_CONNECTOR, PRODUCE_SUBMIT_ACTION, buildPromoRunParams(input.request), job);
156
239
  const handle = parseSubmitResult(raw);
157
240
  if (!handle) throw new Error("No production handle returned");
158
- patchTask(id, {
241
+ patchTask(id, __spreadValues({
159
242
  state: "running",
160
243
  handle,
161
244
  startedAt: Date.now(),
162
245
  lastLine: "Queued"
163
- });
246
+ }, handed ? {
247
+ agentReports: true
248
+ } : {}));
164
249
  if (!pollers.has(id)) {
165
- const job = pollTask(run, id).finally(() => pollers.delete(id));
166
- pollers.set(id, job);
250
+ const job2 = pollTask(run, id).finally(() => pollers.delete(id));
251
+ pollers.set(id, job2);
167
252
  }
168
253
  } catch (err) {
169
254
  patchTask(id, {
@@ -177,11 +262,13 @@ async function startPromoTask(run, input) {
177
262
  }
178
263
  function resumePromoTasks(run) {
179
264
  void hydrateCanvasTasks().then(() => {
265
+ resumeExportTasks(run);
180
266
  for (const task of tasks) {
267
+ if (task.kind !== "promo") continue;
181
268
  if ((task.state === "running" || task.state === "submitting") && task.handle && !pollers.has(task.id)) {
182
269
  const job = pollTask(run, task.id).finally(() => pollers.delete(task.id));
183
270
  pollers.set(task.id, job);
184
271
  }
185
272
  }
186
273
  });
187
- }export{coerceTask,dismissTask,hydrateCanvasTasks,isTerminal,listCanvasTasks,markTaskDelivered,resumePromoTasks,runningTaskCount,startPromoTask,subscribeCanvasTasks};//# sourceMappingURL=canvasTasks.js.map
274
+ }export{addCanvasTask,applyServerPatch,coerceTask,dismissTask,getCanvasTask,hydrateCanvasTasks,isTerminal,listCanvasTasks,markTaskDelivered,patchCanvasTask,resumePromoTasks,runningTaskCount,startPromoTask,subscribeCanvasTasks};//# sourceMappingURL=canvasTasks.js.map