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.
- package/README.md +0 -8
- package/bin/shipscreens.js +223 -28
- package/cli/assets/demo-screens/01.png +0 -0
- package/cli/assets/demo-screens/02.png +0 -0
- package/cli/assets/demo-screens/03.png +0 -0
- package/cli/assets/demo-screens/04.png +0 -0
- package/cli/commands/build.js +27 -27
- package/cli/schema.js +59 -20
- package/dist/app/assets/{AgentCli-8e28laT4.js → AgentCli-FWNCO1pb.js} +5 -5
- package/dist/app/assets/AgentCli-FWNCO1pb.js.map +1 -0
- package/dist/app/assets/App-yU7CFXCv.js +13 -0
- package/dist/app/assets/App-yU7CFXCv.js.map +1 -0
- package/dist/app/assets/{AppReviewAso-OLwc4Rw_.js → AppReviewAso-BYfaP1Lu.js} +3 -3
- package/dist/{assets/AppReviewAso-OLwc4Rw_.js.map → app/assets/AppReviewAso-BYfaP1Lu.js.map} +1 -1
- package/dist/app/assets/{Cancelled-BoeZQa1S.js → Cancelled-66ixTUrm.js} +2 -2
- package/dist/app/assets/{Cancelled-BoeZQa1S.js.map → Cancelled-66ixTUrm.js.map} +1 -1
- package/dist/app/assets/CliPreflightCard-D7utpFux.js +2 -0
- package/dist/app/assets/{CliPreflightCard-CxYyogdF.js.map → CliPreflightCard-D7utpFux.js.map} +1 -1
- package/dist/app/assets/CopyButton-CmVmSaYn.js +2 -0
- package/dist/app/assets/CopyButton-CmVmSaYn.js.map +1 -0
- package/dist/app/assets/{FastlaneScreenshots-CFT6IiRb.js → FastlaneScreenshots-CRMcetq-.js} +4 -4
- package/dist/{assets/FastlaneScreenshots-CFT6IiRb.js.map → app/assets/FastlaneScreenshots-CRMcetq-.js.map} +1 -1
- package/dist/app/assets/{FirstExport-B_UDacZm.js → FirstExport-C0WgL4Hl.js} +3 -3
- package/dist/{assets/FirstExport-B_UDacZm.js.map → app/assets/FirstExport-C0WgL4Hl.js.map} +1 -1
- package/dist/app/assets/{FivePanelFramework-C1z_xUbe.js → FivePanelFramework-BwCRmXIK.js} +3 -3
- package/dist/{assets/FivePanelFramework-C1z_xUbe.js.map → app/assets/FivePanelFramework-BwCRmXIK.js.map} +1 -1
- package/dist/app/assets/{ForgotPassword-ZQ8U3ejz.js → ForgotPassword-B9_si1tH.js} +2 -2
- package/dist/{assets/ForgotPassword-ZQ8U3ejz.js.map → app/assets/ForgotPassword-B9_si1tH.js.map} +1 -1
- package/dist/app/assets/Landing-s1ni1n-7.js +2 -0
- package/dist/app/assets/Landing-s1ni1n-7.js.map +1 -0
- package/dist/app/assets/{LanguageSwitcher-D93nCuuR.js → LanguageSwitcher-BD9N-dla.js} +2 -2
- package/dist/{assets/LanguageSwitcher-D93nCuuR.js.map → app/assets/LanguageSwitcher-BD9N-dla.js.map} +1 -1
- package/dist/app/assets/{Legal-Koe7vi6H.js → Legal-D3cAXQXw.js} +2 -2
- package/dist/app/assets/{Legal-Koe7vi6H.js.map → Legal-D3cAXQXw.js.map} +1 -1
- package/dist/app/assets/{NotFound-CfY31vMZ.js → NotFound-ClV-6RmP.js} +2 -2
- package/dist/app/assets/{NotFound-CfY31vMZ.js.map → NotFound-ClV-6RmP.js.map} +1 -1
- package/dist/app/assets/{Privacy-CaHf2UM7.js → Privacy-DfxgWwC3.js} +2 -2
- package/dist/{assets/Privacy-CaHf2UM7.js.map → app/assets/Privacy-DfxgWwC3.js.map} +1 -1
- package/dist/app/assets/{SignIn-Cyi2_-Mn.js → SignIn-ghQCKcPe.js} +2 -2
- package/dist/app/assets/{SignIn-Cyi2_-Mn.js.map → SignIn-ghQCKcPe.js.map} +1 -1
- package/dist/app/assets/{SignUp-C9QH5po_.js → SignUp-BKB87hd4.js} +2 -2
- package/dist/app/assets/{SignUp-C9QH5po_.js.map → SignUp-BKB87hd4.js.map} +1 -1
- package/dist/app/assets/SiteHeader-DxsqNPnt.js +2 -0
- package/dist/app/assets/SiteHeader-DxsqNPnt.js.map +1 -0
- package/dist/app/assets/{StickyMobileCta-uyWGVhdc.js → StickyMobileCta-C_x2pUIF.js} +2 -2
- package/dist/app/assets/{StickyMobileCta-uyWGVhdc.js.map → StickyMobileCta-C_x2pUIF.js.map} +1 -1
- package/dist/{assets/StoreAssetsPipeline-BJlnD3kD.js → app/assets/StoreAssetsPipeline-4hXRONUd.js} +3 -3
- package/dist/app/assets/{StoreAssetsPipeline-BJlnD3kD.js.map → StoreAssetsPipeline-4hXRONUd.js.map} +1 -1
- package/dist/app/assets/{Success-B7k7FvJj.js → Success-819qvzmp.js} +2 -2
- package/dist/app/assets/{Success-B7k7FvJj.js.map → Success-819qvzmp.js.map} +1 -1
- package/dist/app/assets/{Terms-DgJWOeUD.js → Terms-7k3nA4SU.js} +2 -2
- package/dist/app/assets/{Terms-DgJWOeUD.js.map → Terms-7k3nA4SU.js.map} +1 -1
- package/dist/app/assets/{ToolsIndex-xMPoFom9.js → ToolsIndex-CzrxymnM.js} +3 -3
- package/dist/{assets/ToolsIndex-xMPoFom9.js.map → app/assets/ToolsIndex-CzrxymnM.js.map} +1 -1
- package/dist/app/assets/{WorkloadCalculator-Bdt6qPR3.js → WorkloadCalculator-D2TuKS4B.js} +4 -4
- package/dist/app/assets/{WorkloadCalculator-Bdt6qPR3.js.map → WorkloadCalculator-D2TuKS4B.js.map} +1 -1
- package/dist/app/assets/{calculator-CWfp5Day.js → calculator-CiqKFdRp.js} +2 -2
- package/dist/app/assets/{calculator-CWfp5Day.js.map → calculator-CiqKFdRp.js.map} +1 -1
- package/dist/app/assets/chevron-down-_xIkRLaA.js +2 -0
- package/dist/app/assets/{chevron-down-DbSAem1L.js.map → chevron-down-_xIkRLaA.js.map} +1 -1
- package/dist/app/assets/{clipboard-copy-DgTgnK84.js → clipboard-copy-DCJioqIc.js} +2 -2
- package/dist/app/assets/{clipboard-copy-DgTgnK84.js.map → clipboard-copy-DCJioqIc.js.map} +1 -1
- package/dist/app/assets/{copy-C9t_FzCZ.js → copy-AQdWJCcs.js} +2 -2
- package/dist/app/assets/{copy-C9t_FzCZ.js.map → copy-AQdWJCcs.js.map} +1 -1
- package/dist/app/assets/{crop-B7IxIx6D.js → crop-DOMVp18h.js} +2 -2
- package/dist/app/assets/{crop-B7IxIx6D.js.map → crop-DOMVp18h.js.map} +1 -1
- package/dist/app/assets/{download-65Zuvgwm.js → download-D5RbyXDm.js} +2 -2
- package/dist/app/assets/{download-65Zuvgwm.js.map → download-D5RbyXDm.js.map} +1 -1
- package/dist/app/assets/{eye-ouNo8Nv5.js → eye-CLcZi_uk.js} +2 -2
- package/dist/{assets/eye-ouNo8Nv5.js.map → app/assets/eye-CLcZi_uk.js.map} +1 -1
- package/dist/app/assets/{grid-3x3-U1Nk_ppf.js → grid-3x3-AKmIrhzv.js} +2 -2
- package/dist/{assets/grid-3x3-U1Nk_ppf.js.map → app/assets/grid-3x3-AKmIrhzv.js.map} +1 -1
- package/dist/app/assets/hero/demo-25s-poster.jpg +0 -0
- package/dist/app/assets/hero/demo-25s.mp4 +0 -0
- package/dist/app/assets/hero/demo-25s.webm +0 -0
- package/dist/app/assets/hero/demo-poster.jpg +0 -0
- package/dist/app/assets/hero/demo.mp4 +0 -0
- package/dist/app/assets/hero/demo.webm +0 -0
- package/dist/{assets/index-B8Y8Y-Mi.js → app/assets/index-Bqv2NztY.js} +6 -6
- package/dist/app/assets/index-Bqv2NztY.js.map +1 -0
- package/dist/{assets/index-hyiQFYGv.css → app/assets/index-DGVMa-P7.css} +1 -1
- package/dist/app/assets/{layout-grid-X9Sgull2.js → layout-grid-CpGXUPMR.js} +2 -2
- package/dist/app/assets/{layout-grid-X9Sgull2.js.map → layout-grid-CpGXUPMR.js.map} +1 -1
- package/dist/app/assets/proof/output-01.png +0 -0
- package/dist/app/assets/proof/output-02.png +0 -0
- package/dist/app/assets/proof/output-03.png +0 -0
- package/dist/app/assets/proof/output-04.png +0 -0
- package/dist/app/assets/{rocket-BjOXauLe.js → rocket-IzRFv1aW.js} +2 -2
- package/dist/app/assets/{rocket-BjOXauLe.js.map → rocket-IzRFv1aW.js.map} +1 -1
- package/dist/app/assets/{shield-check-CFNhE_aD.js → shield-check-CngdN3zn.js} +2 -2
- package/dist/{assets/shield-check-CFNhE_aD.js.map → app/assets/shield-check-CngdN3zn.js.map} +1 -1
- package/dist/app/assets/{signIn-CVoSzqCz.js → signIn-Cn6FaQYi.js} +2 -2
- package/dist/{assets/signIn-CVoSzqCz.js.map → app/assets/signIn-Cn6FaQYi.js.map} +1 -1
- package/dist/app/assets/{smartphone-BcQB9gIh.js → smartphone-CpNG_Pfk.js} +2 -2
- package/dist/app/assets/{smartphone-BcQB9gIh.js.map → smartphone-CpNG_Pfk.js.map} +1 -1
- package/dist/app/assets/{terminal-CGhryJds.js → terminal-BbBESfoU.js} +2 -2
- package/dist/{assets/terminal-CGhryJds.js.map → app/assets/terminal-BbBESfoU.js.map} +1 -1
- package/dist/{assets/useAuth-lZZC20Tl.js → app/assets/useAuth-3WWZ3GCD.js} +2 -2
- package/dist/app/assets/{useAuth-lZZC20Tl.js.map → useAuth-3WWZ3GCD.js.map} +1 -1
- package/dist/app/index.html +2 -2
- package/dist/assets/{AgentCli-8e28laT4.js → AgentCli-FWNCO1pb.js} +5 -5
- package/dist/assets/AgentCli-FWNCO1pb.js.map +1 -0
- package/dist/assets/App-yU7CFXCv.js +13 -0
- package/dist/assets/App-yU7CFXCv.js.map +1 -0
- package/dist/assets/{AppReviewAso-OLwc4Rw_.js → AppReviewAso-BYfaP1Lu.js} +3 -3
- package/dist/{app/assets/AppReviewAso-OLwc4Rw_.js.map → assets/AppReviewAso-BYfaP1Lu.js.map} +1 -1
- package/dist/assets/{Cancelled-BoeZQa1S.js → Cancelled-66ixTUrm.js} +2 -2
- package/dist/assets/{Cancelled-BoeZQa1S.js.map → Cancelled-66ixTUrm.js.map} +1 -1
- package/dist/assets/CliPreflightCard-D7utpFux.js +2 -0
- package/dist/assets/{CliPreflightCard-CxYyogdF.js.map → CliPreflightCard-D7utpFux.js.map} +1 -1
- package/dist/assets/CopyButton-CmVmSaYn.js +2 -0
- package/dist/assets/CopyButton-CmVmSaYn.js.map +1 -0
- package/dist/assets/{FastlaneScreenshots-CFT6IiRb.js → FastlaneScreenshots-CRMcetq-.js} +4 -4
- package/dist/{app/assets/FastlaneScreenshots-CFT6IiRb.js.map → assets/FastlaneScreenshots-CRMcetq-.js.map} +1 -1
- package/dist/assets/{FirstExport-B_UDacZm.js → FirstExport-C0WgL4Hl.js} +3 -3
- package/dist/{app/assets/FirstExport-B_UDacZm.js.map → assets/FirstExport-C0WgL4Hl.js.map} +1 -1
- package/dist/assets/{FivePanelFramework-C1z_xUbe.js → FivePanelFramework-BwCRmXIK.js} +3 -3
- package/dist/{app/assets/FivePanelFramework-C1z_xUbe.js.map → assets/FivePanelFramework-BwCRmXIK.js.map} +1 -1
- package/dist/assets/{ForgotPassword-ZQ8U3ejz.js → ForgotPassword-B9_si1tH.js} +2 -2
- package/dist/{app/assets/ForgotPassword-ZQ8U3ejz.js.map → assets/ForgotPassword-B9_si1tH.js.map} +1 -1
- package/dist/assets/Landing-s1ni1n-7.js +2 -0
- package/dist/assets/Landing-s1ni1n-7.js.map +1 -0
- package/dist/assets/{LanguageSwitcher-D93nCuuR.js → LanguageSwitcher-BD9N-dla.js} +2 -2
- package/dist/{app/assets/LanguageSwitcher-D93nCuuR.js.map → assets/LanguageSwitcher-BD9N-dla.js.map} +1 -1
- package/dist/assets/{Legal-Koe7vi6H.js → Legal-D3cAXQXw.js} +2 -2
- package/dist/assets/{Legal-Koe7vi6H.js.map → Legal-D3cAXQXw.js.map} +1 -1
- package/dist/assets/{NotFound-CfY31vMZ.js → NotFound-ClV-6RmP.js} +2 -2
- package/dist/assets/{NotFound-CfY31vMZ.js.map → NotFound-ClV-6RmP.js.map} +1 -1
- package/dist/assets/{Privacy-CaHf2UM7.js → Privacy-DfxgWwC3.js} +2 -2
- package/dist/{app/assets/Privacy-CaHf2UM7.js.map → assets/Privacy-DfxgWwC3.js.map} +1 -1
- package/dist/assets/{SignIn-Cyi2_-Mn.js → SignIn-ghQCKcPe.js} +2 -2
- package/dist/assets/{SignIn-Cyi2_-Mn.js.map → SignIn-ghQCKcPe.js.map} +1 -1
- package/dist/assets/{SignUp-C9QH5po_.js → SignUp-BKB87hd4.js} +2 -2
- package/dist/assets/{SignUp-C9QH5po_.js.map → SignUp-BKB87hd4.js.map} +1 -1
- package/dist/assets/SiteHeader-DxsqNPnt.js +2 -0
- package/dist/assets/SiteHeader-DxsqNPnt.js.map +1 -0
- package/dist/assets/{StickyMobileCta-uyWGVhdc.js → StickyMobileCta-C_x2pUIF.js} +2 -2
- package/dist/assets/{StickyMobileCta-uyWGVhdc.js.map → StickyMobileCta-C_x2pUIF.js.map} +1 -1
- package/dist/{app/assets/StoreAssetsPipeline-BJlnD3kD.js → assets/StoreAssetsPipeline-4hXRONUd.js} +3 -3
- package/dist/assets/{StoreAssetsPipeline-BJlnD3kD.js.map → StoreAssetsPipeline-4hXRONUd.js.map} +1 -1
- package/dist/assets/{Success-B7k7FvJj.js → Success-819qvzmp.js} +2 -2
- package/dist/assets/{Success-B7k7FvJj.js.map → Success-819qvzmp.js.map} +1 -1
- package/dist/assets/{Terms-DgJWOeUD.js → Terms-7k3nA4SU.js} +2 -2
- package/dist/assets/{Terms-DgJWOeUD.js.map → Terms-7k3nA4SU.js.map} +1 -1
- package/dist/assets/{ToolsIndex-xMPoFom9.js → ToolsIndex-CzrxymnM.js} +3 -3
- package/dist/{app/assets/ToolsIndex-xMPoFom9.js.map → assets/ToolsIndex-CzrxymnM.js.map} +1 -1
- package/dist/assets/{WorkloadCalculator-Bdt6qPR3.js → WorkloadCalculator-D2TuKS4B.js} +4 -4
- package/dist/assets/{WorkloadCalculator-Bdt6qPR3.js.map → WorkloadCalculator-D2TuKS4B.js.map} +1 -1
- package/dist/assets/{calculator-CWfp5Day.js → calculator-CiqKFdRp.js} +2 -2
- package/dist/assets/{calculator-CWfp5Day.js.map → calculator-CiqKFdRp.js.map} +1 -1
- package/dist/assets/chevron-down-_xIkRLaA.js +2 -0
- package/dist/assets/{chevron-down-DbSAem1L.js.map → chevron-down-_xIkRLaA.js.map} +1 -1
- package/dist/assets/{clipboard-copy-DgTgnK84.js → clipboard-copy-DCJioqIc.js} +2 -2
- package/dist/assets/{clipboard-copy-DgTgnK84.js.map → clipboard-copy-DCJioqIc.js.map} +1 -1
- package/dist/assets/{copy-C9t_FzCZ.js → copy-AQdWJCcs.js} +2 -2
- package/dist/assets/{copy-C9t_FzCZ.js.map → copy-AQdWJCcs.js.map} +1 -1
- package/dist/assets/{crop-B7IxIx6D.js → crop-DOMVp18h.js} +2 -2
- package/dist/assets/{crop-B7IxIx6D.js.map → crop-DOMVp18h.js.map} +1 -1
- package/dist/assets/{download-65Zuvgwm.js → download-D5RbyXDm.js} +2 -2
- package/dist/assets/{download-65Zuvgwm.js.map → download-D5RbyXDm.js.map} +1 -1
- package/dist/assets/{eye-ouNo8Nv5.js → eye-CLcZi_uk.js} +2 -2
- package/dist/{app/assets/eye-ouNo8Nv5.js.map → assets/eye-CLcZi_uk.js.map} +1 -1
- package/dist/assets/{grid-3x3-U1Nk_ppf.js → grid-3x3-AKmIrhzv.js} +2 -2
- package/dist/{app/assets/grid-3x3-U1Nk_ppf.js.map → assets/grid-3x3-AKmIrhzv.js.map} +1 -1
- package/dist/assets/hero/demo-25s-poster.jpg +0 -0
- package/dist/assets/hero/demo-25s.mp4 +0 -0
- package/dist/assets/hero/demo-25s.webm +0 -0
- package/dist/assets/hero/demo-poster.jpg +0 -0
- package/dist/assets/hero/demo.mp4 +0 -0
- package/dist/assets/hero/demo.webm +0 -0
- package/dist/{app/assets/index-B8Y8Y-Mi.js → assets/index-Bqv2NztY.js} +6 -6
- package/dist/assets/index-Bqv2NztY.js.map +1 -0
- package/dist/{app/assets/index-hyiQFYGv.css → assets/index-DGVMa-P7.css} +1 -1
- package/dist/assets/{layout-grid-X9Sgull2.js → layout-grid-CpGXUPMR.js} +2 -2
- package/dist/assets/{layout-grid-X9Sgull2.js.map → layout-grid-CpGXUPMR.js.map} +1 -1
- package/dist/assets/proof/output-01.png +0 -0
- package/dist/assets/proof/output-02.png +0 -0
- package/dist/assets/proof/output-03.png +0 -0
- package/dist/assets/proof/output-04.png +0 -0
- package/dist/assets/{rocket-BjOXauLe.js → rocket-IzRFv1aW.js} +2 -2
- package/dist/assets/{rocket-BjOXauLe.js.map → rocket-IzRFv1aW.js.map} +1 -1
- package/dist/assets/{shield-check-CFNhE_aD.js → shield-check-CngdN3zn.js} +2 -2
- package/dist/{app/assets/shield-check-CFNhE_aD.js.map → assets/shield-check-CngdN3zn.js.map} +1 -1
- package/dist/assets/{signIn-CVoSzqCz.js → signIn-Cn6FaQYi.js} +2 -2
- package/dist/{app/assets/signIn-CVoSzqCz.js.map → assets/signIn-Cn6FaQYi.js.map} +1 -1
- package/dist/assets/{smartphone-BcQB9gIh.js → smartphone-CpNG_Pfk.js} +2 -2
- package/dist/assets/{smartphone-BcQB9gIh.js.map → smartphone-CpNG_Pfk.js.map} +1 -1
- package/dist/assets/{terminal-CGhryJds.js → terminal-BbBESfoU.js} +2 -2
- package/dist/{app/assets/terminal-CGhryJds.js.map → assets/terminal-BbBESfoU.js.map} +1 -1
- package/dist/{app/assets/useAuth-lZZC20Tl.js → assets/useAuth-3WWZ3GCD.js} +2 -2
- package/dist/assets/{useAuth-lZZC20Tl.js.map → useAuth-3WWZ3GCD.js.map} +1 -1
- package/dist/cancelled/index.html +2 -2
- package/dist/forgot-password/index.html +2 -2
- package/dist/index.html +2 -2
- package/dist/legal/index.html +2 -2
- package/dist/privacy/index.html +2 -2
- package/dist/signin/index.html +2 -2
- package/dist/signup/index.html +2 -2
- package/dist/sitemap.xml +0 -4
- package/dist/success/index.html +2 -2
- package/dist/terms/index.html +2 -2
- package/dist/tools/5-panel-screenshot-framework/index.html +2 -2
- package/dist/tools/agent-cli/index.html +2 -2
- package/dist/tools/app-review-aso/index.html +2 -2
- package/dist/tools/fastlane-screenshots/index.html +2 -2
- package/dist/tools/first-export/index.html +2 -2
- package/dist/tools/index.html +2 -2
- package/dist/tools/screenshot-workload-calculator/index.html +2 -2
- package/dist/tools/store-assets-pipeline/index.html +2 -2
- package/package.json +3 -3
- package/templates/agent/agent-prompt.txt +8 -0
- package/dist/app/assets/AgentCli-8e28laT4.js.map +0 -1
- package/dist/app/assets/App-DU0xew0e.js +0 -13
- package/dist/app/assets/App-DU0xew0e.js.map +0 -1
- package/dist/app/assets/CliPreflightCard-CxYyogdF.js +0 -2
- package/dist/app/assets/CopyButton-CGfuSJtU.js +0 -2
- package/dist/app/assets/CopyButton-CGfuSJtU.js.map +0 -1
- package/dist/app/assets/Landing-DAIP71DD.js +0 -2
- package/dist/app/assets/Landing-DAIP71DD.js.map +0 -1
- package/dist/app/assets/Pricing-BQwtFVbj.js +0 -2
- package/dist/app/assets/Pricing-BQwtFVbj.js.map +0 -1
- package/dist/app/assets/SiteHeader-Dj3gDOnR.js +0 -2
- package/dist/app/assets/SiteHeader-Dj3gDOnR.js.map +0 -1
- package/dist/app/assets/check-BWCiUZ-C.js +0 -2
- package/dist/app/assets/check-BWCiUZ-C.js.map +0 -1
- package/dist/app/assets/chevron-down-DbSAem1L.js +0 -2
- package/dist/app/assets/circle-check-CeakjiXC.js +0 -2
- package/dist/app/assets/circle-check-CeakjiXC.js.map +0 -1
- package/dist/app/assets/hero/cli-build-tree-15s.mp4 +0 -0
- package/dist/app/assets/hero/cli-build-tree-15s.webm +0 -0
- package/dist/app/assets/hero/cli-build-tree-25s.mp4 +0 -0
- package/dist/app/assets/hero/cli-build-tree-25s.webm +0 -0
- package/dist/app/assets/hero/cli-build-tree-poster.png +0 -0
- package/dist/app/assets/hero/demo.gif +0 -0
- package/dist/app/assets/index-B8Y8Y-Mi.js.map +0 -1
- package/dist/assets/AgentCli-8e28laT4.js.map +0 -1
- package/dist/assets/App-DU0xew0e.js +0 -13
- package/dist/assets/App-DU0xew0e.js.map +0 -1
- package/dist/assets/CliPreflightCard-CxYyogdF.js +0 -2
- package/dist/assets/CopyButton-CGfuSJtU.js +0 -2
- package/dist/assets/CopyButton-CGfuSJtU.js.map +0 -1
- package/dist/assets/Landing-DAIP71DD.js +0 -2
- package/dist/assets/Landing-DAIP71DD.js.map +0 -1
- package/dist/assets/Pricing-BQwtFVbj.js +0 -2
- package/dist/assets/Pricing-BQwtFVbj.js.map +0 -1
- package/dist/assets/SiteHeader-Dj3gDOnR.js +0 -2
- package/dist/assets/SiteHeader-Dj3gDOnR.js.map +0 -1
- package/dist/assets/check-BWCiUZ-C.js +0 -2
- package/dist/assets/check-BWCiUZ-C.js.map +0 -1
- package/dist/assets/chevron-down-DbSAem1L.js +0 -2
- package/dist/assets/circle-check-CeakjiXC.js +0 -2
- package/dist/assets/circle-check-CeakjiXC.js.map +0 -1
- package/dist/assets/hero/cli-build-tree-15s.mp4 +0 -0
- package/dist/assets/hero/cli-build-tree-15s.webm +0 -0
- package/dist/assets/hero/cli-build-tree-25s.mp4 +0 -0
- package/dist/assets/hero/cli-build-tree-25s.webm +0 -0
- package/dist/assets/hero/cli-build-tree-poster.png +0 -0
- package/dist/assets/hero/demo.gif +0 -0
- package/dist/assets/index-B8Y8Y-Mi.js.map +0 -1
- 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
|
package/bin/shipscreens.js
CHANGED
|
@@ -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
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
-
|
|
722
|
+
const warning =
|
|
553
723
|
`DEPRECATED: shipscreens build --out <file.zip> will change behavior in a future release.\n` +
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/cli/commands/build.js
CHANGED
|
@@ -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-
|
|
227
|
+
({ chromium } = await import('playwright-chromium'));
|
|
227
228
|
} catch (err) {
|
|
228
229
|
await closeServer();
|
|
229
230
|
throw new Error(
|
|
230
|
-
`Playwright is required for
|
|
231
|
-
`Install
|
|
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
|
-
|
|
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
|
-
|
|
273
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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) => {
|