feat(store-screenshots): add App Store / Google Play screenshot workspace (phase 1) - #6463
feat(store-screenshots): add App Store / Google Play screenshot workspace (phase 1)#6463sucaifenxiang wants to merge 41 commits into
Conversation
…nshot-phase1 # Conflicts: # apps/daemon/src/db.ts # apps/daemon/src/server-context.ts # apps/web/src/components/ProjectView.tsx # apps/web/src/i18n/locales/ar.ts # apps/web/src/i18n/locales/de.ts # apps/web/src/i18n/locales/es-ES.ts # apps/web/src/i18n/locales/fa.ts # apps/web/src/i18n/locales/fr.ts # apps/web/src/i18n/locales/hu.ts # apps/web/src/i18n/locales/id.ts # apps/web/src/i18n/locales/it.ts # apps/web/src/i18n/locales/ko.ts # apps/web/src/i18n/locales/pl.ts # apps/web/src/i18n/locales/pt-BR.ts # apps/web/src/i18n/locales/ru.ts # apps/web/src/i18n/locales/th.ts # apps/web/src/i18n/locales/tr.ts # apps/web/src/i18n/locales/uk.ts # apps/web/src/i18n/locales/zh-TW.ts # apps/web/src/state/projects.ts # apps/web/tests/state/projects.test.ts # pnpm-lock.yaml
|
Thanks @sucaifenxiang — this is a substantial new surface across daemon, web, contracts, templates, and e2e. Since it's opened as a draft, we'll hold off on code review until it's ready for review.
|
|
Marked ready for review. The branch is synced onto the latest |
|
Hey @sucaifenxiang, thanks for rebasing this onto the latest 🧪 This PR changes a real user-facing workflow across project creation, the store-screenshot workspace, daemon routes, and export/generation paths, so it will need a manual QA pass before merge. Please hold off self-merging for now; we'll loop QA in once it's merge-ready and product/design have signed off. |
PerishCode
left a comment
There was a problem hiding this comment.
This phase-one workspace has blocking gaps in the project authority boundary, the required asset-input flow, and the export renderer. The new focused suites otherwise cover a substantial amount of the API, persistence, CLI, and UI behavior, but the renderer determinism test also fails reproducibly on this head.
🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.| if ( | ||
| typeof projectId !== 'string' | ||
| || !projectId | ||
| || !ctx.projectStore.getProject(ctx.db, projectId) |
There was a problem hiding this comment.
Route these endpoints through the authoritative project access gate, not a raw row-existence check. requireProject only proves that the id exists in local SQLite; unlike the project data-plane routes, it never calls the injected authorizeProjectRequest/workspace authority seam. As a result, every new read route and every local-request mutation can access a Workspace-bound project without verifying that the asserted member still owns or may mutate it. The web client compounds this by sending none of the workspaceProjectHeaders used by the rest of the project surface. Please inject the shared project authorization dependency here, apply read versus mutation capability checks to all store-screenshot routes, and thread the active Workspace context through the web API calls (with route tests for wrong workspace, revoked membership, and non-owner mutation).
| <Select | ||
| aria-label={t('storeScreenshots.productScreenshot')} | ||
| value={page.screenshotAssetId ?? ''} | ||
| disabled={locks.has('screenshot') || document.assets.length === 0} |
There was a problem hiding this comment.
Add the product-screenshot upload action to the shipped workspace before relying on this selector. Every newly created document starts with assets: [], this control is therefore disabled, and no component in apps/web/src/features/store-screenshots calls the new multipart asset endpoint. That means a UI user cannot satisfy the phase-one acceptance requirement to upload and reference real screenshots (or a logo); the Playwright test hides the gap by uploading through page.request before opening the page. Please expose a file input/drop action backed by the shared API helper, refresh the returned document/version after upload, provide the corresponding logo assignment if logo is in phase-one scope, and change the UI test to perform the upload through the visible entry point.
| ? `<rect x="${number(width * 0.1)}" y="${number(height * 0.07)}" width="${number(width * 0.2)}" height="${number(height * 0.035)}" fill="${escapeXml(accent)}" rx="${number(width * 0.0175)}"/>` | ||
| : ''; | ||
|
|
||
| return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${backgroundSvg}${labelSvg}<text x="${number(headlineX)}" y="${number(height * 0.17)}" fill="${escapeXml(text)}" font-size="${number(headlineSize)}" text-anchor="${headlineAnchor}">${escapeXml(page.headline)}</text>${bodySvg}${screenshot}</svg>`; |
There was a problem hiding this comment.
Use one bounded text-layout rule for the editor and exported image. The editor renders headline/body as Fabric IText with an 80% width, but this export emits each value as one unbounded SVG <text> node; the contract also puts no useful length limit on generated or manually entered copy. A headline or body that wraps in fine-edit can therefore run off the exported canvas as a single line, so the reviewed preview does not represent the durable ZIP output. Please implement shared line breaking/line-height/max-line behavior (or an equivalent bounded SVG layout) and add renderer fixtures for long Latin text, CJK text, and explicit newlines that assert the pixels remain within the intended text box and match the editor's layout decisions.
| const derivedPage = deriveStoreScreenshotPage(document, pageId, platform); | ||
| const svg = compileStoreScreenshotSvg(derivedPage); | ||
| const composites = await assetComposites(document, pageId, platform, options); | ||
| const pipeline = sharp(Buffer.from(svg)); |
There was a problem hiding this comment.
Make rasterization deterministic in the supported fontless/headless environment before shipping the hash-bearing export contract. On this head, the focused renderer suite fails reproducibly at both renders deterministic ... PNGs cases: two sequential calls for the same document/platform produce different buffers, alongside Fontconfig error: Cannot load default config file. This makes the manifest SHA and ZIP bytes depend on host font discovery and leaves a newly added required test red. Please bundle/register a known font (or convert text through a deterministic owned layout path) before passing the SVG to Sharp, then verify apps/daemon/tests/store-screenshot-renderer.test.ts passes in a clean environment without system fontconfig; keep the byte-equality assertions as the acceptance signal.
|
Thanks for getting this onto the latest @PerishCode's blocking review already covers the actionable gaps on this head, especially the project authority boundary, the missing in-product asset upload flow, and the renderer/export mismatches. Once those threads are addressed, could you also add a short |
Why
Launch Studio needs a focused workspace for producing App Store / Google Play
phone-screenshot listings. Today the flow is manual: designers compose each
frame in a graphics tool, then export and resize per platform. This PR ships
the first phase of a dedicated store-screenshot project type that turns the
product brief, real screenshots, logo, and brand assets into a 4-page minimal
document, then lets the user generate, fine-edit, version, and export the
result — with a fully manual fallback when no AI provider is configured.
What users will see
intent: store-screenshot: after creation theproject initializes a 4-page store document (minimal-center template).
per-page cards, fine-edit headline/body fields, changes review dialog,
version history, and ZIP export.
odCLI surface:od projectreads the same store-screenshot metadata; thedaemon exposes
/api/projects/:id/store-screenshots(create/read/update,assets, generate job, render, export).
BYOK provider and fails closed with a clear message otherwise.
Surface area
od projectand daemon routes/api/projects/:id/store-screenshotsendpoints andStoreScreenshotDocumentResponsecontractdesign-templates/store-screenshotstemplate assetspackages/store-screenshot; app-level deps: fabric, jszip, pngjs, sharp (no root package.json change)Acceptance status
specs/current/launch-studio-store-screenshot-acceptance.md复跑记录 2026-08-05);harness and evidence are ready for rerun once quota is restored.
Screenshots
Pending — workspace entry-point screenshots will be attached once the real-AI
path can be exercised end-to-end.