Commit graph

1209 commits

Author SHA1 Message Date
lefarcen
88dee44892
feat(analytics): always-on $exception capture with early window hooks (#2521)
PostHog Error tracking was missing the vast majority of real exceptions:

  1. posthog-js's capture_exceptions: true is silenced by opt_out_capturing,
     so every opted-out user vanished from the error feed even though we
     could perfectly safely keep collecting their stacks (the consent
     toggle's user copy gates analytics, not safety telemetry).
  2. posthog-js is dynamically imported only after /api/analytics/config
     resolves AND the user has consented. Errors thrown during the first
     1-2 seconds (React hydration, early effects) had no listener to
     catch them.

Net effect: 14d $exception count was 54 events / 10 users across ~5k DAU,
producing the misleading 99.93% crash-free curve in PostHog's dashboard.

This PR makes exception capture independent of both gates:

  - apps/web/src/analytics/error-tracking.ts (new): own window.error +
    unhandledrejection handlers, in-memory buffer (capped at 50 entries),
    direct fetch to https://<host>/i/v0/e/ with the public phc_ key. Same
    scrub layer as the posthog-js path so file paths still get redacted.
  - apps/web/app/[[...slug]]/client-app.tsx: installErrorHandlers() at
    module-load, before React or any feature code can throw.
  - apps/web/src/analytics/provider.tsx: bootstrapExceptionTracking() in
    the identity useEffect, parallel to getAnalyticsClient() — runs
    regardless of consent state, fetches /api/analytics/config, hands the
    phc_ key + host + distinctId to the error tracker so buffered events
    can flush.
  - apps/web/src/analytics/client.ts: capture_exceptions: false so
    posthog-js stops also emitting $exception (would have produced
    duplicate events server-side); also re-bridges the error-tracking
    context inside the loaded() callback so future events inherit the
    fully-resolved appVersion / sessionId.
  - apps/daemon/src/server.ts + packages/contracts: /api/analytics/config
    now returns key + host even when consent=false. enabled still reflects
    only the analytics consent toggle (posthog-js full autocapture stays
    off when enabled=false), but the always-on error tracker can read key
    directly. Forks without POSTHOG_KEY still get key=null and the whole
    pipeline becomes a no-op — fork-safe by construction.
  - apps/web/src/analytics/scrub.ts: regex fix so packaged-mac paths like
    /Applications/Open Design.app/Contents/Resources/apps/web/... (which
    contain a space) get fully rewritten to app://apps/web/...; previously
    the [^\s] guard stopped at 'Open' and leaked the install dir.

Validation:

  - pnpm --filter @open-design/web typecheck: pass
  - pnpm --filter @open-design/web test: 199 files / 1823 tests pass
    (includes 8 new error-tracking.test.ts cases for buffer cap, hook
    install, scrub, and direct dispatch)
  - pnpm --filter @open-design/daemon test: 250 files / 2971 tests pass
  - pnpm guard: pass

After release/v0.8.0 ships and rolls out, expect the crash-free curve to
drop from the artificial 99.93% to a realistic 95-98% — that's not a
regression, it's the first time we're measuring it.
2026-05-21 13:07:26 +08:00
lefarcen
4f70893b18 Merge branch 'release/v0.8.0' of github.com:nexu-io/open-design into release/v0.8.0 2026-05-21 11:59:46 +08:00
lefarcen
c4a891b184 Merge origin/main into release/v0.8.0 2026-05-21 11:56:39 +08:00
lefarcen
ebf4a3ffca
feat(release): upload browser sourcemaps to PostHog for packaged builds (#2508)
* i18n: add translations for media provider coming soon section (#2415)

* i18n: add translations for media provider coming soon section

- Add 'settings.mediaProviderComingSoonHint' key to all 19 locales
- Replace hardcoded English strings in SettingsDialog.tsx with i18n keys
- Reuse existing 'tasks.comingSoon' and 'settings.agentInstall.docs' keys
- Resolves TODO(i18n) comment at line 5091

* fix: escape single quotes in translation strings

* fix: escape all single quotes in English translation string

* feat(release): upload browser sourcemaps to PostHog for packaged builds

Next.js was emitting minified JS with no browser sourcemaps, so PostHog
Error Tracking surfaces frames like fO / fz / s4 / tD instead of real
file:line locations. This wires up the full pipeline:

- apps/web/next.config.ts: enable productionBrowserSourceMaps so next build
  emits .js.map alongside each chunk.
- tools/pack/src/web-sourcemaps.ts: new helper that runs after next build
  and before any packaging step copies the web output into the Electron
  resources. Uses @posthog/cli to inject chunk IDs and upload sourcemaps
  to PostHog, then ALWAYS strips every .map under .next/static so source
  never ships inside an installer (saves ~14 MB per packaged image too).
- tools/pack/src/{mac/workspace,win/app,linux}.ts: call processWebSourcemaps
  immediately after the @open-design/web build step.
- tools/pack/src/config.ts: read POSTHOG_CLI_API_KEY + POSTHOG_CLI_PROJECT_ID
  (with POSTHOG_PERSONAL_API_KEY / POSTHOG_PROJECT_ID aliases) and expose
  them on ToolPackConfig with the same shape as the existing posthogKey /
  posthogHost fields.
- .github/workflows/release-{beta,preview,stable}.yml: pass the new secrets
  through so all three release channels symbolicate stacks.

When the API key is missing (PR builds, forks, local contributor builds),
the helper logs and skips the upload — but still strips .map files. The
strip step is unconditional because shipping a sourcemap is equivalent to
shipping the source.

Adds tools/pack/tests/web-sourcemaps.test.ts covering: missing chunks dir
silently noop, no-map noop, strip-only path when credentials are absent,
recursive walker for nested subdirectories. CLI happy path is left to the
release workflow itself.

Required follow-up (cannot push from code): add a repo secret named
POSTHOG_CLI_API_KEY (the phx_ personal API key) and a repo var named
POSTHOG_CLI_PROJECT_ID (the numeric project id, 420348 for our project)
in nexu-io/open-design settings before merging.

* fix(web-sourcemaps): use management host for CLI, not ingest host

POSTHOG_HOST is the ingest URL (us.i.posthog.com) used by the runtime SDK
to POST events to /capture/. The @posthog/cli sourcemap upload talks to
the **management** API (us.posthog.com) and gets a 404 on the ingest
host. The two are not interchangeable.

Adds a separate `posthogCliHost` field on ToolPackConfig sourced from
POSTHOG_CLI_HOST (with no fallback to POSTHOG_HOST). When the env is
unset the @posthog/cli defaults to the US Cloud app host on its own,
which is correct for our project — so this PR doesn't need a new repo
variable for it.

---------

Co-authored-by: Nicholas-Xiong <2482929840@qq.com>
2026-05-21 11:48:57 +08:00
open-design-bot[bot]
5bf128ebdc
Update docs/assets/github-metrics.svg (#2493)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-21 11:46:41 +08:00
ashleyashli
86dafa9be8
feat(landing): add 19-locale URL routing with full home translations (#2408)
* feat(landing): add 19-locale URL routing with full home translations

Adds locale-prefixed routes (/zh-CN/, /ja/, /de/, …) for 18 non-default
locales while keeping English as the only unprefixed canonical. Generates
proper hreflang + og:locale, points hreflang="en" / x-default at the
unprefixed canonical, and serves localized RSS and plugin search JSON
under each prefix.

Adds a visible language switcher (globe pill + native names) on every
page, replacing the small topbar dropdown. Native-name menu, current
locale marked aria-current, closes on outside click / Escape, only one
open at a time.

Adds app/_lib/home-copy.ts as the source of truth for marketing copy
on the landing page, with full translations for zh-CN, zh-TW, ja, ko,
de, fr, es-ES, pt-BR. Remaining locales (it, pl, hu, ru, uk, tr, ar, fa,
th, id) fall back to English for marketing copy while still getting
fully localized chrome.

Extracts the IntersectionObserver reveal + GitHub stats + wire ticker
script into _components/home-enhancer.astro so localized homepages
animate in the same way as the canonical home.

- New: app/_lib/i18n.ts, app/_lib/home-copy.ts
- New: app/_components/home-enhancer.astro, locale-switcher-enhancer.astro
- New: app/pages/[locale]/{index,[...path]}.astro, blog/rss.xml.ts,
  plugins/search.json.ts

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(landing): bound localized routing to listing pages only

Detail pages (skills/<slug>, blog/<id>, systems/<slug>, templates/<slug>,
craft/<slug>, plugins/<slug>) no longer fan out across 18 prefixed
locales — they stay at their canonical English URLs. Localized chrome
on listing pages links straight to those English detail URLs.

Generated page count drops from ~6,000+ to ~1,800 and the landing-page
CI build returns to ~50s. Localized homes, listings, and filter index
pages (skills/mode/*, skills/scenario/*, systems/category/*) are all
still produced per locale.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: ashley li <ashleyli@ashleydeMacBook-Air-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 11:36:15 +08:00
chaoxiaoche
6359132d04
Polish Design Systems settings gallery controls (#2392)
* Polish design systems gallery settings

* Address design systems settings review

---------

Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
2026-05-21 11:30:15 +08:00
Nicholas-Xiong
7b80e3e85a
i18n: add translations for media provider coming soon section (#2415)
* i18n: add translations for media provider coming soon section

- Add 'settings.mediaProviderComingSoonHint' key to all 19 locales
- Replace hardcoded English strings in SettingsDialog.tsx with i18n keys
- Reuse existing 'tasks.comingSoon' and 'settings.agentInstall.docs' keys
- Resolves TODO(i18n) comment at line 5091

* fix: escape single quotes in translation strings

* fix: escape all single quotes in English translation string
2026-05-21 10:56:38 +08:00
lefarcen
f5f8937421 Merge origin/main into release/v0.8.0
Conflict resolved by taking origin/main:

- apps/web/src/components/EntryNavRail.tsx  design-systems rail
  button icon name palette-filled (release-side) -> blocks (main);
  main's icon swap is part of the more recent design-systems rail
  pass.
2026-05-21 10:52:08 +08:00
Marc Chan
c45c5c9764
fix(ci): align visual selectors and nix hashes (#2471)
* fix(ci): align visual selectors and nix hashes

* fix(ci): add strict PR visual verification

* fix(ci): repair visual-home captures

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)
2026-05-21 10:45:37 +08:00
Chris Tam
7b1cc16988
fix(nix): force http:// scheme on bundled caddy site address (#2485)
A bare `host:port` site address lets Caddy pick the listener scheme by
port heuristic, which fights `auto_https off` and surfaces as TLS errors
when the browser hits plain HTTP on a non-standard port. Hardcode the
`http://` prefix in both the Home Manager and NixOS Caddyfile templates
— the bundled proxy is plaintext-only by design, so users who need TLS
run their own front-end with `webFrontend.enable = false`.
2026-05-21 10:44:51 +08:00
Eli-tangerine
ce95266586
[codex] Polish home composer working-directory controls (#2468)
Some checks failed
visual-baseline / Capture visual baselines (push) Waiting to run
ci / Detect CI change scopes (push) Successful in 1s
nix-check / build (push) Failing after 3s
ci / Preflight (push) Failing after 2s
ci / Core package tests (push) Failing after 1s
ci / Tools workspace tests (push) Failing after 1s
ci / Daemon workspace tests (1/2) (push) Failing after 1s
ci / Daemon workspace tests (2/2) (push) Failing after 1s
ci / Web workspace tests (push) Failing after 1s
ci / E2E vitest (push) Failing after 1s
ci / Playwright critical (starters) (push) Failing after 1s
ci / Playwright critical (core) (push) Failing after 1s
ci / Build workspaces (push) Failing after 1s
ci / App workspace tests (push) Failing after 0s
ci / Validate workspace (push) Failing after 0s
ci / Runtime trace (push) Has been skipped
* Polish design system home flows

* Polish home prompt presets

* Polish home working directory controls

* test: align home hero chrome smoke

* fix: stabilize home composer ci checks

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
2026-05-21 00:22:46 +08:00
lefarcen
722ddfa235 Merge origin/main into release/v0.8.0
Conflicts resolved by taking origin/main on both files. Root cause:
main's PR #2460 (fix(landing): align logo.webp with brand icon) changed
HomeHero.tsx's .home-hero__brand-mark to render <img src=/app-icon.svg>
instead of an inlined <HeroBrandIcon /> SVG, and bundled the matching
CSS (26px round badge with bg-panel + border + padding 2px) plus a
gap/font-size tune. The release-side visual-refresh CSS still targeted
the SVG layout (38px square, transparent, inset SVG selector). Keeping
release's CSS would leave main's <img> unstyled.

- apps/web/src/styles/home/home-hero.css  three blocks, all taken from
  main: .home-hero__brand gap 8px, .home-hero__brand-mark redesigned for
  <img> child, .home-hero__brand-name font-size 16px.
- apps/web/src/index.css  two blocks, both taken from main: workspace
  tab close column 22px and .workspace-tab__close 18x18 (paired
  tune-down of tab UI spacing).
2026-05-20 22:28:38 +08:00
Marc Chan
e727168676
chore(ci): expand visual regression coverage (#2381)
Some checks failed
ci / Runtime trace (push) Blocked by required conditions
visual-baseline / Capture visual baselines (push) Waiting to run
ci / Detect CI change scopes (push) Successful in 0s
landing-page-ci / Validate landing page (push) Failing after 2s
landing-page-deploy / Deploy landing page (push) Has been skipped
nix-check / build (push) Failing after 2s
ci / Preflight (push) Failing after 1s
ci / Core package tests (push) Failing after 1s
ci / Tools workspace tests (push) Failing after 1s
ci / Daemon workspace tests (1/2) (push) Failing after 1s
ci / Daemon workspace tests (2/2) (push) Failing after 2s
ci / Web workspace tests (push) Failing after 1s
ci / E2E vitest (push) Failing after 2s
ci / Playwright critical (starters) (push) Failing after 1s
ci / Playwright critical (core) (push) Failing after 1s
ci / Build workspaces (push) Failing after 1s
ci / App workspace tests (push) Failing after 0s
ci / Validate workspace (push) Failing after 14m14s
* Improve visual diff annotations

* Expand visual regression coverage

* fix(ci): cap visual diff canvas pixels

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)

* Stabilize visual regression screenshots

* test(e2e): stub routines for visual snapshot

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)

* Expand visual regression surfaces

* fix(e2e): order design system visual mocks

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)

* fix(e2e): order design system visual mocks

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)

* Tune visual diff box stroke

* fix(e2e): stabilize visual detail mocks

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)

* fix(e2e): harden visual diff box helpers

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)

* fix(web): preserve deep-linked project bootstrap

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)

* fix(e2e): stub automation task mocks

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)
2026-05-20 22:25:41 +08:00
PerishFire
33b20aa9cb
Fix Windows reinstall detection and duplicate plugin nav (#2466)
* Fix Windows silent reinstall detection

* Remove duplicate entry plugins nav
2026-05-20 22:08:44 +08:00
Eli-tangerine
8193981511
Keep PR 2400 changes without folder pickers (#2462)
* feat(daemon): add project working directory management and editor hand-off functionality

- Introduced new flags for project commands to manage working directories, including `--working-dir` and `--dir`.
- Implemented API routes for listing available editors and opening projects in selected editors.
- Added a hand-off button in the ChatPane header to facilitate opening project folders in local applications.
- Enhanced the HomeHero component to include working directory and design system settings, improving user experience in project creation.
- Created HomeHeroSettingsChips component for inline management of working directory and design system selection.

* feat(chat): implement voice transcription proxy and enhance UI components

- Added a new API route for voice transcription using OpenAI's `/audio/transcriptions` endpoint, allowing users to send audio blobs directly for transcription.
- Integrated multer for handling audio file uploads in memory, ensuring efficient processing without disk storage.
- Updated the HomeHero component to include example prompt suggestions for plugins, enhancing user interaction.
- Introduced the EditorIcon component to visually represent different editors in the hand-off menu, improving the user experience.
- Refined the HandoffButton component to utilize the new EditorIcon, providing a more cohesive interface for selecting editors.
- Enhanced CSS styles for various components to improve layout and responsiveness, including adjustments to tab and button sizes for better usability.

* style(workspace-shell): enhance layout and overflow handling

- Updated CSS for .workspace-shell to ensure full viewport width and height, with proper overflow management.
- Adjusted grid layout to prevent content overflow and maintain responsiveness.
- Modified styles for .workspace-tabs-chrome to improve width handling and prevent overflow issues.

* refactor(chat): remove voice transcription proxy and related components

- Deleted the voice transcription proxy implementation, including the associated API route and multer configuration.
- Removed the MicButton component from the ChatComposer and HomeHero components to streamline the UI.
- Updated HomeHero to include example suggestions without the voice input functionality.
- Adjusted CSS styles for various components to maintain layout consistency after the removal of the MicButton.

* feat(daemon): implement minting of HMAC tokens for working directory management

- Added a new function `mintImportTokenFromCurrentSecret` to generate HMAC tokens bound to a specified base directory, enhancing security for working directory operations.
- Updated the `desktop-auth.ts` file to include the new token minting functionality, which returns structured errors when the desktop auth secret is cleared.
- Introduced new IPC message types for minting import tokens in the sidecar protocol, allowing seamless integration with the daemon's working directory management.
- Enhanced the `WorkingDirPill` component to utilize the new token minting flow for secure directory selection in desktop builds.
- Updated CSS styles for the HomeHero component to accommodate new example suggestion features and maintain layout consistency.

* fix(HomeView): import HOME_HERO_CHIPS constant for improved chip management

- Updated the HomeView component to import the HOME_HERO_CHIPS constant from the chips module, enhancing the management of hero chips within the component.

* feat(daemon): implement mintImportTokenViaSidecar for secure working directory management

- Introduced the `mintImportTokenViaSidecar` function to facilitate the minting of HMAC tokens for desktop-import operations via the daemon's sidecar IPC. This allows CLI commands to bypass authentication when the desktop-auth gate is active.
- Updated the CLI to utilize the new token minting function when setting the working directory, ensuring secure access to trust-gated API endpoints.
- Enhanced the sidecar server to handle minting requests and return structured error messages for improved user feedback.
- Added tests to validate the new token minting functionality and its integration with the working directory management process.
- Refactored related components to support the new token flow, improving overall security and user experience.

* feat(HomeHero): enhance UI components and styles for improved user experience

- Updated HomeHero component to replace active dot indicators with Plug icons for better visual representation of active plugins.
- Adjusted CSS styles for various elements, including padding and dimensions, to enhance layout consistency and responsiveness.
- Introduced new styles for active type icons and improved hover effects for buttons.
- Updated HomeHeroSettingsChips to change button titles and icons for clarity.
- Added tests to ensure proper rendering and functionality of updated components.

* feat(ProjectDesignSystemPicker): enhance design system selection with preview functionality

- Updated the ProjectDesignSystemPicker component to include a preview feature for design systems, allowing users to see a preview of the selected design system.
- Implemented hover functionality to update the preview based on the hovered design system.
- Added fullscreen preview capability for a more immersive experience.
- Enhanced CSS styles for the design system picker to improve layout and responsiveness.
- Introduced tests to validate the new preview functionality and ensure proper interaction within the component.

* feat: refactor project metadata handling and enhance design system picker

- Updated the default scenario plugin ID retrieval to use project metadata, improving the logic for determining the appropriate plugin based on project intent.
- Enhanced the ProjectDesignSystemPicker and related components to support localized design system summaries and categories, improving user experience.
- Introduced new translations for working directory and design system picker components, ensuring better accessibility and usability across different locales.
- Added a new 'live-artifact' project type to the HomeHero chips, expanding the functionality for users creating refreshable artifacts.
- Updated tests to validate the new project metadata handling and design system picker functionalities.

* feat: enhance localization and styling for design system components

- Added French translations for working directory and design system picker components, improving accessibility for French-speaking users.
- Updated CSS styles for the pet task item to ensure consistent padding and layout.
- Introduced a new test suite for HomeHeroSettingsChips to validate localization and design system selection functionality.
- Enhanced ProjectDesignSystemPicker tests to ensure proper localization and interaction with design system categories.

* fix: update .gitignore to include all claude-sessions directories and remove specific session files

- Modified .gitignore to ensure all claude-sessions directories are ignored by using a wildcard pattern.
- Deleted two specific claude-sessions markdown files to clean up unnecessary session data.

* fix: repair home automation ci regressions

* fix: stabilize artifact consistency e2e

* Remove folder picker changes from PR 2400

---------

Co-authored-by: pftom <1043269994@qq.com>
Co-authored-by: qiongyu1999 <2694684348@qq.com>
2026-05-20 22:07:30 +08:00
lefarcen
255c3058c5
fix(analytics): app_version=0.0.0 + media providers clicks + lock run_finished error_code (#2453)
* fix(analytics): use state for runtime app version so PostHog gets the real value

`useAppVersion()` stored the fetched `/api/version` result in a `useRef`,
but ref writes do NOT trigger a re-render. The hook therefore kept
returning '0.0.0' forever and the downstream `useEffect` that calls
`client.register({ app_version, ui_version })` never re-ran with the
real version. PostHog dashboards then showed `app_version=0.0.0` and
`ui_version=0.0.0` on every event ever shipped from the web client.

Switching to `useState` lets the resolved version flow through React's
render cycle so the register-on-change effect picks it up. The boot
placeholder still ships as '0.0.0' for the first events before the
fetch resolves (we don't re-emit those), but every event after init
now carries the real daemon-pinned version.

Adds a red-spec at apps/web/tests/analytics-app-version.test.tsx that
went red on the `useRef` shape (`expected '0.0.0' to be '1.2.3'`) and
green on the `useState` shape, so a future refactor can't silently
regress it.

* feat(analytics): wire media providers click events + lock run_finished error_code invariant

Two analytics gaps shipped together because both came out of the same
PostHog spot-check after PR #2390 landed:

1. Settings → Media providers (CSV row "client_type=desktop / mason /
   media_providers") wasn't emitting any ui_click events. The contract
   type `SettingsMediaProvidersClickProps` and helper
   `trackSettingsMediaProvidersClick` were defined but no call site
   used them, so the dashboard showed zero traffic on every element.
   Added the four v2 elements:
   - `reload` on the "Reload from daemon" button
   - `key_input` on every per-provider API key field (onFocus, mirrors
     the BYOK key field pattern in this same dialog)
   - `url_input` on every per-provider base-URL field
   - `clear` on each row's Clear button (fires before the confirm
     dialog so the intent signal is recorded even if the user backs
     out)
   Each event carries `providers_id` (provider.id) and `is_configured`
   (truthy when the row has a stored entry).

2. `run_finished` with `result=failed` was reported as missing
   `error_code` on PostHog. Audited every failure path: the daemon's
   `child.on('close', ...)` handler has several branches that call
   `runs.finish('failed', code, signal)` directly without first
   emitting an SSE `error` event (ACP fatal, agentStreamError fall
   through, child close without diagnostic), leaving
   `run.errorCode === null` in the status body. The existing fallback
   in `server.ts` already derives `AGENT_SIGNAL_*` / `AGENT_EXIT_*` /
   `AGENT_TERMINATED_UNKNOWN` from `signal` / `exitCode` for those
   cases, so the wire emission should never blank out — but the logic
   was inline and had no unit coverage.

   Extracted the result/error_code derivation into
   `apps/daemon/src/run-result.ts` and added 12 unit tests covering:
   - explicit errorCode forwarding
   - signal-only failures
   - exit-code-only failures
   - clean (code=0) failures (ACP fatal shape)
   - cancelled runs (with and without stamped code)
   - empty-string errorCode defensive case
   - status→result mapping for succeeded/canceled/failed/unknown

   All 12 pass — confirming the invariant "result=failed always
   carries error_code" holds for every failure shape the daemon
   produces. The refactor pins that invariant so a future change
   loses test coverage rather than silently regressing on PostHog.

   If `error_code` still looks empty on a live event, share the
   PostHog event JSON + the agent id and I'll dig further — at this
   point the daemon emission itself is exercised end-to-end.
2026-05-20 21:50:11 +08:00
koki
5e9687db4a
fix(landing): align logo.webp with brand icon (#2460)
Co-authored-by: koki yanlai xu <koki@kokideMacBook-Air.local>
2026-05-20 21:27:32 +08:00
lefarcen
aedbb9dbe4 release: Open Design 0.8.0
Bumps 14 workspace package.json files from 0.7.0 to 0.8.0:
- root, apps/{web,daemon,desktop,landing-page}
- packages/{contracts,host,platform,sidecar,sidecar-proto}
- tools/{dev,pack,pr}, e2e

apps/packaged was already at 0.8.0 from the preview lane.
Independently versioned packages keep their own tracks.

Adds CHANGELOG [0.8.0] - 2026-05-20 entry covering the
305 PRs merged since 0.7.0 by 75 contributors:

- Plugin engine rebuild + Plugin Registry surface
- Headless by default (desktop is thin wrapper around CLI)
- Critique Theater Phases 9 through 16
- 149 design systems with structured tokens.css
- Italian locale + CJK font fallback
- Leonardo.ai, ElevenLabs, SenseAudio providers
- Windows packaged auto-update
- Visual refresh + Quick-brief discovery overhaul
- PostHog v2 analytics
- Manual edit UX overhaul
2026-05-20 21:22:17 +08:00
Tuola-waj
b1aa62e63c
fix(landing-page): restore 'tutorials' in Header active union (#2458) 2026-05-20 21:20:56 +08:00
lefarcen
1cfe274a90 Merge origin/main into release/v0.8.0
Conflicts resolved by taking origin/main on all six points:

- apps/web/src/components/HomeHero.tsx:479-487  brand div removed
  (main dropped the .home-hero__brand wrapper; the release-side visual
  refresh still had it).
- apps/web/src/components/HomeHero.tsx:894-898  attach Icon size
  18 (main's update) replaces 20 from release.
- apps/web/src/components/HomeHero.tsx:913-927  submit button uses
  <Icon name="arrow-up" size={22} /> (main's component refactor)
  instead of the release-side inline SVG.
- apps/web/src/components/EntryShell.tsx:578-582  Discord Icon size
  14 (main) instead of 16 (release).
- apps/web/src/styles/home/home-hero.css  drop .home-hero__brand /
  __brand-mark / __brand-name rules — main removed both the component
  div and these CSS rules together; keeping the CSS would be dead code.
- apps/web/src/styles/home/entry-layout.css  Discord badge icon color
  #5865f2 (main, the brand color introduced by PR #2386) instead of
  release's neutral var(--text-strong).
2026-05-20 20:59:00 +08:00
Tuola-waj
69469c639e
feat(landing-page): add HTML Anything page and responsive header (#2452) 2026-05-20 20:37:34 +08:00
PerishFire
7a47829279
Fix nightly release smoke identity (#2446)
* Fix Windows nightly release smoke identity

* Fix mac nightly release smoke identity
2026-05-20 20:34:26 +08:00
Leon Wang
71c490538f
Merge pull request #2436 from nexu-io/codex/open-design-visual-refresh
[codex] Refresh Open Design app visuals
2026-05-20 20:33:55 +08:00
Siri-Ray
0ee363f23e
Polish design systems library layout (#2421) 2026-05-20 20:31:10 +08:00
“wangchenglong”
69d3bf4f71 Refresh Open Design app visuals 2026-05-20 20:28:15 +08:00
PerishFire
899c9fe4d8
Support nightly and preview package identity (#2437) 2026-05-20 19:46:39 +08:00
PerishFire
31ca20f2c6
Add packaged update apply observations (#2429) 2026-05-20 19:11:36 +08:00
ashleyashli
e4d6d4e805
fix(landing): keep template preview lookup stable in CI (#2412)
Resolve generated preview thumbnails from both workspace-root and package-root
build layouts so Astro production builds can see the images created by the
landing preview step. A later merge restored the source-relative lookup, which
made /templates render 111 cards with placeholder thumbs despite the PNG files
being deployed.

Co-authored-by: ashley li <ashleyli@ashleydeMacBook-Air-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 18:48:02 +08:00
lefarcen
41a33aed9e
Reapply "fix(web): demote Plugins and Integrations to nav rail footer (#1806)" (#2360) (#2397)
* Reapply "fix(web): demote Plugins and Integrations to nav rail footer (#1806)" (#2360)

This reverts commit 1ab8758045.

* fixup: align EntryHelpMenu Discord URL with #2386 update

The revert of #2360 brought back EntryHelpMenu.tsx as #1806 originally
added it, with DISCORD_URL = 'https://discord.gg/BYShPgWpq'. #2386 later
rotated the Discord invite to mHAjSMV6gz, but only in the places that
existed on main at the time (EntryShell avatar dropdown + the e2e test);
EntryHelpMenu didn't exist then, so it never got updated. The e2e test
the revert reintroduced asserts the new URL, so the component must
match.
2026-05-20 18:27:48 +08:00
Chris Seifert
59c8d72ae4
feat(tweaks): bind toolbar toggle to artifact panel (#2348)
* feat(tweaks): bind toolbar toggle to artifact panel

Wires the host viewer's Tweaks toggle to artifact panels via two
protocols: postMessage __edit_mode_* for agent-generated .twk-panel
artifacts, and the existing class-based bridge for tweaks-skill
.tw-panel artifacts. Toggle disables itself when the artifact exposes
no panel, syncs state when the user closes the panel locally, and no
longer regenerates srcdoc on toggle (no iframe remount).

- bridge: emit od:tweaks-available + MutationObserver-driven
  od:tweaks-panel-state so host learns availability and mirrors local
  closes; transform-based hide preserves the artifact's transition
- host: listen for both od:tweaks-* (bridge) and __edit_mode_*
  (artifact) dialects, send both on toggle, disable button when no
  panel, reset state on file change
- skill: document the two protocols as a host integration contract
- i18n: add fileViewer.tweaksUnavailable for all 13 locales

* fix(tweaks): keep srcDoc path for `.tw-panel` artifacts

The class based tweaks template (`.tw-panel` / `.tw-hidden`) needs the
bridge `injectTweaksBridge` emits, but the default plain HTML preview
URL loads the iframe, which bypasses buildSrcdoc entirely. Without the
bridge there is no `od:tweaks-available` ping, so the toolbar toggle
stays disabled on first load of a tweaks template artifact unless an
unrelated mode (palette, inspect, etc.) coincidentally forces srcDoc.

Add a `tweaksBridge` flag to `shouldUrlLoadHtmlPreview` and detect the
fixed `.tw-panel` / `.tw-hidden` template selectors in the artifact
source via a new `hasTweaksTemplate` helper. FileViewer passes the
detected flag through so tweaks template artifacts pick the srcDoc
render path on first load.

Tests in `file-viewer-render-mode.test.ts` cover the new disqualifier,
the helper positive and negative cases, and combinations with the
existing flags.

* fix(tweaks): resolve v0.7 UI ambiguity between toolbar toggle and palette

After rebasing onto v0.7, three problems surfaced. v0.7 ships a palette
popover button also labeled `Tweaks` with the same sliders icon as the
new toolbar toggle. Toggling that popover flipped render mode (URL load
to srcDoc) and reloaded the iframe, flashing the preview. The resulting
iframe remount caused agent protocol artifacts to re-announce
`__edit_mode_available`, which flipped the toolbar toggle back on
without user input.

Rename the palette popover button to `Themes` (button label, dialog
title, `aria-label`) and swap its icon to a new `paint-bucket` glyph.
The artifact tweaks toggle keeps the `tweaks` sliders icon. Internal
identifiers (`data-testid="palette-tweaks-toggle"`, CSS classes, the
`PaletteTweaks` component name) stay stable so existing tests and
styles still target the same elements.

Drop the auto set true on `__edit_mode_available`; that signal now
flips `tweaksAvailable` only. `syncBridgeModes` posts the current
`tweaksMode` to the artifact (both the bridge dialect and
`__activate_edit_mode` / `__deactivate_edit_mode`) on every iframe
load so the panel matches the toolbar.

Mount both URL load and srcDoc iframes simultaneously, absolutely
positioned and overlapping, with CSS visibility flipping between
them. Toggling render mode no longer reloads the iframe so there is
no flash. `isOurIframe(source)` accepts messages from either iframe
so startup announcements from the hidden iframe are not lost; six
receive filter sites switch from `iframeRef.current?.contentWindow`
to the helper. Sends still target `iframeRef.current`, kept aligned
with the active iframe via a `useEffect`, and a `syncBridgeModesRef`
pushes current bridge state to the now visible iframe whenever the
render mode flips.

Tests that previously asserted exclusive render mode (`url-load`
vs. `srcdoc` presence) now assert the active `data-testid` sits on
the expected iframe via a co-attribute regex. The Draw bar element
picking test switches from a cached frame reference to a `getFrame()`
helper since `data-testid` follows the active iframe across toggles.

Add a `paint-bucket` entry to `Icon` (Lucide style stroke icon).

* fix(tweaks): scope `od:tweaks-available` to the active iframe

The dual iframe setup mounts both the URL load and srcDoc iframes at
once and accepts postMessage events from either via `isOurIframe`. The
srcDoc iframe always carries the always injected tweaks bridge, which
runs `document.querySelector('.tw-panel')` on mount and posts
`{ type: 'od:tweaks-available', available: false }` for any artifact
that does not ship the class based panel. For an agent protocol
artifact (`.twk-panel`, `__edit_mode_*`), the URL load iframe correctly
announces `__edit_mode_available` and the host sets
`tweaksAvailable = true`. The hidden srcDoc iframe's `available: false`
ping arrives shortly after and overrides that to false, silently
disabling the toolbar button.

Scope `od:tweaks-available` to the active iframe only by re-checking
`ev.source === iframeRef.current?.contentWindow` before applying it.
`__edit_mode_available` and `__edit_mode_dismissed` stay accepted from
either iframe so the artifact's own announcement still drives the
toolbar toggle across render mode flips.

Spotted by Siri-Ray on PR #1643.

* fix(tweaks): start the toolbar toggle ON when the artifact mounts its panel visible

Both tweaks dialects (the class-based `.tw-panel` skill template and the
`.twk-panel` agent-generated edit-mode protocol) mount their panel visible
by default. Before this change the toolbar `Tweaks` toggle started in the
OFF state regardless, so the user saw the panel but had to click
toggle-on → toggle-off to actually hide it — confusing because the toggle
disagreed with what they could plainly see in the preview.

Two changes wire the initial state through to the toolbar:

- `srcdoc.ts` (class-based dialect): the tweaks bridge's `onReady` now
  fires `postState()` alongside `postAvailability()`. `postState()` reads
  `!panel.classList.contains('tw-hidden')` and posts the artifact's actual
  initial visibility, so the host's existing `od:tweaks-panel-state`
  handler picks it up and mirrors it into `tweaksMode`. Previously only
  MutationObserver-driven changes were posted, so the host never learned
  the artifact's initial state.

- `FileViewer.tsx` (twk-panel dialect): the agent dialect's
  `__edit_mode_available` carries no visibility payload, so we infer
  default-open from the fact that the artifact bothered to announce
  availability at all (the SDK pattern is `useState(true)`). Mirror that
  into `tweaksMode` exactly once per file (tracked by
  `firstEditModeAvailableSeenForFileRef`), so an iframe remount triggered
  by, e.g., flipping render mode through the Themes popover does not snap
  a user-driven OFF back to ON.

Also fix a runtime `ReferenceError: panel is not defined` regression
this same change introduced when first written with backticks inside the
new code comment — the comment lived inside a `\`...\``-delimited script
template literal, so the embedded backticks closed and reopened the outer
literal and broke the bridge's JS body. Replaced with plain text.

Validation: web typecheck clean, 1597/1597 tests pass. Manually verified
with a `.twk-panel` artifact: open file → tweaks toggle is ON, panel
visible → one click hides both.

* fix(tweaks): seed bridge state from the panel's authored class, not from the host hidden attribute

The bridge installs `data-od-tweaks-hidden` on `<html>` synchronously in
`<head>` so the panel never flashes on initial paint. That attribute is
therefore *always* present by the time `onReady()` fires, which meant the
previous `applyClassesToPanel(!hasAttribute(...))` call unconditionally
forced `.tw-hidden` onto the panel, the follow-up `postState()` read that
forced-hidden class, and the host saw `visible: false` even when the
artifact had authored the panel as default-visible. The PR-1643 attempt
at "start ON when the artifact mounts visible" therefore still reported
OFF for the class-based template path.

Read the panel's authored class state first (the artifact body has just
parsed, so the panel's class is what the artifact wrote and nothing
else has touched it yet), then drive the attribute, the applied class,
and the `od:tweaks-panel-state` post from that captured value:

```ts
var panel = panelEl();
var initialVisible = !!panel && !panel.classList.contains('tw-hidden');
document.documentElement.toggleAttribute('data-od-tweaks-hidden', !initialVisible);
applyClassesToPanel(initialVisible);
attachObserver();
postAvailability();
postState();
```

A default-visible `.tw-panel` now reports `visible: true` on mount, the
host mirrors that into `tweaksMode = true`, and the toolbar Tweaks toggle
starts in the ON state instead of disagreeing with what the user sees in
the preview. The `.twk-panel` agent-protocol path is unaffected; its
initial-state mirror still goes through the
`firstEditModeAvailableSeenForFileRef` guard in `FileViewer.tsx`.

Surfaced by Siri-Ray in https://github.com/nexu-io/open-design/pull/1643#discussion_r3263571196.

Validation: web typecheck clean, 1597/1597 tests pass.

* fix(tweaks): re-mirror __edit_mode_available default-open state when switching .twk-panel files

The once-per-file guard that mirrors a `.twk-panel` artifact's
default-open state into the toolbar `tweaksMode` lives inside a
`window.addEventListener('message', ...)` handler installed in a
`useEffect(..., [])` with an empty dep list. The handler therefore
closed over the first-render `file.name`. After opening one
`.twk-panel` artifact, `firstEditModeAvailableSeenForFileRef.current`
got set to that first file; switching to a second `.twk-panel` file
left the message listener still comparing the new artifact's
`__edit_mode_available` against the stale captured name, so the
guard never re-fired and the toolbar stayed OFF while the new
artifact's panel was clearly visible — exactly the mismatch the
guard was supposed to prevent on initial load.

Add `file.name` to the listener effect's dep list so the handler
gets a fresh closure on every file switch. The bridge-message setters
(`setTweaksAvailable`, `setTweaksMode`), `isOurPreviewIframeSource`,
and `firstEditModeAvailableSeenForFileRef` are stable across renders,
so re-binding the listener has no other side effects beyond updating
the captured `file.name`.

Surfaced by Siri-Ray in
https://github.com/nexu-io/open-design/pull/1643#discussion_r3266838151.

Red-spec regression test added: `FileViewer tweaks toolbar > mirrors
__edit_mode_available default-open state for each switched-to
.twk-panel file`. Verified to go red on the bug (deps `[]`) and green
on the fix (deps `[file.name]`).

Validation: web typecheck clean, 1598/1598 tests pass (was 1597).

* i18n(tweaks): add fileViewer.tweaksUnavailable to the remaining 6 locales

The toolbar's disabled-Tweaks tooltip key landed in 13 locale files but
6 were missed (ar, fr, id, it, th, uk). Those locales were still falling
through to the English string via the `...en` spread, which contradicts
the repo convention that every key be defined explicitly in each locale.
Add the translation alongside the existing `fileViewer.tweaks` entry so
the full set of 19 locales now ships native copy for the disabled state.

Surfaced by Siri-Ray in
https://github.com/nexu-io/open-design/pull/1643#discussion_r3267654385.

* fix(tweaks): respect default-closed dynamic panels in __edit_mode_available

Protocol A in `design-templates/tweaks/SKILL.md` documents that the
artifact may default the panel to either open or closed and the host
should sync its toolbar toggle to whichever state the artifact reports.
The previous handler ignored that and unconditionally mirrored
availability into `tweaksMode = true`, so a default-closed dynamic
artifact would be force-opened the moment `syncBridgeModes` ran and
fired `__activate_edit_mode` — the artifact could not stay closed even
though the contract said it could.

Extend the message shape so the artifact can report its initial state
on the same payload:

  { type: '__edit_mode_available', visible?: boolean }

The host now reads `data.visible`:

- omitted          → treat as `true` (back-compat: existing artifacts
                     emitting the legacy zero-arg shape mount with the
                     panel already on screen, which is the SDK pattern
                     `useState(true)`).
- `visible: true`  → toolbar starts ON.
- `visible: false` → toolbar starts OFF, panel stays closed; the user
                     opts in by clicking the toggle, which then fires
                     `__activate_edit_mode` via the existing
                     `syncBridgeModes` path.

Update `design-templates/tweaks/SKILL.md` to document the new optional
field alongside the legacy shape.

Surfaced by Siri-Ray in
https://github.com/nexu-io/open-design/pull/1643#discussion_r3269955351.

Red-spec regression test added: `FileViewer tweaks toolbar > respects
__edit_mode_available { visible: false } for default-closed dynamic
artifacts`. Verified red without the fix (always-true mirror) and green
with the fix (`data.visible !== false`).

Validation: web typecheck clean, 1599/1599 tests pass.
2026-05-20 18:00:49 +08:00
open-design-bot[bot]
7905e72962
docs(blog): refresh 3-day traffic digest (#2349)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-20 18:00:22 +08:00
PerishFire
32696d640c
fix(desktop): keep the macOS Dock icon when the desktop-pet window opens (#2413)
The desktop-pet companion window calls setVisibleOnAllWorkspaces so it
floats across every Space. By default Electron's macOS implementation
transforms the whole process type between UIElementApplication and
ForegroundApplication while applying that call. On Electron 41 / macOS 26
that round-trip races during the launch burst — the pet window is
created alongside the main window — and the process can stay stuck as an
accessory app: no Dock icon, no menu bar, even though the windows render
fine.

Pass skipTransformProcessType: true so the pet window's all-Spaces
behavior never touches the app's process type. The desktop pet is a
cosmetic companion window; the main window keeps the app a regular Dock
app. The pet still floats on every Space via its alwaysOnTop floating
level.

Fixes #2394
2026-05-20 17:48:01 +08:00
ashleyashli
93dd7066fd
feat(landing): refresh templates and add tutorials channel (#2409)
* feat(landing): rebuild /templates/ catalog from design-templates

Source the public templates page from `design-templates/*/SKILL.md` so
the catalog reflects the renderable template registry (111 entries with
mode, platform, and scenario metadata) instead of the previous handful
of live-artifact + skill-mode shims. Render per-template thumbnails by
shooting each design template's `example.html`, surface mode/platform/
scenario chips on cards and detail pages, and bump the preview
navigation timeout so heavier examples no longer flake under
concurrency.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(landing-page): add /tutorials/ channel for YouTube content

Adds an editorial tutorials channel to open-design.ai. Each video maps
to a Markdown entry in `app/content/tutorials/` and renders inline via
`youtube-nocookie.com` so the page does not require a cookie consent
banner. The list page mirrors the magazine-style /blog/ layout and
shares the same category-filter UX.

Seeds the channel with 12 community-produced videos (English + Chinese)
sourced from YouTube as of 2026-05-19, spanning getting-started,
tutorial, demo, and review categories. Header now exposes a "Tutorials"
nav item between Craft and Blog.

* fix(landing-page): switch tutorial player to click-through to YouTube

The original inline `youtube-nocookie.com/embed/<id>` iframe failed
to play in practice. YouTube's embedder identity check rejected the
request with `PLAYABILITY_ERROR_CODE_EMBEDDER_IDENTITY_MISSING_REFERRER`
and the in-frame "Sign in to confirm you're not a bot" challenge.
Removing the explicit `referrerpolicy` attribute did not resolve it,
and the alternative — providing a Referrer-Policy that always sends
a full referrer to a third party — is a regression we don't want
to ship.

Replace the iframe with a click-through facade: high-resolution
thumbnail + YouTube-style red play button + "Watch on YouTube ↗"
pill, wrapped in an anchor that opens the canonical
`youtube.com/watch?v=<id>` page in a new tab.

The detail page still renders all of the editorial content — title,
summary, author, date, duration, chapter notes — so it remains a
useful in-site landing for SEO and social shares. Only the playback
itself moves to YouTube proper.

* fix(landing-page): correct chase-ai tutorial chapter map

The seeded chapter map for the Chase AI tutorial mirrored the
typo in the original YouTube description: it listed
`14:06 — Dashboard` (past the 13:47 runtime) and put
`12:40 — Final verdict` after it, breaking strictly-ascending
order. YouTube's auto-detected chapter list for this video
(`yt-dlp --dump-json` `chapters` field) shows only four
sections — Open Design, Install + Demo, Design Systems, Final
Verdict — with no Dashboard segment, so drop that line and
keep the remaining four chapters in order.

---------

Co-authored-by: ashley li <ashleyli@ashleydeMacBook-Air-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-20 17:44:28 +08:00
Mason
1855b22d24
Improve desktop updater ready UI (#2403) 2026-05-20 17:29:28 +08:00
Siri-Ray
f4b8fbece2
Fix template project creation flow (#2399) 2026-05-20 17:18:24 +08:00
Eli
a5e43ae2a4
add discord feedback entry points (#2386) 2026-05-20 16:22:45 +08:00
shangxinyu1
71044bd3d6
test(e2e): harden extended coverage state assertions (#2245)
* test(e2e): harden extended coverage contracts

* docs(testing): add e2e hardening status

* fix(web): persist artifact chips after daemon runs

* ci: install playwright browsers for e2e vitest

* Fix daemon run recovery across reloads

Pin daemon-created runs to assistant messages immediately so hard reloads before the create response can reattach.

Replay terminal and active run events from the beginning on reload so restored turns keep assistant text, thinking events, produced files, and artifacts.

Fixes #2366

Fixes #2368

Fixes #2371

* test(e2e): preserve fake runtime selection across reload

* fix(web): scope daemon run recovery to daemon mode

* fix(e2e): remove duplicate delayed smoke flag

* fix(web): scope replay artifact recovery to current run

* fix(daemon): remove duplicate run-create pin
2026-05-20 16:21:01 +08:00
lefarcen
579db9e8c7
feat(analytics): unify page_name + onboarding/design-system page_views (#2390)
Doc v2 row 12 (settings) was renamed `page` -> `page_name` in the doc;
mirror that in code so every event surface uses the same field name on
the wire. Drops `TrackingSettingsPage` as a separate discriminator and
merges 'settings' into `TrackingPageName` so `page_name='settings'`
sits beside the product surfaces.

Adds two new page_view surfaces from the v2 doc:

- `page_name=onboarding`: one emission per step exposure in the
  Connect / About you / Design system / Generation 4-step funnel.
  `onboarding_session_id` is generated once in sessionStorage so the
  4th step (which fires from `DesignSystemDetailView` after onboarding
  navigates away) stays attributable. Cleared on onboarding finish
  so a later unrelated DS visit doesn't inherit the id.

- `page_name=design_system_project | design_systems`: multi-surface
  DS page_view. Wires the page-level emissions (list / create /
  generation / preview); the module / popover / panel exposures
  inside home and studio are intentionally left for a follow-up so
  this PR doesn't blur with the unresolved "page_view vs surface_view
  for non-page exposures" question raised with product.

`PageViewProps` becomes a discriminated union (`GenericPageViewProps |
OnboardingPageViewProps | DesignSystemsPageViewProps`) so each surface
gets its own typed contract. `design_system_source` is a new field on
the DS page_view; it shares the name from the v2 doc with the
existing run_created/run_finished field, but the value set is
disjoint so a new `TrackingDesignSystemOrigin` type carries it.
2026-05-20 16:20:48 +08:00
lefarcen
c80acfefeb
fix(daemon,web): block pitch-deck placeholder publishes and unbreak framework decks (#2384)
Two preview-time bugs surfaced ahead of 0.8.0:

1. Pitch-deck example (#2215): the official html-ppt-pitch-deck prompt asked
   the agent to confirm three facts first, but the manifest had no
   structured `od.inputs`, so the platform's required-input gate had no
   fields to enforce and the run could publish HTML that still contained
   unresolved fundraising placeholders (`Name to confirm`, `$X.XM`,
   `Replace this panel with`, ...). Add structured required inputs to the
   manifest and a daemon-side publication guard that rejects HTML/deck
   artifact writes whose body still contains those placeholders. Scope is
   the file-write boundary only (no assistant-text scanning), so the
   guard cannot trip on the agent's chat prose mid-clarification.

2. Framework deck preview off-screen: `injectDeckBridge` injected
   `place-content: center !important` on `.deck-shell` for every deck-mode
   srcdoc, which forced the framework's `display: grid` shell to re-center
   its implicit track. The framework's `fit()` already centers a
   `transform-origin: top left` stage with an explicit `translate(tx, ty)`
   that assumes the stage's natural layout position is (0, 0); the two
   centerings stacked and the scaled stage landed ~1000px off-screen, so
   the preview showed a sliver of slide content in the top-left with the
   rest black. Skip the override when the framework's `id="deck-stage"`
   marker is in the doc, and drop the dead `display: grid; place-items:
   center` from the deck framework template so future drift can't
   re-introduce the same stack.
2026-05-20 16:20:34 +08:00
ashleyashli
65e760b88a
feat(seo): add GSC report opportunities (#2388)
Co-authored-by: ashley li <ashleyli@ashleydeMacBook-Air-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 16:19:14 +08:00
open-design-bot[bot]
b409e5b923
docs(blog): refresh daily indexing status (#2346)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-20 16:13:16 +08:00
Eli
1f2a818c02
[codex] Restore chat message role headers and user bubbles (#2382)
* Restore chat message role headers

* Restore user message bubble styling
2026-05-20 15:53:40 +08:00
lefarcen
3af2ed593c Merge origin/main into release/v0.8.0 2026-05-20 15:46:59 +08:00
lefarcen
d03b8fd095
fix(plugins): stop recommending raw publish CLIs from authoring summary (#2380)
QA repro (transcript shared on PR #2363): the agent finishes the
Home "Create plugin" → plugin-authoring chip flow, validates / packs
/ installs the generated plugin, then in its summary turn freeform-
recommends shell commands like:

  od plugin publish ./generated-plugin --to open-design
  cd generated-plugin && git init && git add . && git commit -m "..."
  gh repo create lefarcen/<name> --public --source=. --push

Two things go wrong:

1. The summary recreates the exact flows the plugin-folder card buttons
   already drive end-to-end (PR #2363 wired the buttons through the
   right gh + git sequences with auth gates, jq fallback, and retry
   rules baked in). The freeform suggestions drop those guarantees —
   they're the source of bug #2332 where `od plugin publish --to
   open-design` was the recommended action even though it produces an
   issue URL rather than creating a public repo.

2. The prompt explicitly hinted at `od plugin publish` in step 3 ("Then
   run or prepare the CLI path: ... and `od plugin publish` when the
   user is ready to open a registry PR"). The agent dutifully repeated
   that as the next step, even though the publish work belongs to the
   plugin-folder card button prompts now.

Rewrites `PLUGIN_AUTHORING_PROMPT_TEMPLATE` to:

* Keep the original scaffolding instructions (generated-plugin/ +
  SKILL.md + open-design.json + plugin.repo + inputs).
* Replace the loose "od plugin whoami/login through gh, and od plugin
  publish when the user is ready" sentence with a precise local-
  validation chain: `od plugin validate` → `od plugin pack` →
  `od plugin install --source <abs-path>`.
* Tell the agent to STOP after the summary, and explicitly ban
  follow-up suggestions of `od plugin publish`, `od plugin publish
  --to open-design`, `gh repo create`, `git init` / `git remote add` /
  `git push`. The ban names the workarounds it has actually emitted
  in QA transcripts.
* Point the user at the plugin-folder card buttons (Add to My plugins
  / Publish repo / Open Design PR) and call out that those prompts
  encode the auth gates and fallback rules the summary suggestions
  would skip.
* Same jq guidance carried over from PR #2363: do not assume the
  standalone `jq` binary, prefer the built-in Read tool / cat / node
  -e, and disambiguate from `gh ... --jq` (which is fine because gh
  ships its own embedded library).

Tests:

* `apps/web/tests/components/home-hero/plugin-authoring-prompt.test.ts`
  (new) — nine assertions covering: goal-placeholder interpolation,
  generated-plugin scaffolding mentions, local validation chain, the
  follow-up CLI ban list (`od plugin publish --to open-design`,
  `gh repo create`, `git push`), the plugin-folder card hand-off, the
  jq fallback + gh-flag carve-out, and the round-trip helpers
  (`buildPluginAuthoringInputs` / `buildPluginAuthoringPromptForInputs`
  / `createPluginAuthoringHandoff`).

Validation:

* `pnpm --filter @open-design/web exec vitest run tests/components/home-hero/plugin-authoring-prompt.test.ts`
  → 9/9 passed
* `pnpm --filter @open-design/web exec vitest run tests/components/HomeView.prefill.test.tsx`
  → 11/11 passed (no regression in the adjacent HomeView prompt-prefill
  suite; that test references the template literal directly so a
  silent shape change would surface here)

Related: PR #2363 fixes the same source-of-bug shape on the
plugin-folder card "Publish repo" button. That PR is the canonical
home for the publish flow; this PR makes sure the authoring summary
hands off to it rather than re-implementing it inline.
2026-05-20 15:39:42 +08:00
lefarcen
c617e30e27
fix(plugins): make Publish repo actually create the author's repo (#2332) (#2363)
* fix(plugins): make Publish repo actually create the author's repo (#2332)

QA repro from 0.8.0 preview: clicking "Publish repo" on a generated
plugin's `DesignFilesPanel` card ran the agent down a path that
produced an Open Design registry-submission URL but never created the
author's GitHub repo. After the action finished, `gh repo view
nuomi/cat` still returned 404 and `git ls-remote
https://github.com/nuomi/cat.git HEAD` failed with "Repository not
found".

Root cause is the action prompt at
`apps/web/src/components/design-files/pluginFolderActions.ts:11-12`:

  publish: 'Use the supported `od plugin publish` or
            repository-publish flow after confirming the manifest.'

That sentence let the agent pick the legacy registry-link CLI (`od
plugin publish --to open-design`), which mirrors the path the
"Open Design PR" button takes and emits an issue URL instead of
creating a public repo. The button label said "Publish repo" but the
behavior collapsed onto the registry-submission flow.

This PR rewrites the `publish` prompt in the same shape PR #2182
used for `contribute` — a numbered gh + git sequence that drives the
real action end-to-end:

  1. Pre-flight `gh --version` / `gh auth status`. Invalid / expired
     tokens are treated the same as not-logged-in (the bug-report
     agent kept going past an "invalid token" warning).
  2. Read manifest, capture `name`, `version`, `description`,
     `plugin.repo`. Fall back to `https://github.com/<gh-login>/<name>`
     when `plugin.repo` is missing and write it back into the
     manifest.
  3. `gh repo view <owner>/<name>` to decide create-vs-update.
  4a. Repo does not exist → `git init` + commit + tag +
      `gh repo create <owner>/<name> --public --source . --push`.
  4b. Repo exists → reuse the remote, `git add -A` + `git commit -m
      "Update: <name> v<version>"` (skip if working tree is clean),
      `git tag v<version>` (skip if already published), `git push`.
  5. Verify with `gh repo view <owner>/<name> --json url,nameWithOwner`.
  6. Hand off the resolved `https://github.com/<owner>/<name>` URL to
     chat. End the turn.

Hard constraints encoded in the prompt:

* Do NOT call `od plugin publish --to open-design` (or any `--to
  <catalog>` variant). That is the registry-submission flow.
* Do NOT call `AskUserQuestion` — fire-and-forget, same as the
  `contribute` flow's stall fix.
* Do NOT auto-install gh/git. Detect-and-instruct only.
* Do NOT force-push or overwrite a published tag.
* Do NOT retry a failed step. Report and stop.

Refactor: pulled `publish` out of the shared `ACTION_TITLES`/
`ACTION_NOTES` template into its own `buildPublishPrompt(folderPath)`
function (mirrors `buildContributePrompt` from PR #2182). `install`
keeps the simple shared template — that action stays inferrable from
the manifest and doesn't need the same blast radius.

Tests:

* `apps/web/tests/components/pluginFolderActions.test.ts` — extends
  the existing contract suite with seven new `publish` assertions:
  targets `plugin.repo` not the registry catalog, drives the full gh
  + git command list, handles both new-repo and existing-repo
  branches, explicit ban on the registry-submission CLI, hard-bans on
  AskUserQuestion / auto-install / force-push / retry, "invalid
  token" treated as STOP, `${folderPath}` interpolation guard, ends
  by handing the repo URL back to chat.

Validation:

* `pnpm --filter @open-design/web exec vitest run tests/components/pluginFolderActions.test.ts`
  → 16/16 passed (was 9/9 before this PR; +7 new publish-flow
  assertions, the old generic "mentions od plugin publish" assertion
  replaced with the precise contract above)

(Local `pnpm --filter @open-design/web typecheck` fails on
`tests/runtime/exports.test.ts` because `packages/host/dist/testing`
isn't built in this checkout — pre-existing breakage from
`2c128e0e refactor desktop host bridge` on main, unrelated to this
prompt change. CI runs a fresh install and was green on the four
previous prompt-only PRs that touched the same module.)

Closes #2332.

* fix(plugins): don't assume standalone jq when reading the manifest

QA repro from the Open Design PR button (transcript shared with the
PR #2363 thread): the agent reached step 2 of the contribute prompt,
ran `jq '{name,title,description,version}' generated-plugin/open-design.json`,
got `zsh:1: command not found: jq`, and stopped per the prompt's
"stop on first hard failure" rule. No fork, no branch, no PR.

jq is not part of the OD agent runtime baseline — default macOS and
Windows shells don't ship it. The agent reached for it first because
"jq" is the default JSON tool in claude/codex's shell training
distribution, not because the prompt asked for it. The prompt just
said "Load and capture", which the agent interpreted as "shell out to
the most common JSON parser".

Updates both step-2 instructions (contribute + publish prompts) to:

  - List portable manifest-read alternatives in priority order:
    the built-in Read tool (always available); `cat` + manual JSON
    parsing; `node -e 'JSON.parse(...)'` as the shell-only fallback.
  - Add an explicit "Do not assume the standalone `jq` binary is
    installed" guard with the macOS / Windows shell rationale.
  - Disambiguate the standalone `jq` CLI from `gh ... --jq`. The gh
    flag uses an embedded library and is fine — without the
    disambiguation the agent reads the ban literally and stops using
    `gh api user --jq .login` at step 3.

Tests:

* `apps/web/tests/components/pluginFolderActions.test.ts` — two new
  contract assertions:
  - publish prompt: warns against assuming standalone jq is
    installed; lists cat and node -e as alternatives. Closes the
    regression on its own surface.
  - shared block: both contribute and publish prompts disambiguate
    standalone jq from `gh ... --jq`. One assertion guards both
    flows so a future prose edit can't drop the carve-out on one
    side.

Validation:

* `pnpm --filter @open-design/web exec vitest run tests/components/pluginFolderActions.test.ts`
  → 18/18 passed (was 16/16 on the previous PR #2363 commit; +2 new
  jq-guidance assertions)

Continues PR #2363. Same source-of-bug shape as the registry-submission
fallback issue this PR was opened to fix: agent picks a tool the
prompt didn't actually ask for because the prompt was loose.
2026-05-20 15:38:29 +08:00
PerishFire
c0fb221e72
fix: align windows smoke update root with portable installs (#2376) 2026-05-20 15:36:10 +08:00
Patrick A
aa8f02dbac
chore(deps): upgrade posthog-node 4.18.0 -> 5.34.6 in daemon (#2309)
Breaking changes addressed:
- posthog-node v5 replaces axios/follow-redirects/proxy-from-env with
  @posthog/core (native fetch); no call-site changes required — the
  PostHog constructor signature, capture(), identify(), groupIdentify(),
  on(), and shutdown() surface used by apps/daemon/src/analytics.ts is
  stable across the major boundary.
- shutdown() is still async in @posthog/core (PostHogCoreStateless base
  class); the IPostHog interface in posthog-node types it as void but the
  inherited Promise<void> from @posthog/core keeps await client.shutdown()
  correct at runtime.
- protobufjs resolved version: 7.5.7 (pre-existing; posthog-node v5 does
  not pull in @opentelemetry, so no change to protobufjs from this bump).
2026-05-20 15:23:28 +08:00
kami
fd2e8d9352
fix: resolve shared frame screen paths from referrer (#2318)
Co-authored-by: multica-agent <github@multica.ai>
2026-05-20 15:22:59 +08:00
chaoxiaoche
25f977c84c
fix(web): rename FileViewer Share button label to Export (#2233)
* fix(web): rename FileViewer Share button label to Export

The toolbar button in FileViewer triggers a menu where 5 of 8 items are
Export/Download actions and only Deploy to Vercel + Copy link are real
"share" semantics. The previous "Share" label mismatched user mental
model (Share = send a link to someone, Export = save a file locally),
which likely contributed to a steep studio_view -> studio_click drop in
the artifact-export funnel (~18% step conversion).

Rename only the i18n value for fileViewer.shareLabel across all 18
locales so the button reads "Export" (and the locale-equivalent). Keys,
component code, icon, and menu contents are unchanged. The unused
fileViewer.share key and the unrelated examples.shareMenu /
preview.shareMenu keys are left intact.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Improve export CTA and BYOK test feedback

* Fix BYOK provider review regressions

* Fix settings locale regressions

* Fix design system import header layout

---------

Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 15:22:32 +08:00