feat(landing): add image generation showcase - #6904
Conversation
🚀 Landing page previewThis PR is deployed to a Cloudflare Pages preview — not staging or production:
|
mrcfps
left a comment
There was a problem hiding this comment.
Thanks @app/open-design-crew — this is a lovely showcase. The 19-image gallery, prompt modal, membership/model story, and locale wrapper all land cleanly, and the hero/footer download CTAs match the existing solution-page contract.
A few non-blocking maintainability notes below. The page itself works; these are about keeping the copy, mosaic, and discoverability from drifting the next time this route is edited.
🔁 Powered by Looper · runner=reviewer · agent=grok-build · An autonomous AI dev team for your GitHub repos.
| const pageCopy = isZh | ||
| ? { | ||
| pageName: 'AI 图片生成', | ||
| promptLabel: '提示词', | ||
| modelRailTitle: '一个工作区,三种图片模型', | ||
| modelRailBody: '无需单独配置供应商 API Key', | ||
| galleryTitle: '看看 Open Design 能生成什么', | ||
| galleryBody: '这些图片均由 Open Design 实时生成。点击任意案例,即可查看完整提示词。', | ||
| workflowTitle: '从一句话,到最终成图', | ||
| accessTitle: '一个订阅,无需配置供应商 Key', | ||
| accessBody: '登录 Open Design 并开通会员,即可在同一个项目里生成图片、继续调整并保留完整提示词。', | ||
| faqTitle: '图片生成常见问题', | ||
| finalTitle: '把下一条提示词,变成一张图片', | ||
| finalBody: '下载 Open Design,使用会员订阅开始生成图片。', | ||
| dialogTitle: '生成提示词', | ||
| close: '关闭', | ||
| } | ||
| : { | ||
| pageName: 'AI image generation', | ||
| promptLabel: 'Prompt', | ||
| modelRailTitle: 'One workspace, three image models', | ||
| modelRailBody: 'No separate provider API keys', | ||
| galleryTitle: 'See what Open Design can generate', | ||
| galleryBody: 'Every image is generated live in Open Design. Select any example to inspect its complete prompt.', | ||
| workflowTitle: 'From one sentence to a finished image', | ||
| accessTitle: 'One subscription. No provider-key setup.', | ||
| accessBody: 'Sign in with an active membership to generate, refine, and keep every prompt inside the same Open Design project.', | ||
| faqTitle: 'Image generation FAQ', | ||
| finalTitle: 'Turn your next prompt into an image', | ||
| finalBody: 'Download Open Design and start generating with your membership.', | ||
| dialogTitle: 'Generation prompt', | ||
| close: 'Close', | ||
| }; |
There was a problem hiding this comment.
Visible copy is split across two sources, and they have already drifted.
pageCopy hardcodes the section titles and bodies that the template actually renders (galleryTitle, galleryBody, workflowTitle, accessTitle, accessBody, faqTitle, finalTitle, finalBody, dialogTitle, close). IMAGE_GENERATION_COPY already defines the same slots (galleryHeading, galleryLead, howHeading, accessHeading, accessBody, faqTitle, finalHeading, finalBody, promptTitle, close) plus a large unused set (copyPrompt, copied, viewPrompt, generatedLive, liveLabel, modelsEyebrow, modelsHeading, …).
Those two sources already disagree. For example:
pageCopy.galleryBodysays visitors should “inspect its complete prompt”IMAGE_GENERATION_COPY.en.galleryLeadsays “Open any prompt, copy it, and make it your own”pageCopy.accessBody/copy.accessBodyandpageCopy.finalBody/copy.finalBodyalso differ
The template only reads copy.title, copy.description, copy.eyebrow, copy.headingA / headingB, copy.lead, copy.secondaryCta, copy.steps, copy.accessItems, and copy.faq. Everything else in the data file is dead.
This matters because the next copy edit will almost certainly go into IMAGE_GENERATION_COPY and silently miss the page. The leftover “copy it” / copyPrompt strings also describe a copy button that was never wired.
Suggested change: delete pageCopy. Keep one locale map in IMAGE_GENERATION_COPY and render from it. Reconcile the drifted strings first, then drop the unused keys so the data file only contains fields the template reads.
🔁 Powered by Looper · runner=reviewer · agent=grok-build · An autonomous AI dev team for your GitHub repos.
| .image-generation-solution .ig-gallery-card:nth-child(1), | ||
| .image-generation-solution .ig-gallery-card:nth-child(14) { | ||
| grid-column: span 8; | ||
| grid-row: span 2; | ||
| } | ||
|
|
||
| .image-generation-solution .ig-gallery-card:nth-child(2), | ||
| .image-generation-solution .ig-gallery-card:nth-child(3), | ||
| .image-generation-solution .ig-gallery-card:nth-child(6), | ||
| .image-generation-solution .ig-gallery-card:nth-child(7), | ||
| .image-generation-solution .ig-gallery-card:nth-child(8), | ||
| .image-generation-solution .ig-gallery-card:nth-child(15), | ||
| .image-generation-solution .ig-gallery-card:nth-child(16) { | ||
| grid-column: span 4; | ||
| grid-row: span 1; | ||
| } | ||
|
|
||
| .image-generation-solution .ig-gallery-card:nth-child(4), | ||
| .image-generation-solution .ig-gallery-card:nth-child(5), | ||
| .image-generation-solution .ig-gallery-card:nth-child(12), | ||
| .image-generation-solution .ig-gallery-card:nth-child(13) { | ||
| grid-column: span 6; | ||
| grid-row: span 2; | ||
| } |
There was a problem hiding this comment.
Desktop mosaic is load-bearing on example order, not on the layout field the data already has.
Each example already carries layout: 'wide' | 'portrait' | 'square', and index.astro applies ig-gallery-card-${example.layout}. On desktop those classes do not control placement. This block assigns grid-column / grid-row with hardcoded :nth-child(1), :nth-child(14), :nth-child(2,3,6,7,8,15,16), and :nth-child(4,5,12,13). The layout-class rules only appear later, inside the max-width: 720px breakpoint.
That means IMAGE_GENERATION_EXAMPLES order is part of the visual contract, but nothing enforces it. Adding a 20th image, swapping two portraits, or reordering the celestial-world set will silently break the 12-column mosaic. The layout field looks like the source of truth and is not.
Suggested change: drive desktop spans from the layout class (or a data-span derived from example.layout) the same way mobile already does. If this exact 19-card mosaic is intentional, keep the nth-child rules but add a short comment that the example order is load-bearing, and a test that the array length / layout sequence still matches these selectors.
🔁 Powered by Looper · runner=reviewer · agent=grok-build · An autonomous AI dev team for your GitHub repos.
| <Layout | ||
| title={copy.title} | ||
| description={copy.description} | ||
| active="solution" | ||
| jsonLd={jsonLd} | ||
| mainClassName="sub-main container image-generation-main" | ||
| > | ||
| <article class="info-page solution-page image-generation-solution" data-image-generation-page> |
There was a problem hiding this comment.
This new route is not reachable from the existing Image solution surfaces.
The page ships at /solutions/image-generation/ (and locale prefixes), but this PR does not register it in:
apps/landing-page/app/pages/solutions/index.astroUSE_CASES— still only{ key: 'image', slug: 'image' }apps/landing-page/app/_components/site-footer.astroUSE_CASE_HREFS— still/solutions/image/- the header Solution → Image item, which continues to open
/solutions/image/ apps/landing-page/app/pages/solutions/image/index.astro, whose browse/gallery link still points at/plugins/templates/
So a visitor who follows the current Image use-case never sees this showcase. The two URLs will also compete for the same intent.
I would not expand the fixed 6-item useCaseItems nav tuple just for this. A lighter close is: point /solutions/image/’s browse/gallery CTA (exampleHref) at /solutions/image-generation/, and/or add a hub card on /solutions/ that links here. That keeps the existing Image nav entry and makes the showcase discoverable.
🔁 Powered by Looper · runner=reviewer · agent=grok-build · An autonomous AI dev team for your GitHub repos.
mrcfps
left a comment
There was a problem hiding this comment.
@app/open-design-crew thank you for the follow-up polish on this showcase — the simplified hero CTAs and refined prompt-dialog close control look clean and match the existing solution-page contract.
I re-checked this head (a2b0580) end to end: locale wrapper, 19-example gallery + prompt modal wiring, membership/model FAQ copy, download/GitHub CTA hierarchy, asset/id alignment, and the solution-page count test bump. No new correctness, safety, or test issues on the changed ranges.
The three earlier non-blocking threads (single copy source, mosaic order vs layout, and discoverability from /solutions/image/) are still open as follow-ups and do not block merge. Nice work shipping this page 🙌
🔁 Powered by Looper · runner=reviewer · agent=opencode · An autonomous AI dev team for your GitHub repos.
|
@app/open-design-crew Thanks for the contribution. I completed QA validation for this PR. QA Acceptance Record Scope:
Scope-to-evidence:
Verified:
Not verified:
Risks / notes:
Conclusion:
|
Why
Open Design now supports image generation through an active membership, but the public site did not have a dedicated solution page that explains the capability or demonstrates the range of results. This adds a production landing page aligned with the existing solution-page information architecture so visitors can understand the supported models, inspect complete prompts, and download the desktop client.
What users will see
/solutions/image-generation/page, including/zh/solutions/image-generation/Surface area
Screenshots
Validated locally at
http://127.0.0.1:17674/zh/solutions/image-generation/. The per-PR landing-page preview will be generated by CI for visual review.Bug fix verification
Not a bug fix.
Validation
pnpm guardpnpm --filter @open-design/landing-page test— 137 passedpnpm --filter @open-design/landing-page build— 6631 pages built