shipscreens 0.1.0 → 0.1.2

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 (260) hide show
  1. package/README.md +0 -8
  2. package/bin/shipscreens.js +223 -28
  3. package/cli/assets/demo-screens/01.png +0 -0
  4. package/cli/assets/demo-screens/02.png +0 -0
  5. package/cli/assets/demo-screens/03.png +0 -0
  6. package/cli/assets/demo-screens/04.png +0 -0
  7. package/cli/commands/build.js +27 -27
  8. package/cli/schema.js +59 -20
  9. package/dist/app/assets/{AgentCli-8e28laT4.js → AgentCli-FWNCO1pb.js} +5 -5
  10. package/dist/app/assets/AgentCli-FWNCO1pb.js.map +1 -0
  11. package/dist/app/assets/App-yU7CFXCv.js +13 -0
  12. package/dist/app/assets/App-yU7CFXCv.js.map +1 -0
  13. package/dist/app/assets/{AppReviewAso-OLwc4Rw_.js → AppReviewAso-BYfaP1Lu.js} +3 -3
  14. package/dist/{assets/AppReviewAso-OLwc4Rw_.js.map → app/assets/AppReviewAso-BYfaP1Lu.js.map} +1 -1
  15. package/dist/app/assets/{Cancelled-BoeZQa1S.js → Cancelled-66ixTUrm.js} +2 -2
  16. package/dist/app/assets/{Cancelled-BoeZQa1S.js.map → Cancelled-66ixTUrm.js.map} +1 -1
  17. package/dist/app/assets/CliPreflightCard-D7utpFux.js +2 -0
  18. package/dist/app/assets/{CliPreflightCard-CxYyogdF.js.map → CliPreflightCard-D7utpFux.js.map} +1 -1
  19. package/dist/app/assets/CopyButton-CmVmSaYn.js +2 -0
  20. package/dist/app/assets/CopyButton-CmVmSaYn.js.map +1 -0
  21. package/dist/app/assets/{FastlaneScreenshots-CFT6IiRb.js → FastlaneScreenshots-CRMcetq-.js} +4 -4
  22. package/dist/{assets/FastlaneScreenshots-CFT6IiRb.js.map → app/assets/FastlaneScreenshots-CRMcetq-.js.map} +1 -1
  23. package/dist/app/assets/{FirstExport-B_UDacZm.js → FirstExport-C0WgL4Hl.js} +3 -3
  24. package/dist/{assets/FirstExport-B_UDacZm.js.map → app/assets/FirstExport-C0WgL4Hl.js.map} +1 -1
  25. package/dist/app/assets/{FivePanelFramework-C1z_xUbe.js → FivePanelFramework-BwCRmXIK.js} +3 -3
  26. package/dist/{assets/FivePanelFramework-C1z_xUbe.js.map → app/assets/FivePanelFramework-BwCRmXIK.js.map} +1 -1
  27. package/dist/app/assets/{ForgotPassword-ZQ8U3ejz.js → ForgotPassword-B9_si1tH.js} +2 -2
  28. package/dist/{assets/ForgotPassword-ZQ8U3ejz.js.map → app/assets/ForgotPassword-B9_si1tH.js.map} +1 -1
  29. package/dist/app/assets/Landing-s1ni1n-7.js +2 -0
  30. package/dist/app/assets/Landing-s1ni1n-7.js.map +1 -0
  31. package/dist/app/assets/{LanguageSwitcher-D93nCuuR.js → LanguageSwitcher-BD9N-dla.js} +2 -2
  32. package/dist/{assets/LanguageSwitcher-D93nCuuR.js.map → app/assets/LanguageSwitcher-BD9N-dla.js.map} +1 -1
  33. package/dist/app/assets/{Legal-Koe7vi6H.js → Legal-D3cAXQXw.js} +2 -2
  34. package/dist/app/assets/{Legal-Koe7vi6H.js.map → Legal-D3cAXQXw.js.map} +1 -1
  35. package/dist/app/assets/{NotFound-CfY31vMZ.js → NotFound-ClV-6RmP.js} +2 -2
  36. package/dist/app/assets/{NotFound-CfY31vMZ.js.map → NotFound-ClV-6RmP.js.map} +1 -1
  37. package/dist/app/assets/{Privacy-CaHf2UM7.js → Privacy-DfxgWwC3.js} +2 -2
  38. package/dist/{assets/Privacy-CaHf2UM7.js.map → app/assets/Privacy-DfxgWwC3.js.map} +1 -1
  39. package/dist/app/assets/{SignIn-Cyi2_-Mn.js → SignIn-ghQCKcPe.js} +2 -2
  40. package/dist/app/assets/{SignIn-Cyi2_-Mn.js.map → SignIn-ghQCKcPe.js.map} +1 -1
  41. package/dist/app/assets/{SignUp-C9QH5po_.js → SignUp-BKB87hd4.js} +2 -2
  42. package/dist/app/assets/{SignUp-C9QH5po_.js.map → SignUp-BKB87hd4.js.map} +1 -1
  43. package/dist/app/assets/SiteHeader-DxsqNPnt.js +2 -0
  44. package/dist/app/assets/SiteHeader-DxsqNPnt.js.map +1 -0
  45. package/dist/app/assets/{StickyMobileCta-uyWGVhdc.js → StickyMobileCta-C_x2pUIF.js} +2 -2
  46. package/dist/app/assets/{StickyMobileCta-uyWGVhdc.js.map → StickyMobileCta-C_x2pUIF.js.map} +1 -1
  47. package/dist/{assets/StoreAssetsPipeline-BJlnD3kD.js → app/assets/StoreAssetsPipeline-4hXRONUd.js} +3 -3
  48. package/dist/app/assets/{StoreAssetsPipeline-BJlnD3kD.js.map → StoreAssetsPipeline-4hXRONUd.js.map} +1 -1
  49. package/dist/app/assets/{Success-B7k7FvJj.js → Success-819qvzmp.js} +2 -2
  50. package/dist/app/assets/{Success-B7k7FvJj.js.map → Success-819qvzmp.js.map} +1 -1
  51. package/dist/app/assets/{Terms-DgJWOeUD.js → Terms-7k3nA4SU.js} +2 -2
  52. package/dist/app/assets/{Terms-DgJWOeUD.js.map → Terms-7k3nA4SU.js.map} +1 -1
  53. package/dist/app/assets/{ToolsIndex-xMPoFom9.js → ToolsIndex-CzrxymnM.js} +3 -3
  54. package/dist/{assets/ToolsIndex-xMPoFom9.js.map → app/assets/ToolsIndex-CzrxymnM.js.map} +1 -1
  55. package/dist/app/assets/{WorkloadCalculator-Bdt6qPR3.js → WorkloadCalculator-D2TuKS4B.js} +4 -4
  56. package/dist/app/assets/{WorkloadCalculator-Bdt6qPR3.js.map → WorkloadCalculator-D2TuKS4B.js.map} +1 -1
  57. package/dist/app/assets/{calculator-CWfp5Day.js → calculator-CiqKFdRp.js} +2 -2
  58. package/dist/app/assets/{calculator-CWfp5Day.js.map → calculator-CiqKFdRp.js.map} +1 -1
  59. package/dist/app/assets/chevron-down-_xIkRLaA.js +2 -0
  60. package/dist/app/assets/{chevron-down-DbSAem1L.js.map → chevron-down-_xIkRLaA.js.map} +1 -1
  61. package/dist/app/assets/{clipboard-copy-DgTgnK84.js → clipboard-copy-DCJioqIc.js} +2 -2
  62. package/dist/app/assets/{clipboard-copy-DgTgnK84.js.map → clipboard-copy-DCJioqIc.js.map} +1 -1
  63. package/dist/app/assets/{copy-C9t_FzCZ.js → copy-AQdWJCcs.js} +2 -2
  64. package/dist/app/assets/{copy-C9t_FzCZ.js.map → copy-AQdWJCcs.js.map} +1 -1
  65. package/dist/app/assets/{crop-B7IxIx6D.js → crop-DOMVp18h.js} +2 -2
  66. package/dist/app/assets/{crop-B7IxIx6D.js.map → crop-DOMVp18h.js.map} +1 -1
  67. package/dist/app/assets/{download-65Zuvgwm.js → download-D5RbyXDm.js} +2 -2
  68. package/dist/app/assets/{download-65Zuvgwm.js.map → download-D5RbyXDm.js.map} +1 -1
  69. package/dist/app/assets/{eye-ouNo8Nv5.js → eye-CLcZi_uk.js} +2 -2
  70. package/dist/{assets/eye-ouNo8Nv5.js.map → app/assets/eye-CLcZi_uk.js.map} +1 -1
  71. package/dist/app/assets/{grid-3x3-U1Nk_ppf.js → grid-3x3-AKmIrhzv.js} +2 -2
  72. package/dist/{assets/grid-3x3-U1Nk_ppf.js.map → app/assets/grid-3x3-AKmIrhzv.js.map} +1 -1
  73. package/dist/app/assets/hero/demo-25s-poster.jpg +0 -0
  74. package/dist/app/assets/hero/demo-25s.mp4 +0 -0
  75. package/dist/app/assets/hero/demo-25s.webm +0 -0
  76. package/dist/app/assets/hero/demo-poster.jpg +0 -0
  77. package/dist/app/assets/hero/demo.mp4 +0 -0
  78. package/dist/app/assets/hero/demo.webm +0 -0
  79. package/dist/{assets/index-B8Y8Y-Mi.js → app/assets/index-Bqv2NztY.js} +6 -6
  80. package/dist/app/assets/index-Bqv2NztY.js.map +1 -0
  81. package/dist/{assets/index-hyiQFYGv.css → app/assets/index-DGVMa-P7.css} +1 -1
  82. package/dist/app/assets/{layout-grid-X9Sgull2.js → layout-grid-CpGXUPMR.js} +2 -2
  83. package/dist/app/assets/{layout-grid-X9Sgull2.js.map → layout-grid-CpGXUPMR.js.map} +1 -1
  84. package/dist/app/assets/proof/output-01.png +0 -0
  85. package/dist/app/assets/proof/output-02.png +0 -0
  86. package/dist/app/assets/proof/output-03.png +0 -0
  87. package/dist/app/assets/proof/output-04.png +0 -0
  88. package/dist/app/assets/{rocket-BjOXauLe.js → rocket-IzRFv1aW.js} +2 -2
  89. package/dist/app/assets/{rocket-BjOXauLe.js.map → rocket-IzRFv1aW.js.map} +1 -1
  90. package/dist/app/assets/{shield-check-CFNhE_aD.js → shield-check-CngdN3zn.js} +2 -2
  91. package/dist/{assets/shield-check-CFNhE_aD.js.map → app/assets/shield-check-CngdN3zn.js.map} +1 -1
  92. package/dist/app/assets/{signIn-CVoSzqCz.js → signIn-Cn6FaQYi.js} +2 -2
  93. package/dist/{assets/signIn-CVoSzqCz.js.map → app/assets/signIn-Cn6FaQYi.js.map} +1 -1
  94. package/dist/app/assets/{smartphone-BcQB9gIh.js → smartphone-CpNG_Pfk.js} +2 -2
  95. package/dist/app/assets/{smartphone-BcQB9gIh.js.map → smartphone-CpNG_Pfk.js.map} +1 -1
  96. package/dist/app/assets/{terminal-CGhryJds.js → terminal-BbBESfoU.js} +2 -2
  97. package/dist/{assets/terminal-CGhryJds.js.map → app/assets/terminal-BbBESfoU.js.map} +1 -1
  98. package/dist/{assets/useAuth-lZZC20Tl.js → app/assets/useAuth-3WWZ3GCD.js} +2 -2
  99. package/dist/app/assets/{useAuth-lZZC20Tl.js.map → useAuth-3WWZ3GCD.js.map} +1 -1
  100. package/dist/app/index.html +2 -2
  101. package/dist/assets/{AgentCli-8e28laT4.js → AgentCli-FWNCO1pb.js} +5 -5
  102. package/dist/assets/AgentCli-FWNCO1pb.js.map +1 -0
  103. package/dist/assets/App-yU7CFXCv.js +13 -0
  104. package/dist/assets/App-yU7CFXCv.js.map +1 -0
  105. package/dist/assets/{AppReviewAso-OLwc4Rw_.js → AppReviewAso-BYfaP1Lu.js} +3 -3
  106. package/dist/{app/assets/AppReviewAso-OLwc4Rw_.js.map → assets/AppReviewAso-BYfaP1Lu.js.map} +1 -1
  107. package/dist/assets/{Cancelled-BoeZQa1S.js → Cancelled-66ixTUrm.js} +2 -2
  108. package/dist/assets/{Cancelled-BoeZQa1S.js.map → Cancelled-66ixTUrm.js.map} +1 -1
  109. package/dist/assets/CliPreflightCard-D7utpFux.js +2 -0
  110. package/dist/assets/{CliPreflightCard-CxYyogdF.js.map → CliPreflightCard-D7utpFux.js.map} +1 -1
  111. package/dist/assets/CopyButton-CmVmSaYn.js +2 -0
  112. package/dist/assets/CopyButton-CmVmSaYn.js.map +1 -0
  113. package/dist/assets/{FastlaneScreenshots-CFT6IiRb.js → FastlaneScreenshots-CRMcetq-.js} +4 -4
  114. package/dist/{app/assets/FastlaneScreenshots-CFT6IiRb.js.map → assets/FastlaneScreenshots-CRMcetq-.js.map} +1 -1
  115. package/dist/assets/{FirstExport-B_UDacZm.js → FirstExport-C0WgL4Hl.js} +3 -3
  116. package/dist/{app/assets/FirstExport-B_UDacZm.js.map → assets/FirstExport-C0WgL4Hl.js.map} +1 -1
  117. package/dist/assets/{FivePanelFramework-C1z_xUbe.js → FivePanelFramework-BwCRmXIK.js} +3 -3
  118. package/dist/{app/assets/FivePanelFramework-C1z_xUbe.js.map → assets/FivePanelFramework-BwCRmXIK.js.map} +1 -1
  119. package/dist/assets/{ForgotPassword-ZQ8U3ejz.js → ForgotPassword-B9_si1tH.js} +2 -2
  120. package/dist/{app/assets/ForgotPassword-ZQ8U3ejz.js.map → assets/ForgotPassword-B9_si1tH.js.map} +1 -1
  121. package/dist/assets/Landing-s1ni1n-7.js +2 -0
  122. package/dist/assets/Landing-s1ni1n-7.js.map +1 -0
  123. package/dist/assets/{LanguageSwitcher-D93nCuuR.js → LanguageSwitcher-BD9N-dla.js} +2 -2
  124. package/dist/{app/assets/LanguageSwitcher-D93nCuuR.js.map → assets/LanguageSwitcher-BD9N-dla.js.map} +1 -1
  125. package/dist/assets/{Legal-Koe7vi6H.js → Legal-D3cAXQXw.js} +2 -2
  126. package/dist/assets/{Legal-Koe7vi6H.js.map → Legal-D3cAXQXw.js.map} +1 -1
  127. package/dist/assets/{NotFound-CfY31vMZ.js → NotFound-ClV-6RmP.js} +2 -2
  128. package/dist/assets/{NotFound-CfY31vMZ.js.map → NotFound-ClV-6RmP.js.map} +1 -1
  129. package/dist/assets/{Privacy-CaHf2UM7.js → Privacy-DfxgWwC3.js} +2 -2
  130. package/dist/{app/assets/Privacy-CaHf2UM7.js.map → assets/Privacy-DfxgWwC3.js.map} +1 -1
  131. package/dist/assets/{SignIn-Cyi2_-Mn.js → SignIn-ghQCKcPe.js} +2 -2
  132. package/dist/assets/{SignIn-Cyi2_-Mn.js.map → SignIn-ghQCKcPe.js.map} +1 -1
  133. package/dist/assets/{SignUp-C9QH5po_.js → SignUp-BKB87hd4.js} +2 -2
  134. package/dist/assets/{SignUp-C9QH5po_.js.map → SignUp-BKB87hd4.js.map} +1 -1
  135. package/dist/assets/SiteHeader-DxsqNPnt.js +2 -0
  136. package/dist/assets/SiteHeader-DxsqNPnt.js.map +1 -0
  137. package/dist/assets/{StickyMobileCta-uyWGVhdc.js → StickyMobileCta-C_x2pUIF.js} +2 -2
  138. package/dist/assets/{StickyMobileCta-uyWGVhdc.js.map → StickyMobileCta-C_x2pUIF.js.map} +1 -1
  139. package/dist/{app/assets/StoreAssetsPipeline-BJlnD3kD.js → assets/StoreAssetsPipeline-4hXRONUd.js} +3 -3
  140. package/dist/assets/{StoreAssetsPipeline-BJlnD3kD.js.map → StoreAssetsPipeline-4hXRONUd.js.map} +1 -1
  141. package/dist/assets/{Success-B7k7FvJj.js → Success-819qvzmp.js} +2 -2
  142. package/dist/assets/{Success-B7k7FvJj.js.map → Success-819qvzmp.js.map} +1 -1
  143. package/dist/assets/{Terms-DgJWOeUD.js → Terms-7k3nA4SU.js} +2 -2
  144. package/dist/assets/{Terms-DgJWOeUD.js.map → Terms-7k3nA4SU.js.map} +1 -1
  145. package/dist/assets/{ToolsIndex-xMPoFom9.js → ToolsIndex-CzrxymnM.js} +3 -3
  146. package/dist/{app/assets/ToolsIndex-xMPoFom9.js.map → assets/ToolsIndex-CzrxymnM.js.map} +1 -1
  147. package/dist/assets/{WorkloadCalculator-Bdt6qPR3.js → WorkloadCalculator-D2TuKS4B.js} +4 -4
  148. package/dist/assets/{WorkloadCalculator-Bdt6qPR3.js.map → WorkloadCalculator-D2TuKS4B.js.map} +1 -1
  149. package/dist/assets/{calculator-CWfp5Day.js → calculator-CiqKFdRp.js} +2 -2
  150. package/dist/assets/{calculator-CWfp5Day.js.map → calculator-CiqKFdRp.js.map} +1 -1
  151. package/dist/assets/chevron-down-_xIkRLaA.js +2 -0
  152. package/dist/assets/{chevron-down-DbSAem1L.js.map → chevron-down-_xIkRLaA.js.map} +1 -1
  153. package/dist/assets/{clipboard-copy-DgTgnK84.js → clipboard-copy-DCJioqIc.js} +2 -2
  154. package/dist/assets/{clipboard-copy-DgTgnK84.js.map → clipboard-copy-DCJioqIc.js.map} +1 -1
  155. package/dist/assets/{copy-C9t_FzCZ.js → copy-AQdWJCcs.js} +2 -2
  156. package/dist/assets/{copy-C9t_FzCZ.js.map → copy-AQdWJCcs.js.map} +1 -1
  157. package/dist/assets/{crop-B7IxIx6D.js → crop-DOMVp18h.js} +2 -2
  158. package/dist/assets/{crop-B7IxIx6D.js.map → crop-DOMVp18h.js.map} +1 -1
  159. package/dist/assets/{download-65Zuvgwm.js → download-D5RbyXDm.js} +2 -2
  160. package/dist/assets/{download-65Zuvgwm.js.map → download-D5RbyXDm.js.map} +1 -1
  161. package/dist/assets/{eye-ouNo8Nv5.js → eye-CLcZi_uk.js} +2 -2
  162. package/dist/{app/assets/eye-ouNo8Nv5.js.map → assets/eye-CLcZi_uk.js.map} +1 -1
  163. package/dist/assets/{grid-3x3-U1Nk_ppf.js → grid-3x3-AKmIrhzv.js} +2 -2
  164. package/dist/{app/assets/grid-3x3-U1Nk_ppf.js.map → assets/grid-3x3-AKmIrhzv.js.map} +1 -1
  165. package/dist/assets/hero/demo-25s-poster.jpg +0 -0
  166. package/dist/assets/hero/demo-25s.mp4 +0 -0
  167. package/dist/assets/hero/demo-25s.webm +0 -0
  168. package/dist/assets/hero/demo-poster.jpg +0 -0
  169. package/dist/assets/hero/demo.mp4 +0 -0
  170. package/dist/assets/hero/demo.webm +0 -0
  171. package/dist/{app/assets/index-B8Y8Y-Mi.js → assets/index-Bqv2NztY.js} +6 -6
  172. package/dist/assets/index-Bqv2NztY.js.map +1 -0
  173. package/dist/{app/assets/index-hyiQFYGv.css → assets/index-DGVMa-P7.css} +1 -1
  174. package/dist/assets/{layout-grid-X9Sgull2.js → layout-grid-CpGXUPMR.js} +2 -2
  175. package/dist/assets/{layout-grid-X9Sgull2.js.map → layout-grid-CpGXUPMR.js.map} +1 -1
  176. package/dist/assets/proof/output-01.png +0 -0
  177. package/dist/assets/proof/output-02.png +0 -0
  178. package/dist/assets/proof/output-03.png +0 -0
  179. package/dist/assets/proof/output-04.png +0 -0
  180. package/dist/assets/{rocket-BjOXauLe.js → rocket-IzRFv1aW.js} +2 -2
  181. package/dist/assets/{rocket-BjOXauLe.js.map → rocket-IzRFv1aW.js.map} +1 -1
  182. package/dist/assets/{shield-check-CFNhE_aD.js → shield-check-CngdN3zn.js} +2 -2
  183. package/dist/{app/assets/shield-check-CFNhE_aD.js.map → assets/shield-check-CngdN3zn.js.map} +1 -1
  184. package/dist/assets/{signIn-CVoSzqCz.js → signIn-Cn6FaQYi.js} +2 -2
  185. package/dist/{app/assets/signIn-CVoSzqCz.js.map → assets/signIn-Cn6FaQYi.js.map} +1 -1
  186. package/dist/assets/{smartphone-BcQB9gIh.js → smartphone-CpNG_Pfk.js} +2 -2
  187. package/dist/assets/{smartphone-BcQB9gIh.js.map → smartphone-CpNG_Pfk.js.map} +1 -1
  188. package/dist/assets/{terminal-CGhryJds.js → terminal-BbBESfoU.js} +2 -2
  189. package/dist/{app/assets/terminal-CGhryJds.js.map → assets/terminal-BbBESfoU.js.map} +1 -1
  190. package/dist/{app/assets/useAuth-lZZC20Tl.js → assets/useAuth-3WWZ3GCD.js} +2 -2
  191. package/dist/assets/{useAuth-lZZC20Tl.js.map → useAuth-3WWZ3GCD.js.map} +1 -1
  192. package/dist/cancelled/index.html +2 -2
  193. package/dist/forgot-password/index.html +2 -2
  194. package/dist/index.html +2 -2
  195. package/dist/legal/index.html +2 -2
  196. package/dist/privacy/index.html +2 -2
  197. package/dist/signin/index.html +2 -2
  198. package/dist/signup/index.html +2 -2
  199. package/dist/sitemap.xml +0 -4
  200. package/dist/success/index.html +2 -2
  201. package/dist/terms/index.html +2 -2
  202. package/dist/tools/5-panel-screenshot-framework/index.html +2 -2
  203. package/dist/tools/agent-cli/index.html +2 -2
  204. package/dist/tools/app-review-aso/index.html +2 -2
  205. package/dist/tools/fastlane-screenshots/index.html +2 -2
  206. package/dist/tools/first-export/index.html +2 -2
  207. package/dist/tools/index.html +2 -2
  208. package/dist/tools/screenshot-workload-calculator/index.html +2 -2
  209. package/dist/tools/store-assets-pipeline/index.html +2 -2
  210. package/package.json +3 -3
  211. package/templates/agent/agent-prompt.txt +8 -0
  212. package/dist/app/assets/AgentCli-8e28laT4.js.map +0 -1
  213. package/dist/app/assets/App-DU0xew0e.js +0 -13
  214. package/dist/app/assets/App-DU0xew0e.js.map +0 -1
  215. package/dist/app/assets/CliPreflightCard-CxYyogdF.js +0 -2
  216. package/dist/app/assets/CopyButton-CGfuSJtU.js +0 -2
  217. package/dist/app/assets/CopyButton-CGfuSJtU.js.map +0 -1
  218. package/dist/app/assets/Landing-DAIP71DD.js +0 -2
  219. package/dist/app/assets/Landing-DAIP71DD.js.map +0 -1
  220. package/dist/app/assets/Pricing-BQwtFVbj.js +0 -2
  221. package/dist/app/assets/Pricing-BQwtFVbj.js.map +0 -1
  222. package/dist/app/assets/SiteHeader-Dj3gDOnR.js +0 -2
  223. package/dist/app/assets/SiteHeader-Dj3gDOnR.js.map +0 -1
  224. package/dist/app/assets/check-BWCiUZ-C.js +0 -2
  225. package/dist/app/assets/check-BWCiUZ-C.js.map +0 -1
  226. package/dist/app/assets/chevron-down-DbSAem1L.js +0 -2
  227. package/dist/app/assets/circle-check-CeakjiXC.js +0 -2
  228. package/dist/app/assets/circle-check-CeakjiXC.js.map +0 -1
  229. package/dist/app/assets/hero/cli-build-tree-15s.mp4 +0 -0
  230. package/dist/app/assets/hero/cli-build-tree-15s.webm +0 -0
  231. package/dist/app/assets/hero/cli-build-tree-25s.mp4 +0 -0
  232. package/dist/app/assets/hero/cli-build-tree-25s.webm +0 -0
  233. package/dist/app/assets/hero/cli-build-tree-poster.png +0 -0
  234. package/dist/app/assets/hero/demo.gif +0 -0
  235. package/dist/app/assets/index-B8Y8Y-Mi.js.map +0 -1
  236. package/dist/assets/AgentCli-8e28laT4.js.map +0 -1
  237. package/dist/assets/App-DU0xew0e.js +0 -13
  238. package/dist/assets/App-DU0xew0e.js.map +0 -1
  239. package/dist/assets/CliPreflightCard-CxYyogdF.js +0 -2
  240. package/dist/assets/CopyButton-CGfuSJtU.js +0 -2
  241. package/dist/assets/CopyButton-CGfuSJtU.js.map +0 -1
  242. package/dist/assets/Landing-DAIP71DD.js +0 -2
  243. package/dist/assets/Landing-DAIP71DD.js.map +0 -1
  244. package/dist/assets/Pricing-BQwtFVbj.js +0 -2
  245. package/dist/assets/Pricing-BQwtFVbj.js.map +0 -1
  246. package/dist/assets/SiteHeader-Dj3gDOnR.js +0 -2
  247. package/dist/assets/SiteHeader-Dj3gDOnR.js.map +0 -1
  248. package/dist/assets/check-BWCiUZ-C.js +0 -2
  249. package/dist/assets/check-BWCiUZ-C.js.map +0 -1
  250. package/dist/assets/chevron-down-DbSAem1L.js +0 -2
  251. package/dist/assets/circle-check-CeakjiXC.js +0 -2
  252. package/dist/assets/circle-check-CeakjiXC.js.map +0 -1
  253. package/dist/assets/hero/cli-build-tree-15s.mp4 +0 -0
  254. package/dist/assets/hero/cli-build-tree-15s.webm +0 -0
  255. package/dist/assets/hero/cli-build-tree-25s.mp4 +0 -0
  256. package/dist/assets/hero/cli-build-tree-25s.webm +0 -0
  257. package/dist/assets/hero/cli-build-tree-poster.png +0 -0
  258. package/dist/assets/hero/demo.gif +0 -0
  259. package/dist/assets/index-B8Y8Y-Mi.js.map +0 -1
  260. package/dist/pricing/index.html +0 -33
package/README.md CHANGED
@@ -7,14 +7,6 @@ Build store screenshots as a build step.
7
7
  npx --yes shipscreens@latest demo
8
8
  ```
9
9
 
10
- ## Rendered mode (optional)
11
- Rendered mode runs the UI renderer headlessly via Playwright Chromium.
12
- Install Chromium once per machine/CI runner:
13
-
14
- ```bash
15
- npx playwright install --with-deps chromium
16
- ```
17
-
18
10
  ## CI pipeline (copy/paste)
19
11
  ```bash
20
12
  npx --yes shipscreens@latest validate
@@ -2,7 +2,9 @@
2
2
  import { parseArgs } from 'node:util';
3
3
  import fs from 'node:fs/promises';
4
4
  import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
5
6
  import { resolveDefaultConfigPath } from '../cli/loadConfig.js';
7
+ import { DEVICE_GROUPS, DEVICE_LIBRARY } from '../src/constants/devices.js';
6
8
  import { runValidate } from '../cli/commands/validate.js';
7
9
  import { runLock } from '../cli/commands/lock.js';
8
10
  import { runBuild } from '../cli/commands/build.js';
@@ -25,6 +27,9 @@ import {
25
27
  PLACEHOLDER_WIDTH,
26
28
  } from '../cli/commands/init.js';
27
29
 
30
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
31
+ const DEMO_SCREENS_DIR = path.join(PACKAGE_ROOT, 'cli', 'assets', 'demo-screens');
32
+
28
33
  function printHelp() {
29
34
  console.log(`ShipScreens CLI
30
35
 
@@ -33,6 +38,8 @@ Usage:
33
38
  shipscreens wizard [--dir <path>] [--force] [--style <value>] [--template <id>] [--defaults]
34
39
  shipscreens demo [--out <dir>] [--slides <n>] [--locales <id,id>] [--devices <id,id>] [--template <id>] [--mode <raw|rendered>] [--zip-out <path>] [--profile <appstore|playstore|any>] [--force]
35
40
  shipscreens agent-run [--dir <path>] [--slides <n>] [--locales <id,id>] [--devices <id,id>] [--style <value>] [--template <id>] [--mode <raw|rendered>] [--out <dir>] [--zip] [--profile <appstore|playstore|any>] [--require-pro] [--force]
41
+ shipscreens devices [--json]
42
+ shipscreens templates [--json]
36
43
  shipscreens validate [-c shipscreens.config.yml]
37
44
  shipscreens lock [-c shipscreens.config.yml] [--out shipscreens.lock.json] [--check]
38
45
  shipscreens build [-c shipscreens.config.yml] [--mode shipscreens|rendered|raw|frameit] [--format dir|zip] [--out <dir>] [--zip] [--zip-out <path>] [--clean]
@@ -48,6 +55,8 @@ Commands:
48
55
  wizard Step-by-step setup (locales/devices/template)
49
56
  demo Create a demo project + export (no assets required)
50
57
  agent-run Non-interactive end-to-end flow (init -> validate -> build -> verify)
58
+ devices List device ids (for shipscreens.config.yml)
59
+ templates List style template ids (for init/wizard/agent-run)
51
60
  validate Validate shipscreens.config.yml (schema + asset paths)
52
61
  lock Generate shipscreens.lock.json with sha256 hashes
53
62
  build Generate Fastlane-ready outputs (headless Chromium, zero-padded filenames)
@@ -76,7 +85,7 @@ Options:
76
85
  --profile <appstore|playstore|any> Strict verification/staging rules per store (default: any)
77
86
  --from <dir> Source output root for staging (default: config dir)
78
87
  --check Verify outputs are valid/up-to-date (no write)
79
- --json Emit machine-readable JSON (plan)
88
+ --json Emit machine-readable JSON (most commands; plan always emits JSON)
80
89
  --mode <shipscreens|rendered|raw|frameit> Build mode (default: rendered). shipscreens=rendered alias, frameit runs raw + export framefile --check
81
90
  --format <dir|zip> Output format (default: dir)
82
91
  --zip Also write a ZIP (same as --format zip)
@@ -165,6 +174,26 @@ async function pathExists(targetPath) {
165
174
  }
166
175
  }
167
176
 
177
+ async function loadDemoScreenPngs() {
178
+ try {
179
+ const entries = await fs.readdir(DEMO_SCREENS_DIR);
180
+ const pngNames = entries
181
+ .filter((name) => String(name).toLowerCase().endsWith('.png'))
182
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
183
+ if (!pngNames.length) return [];
184
+
185
+ const buffers = [];
186
+ for (const name of pngNames) {
187
+ // eslint-disable-next-line no-await-in-loop
188
+ buffers.push(await fs.readFile(path.join(DEMO_SCREENS_DIR, name)));
189
+ }
190
+ return buffers;
191
+ } catch (err) {
192
+ void err;
193
+ return [];
194
+ }
195
+ }
196
+
168
197
  async function findPlaceholderAssets({ configPath }) {
169
198
  const { config, configDir } = await loadShipScreensConfig(configPath);
170
199
  const placeholder = createSolidPng({
@@ -235,12 +264,75 @@ async function main() {
235
264
  },
236
265
  });
237
266
 
267
+ const emitJson = Boolean(values.json);
268
+ const jsonEnvelope = (command, payload) => ({ version: 1, ok: true, command, ...payload });
269
+ const emitPayload = (command, payload, textLines) => {
270
+ if (emitJson) {
271
+ process.stdout.write(JSON.stringify(jsonEnvelope(command, payload), null, 2) + '\n');
272
+ return;
273
+ }
274
+ if (Array.isArray(textLines)) {
275
+ console.log(textLines.join('\n'));
276
+ return;
277
+ }
278
+ console.log(String(textLines || ''));
279
+ };
280
+
238
281
  const command = positionals[0];
239
282
  if (values.help || !command || command === 'help') {
240
283
  printHelp();
241
284
  return;
242
285
  }
243
286
 
287
+ if (command === 'devices') {
288
+ const devices = Object.entries(DEVICE_LIBRARY)
289
+ .map(([id, def]) => ({ id, ...def }))
290
+ .sort((a, b) => {
291
+ const groupA = String(a.group || '');
292
+ const groupB = String(b.group || '');
293
+ if (groupA !== groupB) return groupA < groupB ? -1 : 1;
294
+ const nameA = String(a.name || a.id);
295
+ const nameB = String(b.name || b.id);
296
+ return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
297
+ });
298
+
299
+ emitPayload(
300
+ 'devices',
301
+ { devices, groups: DEVICE_GROUPS },
302
+ [
303
+ `Device ids (${devices.length}):`,
304
+ ...devices.map((d) => `- ${d.id} ${d.width}x${d.height} ${d.group || 'Unknown'} ${d.name || ''}`.trim()),
305
+ '',
306
+ `Tip: devices[].platform is inferred for known device ids. For multiple Android devices, set devices[].androidBucket explicitly.`,
307
+ ]
308
+ );
309
+ return;
310
+ }
311
+
312
+ if (command === 'templates') {
313
+ const templates = listTemplateIds()
314
+ .map((id) => {
315
+ const resolved = resolveTemplateConfig(id);
316
+ if (!resolved) return null;
317
+ const { template, style, assets } = resolved;
318
+ return {
319
+ id: template.id,
320
+ name: template.name,
321
+ tags: template.tags || {},
322
+ style: style || null,
323
+ assets: assets || null,
324
+ };
325
+ })
326
+ .filter(Boolean);
327
+
328
+ emitPayload(
329
+ 'templates',
330
+ { templates },
331
+ [`Templates (${templates.length}):`, ...templates.map((t) => `- ${t.id} ${t.name}`)]
332
+ );
333
+ return;
334
+ }
335
+
244
336
  if (command === 'auth') {
245
337
  await runAuth({ subcommand: positionals[1], values });
246
338
  return;
@@ -292,6 +384,8 @@ async function main() {
292
384
  });
293
385
 
294
386
  // Overwrite placeholders with deterministic, non-placeholder demo PNGs.
387
+ // Prefer realistic UI samples (bundled with the npm package). Fall back to solid colors.
388
+ const demoScreens = await loadDemoScreenPngs();
295
389
  const demoColors = [
296
390
  [59, 130, 246, 255], // blue
297
391
  [16, 185, 129, 255], // emerald
@@ -304,19 +398,61 @@ async function main() {
304
398
  for (let localeIndex = 0; localeIndex < initResult.locales.length; localeIndex += 1) {
305
399
  const localeId = initResult.locales[localeIndex];
306
400
  for (let i = 1; i <= initResult.slides; i += 1) {
307
- const color = demoColors[(i - 1 + localeIndex) % demoColors.length];
308
- const png = createSolidPng({
309
- width: PLACEHOLDER_WIDTH,
310
- height: PLACEHOLDER_HEIGHT,
311
- color,
312
- });
401
+ const png =
402
+ demoScreens.length
403
+ ? demoScreens[(i - 1) % demoScreens.length]
404
+ : (() => {
405
+ const color = demoColors[(i - 1 + localeIndex) % demoColors.length];
406
+ return createSolidPng({
407
+ width: PLACEHOLDER_WIDTH,
408
+ height: PLACEHOLDER_HEIGHT,
409
+ color,
410
+ });
411
+ })();
313
412
  const index = formatDemoIndex(i, indexWidth);
314
413
  const assetPath = path.join(projectDir, 'assets', localeId, `${index}.png`);
315
414
  await fs.writeFile(assetPath, png);
316
415
  }
317
416
  }
318
417
 
418
+ // Replace placeholder copy with a more meaningful demo (still safe: no translation/upload/speed claims).
419
+ const demoCopyByLocale = {
420
+ // Keep demo copy ASCII-only so it renders consistently across environments/fonts.
421
+ 'en-US': [
422
+ ['Screenshots as a build step', 'wizard -> build -> verify -> tree'],
423
+ ['Fastlane-ready outputs', 'deliver (iOS) + supply (Android)'],
424
+ ['Reproducible exports', 'same inputs -> same outputs'],
425
+ ['Verify before upload', 'catch mixed folders early'],
426
+ ['Try it free', 'sample -> edit -> export'],
427
+ ],
428
+ // If the demo includes non-English locales, reuse the same copy to avoid implying translation is built-in.
429
+ 'ja-JP': [
430
+ ['Screenshots as a build step', 'wizard -> build -> verify -> tree'],
431
+ ['Fastlane-ready outputs', 'deliver (iOS) + supply (Android)'],
432
+ ['Reproducible exports', 'same inputs -> same outputs'],
433
+ ['Verify before upload', 'catch mixed folders early'],
434
+ ['Try it free', 'sample -> edit -> export'],
435
+ ],
436
+ };
437
+ const demoCsvHeader = ['Locale'];
438
+ for (let i = 1; i <= initResult.slides; i += 1) {
439
+ demoCsvHeader.push(`Header_${i}`, `Subtitle_${i}`);
440
+ }
441
+ const demoCsvRows = [demoCsvHeader.join(',')];
442
+ for (const localeId of initResult.locales) {
443
+ const fallback = demoCopyByLocale['en-US'] || [];
444
+ const pairs = demoCopyByLocale[localeId] || fallback;
445
+ const row = [localeId];
446
+ for (let i = 1; i <= initResult.slides; i += 1) {
447
+ const pair = pairs[i - 1] || fallback[i - 1] || [`Slide ${i}`, 'Edit copy.csv -> rerun export'];
448
+ row.push(pair[0], pair[1]);
449
+ }
450
+ demoCsvRows.push(row.join(','));
451
+ }
452
+ await fs.writeFile(initResult.csvPath, `${demoCsvRows.join('\n')}\n`, 'utf8');
453
+
319
454
  const configPath = path.join(projectDir, 'shipscreens.config.yml');
455
+ await runCopyImport({ configPath, csvPath: initResult.csvPath, mode: 'replace' });
320
456
  await runValidate({ configPath });
321
457
  await runLock({ configPath });
322
458
 
@@ -335,7 +471,7 @@ async function main() {
335
471
 
336
472
  const rawZipOut = typeof values['zip-out'] === 'string' ? values['zip-out'].trim() : '';
337
473
  const zipOut = rawZipOut || undefined;
338
- const { token: cliAuthToken } = await resolveCliAuth({ requirePro: false });
474
+ const { token: cliAuthToken, profile: cliAuthProfile } = await resolveCliAuth({ requirePro: false });
339
475
 
340
476
  const result = await runBuild({
341
477
  configPath,
@@ -345,6 +481,7 @@ async function main() {
345
481
  zipOut,
346
482
  clean: true,
347
483
  cliAuthToken,
484
+ cliAuthProfile,
348
485
  });
349
486
 
350
487
  const verifyResult = await runVerify({
@@ -372,7 +509,22 @@ async function main() {
372
509
  `stage -> ${stageResult.out}`,
373
510
  `project -> ${projectDir}`,
374
511
  ];
375
- console.log(summary.join(', '));
512
+ emitPayload(
513
+ 'demo',
514
+ {
515
+ demoRoot,
516
+ projectDir,
517
+ buildOutDir,
518
+ stageOutDir,
519
+ mode,
520
+ profile,
521
+ init: initResult,
522
+ build: result,
523
+ verify: verifyResult,
524
+ stage: stageResult,
525
+ },
526
+ summary.join(', ')
527
+ );
376
528
  return;
377
529
  }
378
530
 
@@ -398,7 +550,9 @@ async function main() {
398
550
  style: stylePreset.presetStyle,
399
551
  force: values.force,
400
552
  });
401
- console.log(
553
+ emitPayload(
554
+ 'init',
555
+ result,
402
556
  `OK: created starter files in ${result.dir}\n` +
403
557
  `- ${result.configPath}\n` +
404
558
  `- ${result.csvPath}\n` +
@@ -459,7 +613,7 @@ async function main() {
459
613
  const defaultProfile = hasIos && !hasAndroid ? 'appstore' : !hasIos && hasAndroid ? 'playstore' : 'any';
460
614
  const profile = values.profile || defaultProfile;
461
615
  const requirePro = Boolean(values['require-pro']);
462
- const { token: cliAuthToken } = await resolveCliAuth({ requirePro });
616
+ const { token: cliAuthToken, profile: cliAuthProfile } = await resolveCliAuth({ requirePro });
463
617
 
464
618
  const result = await runBuild({
465
619
  configPath,
@@ -473,6 +627,7 @@ async function main() {
473
627
  framefileStrategy: values['framefile-strategy'],
474
628
  clean: values.clean,
475
629
  cliAuthToken,
630
+ cliAuthProfile,
476
631
  });
477
632
 
478
633
  const verifyResult = await runVerify({
@@ -493,7 +648,21 @@ async function main() {
493
648
  `${verifyResult.androidDevices} Android device(s) -> ${verifyResult.androidDir}`,
494
649
  ];
495
650
  if (result.zipPath || verifyResult.zipPath) summary.push(`zip -> ${result.zipPath || verifyResult.zipPath}`);
496
- console.log(summary.join(', '));
651
+ emitPayload(
652
+ 'agent-run',
653
+ {
654
+ configPath,
655
+ outDir,
656
+ zipOut: zipOut || null,
657
+ format,
658
+ mode,
659
+ profile,
660
+ requirePro,
661
+ build: result,
662
+ verify: verifyResult,
663
+ },
664
+ summary.join(', ')
665
+ );
497
666
  return;
498
667
  }
499
668
 
@@ -513,7 +682,9 @@ async function main() {
513
682
  csvPath: values.csv,
514
683
  mode: values['import-mode'],
515
684
  });
516
- console.log(
685
+ emitPayload(
686
+ 'copy import',
687
+ result,
517
688
  `OK: imported copy.csv (${result.mode}) -> ${result.configPath}\n` +
518
689
  `- locales: ${result.locales}\n` +
519
690
  `- slides: ${result.slides}`
@@ -525,17 +696,16 @@ async function main() {
525
696
 
526
697
  if (command === 'validate') {
527
698
  const result = await runValidate({ configPath });
528
- console.log(`OK: validated (${result.assetCount} asset(s))`);
699
+ emitPayload('validate', { configPath, ...result }, `OK: validated (${result.assetCount} asset(s))`);
529
700
  return;
530
701
  }
531
702
 
532
703
  if (command === 'lock') {
533
704
  const result = await runLock({ configPath, outPath: values.out, check: values.check });
534
- if (result.checked) {
535
- console.log(`OK: lockfile up to date (${result.assetCount} asset(s)) -> ${result.outPath}`);
536
- } else {
537
- console.log(`OK: wrote lockfile (${result.assetCount} asset(s)) -> ${result.outPath}`);
538
- }
705
+ const text = result.checked
706
+ ? `OK: lockfile up to date (${result.assetCount} asset(s)) -> ${result.outPath}`
707
+ : `OK: wrote lockfile (${result.assetCount} asset(s)) -> ${result.outPath}`;
708
+ emitPayload('lock', { configPath, ...result }, text);
539
709
  return;
540
710
  }
541
711
 
@@ -549,15 +719,15 @@ async function main() {
549
719
  if ((values.zip || format === 'zip') && outDir && outDir.toLowerCase().endsWith('.zip') && !zipOut) {
550
720
  zipOut = outDir;
551
721
  outDir = undefined;
552
- console.warn(
722
+ const warning =
553
723
  `DEPRECATED: shipscreens build --out <file.zip> will change behavior in a future release.\n` +
554
- `Use: shipscreens build --zip-out <file.zip>\n` +
555
- `Note: --out now specifies an output directory root.`
556
- );
724
+ `Use: shipscreens build --zip-out <file.zip>\n` +
725
+ `Note: --out now specifies an output directory root.\n`;
726
+ process.stderr.write(warning);
557
727
  }
558
728
 
559
729
  const requirePro = Boolean(values['require-pro']);
560
- const { token: cliAuthToken } = await resolveCliAuth({ requirePro });
730
+ const { token: cliAuthToken, profile: cliAuthProfile } = await resolveCliAuth({ requirePro });
561
731
  const result = await runBuild({
562
732
  configPath,
563
733
  format,
@@ -570,6 +740,7 @@ async function main() {
570
740
  framefileStrategy: values['framefile-strategy'],
571
741
  clean: values.clean,
572
742
  cliAuthToken,
743
+ cliAuthProfile,
573
744
  });
574
745
  const summary = [
575
746
  `OK: built`,
@@ -579,7 +750,11 @@ async function main() {
579
750
  `${result.androidDevices} Android device(s) -> ${result.androidDir}`,
580
751
  ];
581
752
  if (result.zipPath) summary.push(`zip -> ${result.zipPath}`);
582
- console.log(summary.join(', '));
753
+ emitPayload(
754
+ 'build',
755
+ { configPath, format, mode: values.mode || 'rendered', outDir: outDir || null, zipOut: zipOut || null, ...result },
756
+ summary.join(', ')
757
+ );
583
758
  return;
584
759
  }
585
760
 
@@ -603,7 +778,17 @@ async function main() {
603
778
  `${result.androidDevices} Android device(s) -> ${result.androidDir}`,
604
779
  ];
605
780
  if (result.zipPath) summary.push(`zip -> ${result.zipPath}`);
606
- console.log(summary.join(', '));
781
+ emitPayload(
782
+ 'verify',
783
+ {
784
+ configPath,
785
+ outDir: rawOut || null,
786
+ zipOut: rawZipOut || null,
787
+ profile: values.profile || null,
788
+ ...result,
789
+ },
790
+ summary.join(', ')
791
+ );
607
792
  return;
608
793
  }
609
794
 
@@ -617,7 +802,11 @@ async function main() {
617
802
  profile: values.profile,
618
803
  });
619
804
  const details = result.iosVariant ? `, ios=${result.iosVariant}` : '';
620
- console.log(`OK: staged (${result.profile}${details}), ${result.files} file(s) -> ${result.out}`);
805
+ emitPayload(
806
+ 'stage',
807
+ { configPath, ...result },
808
+ `OK: staged (${result.profile}${details}), ${result.files} file(s) -> ${result.out}`
809
+ );
621
810
  return;
622
811
  }
623
812
 
@@ -658,7 +847,13 @@ async function main() {
658
847
  throw new Error(`Unknown command: ${command}`);
659
848
  }
660
849
 
850
+ const wantsJson = process.argv.includes('--json');
661
851
  main().catch((err) => {
662
- console.error(err?.message || String(err));
852
+ const message = err?.message || String(err);
853
+ if (wantsJson) {
854
+ process.stderr.write(JSON.stringify({ version: 1, ok: false, error: message }, null, 2) + '\n');
855
+ } else {
856
+ console.error(message);
857
+ }
663
858
  process.exitCode = 1;
664
859
  });
Binary file
Binary file
Binary file
Binary file
@@ -69,6 +69,7 @@ export async function runBuild({
69
69
  framefileStrategy,
70
70
  clean = false,
71
71
  cliAuthToken,
72
+ cliAuthProfile,
72
73
  } = {}) {
73
74
  await runValidate({ configPath });
74
75
  const { config, configDir } = await loadShipScreensConfig(configPath);
@@ -223,13 +224,12 @@ export async function runBuild({
223
224
 
224
225
  let chromium;
225
226
  try {
226
- ({ chromium } = await import('playwright-core'));
227
+ ({ chromium } = await import('playwright-chromium'));
227
228
  } catch (err) {
228
229
  await closeServer();
229
230
  throw new Error(
230
- `Playwright is required for rendered builds.\n` +
231
- `Install Chromium once (or use --mode raw):\n` +
232
- ` npx playwright install --with-deps chromium\n` +
231
+ `Playwright (Chromium) is required for shipscreens build.\n` +
232
+ `Install dependencies and try again.\n` +
233
233
  `${err?.message || String(err)}`
234
234
  );
235
235
  }
@@ -240,21 +240,7 @@ export async function runBuild({
240
240
 
241
241
  let browser = null;
242
242
  try {
243
- try {
244
- browser = await chromium.launch({ headless: true });
245
- } catch (err) {
246
- const msg = err?.message || String(err);
247
- if (msg.includes("Executable doesn't exist") || msg.includes('playwright install')) {
248
- throw new Error(
249
- `Playwright Chromium is not installed on this machine.\n` +
250
- `Run:\n` +
251
- ` npx playwright install --with-deps chromium\n` +
252
- `Then rerun shipscreens build.\n` +
253
- msg
254
- );
255
- }
256
- throw err;
257
- }
243
+ browser = await chromium.launch({ headless: true });
258
244
  const context = await browser.newContext({
259
245
  acceptDownloads: true,
260
246
  viewport: { width: 1400, height: 900 },
@@ -267,17 +253,31 @@ export async function runBuild({
267
253
  };
268
254
 
269
255
  await context.addInitScript(
270
- (payload, exportStructureMode, authToken) => {
256
+ ({ payload, exportStructureMode, authToken, authProfile }) => {
271
257
  window.__SHIPSCREENS_CLI = true;
272
- if (authToken) {
273
- window.__SHIPSCREENS_CLI_AUTH = { token: authToken };
258
+ // Prefer an injected in-memory payload so large screenshots won't fail on localStorage quota.
259
+ window.__SHIPSCREENS_CLI_PROJECT = payload;
260
+ window.__SHIPSCREENS_CLI_EXPORT_STRUCTURE_MODE = exportStructureMode;
261
+ if (authToken || authProfile) {
262
+ window.__SHIPSCREENS_CLI_AUTH = { token: authToken || null, profile: authProfile || null };
263
+ }
264
+ try {
265
+ localStorage.setItem('shipscreens_project', JSON.stringify(payload));
266
+ } catch (err) {
267
+ void err;
268
+ }
269
+ try {
270
+ localStorage.setItem('shipScreensExportStructureMode', exportStructureMode);
271
+ } catch (err) {
272
+ void err;
274
273
  }
275
- localStorage.setItem('shipscreens_project', JSON.stringify(payload));
276
- localStorage.setItem('shipScreensExportStructureMode', exportStructureMode);
277
274
  },
278
- projectPayload,
279
- outputMode,
280
- cliAuthToken || null
275
+ {
276
+ payload: projectPayload,
277
+ exportStructureMode: outputMode,
278
+ authToken: cliAuthToken || null,
279
+ authProfile: cliAuthProfile || null,
280
+ }
281
281
  );
282
282
 
283
283
  const page = await context.newPage();
package/cli/schema.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { isDisallowedPath, normalizeRepoRelativePath } from './pathUtils.js';
3
+ import { DEVICE_LIBRARY } from '../src/constants/devices.js';
3
4
 
4
5
  export const SHIPSCREENS_CONFIG_SCHEMA_VERSION = 1;
5
6
 
@@ -19,6 +20,20 @@ const DeviceId = z
19
20
  .max(64)
20
21
  .regex(/^[a-z0-9_-]+$/i, 'Expected a device id like iphone69 or pixel8');
21
22
 
23
+ const inferDevicePlatform = (deviceId) => {
24
+ const def = DEVICE_LIBRARY[deviceId];
25
+ const group = String(def?.group || '').toLowerCase();
26
+ if (!group) return null;
27
+ return group.includes('android') ? 'android' : 'ios';
28
+ };
29
+
30
+ const inferAndroidBucket = (deviceId) => {
31
+ const def = DEVICE_LIBRARY[deviceId];
32
+ const group = String(def?.group || '').toLowerCase();
33
+ if (group.includes('tablet')) return 'tenInchScreenshots';
34
+ return 'phoneScreenshots';
35
+ };
36
+
22
37
  const LayoutMode = z.enum(['top', 'bottom', 'split']);
23
38
  const BadgeType = z.enum(['none', 'stars', 'award', 'download', 'googleplay']);
24
39
  const FrameColor = z.enum(['black', 'silver', 'titanium', 'blue', 'gold']);
@@ -143,6 +158,23 @@ const Slide = z
143
158
  })
144
159
  .strict();
145
160
 
161
+ const DeviceConfig = z
162
+ .object({
163
+ id: DeviceId,
164
+ platform: z.enum(['ios', 'android']).optional(),
165
+ androidBucket: z
166
+ .enum(['phoneScreenshots', 'sevenInchScreenshots', 'tenInchScreenshots', 'tvScreenshots', 'wearScreenshots'])
167
+ .optional(),
168
+ label: z.string().min(1).optional(),
169
+ })
170
+ .strict()
171
+ .transform((device) => {
172
+ const platform = device.platform || inferDevicePlatform(device.id) || undefined;
173
+ const androidBucket =
174
+ platform === 'android' ? device.androidBucket || inferAndroidBucket(device.id) : device.androidBucket;
175
+ return { ...device, platform, androidBucket };
176
+ });
177
+
146
178
  const OutputCompat = z
147
179
  .object({
148
180
  directory: z.string().min(1).optional(),
@@ -186,26 +218,7 @@ export const ShipScreensConfigSchema = z
186
218
  .strict()
187
219
  )
188
220
  .min(1),
189
- devices: z
190
- .array(
191
- z
192
- .object({
193
- id: DeviceId,
194
- platform: z.enum(['ios', 'android']).optional(),
195
- androidBucket: z
196
- .enum([
197
- 'phoneScreenshots',
198
- 'sevenInchScreenshots',
199
- 'tenInchScreenshots',
200
- 'tvScreenshots',
201
- 'wearScreenshots',
202
- ])
203
- .optional(),
204
- label: z.string().min(1).optional(),
205
- })
206
- .strict()
207
- )
208
- .min(1),
221
+ devices: z.array(DeviceConfig).min(1),
209
222
  assets: Assets,
210
223
  style: Style,
211
224
  slides: z.array(Slide).min(1),
@@ -247,6 +260,32 @@ export const ShipScreensConfigSchema = z
247
260
  if (config.assets?.badges) unique(config.assets.badges, 'assets.badges', (b) => b.id);
248
261
  unique(config.slides, 'slides', (s) => String(s.id));
249
262
 
263
+ const androidBuckets = new Map();
264
+ config.devices.forEach((device, deviceIdx) => {
265
+ if (device.platform === 'android') {
266
+ const bucket = device.androidBucket || 'phoneScreenshots';
267
+ const existingIdx = androidBuckets.get(bucket);
268
+ if (existingIdx != null) {
269
+ ctx.addIssue({
270
+ code: z.ZodIssueCode.custom,
271
+ message:
272
+ `Multiple Android devices map to the same Google Play bucket (${bucket}): ` +
273
+ `${config.devices[existingIdx].id}, ${device.id}\n` +
274
+ `Each bucket represents a single screenshot set on Google Play. Set devices[].androidBucket explicitly.`,
275
+ path: ['devices', deviceIdx, 'androidBucket'],
276
+ });
277
+ } else {
278
+ androidBuckets.set(bucket, deviceIdx);
279
+ }
280
+ } else if (device.androidBucket) {
281
+ ctx.addIssue({
282
+ code: z.ZodIssueCode.custom,
283
+ message: `androidBucket is only valid when platform=android`,
284
+ path: ['devices', deviceIdx, 'androidBucket'],
285
+ });
286
+ }
287
+ });
288
+
250
289
  const localeIds = new Set(config.locales.map((l) => l.id));
251
290
 
252
291
  config.slides.forEach((slide, slideIdx) => {