open-design/apps/web/tests
lefarcen b2b94dbde7
feat(desktop): follow OS language in packaged builds (cherry-pick of #2544 into release/v0.8.0) (#2560)
* feat(desktop): follow OS language in packaged builds

Packaged Electron currently shows Open Design in en-US regardless of
the OS language setting, because the renderer's i18n picks its locale
from `navigator.language` and Chromium hard-codes that to en-US unless
the host process intervenes. Browser users and `tools-dev` users are
unaffected because their `navigator.language` already reflects the
OS / browser preference.

This change:

- Adds `applyOsLocaleSwitch(app)` in `@open-design/desktop/main`. It
  reads `app.getPreferredSystemLanguages()[0]` and (when called before
  Electron's `ready` event) points Chromium's `--lang` flag at it, so
  the renderer's `navigator.language` follows the OS. Safe to call
  more than once: `appendSwitch` is a no-op once `app.isReady()`.
- Calls the helper from both Electron entries: `apps/packaged` before
  its own `whenReady`, and `runDesktopMain` for tools-dev parity.
- Forwards the resolved locale through
  `BrowserWindow.webPreferences.additionalArguments` as
  `--od-os-locale=<bcp-47>`, parsed by the preload and exposed at
  `window.__od__.client.osLocale`. The host bridge type
  (`OpenDesignHostClient.osLocale`) is extended accordingly.
- Updates `detectInitialLocale` in `apps/web/src/i18n/index.tsx` to
  read that field as a new step between the existing localStorage and
  navigator fallbacks. Browser/web continues to fall through to
  `navigator.languages` unchanged.

The explicit `osLocale` channel exists in addition to `--lang` because
some `app.getPreferredSystemLanguages()` strings (e.g. `zh-Hant-TW`,
`pt-PT`) need to round-trip through `resolveSystemLocale` to land on
the right supported locale, which Chromium's `navigator.language`
cannot do on its own.

* fix(web): route OS locale read through getOpenDesignHost

The first cut of detectInitialLocale read `window.__od__.client.osLocale`
directly, which trips `tests/host-boundary.test.ts` — that guard test
keeps web source from referencing preload globals by name so the
boundary stays single-source. Switch to `getOpenDesignHost()` from
`@open-design/host`, and rewrite the i18n test to install the host via
`installMockOpenDesignHost` instead of poking the global directly.

* fix(tools-pack): unblock mac packaged build on pnpm workspaces

Two independent issues prevented `pnpm tools-pack mac build --to all`
from completing on a clean macOS workspace, both unrelated to the
desktop OS-locale change in this PR but bundled here because verifying
that change end-to-end required the packaged pipeline to actually
finish.

1. `apps/web/.next/standalone/node_modules/.pnpm/node_modules/<pkg>`
   contained dangling symlinks left by Next's nft trace (e.g. a
   `semver -> ../semver@5.7.2/node_modules/semver` link to a
   `.pnpm/<pkg>@<ver>` directory pnpm never created). The downstream
   `cp { dereference: true }` aborted the whole packaged pipeline
   with ENOENT. Walk every artifact tree before copy and unlink
   symlinks whose target doesn't resolve. Targets that *do* resolve
   stay untouched.

2. Next 16's standalone build under pnpm workspaces does not hoist
   peer-dep packages (react, react-dom, styled-jsx) into
   `<standalone>/apps/web/node_modules`. The downstream
   `web-standalone-after-pack.cjs` audit then does
   `createRequire(server.js).resolve('react/package.json')`, whose
   module walk falls out of the standalone tree and aborts the
   electron-builder phase. Add a `hoistStandaloneNextPeerDeps` step
   for the web standalone artifact only: it locates the
   `<pkg>@<version>` (not peer-resolved sibling) directory under
   `.pnpm` and symlinks it into `apps/web/node_modules/<pkg>`. The
   subsequent `cp { dereference: true }` then writes the real
   directory into the cache so the packaged tree stays self-contained.

Verified by `pnpm tools-pack mac build --to all` succeeding end-to-end
(zip + dmg + app), then `pnpm tools-pack mac install` and
`pnpm exec tools-pack mac inspect --expr` reading the desired
`__od__.client.osLocale` from the packaged renderer.

* feat(desktop): fold encodeURIComponent + manual locale source + pet window from #2554

Three defensive improvements lifted from @Eli-tangerine's parallel
implementation on #2554, kept consistent with the OS-locale chain
already on this branch:

- The argv value crossing main → preload is now wrapped with
  encodeURIComponent / decodeURIComponent so a locale string with `;`,
  `=`, or any other Chromium argv special char round-trips cleanly.
  BCP-47 region tags don't carry those today, but the renderer parser
  no longer has to assume it.
- `setLocale` now also writes `open-design:locale-source = "manual"`
  to localStorage, and `detectInitialLocale` only treats the stored
  locale as winning when that marker is present. An untagged value
  (left over from a future auto-write path, or a stale install) no
  longer pins the app to an old language once the host injects a
  fresh OS locale. Today `setLocale` is the only writer so the marker
  has no behaviour difference yet — this is a defensive net.
- `createDesktopPetWindow` now receives `osLocale` and forwards the
  same `additionalArguments` as the main `BrowserWindow`, so the
  pet renderer's `__od__.client.osLocale` is consistent with the main
  window's instead of being silently undefined.

Co-authored idea credit: changes mirror the locale-piece of
@Eli-tangerine on #2554 — that PR is closing in favour of this one.

Tests: detect-initial-locale gets a new "untagged localStorage value
loses to host locale" case. desktop 62/62, host 13/13, web i18n +
host-boundary 15/15 stay green.

* feat(web): fold onboarding view styles from #2554

Pulls the 747-line addition to `apps/web/src/styles/home/entry-layout.css`
from @Eli-tangerine's #2554 — the visual layer for the global onboarding
flow (`/onboarding` view, Connect / About-you / Design-system steps).
The view itself was already plumbed through `EntryShell.tsx`; this adds
the styling that makes it shippable on v0.8.0.

#2554 is closing in favour of this branch, so the CSS lands here so the
onboarding work doesn't get dropped on the floor.

Co-authored idea credit: @Eli-tangerine — original styling on #2554.

* fix(tools-pack): make hoistStandaloneNextPeerDeps idempotent across builds

Addresses non-blocking review by @PerishCode on #2560: the previous
`if (await pathExists(linkPath)) continue;` guard uses `access()`,
which follows symlinks. A stale symlink from a previous build whose
`.pnpm/<pkg>@<version>` target moved (e.g. after a react/react-dom
version bump that invalidates the workspace-build cache key and forces
a re-run) reports as missing through `pathExists`, then `symlink()`
rejects with EEXIST and the unhandled rejection aborts the packaged
build.

Switch to `lstat` (which does not follow the link) so we can tell
"genuinely empty slot", "real directory left by Next" and "stale
symlink" apart, then unlink stale entries before re-creating. Also
move `stripBrokenSymlinks` ahead of `hoistStandaloneNextPeerDeps` in
`copyWorkspaceBuildArtifactsToCache` so any leftover dangling links
that survived a previous run are cleared before hoist tries to write.
2026-05-21 18:23:20 +08:00
..
analytics feat(analytics): always-on $exception capture with early window hooks (#2521) 2026-05-21 13:07:26 +08:00
artifacts Fix pointer HTML artifact previews (#2075) 2026-05-18 18:28:28 +08:00
components Fix packaged auto-update release validation (#2565) 2026-05-21 18:15:53 +08:00
edit-mode Implement manual edit inspector (#1448) 2026-05-13 13:25:58 +08:00
hooks fix(web): coalesce chokidar rewrite bursts before refreshing files (#2326) 2026-05-20 11:12:53 +08:00
i18n feat(desktop): follow OS language in packaged builds (cherry-pick of #2544 into release/v0.8.0) (#2560) 2026-05-21 18:23:20 +08:00
lib Fix packaged auto-update release validation (#2565) 2026-05-21 18:15:53 +08:00
observability feat(observability): web lifecycle telemetry + stable installationId migration (#2527) 2026-05-21 15:37:48 +08:00
providers fix: stop stale pinned todos after terminal runs (#2321) 2026-05-20 11:13:20 +08:00
runtime fix(daemon,web): block pitch-deck placeholder publishes and unbreak framework decks (#2384) 2026-05-20 16:20:34 +08:00
state Remove resume conversation button (#2562) 2026-05-21 17:55:03 +08:00
styles Keep PR 2400 changes without folder pickers (#2462) 2026-05-20 22:07:30 +08:00
utils [codex] Add pet task center and desktop pet (#1833) 2026-05-19 15:38:39 +08:00
analytics-app-version.test.tsx fix(analytics): app_version=0.0.0 + media providers clicks + lock run_finished error_code (#2453) 2026-05-20 21:50:11 +08:00
analytics-configure-globals.test.ts feat(analytics): ship PostHog v2 event schema (#2285) 2026-05-20 13:04:20 +08:00
analytics-identity.test.ts feat(analytics): PostHog product analytics (P0 events, consent-gated, packaged) (#1428) 2026-05-12 22:32:42 +08:00
analytics-scrub.test.ts feat(analytics): PostHog product analytics (P0 events, consent-gated, packaged) (#1428) 2026-05-12 22:32:42 +08:00
api-attachment-context.test.ts Inline attached file context for BYOK chats (#1730) 2026-05-15 15:52:15 +08:00
App.test.ts fix(web): suppress autosave indicator for draft-only Connector key edits (#1232) 2026-05-11 20:52:45 +08:00
comments.test.ts [codex] Add visual draw annotation context (#1547) 2026-05-13 20:02:19 +08:00
host-boundary.test.ts refactor desktop host bridge (#2246) 2026-05-19 18:27:05 +08:00
quickSwitcherRecents.test.ts feat(web): add Cmd/Ctrl+P quick file switcher (#556) 2026-05-06 10:31:50 +08:00
router-marketplace.test.ts [codex] Add global onboarding flow without AMR (#2272) 2026-05-19 22:00:40 +08:00
router.test.ts Garnet hemisphere (#1702) 2026-05-14 21:12:50 +08:00
sidecar-proxy.test.ts Enable LAN web dev access (#1947) 2026-05-19 17:50:50 +08:00