Skip to content

feat(core,exporters,desktop): end-to-end first demo + HTML export#4

Merged
hqhq1025 merged 1 commit intomainfrom
wt/first-demo
Apr 18, 2026
Merged

feat(core,exporters,desktop): end-to-end first demo + HTML export#4
hqhq1025 merged 1 commit intomainfrom
wt/first-demo

Conversation

@hqhq1025
Copy link
Copy Markdown
Collaborator

Goal

Make the README's "first demo" actually work end-to-end IF the user provides
an API key:

  1. Pick the Calm Spaces meditation app starter
  2. Send → an HTML mockup renders in the sandboxed iframe
  3. Click Export → HTML → save → opens in the browser as a self-contained file
  4. Click Export → PDF / PPTX / ZIP → loud toast: "… ships in Phase 2"

Files in

New

  • packages/templates/src/system/index.tsSYSTEM_PROMPTS.designGenerator
  • packages/templates/src/system/design-generator.md — reviewable prompt source
  • packages/exporters/src/html.ts — real exportHtml() with Tailwind injection + prettifier
  • packages/exporters/src/{pdf,pptx,zip}.ts — Phase-2 stubs that throw CodesignError(EXPORTER_NOT_READY)
  • apps/desktop/src/main/exporter-ipc.tscodesign:export handler over dialog.showSaveDialog
  • apps/desktop/src/renderer/src/components/PreviewToolbar.tsx — Export dropdown + toast slot
  • packages/core/src/generate.test.ts — vitest with mocked provider
  • examples/calm-spaces/README.md — demo doc

Modified

  • packages/core/src/index.ts — pulls system prompt from templates; collapsed duplicated artifact-extraction loop into a collect() helper
  • packages/exporters/src/index.ts + package.json — top-level exportArtifact() that lazy-imports format modules; ./html subpath export added
  • apps/desktop/src/main/index.ts — one new line: registerExporterIpc(() => mainWindow)
  • apps/desktop/src/preload/index.ts — single new method: export(payload)
  • apps/desktop/src/renderer/src/store.ts — additive: VITE_OPEN_CODESIGN_DEV_KEY fallback + exportActive(format) + toast slot
  • apps/desktop/src/renderer/src/App.tsx — single import + <PreviewToolbar /> above the iframe (layout untouched for wt/preview-ux)

Files NOT touched

Per worktree boundary: apps/desktop/src/main/{config,keychain,onboarding-ipc,locale-ipc}.ts,
apps/desktop/src/renderer/src/onboarding/**, all preview-ux components
(Toast, Settings, CommandPalette, ThemeToggle, TopBar, Sidebar,
PreviewPane, preview/{Empty,Loading,Error}State.tsx, hooks/useKeyboard.ts),
packages/i18n/**, packages/ui/src/components/{IconButton,Tooltip}.tsx,
packages/{runtime,providers,artifacts,shared}/**.

NO new prod deps

Confirmed:

  • packages/exporters/package.json — no new dep
  • packages/templates/package.json — no new dep
  • packages/core/package.json — only added @open-codesign/templates (workspace internal)
  • apps/desktop/package.json — only added @open-codesign/exporters (workspace internal); uses Electron's built-in dialog

Lockfile diff is workspace edges only. Install size budget unaffected.

Acceptance test outcomes

  • pnpm install clean
  • pnpm -r typecheck green across 9 workspaces (incl. desktop main + web)
  • pnpm lint green (biome check, 61 files, 0 errors, 0 warnings)
  • pnpm -r test green: 3 new tests in packages/core/src/generate.test.ts (empty-prompt error, artifact extraction shape, system-prompt wiring) + existing artifact + runtime suites
  • Manual smoke (renderer wiring): VITE_OPEN_CODESIGN_DEV_KEY is read at build time via import.meta.env, falls through to existing assistant-message guidance when unset

A reference exported sample will be attached to this PR thread once a key is plumbed in CI; the IPC + exporter logic is unit-covered today.

Integration notes for parallel worktrees

wt/onboarding — when real keychain plumbing lands:

  1. In apps/desktop/src/renderer/src/store.ts, replace the DEV_API_KEY constant + the if (!apiKey) guidance branch with the keychain-fetched key (await the new IPC).
  2. Delete the VITE_OPEN_CODESIGN_DEV_KEY mention in examples/calm-spaces/README.md.
  3. The existing fallback is intentionally guarded by import.meta.env, so leaving the env var unset is harmless until that integration ships.

wt/preview-uxPreviewToolbar lives in apps/desktop/src/renderer/src/components/, the directory it claims. If it migrates to a PreviewPane shell, simply move the single <PreviewToolbar /> JSX line into the new container. The component itself depends only on the store; no layout assumptions baked in. The toast slot in the store (toastMessage / dismissToast) deliberately mirrors what the Toast component will need so the two can be merged trivially.

wt/i18n — all user-visible strings in PreviewToolbar and the examples/ README are short and English-only; they're easy to swap to t() once the i18n keys exist. The Phase-2 messaging ("… ships in Phase 2") is a stable string suitable for the i18n catalogue.

Hard rules checklist

  • No silent fallbacks — every error path throws CodesignError with a structured code
  • All UI uses var(--color-*) tokens; no hardcoded colors / fonts / px
  • TypeScript strict; no any; bracket notation; import type for types
  • Conventional commit, signed (DCO)
  • No new prod deps
  • pnpm -r typecheck && pnpm lint && pnpm -r test all green from worktree root

Wires the full prompt → artifact → preview → export loop so the README's
"first demo" actually produces a design when an API key is provided.

- packages/templates: externalize the design-generator system prompt as
  SYSTEM_PROMPTS.designGenerator with a sibling design-generator.md for
  reviewable diffs. Replaces the inline string previously hard-coded in
  packages/core. Prompt embeds the research-backed Claude Design rules
  (single artifact, Tailwind CDN, semantic HTML, CSS variable tokens,
  WCAG AA, no lorem ipsum).
- packages/core: pull SYSTEM_PROMPTS.designGenerator from templates,
  collapse the duplicated artifact-extraction loop into a `collect()`
  helper, add generate.test.ts (mocks the providers boundary, asserts
  empty-prompt error, artifact extraction shape, system-prompt wiring).
- packages/exporters: real exportHtml() that ensures a doctype, injects
  the Tailwind CDN tag if missing, stamps a generator meta/banner, and
  pretty-prints. PDF / PPTX / ZIP each throw CodesignError with code
  EXPORTER_NOT_READY ("ships in Phase 2") — no silent fallbacks
  (PRINCIPLES §10). Top-level exportArtifact() dispatches lazily so
  unused formats stay out of the cold-start bundle (PRINCIPLES §1).
- apps/desktop: codesign:export IPC backed by Electron dialog
  showSaveDialog, validates payload via CodesignError, propagates
  Phase-2 errors loudly. Preload exposes window.codesign.export().
  Store gains exportActive(format) + a toast slot. PreviewToolbar
  renders an Export dropdown with HTML enabled and PDF/PPTX/ZIP
  disabled with "Coming in Phase 2" tooltips.
- TIER 1 / dev-only fallback: store reads VITE_OPEN_CODESIGN_DEV_KEY
  so the demo runs before wt/onboarding lands real keychain plumbing.
  Marked clearly for removal in the integration commit.
- examples/calm-spaces: README documents the demo + expected behaviour
  + intentional loud failure modes.

No new third-party dependencies. All UI uses var(--color-*) tokens.

Acceptance test (manual):
  VITE_OPEN_CODESIGN_DEV_KEY=sk-ant-... \
    pnpm --filter @open-codesign/desktop dev
  → click "Calm Spaces meditation app" → Send → iframe renders
  → Export → HTML → /tmp/out.html → open in browser
  → Export → PDF → toast "PDF export ships in Phase 2"

Signed-off-by: Haoqing Wang <1506751656@qq.com>
@hqhq1025 hqhq1025 merged commit 1867f38 into main Apr 18, 2026
4 of 7 checks passed
@hqhq1025 hqhq1025 deleted the wt/first-demo branch April 18, 2026 04:38
hqhq1025 added a commit that referenced this pull request Apr 18, 2026
…rate uiux iterations

The squash-merge of #4 (first-demo) accidentally pushed conflict markers in
main/index.ts, preload/index.ts, App.tsx, store.ts. This commit:

- Properly merges onboarding IPC + exporter IPC registration in main/index.ts
- Cleans preload/index.ts (one bogus marker)
- Cleans App.tsx (Onboarding gate + PreviewToolbar both retained)
- Adds Wordmark component + token additions from the in-flight UIUX iterator
- Bumps onboarding/Welcome polish in flight

All checks green: pnpm install, pnpm -r typecheck pass.

Signed-off-by: hqhq1025 <1506751656@qq.com>
hqhq1025 added a commit that referenced this pull request Apr 22, 2026
…opencode banner)

Three round-4 items closed at once — they all touch the import UX but
are small, so one commit keeps the diff readable:

1. (#3) parseDotEnv malformed-line warnings. The old parser silently
   dropped `GEMINI_API_KEY value` (space instead of `=`) with zero
   signal. Split into parseDotEnvLines (returns {vars, skipped}) +
   parseDotEnv (thin wrapper preserving the old signature). In
   readGeminiCliConfig, scan each file's skipped lines for anything
   that LOOKS like a GEMINI_API_KEY declaration but is missing `=`
   and surface a targeted warning — works even when the key
   eventually resolves from shell env, so the user knows their
   .env line is broken.

2. (#4) detectChatgptSubscription test coverage. Export the function
   and accept an optional `authPath` test-seam param. Four tests
   cover: chatgpt auth_mode → true, other auth_mode → false, ENOENT
   → false (silent), malformed JSON → false (loud, goes through the
   new logger call from the prior commit).

3. (#5) opencode banner shows provider names, not just a count.
   Before: "OpenCode config detected — import 4 providers?" left the
   user guessing WHICH four. Now: "OpenCode detected — import 4
   providers (OpenCode · Anthropic, OpenCode · OpenAI, OpenCode ·
   Google +1 more)?". Providers ride along in the banner state as
   `providerLabels: string[]`.

No functional regression; 779 → 787 desktop tests (+8).

Signed-off-by: hqhq1025 <1506751656@qq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant