Commit Graph
1255 Commits
Author SHA1 Message Date
Siri-RayandGitHub 9d7e4658df fix(host): accept folder imports without entry files (#2701)
Generated-By: looper 0.8.1 (runner=worker, agent=codex)
2026-05-22 17:21:22 +08:00
lefarcenandGitHub 1fd50197dd perf(landing): route HTML Anything screenshots through R2 + Image Resizing (#2696)
The `/html-anything/` landing page was loading 11 screenshots directly
from `raw.githubusercontent.com/nexu-io/html-anything/main/docs/…` —
that's 6.5 MB of raw PNGs per first visit, no AVIF/WebP transcoding,
and a separate cross-origin TLS handshake the rest of the site doesn't
pay. The TODO comment at line 70 of the page has been calling this out
since the page landed.

This PR routes those images through the same pipeline as `hero.png`:

- Source PNGs upload to `static.open-design.ai/landing/assets/html-anything/`
  on the existing `open-design-static` R2 bucket. Upload package lives
  at `~/Downloads/r2-html-anything-orig/` (README + upload.sh ready);
  merge AFTER running that script.
- Page uses `imageAsset('html-anything/<name>.png', { width, quality })`
  so CF Image Resizing emits `format=auto` AVIF/WebP variants — same
  contract every other landing image already uses.
- Banner is the LCP element; gets `srcset` across 768/1280/1920/2400
  (mirroring `heroImageSrcset`) plus its existing `fetchpriority="high"`.
- 10 below-the-fold screenshots become `<LazyImg>` so they pick up the
  precise IntersectionObserver (rootMargin 300px) instead of Chrome's
  native lazy load that over-prefetches up to 3000px.

Expected on first visit (mobile, AVIF-capable browser):
  banner:        ~150 KB AVIF      (was 2.05 MB PNG)
  10 screenshots ~80-120 KB each  (was 500-900 KB PNG each)
  total          ~1.2-1.5 MB      (was 6.5 MB)
  LCP            estimated -800ms to -1.5s
2026-05-22 17:21:00 +08:00
YOMXXXandGitHub 3e2f037730 feat(daemon): add CTA hierarchy static QA pass (refs #2251) (#2427)
* feat(daemon): add CTA hierarchy static QA pass

Introduce apps/daemon/src/qa/cta-hierarchy.ts exporting a pure
analyseCtaHierarchy(html) that parses generated prototypes with cheerio
and flags three precision-biased findings: multiple-primary CTAs in the
same section, ambiguous-weight (all CTAs share identical class + inline
style), and misleading-prominence (secondary-coded copy like "Learn
more" / "了解更多" styled with primary weight).

CTA candidates come from <button>, <a>, role="button" with btn/button/cta
class markers plus CTA copy keywords covering both English (Get started,
Sign up, Buy, Subscribe, Learn more, ...) and Chinese (立即购买,
立即下单, 了解更多, ...). Weight is inferred from class tokens
(primary/solid/filled/accent/cta) and from non-transparent inline
background-color, matching the inverse of the issue #2251 sample where
the header CTA was rendered with the neutral .btn style.

This PR only ships the pure function plus its tests. HTTP route, CLI
subcommand, and any auto-repair feedback loop are deliberate follow-ups
so the first cut can land without touching the daemon HTTP surface.

Refs #2251

* fix(qa): respect container boundaries in CTA hierarchy heuristics

Two precision fixes from review of #2427:

- computeContainerKey()'s parent fallback keyed by tag name alone, so
  flat layouts like <div><a class=btn-primary>...</a></div> repeated
  for sibling cards all landed in 'parent:div' and
  detectMultiplePrimary() reported a fake shared-section conflict on
  what is in fact one CTA per card. Switch to parent-node identity
  (positional index of the matched parent within its tag group, same
  trick the landmark branch already uses), so each sibling wrapper
  gets its own bucket.
- detectAmbiguousWeight() compared signatures across the entire
  document, so two unrelated sections each containing one '.btn' CTA
  with matching style would trigger 'ambiguous-weight' despite neither
  container having 2+ CTAs. The PR body's rule is narrower — 'every
  CTA in a container shares the same class + inline style' — so bucket
  by containerKey first and only emit the finding for containers with
  2+ CTAs whose signatures are identical.

Tests lock both behaviors down:
- sibling <div> card-grid without a landmark ancestor stays under the
  multiple-primary threshold;
- one-CTA-per-section pairs stay under the ambiguous-weight threshold.
2026-05-22 16:53:14 +08:00
3743c65e6e fix(web): allow configured dev origins (#2216)
Co-authored-by: mrcfps <mrc@powerformer.com>
2026-05-22 16:44:16 +08:00
YOMXXXandGitHub ca8de8bd0b fix(web): localize plugin card actions for Chinese locale (#2424)
PluginCard 中 Details/Use/Use with query/Applying…/Publish/Contribute/
Starting… 等按钮文本及 aria-label/title 此前为硬编码英文,导致中文
locale 下仍显示英文。新增 pluginCard.* 命名空间下 15 个 i18n key,
补全 zh-CN 简体与 zh-TW 繁体翻译;其他 16 个 locale 走 ...en 字典
fallback,保留既有显示行为。

Fixes #2079
2026-05-22 16:43:54 +08:00
Jetsung ChanandGitHub 914c5b3a15 fix(pack): add missing package.json fields for Linux AppImage build (#2276)
Fix electron-builder failure in `pnpm exec tools-pack linux build --to appimage`
by adding required description, author, and repository fields to generated
package.json in tools/pack/src/linux.ts.
2026-05-22 16:30:44 +08:00
Patrick AandGitHub c5e38bbe58 fix(prompts): remove 10-item cap from discovery TodoWrite plan (#2298)
* fix(daemon): remove 10-item cap from discovery TodoWrite plan prompt

The RULE 3 sentence in DISCOVERY_AND_PHILOSOPHY told the model to write
'a plan of 5–10 short imperative items'. That upper bound caused the agent
to cap every plan at exactly ten steps even when the task genuinely needed
more. The TodoWrite JSON schema imposes no maxItems constraint, so the cap
was entirely prompt-driven.

Replace '5–10 short imperative items' with 'short imperative items covering
the work'. TodoWrite intent, RULE 3 label, and planning-before-building
requirement all survive unchanged.

Red spec: apps/daemon/tests/prompts/discovery-todo-cap.test.ts

* fix(prompts): remove 10-item cap from contracts discovery copy and harden tests

[pass-6,7 BLOCKER] packages/contracts/src/prompts/discovery.ts still had
the old '5-10 short imperative items' wording. apps/web imports
composeSystemPrompt from @open-design/contracts (ProjectView.tsx:43),
so web-originated chat runs were still subject to the cap.

[pass-8 WARNING] discovery-todo-cap.test.ts did not cover the contracts
copy, leaving that path unguarded. Also no guard against semantically
equivalent re-introduction via 'at most / maximum / no more than'.

Changes:
- packages/contracts/src/prompts/discovery.ts: apply same wording fix as
  apps/daemon; add inline rationale comment
- apps/daemon/src/prompts/discovery.ts: add inline rationale comment
- apps/daemon/tests/prompts/discovery-todo-cap.test.ts: add 4th assertion
  blocking 'at most|maximum|no more than N item' re-introduction
- packages/contracts/tests/system-prompt.test.ts: add 5-assertion suite
  guarding the contracts copy and composed prompt output
2026-05-22 16:23:37 +08:00
Nishant ChhillarandGitHub f183f937c0 fix: improve long project name rendering (#2258) 2026-05-22 16:19:06 +08:00
mehmet turacandGitHub 45c77fac41 fix(web): restore manual source edit tabs (#1854)
* fix(web): restore manual source edit tabs

* test: target active manual edit preview
2026-05-22 16:11:01 +08:00
6273874e30 fix codex stream reconnect handling (#2274)
Co-authored-by: Sumit Dhawan <sumitdhawan@Sumits-MacBook-Air.local>
2026-05-22 16:10:42 +08:00
Neha PrasadandGitHub 4cd93a54dd fix(web): Enter to send in project chat composer (#2676) 2026-05-22 15:57:20 +08:00
0ba88a064d Add Codex interactive capability map example (#2657)
* Add Codex interactive capability map example

* Fix Codex map preview entry

---------

Co-authored-by: tuolaji <tuola@tuolajideMacBook-Air.local>
2026-05-22 15:49:16 +08:00
shangxinyu1andGitHub 7fd0169888 fix(web): keep trailing punctuation out of bare chat URLs (#2678)
* fix(web): keep trailing punctuation out of bare chat URLs (#2675)

* test(web): broaden bare URL punctuation regression coverage

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* test(web): broaden bare URL punctuation regression coverage

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* test(e2e): harden smoke suite tools-dev port startup

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)
2026-05-22 15:49:05 +08:00
Golden Obsidian LLCandGitHub aa0616062d refactor(daemon): introduce HTTP Request Adapter + typed Deps (#2636)
* refactor(daemon): introduce HTTP Request Adapter + typed Deps (proof on active-context-routes)

Adds a typed HTTP boundary Adapter under apps/daemon/src/http/ that replaces
the untyped ServerContext service-locator pattern (30+ fields, mostly any)
for route handlers. Routes become pure (input, deps) -> Result<output>
functions, unit-testable without Express or supertest.

Six new modules under apps/daemon/src/http/:
  - types.ts        Result<T,E>, ok(), err(), JsonRouteSpec, Handler,
                    RouteInputContext, HttpMethod, InputParser
  - parse.ts        rawInput(req), validationError(message, issues?)
  - response.ts     sendJson(), sendApiError(), statusForError() +
                    ERROR_STATUS_BY_CODE map
  - origin-guard.ts guardSameOrigin(req, origin) wrapping isLocalSameOrigin
                    as a Result
  - adapter.ts      defineJsonRoute(), mountJsonRoute() (only place that
                    knows about req/res)
  - index.ts        barrel

active-context-routes.ts migrated as proof of pattern. parsePostActive(),
handlePostActive(), handleGetActive() are now pure functions; postActiveRoute
and getActiveRoute are exported route specs. The wire signature
registerActiveContextRoutes(app, ctx) is preserved so server.ts is untouched.

Spec at specs/current/daemon-http-adapter.md captures the strangler migration
order for the remaining route files (mcp-routes, chat-routes, artifact
routes, etc.) and a StreamRoute follow-up where the Run Orchestrator lands.

Wire-format note: cross-origin response moves from the legacy
{ error: 'cross-origin request rejected' } shape to the structured
{ error: { code: 'FORBIDDEN', message: ... } } shape. Backwards-compatible
via the existing CompatibleErrorResponse = ApiErrorResponse | LegacyErrorResponse
union in @open-design/contracts.

Validation:
  - pnpm install (post-rebase, exit 0)
  - pnpm --filter @open-design/daemon typecheck (both tsconfig.json and
    tsconfig.tests.json silent => pass)
  - pnpm --filter @open-design/daemon test: 15 new tests pass
    (tests/http/adapter.test.ts + tests/active-context-routes.test.ts).
    84 pre-existing failures across 23 files are unchanged and unrelated
    to this PR (Windows symlink / short-name / colon-in-filename, upstream
    behavior drift, missing plugin marketplace fixtures, and a freshly-
    added tools-connectors-cli suite of 38 failures that landed during
    the rebase).

Sharpens W4/W5 of specs/current/maintainability-roadmap.md and unlocks
W6 (Run Orchestrator).

* chore: add core-js, electron-winstaller, protobufjs, sharp to pnpm.onlyBuiltDependencies
2026-05-22 15:20:15 +08:00
558fedd207 fix(landing): wire GA4 rollout config (#2615)
Co-authored-by: ashley li <ashleyli@ashleydeMacBook-Air-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 14:56:58 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
a0e1b9510b docs(blog): refresh 3-day traffic digest (#2669)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-22 14:44:47 +08:00
lefarcenandGitHub 5f7d65d513 perf(landing): preconnect api.github.com + rAF-throttle scroll listener (#2666)
Two PSI-targeted wins (split from #2599 follow-up).

1. New `resource-hints.astro` mounted in every page's <head> declares
   `<link rel="preconnect" href="https://api.github.com" crossorigin>`.
   The inline enhancer script on /` issues 3 fetch() calls to
   api.github.com right after DOMContentLoaded (stars, latest release,
   contributors). Without preconnect each pays a full DNS + TCP + TLS
   handshake (~150-300ms) inline with the fetch. With preconnect those
   handshakes happen in parallel with HTML parse and all three share one
   warmed HTTP/2 connection.

2. Wrap the scroll listener's read + classList write in
   requestAnimationFrame. Trackpads and high-rate wheels fire scroll
   faster than display refresh, and every callback that hits classList
   triggers layout recalc. PSI was attributing ~700ms of "forced reflow"
   to the un-throttled version. The rAF gate collapses each burst to one
   DOM mutation per frame; `{ passive: true }` is preserved so the
   listener still doesn't block the scroll thread.

   Same throttling pattern mirrored to `header-enhancer.astro` (used by
   every sub-page) and `home-enhancer.astro` (kept in lockstep even
   though /` currently uses its own inline copy).

Expected PSI delta:
  - "Preconnect to required origins" hint: cleared
  - "Forced reflow" diagnostic 700ms → near zero
  - LCP: small bonus from earlier GH fetch warm-up (~100-300ms)
2026-05-22 14:06:39 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
97f75c3a27 docs(readme): refresh contributors wall (#2665)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-22 14:06:28 +08:00
shangxinyu1andGitHub cc6edb9afe Proxy GitHub metadata through the daemon (#2654)
* Proxy GitHub metadata through the daemon

* fix(contracts): share GitHub metadata responses

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* fix(contracts): align GitHub fetchedAt payload types

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* Proxy GitHub metadata through the daemon

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)
2026-05-22 14:06:07 +08:00
Yuhao ChenandGitHub 83c4ef979a Polish onboarding dropdown menu (#2601) 2026-05-22 14:05:41 +08:00
Yuhao ChenandGitHub bddec2391d Fix Simple Deck discovery form contract (#2602) 2026-05-22 14:05:24 +08:00
정수현andGitHub 63c8dd1fed fix(web): localize the Routines settings page (#2598)
The Settings → Automations/Routines section (RoutinesSection.tsx) used
hardcoded English for every label, action, status pill, schedule
description, time suffix, and dialog. With the app language set to
Chinese the whole section stayed in English, breaking the localized
experience.

Introduce a dedicated `routines.*` i18n namespace, wire RoutinesSection
and the SettingsDialog routines header through `useT()`, and add
Simplified + Traditional Chinese translations. Schedule summaries are
now built from interpolated keys instead of concatenated English
literals, and the 12-hour AM/PM suffix is localized too. The remaining
locales fall back to English via the existing `...en` spread, matching
the repo's incremental per-locale translation pattern.

A regression guard in locales.test.ts asserts the representative
`routines.*` keys are actually translated (not English fallback) in
zh-CN and zh-TW.
2026-05-22 14:05:11 +08:00
Het SavaniandGitHub bfdd029d7d feat(web): polish automation schedule picker and summary pill UI (#2593)
* feat(web): polish automation schedule picker and summary pill UI

* refactor(web): share automation schedule summary formatting

* fix(web): scope automation pill flex layout to schedule segments
2026-05-22 14:04:51 +08:00
nmsnandGitHub 575d5aaf2f fix(web): strip trailing punctuation from markdown link URLs (#2568) (#2591) 2026-05-22 14:04:39 +08:00
Fl0rencessandGitHub 5b53c44e13 fix: enable SenseAudio BYOK TTS (#2570)
* fix: enable SenseAudio BYOK TTS

* fix: handle BYOK SenseAudio TTS failures

* fix: harden SenseAudio BYOK TTS responses

* fix: preserve BYOK speech tool failure kind

* fix: handle versioned SenseAudio TTS base URLs
2026-05-22 14:04:29 +08:00
epicandGitHub e8b5dd8aaf feat(deploy): add one-click Docker/Podman Compose installer for Linux… (#2414)
* feat(deploy): add one-click Docker/Podman Compose installer for Linux and macOS

- Add install.sh with interactive wizard, Podman/Docker runtime detection,
  port conflict check, health verification, and systemd user unit creation
- Add update.sh for image pull and restart with health check
- Add uninstall.sh with interactive user data backup before removal
- Unify CLI output styling with step/ok/warn/error/info helpers
- Add install-guide.md documentation
- Add install.test.ts integration test suite

* feat(deploy): add one-click Docker/Podman Compose installer

- interactive setup wizard with port, image, CORS, memory prompts
- automatic Docker/Podman detection with install guidance
- systemd user unit for Linux, health check polling
- update.sh (pull + restart + prune) and uninstall.sh (backup + cleanup)
- node:test integration suite and install-guide.md

* style(deploy): improve POSIX sh compatibility and systemd unit handling

- unify shell shebangs to #!/usr/bin/env bash

- add pipefail option for better error handling

- fix systemd unit for Podman: remove After/Requires when no service

- correct documentation to match actual uninstall behavior

* fix(deploy): address review feedback for installer scripts

- remove curl | sh path, document clone-first only

- isolate tests via docker-compose.override.yml with unique names

- support both --image <ref> and --image=<ref> in update.sh

- add running container detection before install

* docs(install): remove demo scripts and add MCP note
2026-05-22 14:04:16 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
bde64f8bdd Update docs/assets/github-metrics.svg (#2664)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-22 13:21:28 +08:00
lefarcenandGitHub 7f03030f3f perf(landing): self-host fonts + inline critical CSS (#2599)
* perf(landing): self-host fonts + inline critical CSS

PageSpeed Insights flagged ~2.3s of render-blocking on /:
  globals.css   12.9 KB external link, 160ms
  fonts CSS     2.2 KB  fonts.googleapis.com, 750ms
  + 4 woff2     ~1200ms each from fonts.gstatic.com

Two changes drop that whole chain:

1. Self-host fonts via @fontsource-variable/{inter,inter-tight,
   playfair-display,jetbrains-mono}. Each family ships a single variable
   woff2 (covers all weights we use) that Astro bundles into /_astro/*
   alongside the rest of the build, served same-origin through CF Pages —
   no separate TLS handshake, no Google Fonts CSS round-trip. The CSS
   variable names get an extra alias in front (`'Inter Tight Variable',
   'Inter Tight', ...`) so a system fallback still works if the package
   ever ships under a different family name.

2. `astro.config.ts: build.inlineStylesheets: 'always'` inlines every
   emitted <style> into the HTML <head> instead of emitting a separate
   /_astro/*.css link. The HTML grows from ~13KB to ~28KB (gzip) but
   loses one stylesheet round-trip + the entire @font-face chain that
   used to gate text rendering.

Component cleanup: the `<FontStylesheet>` component (preconnect + link to
fonts.googleapis.com) is no longer needed and is deleted, removed from
all 7 places that mounted it. og.astro keeps its own font setup since
it renders to a screenshot.

Expected effect (from PageSpeed Insights "Render-blocking requests"
diagnostic on the previous build):
  FCP  1.9s → ~1.2s
  LCP  2.2s → ~1.5s

Verified: pnpm typecheck 0 errors, pnpm build 1853 pages 78s, preview
serves /_astro/*.woff2 as font/woff2 same-origin, 0 fonts.googleapis or
fonts.gstatic references in the built HTML.

* perf(landing): include Playfair italic + bump nix pnpm-deps hash

Two follow-ups on the self-host fonts PR:

1. globals.css imported only `@fontsource-variable/playfair-display`,
   which ships @font-face for font-style: normal only. The previous
   Google Fonts URL included the italic axis (`ital,wght@0,500;1,400;
   ...`) and several rules (.roman, .work-rule .roman, .sec-rule .roman,
   plus 8 other italics across globals.css + sub-pages.css) render
   Playfair italics via `font-family: var(--serif); font-style: italic`.
   Without the italic face self-hosted, those would fall through to
   Times New Roman italic or browser synthesis. Adding
   `wght-italic.css` keeps the typography visually equivalent.

2. nix/pnpm-deps.nix uses a fixed-output derivation hash that has to
   match the pnpm vendored store; adding the four fontsource packages
   changed pnpm-lock.yaml so the hash has to be bumped to the value Nix
   reported in CI.

Codex (Looper reviewer) flagged #1 as non-blocking.

* perf(landing): pin fontsource versions exactly per repo guard

`pnpm add` defaulted to caret ranges (`^5.2.8`) but repo guard rejects
non-exact specs ("dependency specs must be exact versions like 1.2.3 or
workspace:*"). That was the actual cause of the Preflight + Validate
workspace failures — pinning to the locked versions Codex reviewer
called out:

  @fontsource-variable/inter             5.2.8
  @fontsource-variable/inter-tight       5.2.7
  @fontsource-variable/jetbrains-mono    5.2.8
  @fontsource-variable/playfair-display  5.2.8

`pnpm guard` now passes locally (6/6 tests).
2026-05-22 11:49:16 +08:00
Tom HuangandGitHub 6dcf55e777 Update README to clarify Open Design description (#2627) 2026-05-22 10:15:51 +08:00
shangxinyu1andGitHub 95bbdbb734 [codex] test(e2e): harden settings and entry regressions (#2578)
* test(e2e): harden settings and entry regressions

* test(e2e): align entry chrome coverage with current UI

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* fix(web): refresh saved media providers from daemon

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* test(web): align media provider reload expectations

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* fix(web): keep daemon media-provider reloads authoritative

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* fix(web): make media-provider reload precedence depend on dialog edits

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)

* fix(web): preserve pending media-provider edits across stale autosaves

Generated-By: looper 0.6.0 (runner=fixer, agent=codex)
2026-05-22 10:04:12 +08:00
4cb5778669 feat(landing-page): retitle HA page for brand-defense SEO across 18 locales (#2618)
Replaces the HA page `<title>` from the previous descriptive form
("HTML Anything — your local AI agent writes the HTML, you ship
it | Open Design") with a noun-stack that captures three high-value
brand-defense queries:

  - "open source html anything"        — researching alternatives
  - "html anything official"           — verifying the canonical source
  - "open source html anything official" — precise long tail

Why
- The descriptive title before this change told the story but didn't
  match what users actually type when they land on this page from
  search. The brand has been growing fast (4.1K+ stars on the repo);
  visitors increasingly arrive with intent to verify ("is this the
  real one?"), to compare alternatives, or to find the canonical
  install path. The new title format leads with the tokens those
  queries hit.
- Joey called the change explicitly. The brand has reached a stage
  where SEO defense beats descriptive copy on a one-line surface.

What
- New `OPEN_SOURCE_OFFICIAL_TITLE` constant maps every landing-page
  locale to its translated title. Pattern: `{open-source} HTML Anything
  {official}` with no separator. Word order follows what's natural in
  each locale (German: Offiziell at end; French/Romance: adjectives
  after the noun; CJK: adjectives before).
- The three explicit copy blocks (`HTML_ANYTHING_COPY_EN`,
  `_ZH`, `_ZH_TW`) all reference the constant rather than holding
  their own literal title strings, so future title changes update
  in one place.
- `derivedHtmlAnythingCopy()` (which builds copy for the 15
  non-explicit locales by spreading EN over locale UI strings) now
  pulls the title from the same constant via `[locale]` lookup with
  EN as a defensive fallback.
- Adds three new `SoftwareApplication.alternateName` entries for the
  same query stack. Schema-level signal complements the title-level
  signal: the structured-data entity now matches whichever shape the
  user typed regardless of UI locale, since these are proper-noun
  query variants that don't translate.

Surface area
- Marketing site only. Single file: `app/pages/html-anything/index.astro`.
- No `apps/web`, no `apps/daemon`, no contracts, no CLI.
- No new dependencies.

Validation
- `pnpm --filter @open-design/landing-page typecheck` — 0 errors.
- Local dev: every locale's HA page renders the new title.
  - en   → `<title>Open Source HTML Anything Official</title>`
  - zh   → `<title>开源 HTML Anything 官方</title>`
  - ja   → `<title>オープンソース HTML Anything 公式</title>`
  - de   → `<title>Open Source HTML Anything Offiziell</title>`
  - ar   → `<title>HTML Anything مفتوح المصدر الرسمي</title>`
  - …same shape across all 18 landing locales.

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-22 08:36:47 +08:00
3db1e27b81 fix(landing-page): translate Library/Tutorials, drop dropdown numbers, dedupe Product menu (#2610)
* fix(landing-page): translate Library/Tutorials, drop dropdown numbers, dedupe Product menu

Three small UX issues surfaced after #2605 landed Tom's full i18n
bundle. None of them block deploy; all of them affect every visitor
who lands on the site, especially the non-English ones.

Issues
1. The new Library / Tutorials nav labels in #2605's header
   restructure were hardcoded English. On `/zh/`, `/ja/`,
   `/de/`, etc. the rest of the nav was localized but those two
   labels stayed "Library" / "Tutorials" inside otherwise-translated
   chrome.
2. The four catalog facets inside the Library dropdown carried the
   live count badges from the previous top-row treatment ("Skills132",
   "设计系统150", etc.). Inside a dropdown they read as a glued-on
   suffix rather than a tag, and Joey called them out as "weird —
   drop them entirely".
3. The Product dropdown still listed Tutorials as a child item
   alongside Open Design and HTML Anything. After #2605 made
   Tutorials a standalone top-level link, the duplicate was
   confusing — same URL appearing in two places in the same nav row.
4. Chinese (zh and zh-tw) had `skills: 'Skill'` — the singular
   English word, untranslated in an otherwise fully Chinese nav.

Fixes
- Extends `HeaderCopy.nav` interface with `library` and `tutorials`
  keys; populates both across all 18 landing locales (en, zh, zh-tw,
  ja, ko, de, fr, ru, es, pt-br, it, vi, pl, id, nl, ar, tr, uk).
- Updates `header.tsx` to read `headerCopy.nav.library` and
  `headerCopy.nav.tutorials` instead of literal strings.
- Removes the four `<span class="dropdown-num">{count}</span>` badges
  from inside the Library dropdown items. The numbers still sit on
  the catalog index pages themselves (header counts, hero) and on
  the homepage; the dropdown is for navigation, not status.
- Removes the Tutorials `<li>` from the Product dropdown — Tutorials
  lives at the top-row level only. Updates the Product trigger's
  `is-active` set so it no longer highlights when on `/tutorials/`.
- zh/zh-tw `nav.skills` switches to `技能` (the actual translation
  every other Chinese localized site uses for "skills" in this
  context).

Bonus: catalog-row horizontal padding unified at 24px to match the
fix already applied across `featured-card` / `template-card` /
`system-card` / `source-card` (see PR #2600). The skill list grid
on `/skills/` was the last family still on `padding: 22px 0`.

Surface area
- Marketing site only. `apps/landing-page/app/_components/header.tsx`,
  `app/i18n.ts`, `app/sub-pages.css`, `app/globals.css`.
- No `apps/web`, no `apps/daemon`, no contracts, no CLI surfaces.
- No new dependencies.

Validation
- `pnpm --filter @open-design/landing-page typecheck` — 0 errors
- Local dev: `/zh/` shows `资源库 / 教程 / 博客`; `/zh/` Library
  dropdown shows `技能 / 设计系统 / 模板 / 工艺` without count badges.
- Local dev: `/` Product dropdown shows only Open Design + HTML
  Anything (no Tutorials child); top-row Tutorials link is the
  single canonical entry to that page.

* fix(landing-page): keep catalog-row horizontal padding at narrow widths

#2610's catalog-row 24 px fix only addressed the desktop rule.
The `@media (max-width: 720px)` override block still set
`.catalog-row a { padding: 18px 0 }` — zero horizontal — which is
what Joey's screenshot was actually showing on narrow viewports.

Switches the narrow padding to `18px 20px`. Matches the same
narrow inset `.source-card` already uses (line 1058 of this file)
so the catalog-row family stays in lockstep with the rest of the
catalog tile families at narrow widths.

Also drops the now-stale hover override (`padding-left: 8px;
padding-right: 8px`) — that rule existed only to compensate for
the zero-horizontal default and now produces a visible jolt
against the new 20 px base.

* fix(landing-page): featured-card description honors the 24px gutter

Same shorthand fragility pattern that #2600 fixed for template-card,
just one card family further down the file.

`.featured-card p` (the description line on each featured strip
tile, e.g. "Nº 0.001 deck-swiss-international" with the 16-列网格
blurb under it) had `margin: 0 0 12px` — three-arg shorthand,
which expands to TOP RIGHT BOTTOM = LEFT, so margin-left and
margin-right both got reset to 0.

The group rule `.featured-card a > * + *` had set the horizontal
margin to 24px so every text child sits inside a clean 24px gutter
from the card border. The shorthand on `<p>` overrode the group
rule and the description text bled right up against the card's
right edge.

Switches to `margin-bottom: 12px` only. Horizontal margin stays
owned by the group rule, matching the fix template-summary already
got in #2600.

Surfaced by Joey on `/skills/` featured strip — the exact symptom
he's been calling out for the last few rounds. Same shape, different
file.

---------

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-22 01:57:53 +08:00
439f071cb0 feat(landing-page): replicate #2469 SEO content with deploy + regression fixes (#2605)
* chore(landing-page): bring PR #2469 content wholesale onto post-revert main

Step 1 of replicating @pftom's #2469 work without the deploy-blocking
issues that forced #2603. This commit copies the full \`apps/landing-page/\`
diff from #2469's HEAD (`9d2a4f1`) onto current main verbatim — every
i18n bundle, every page rewrite, every \`[locale]/\` wrapper. Subsequent
commits on this branch then surgically restore the SEO fixes that
#2469 silently regressed and configure the sitemap to survive the
Cloudflare Pages 25 MiB limit, so deploy is healthy when this lands.

What's in this commit
- Tom's i18n bundle: \`i18n.ts\` (5377 lines), \`home-page-i18n.ts\`,
  \`info-page-i18n.ts\`, \`landing-ui-i18n.ts\`, \`content-i18n.ts\`
  (~10K lines total of locale data)
- 18 landing-page locales: en, zh, zh-tw, ja, ko, de, fr, ru, es,
  pt-br, it, vi, pl, id, nl, ar, tr, uk
- All existing pages rewritten to consume the new i18n bundle
- Full \`[locale]/<route>/\` wrapper tree for every catalog page
- \`plugin-registry.ts\` rewrite, \`catalog.ts\` adjustments
- \`astro.config.ts\` route + sitemap reconfiguration
- \`public/_headers\`, \`public/_redirects\`, \`public/favicon.svg\` adds
- \`_components/locale-switcher-script.astro\` add

What's intentionally NOT done in this commit (handled in follow-ups
on this same branch):
- Restore brand mark 44px + rounded corners (was lost from #2588)
- Restore HA SoftwareApplication \`alternateName\` array (was lost from #2566)
- Restore HA \`url\` canonical pointing at the landing page (was lost from #2586)
- Restore Product/Library/Tutorials/Blog nav grouping (was lost from #2588)
- Restore catalog-card padding 24px (was lost from #2600)
- Configure sitemap to filter \`[locale]/\` routes so the generated XML
  stays under 25 MiB and Cloudflare Pages accepts the deploy
- Add \`/zh-CN/* → /zh/*\` redirects for backwards-compatibility with
  any externally-linked OD-canonical locale URLs

Validation so far
- \`pnpm --filter @open-design/landing-page typecheck\` — 0 errors

* fix(landing-page): unblock deploy + restore SEO regressions on top of #2469

Step 2 of replicating @pftom's #2469. The previous commit on this
branch brings #2469's content wholesale; this commit applies the
surgical fixes that make the result actually deploy and preserves
the SEO improvements that #2469 silently regressed.

Fix 1 — sitemap stays under Cloudflare Pages 25 MiB upload limit
- `astro.config.ts` `filter` now drops every `/{locale}/...` route
  so the sitemap only emits canonical English URLs.
- Locale variants are still discoverable via the
  `<xhtml:link rel="alternate" hreflang="...">` annotations the
  `namespaces.xhtml: true` option emits inside each canonical entry.
  This is Google's recommended pattern for a multi-language site.
- Verified: post-fix `out/sitemap-0.xml` = 179 KB (was 38.4 MiB
  on the prior attempt that forced #2603's revert).

Fix 2 — header brand block restored to the polished version
- Logo `width/height` 36 → 44 (matches PR #2588's brand-mark refresh
  for visual weight against the new black speech-bubble glyph)
- `.brand-meta` block ("Studio Nº 01 · Berlin / Open / Earth") removed
  from the header bar; the same editorial flourish still lives on the
  rotated `.side-rail .rail-text` pseudo-elements at page edges.

Fix 3 — header nav grouped into Library + standalone Tutorials/Blog
- Skills / Systems / Templates / Craft are now children of a Library
  dropdown (matches PR #2588's grouping). Each row keeps its count
  badge inline; the trigger highlights when any of the four facet
  pages is active.
- Tutorials and Blog stay as standalone top-row items (PR #2588's
  original decision after Joey's review on the Learn dropdown).
- Contact removed from the header — it was a same-page anchor that
  the footer already surfaces.
- Hardcoded "Library" / "Tutorials" labels match the brand-name
  pattern: unlocalized across all 18 landing-page locales.

Fix 4 — HA SoftwareApplication entity canonicalized on the LP again
- `alternateName` is back to an explicit array of real query
  variants `["html anything", "html-anything", "htmlanything",
  "HTML Anything Editor", "The agentic HTML editor"]`. #2469
  re-routed it through `copy.schemaAlternateName` which dropped
  the literal alias declarations Google needs for spaced-vs-
  hyphenated-vs-joined matching. (Restores PR #2566.)
- `url` flips back from `HA_URL` (the GitHub repo) to the LP URL
  itself, matching the `BreadcrumbList` block on the same page.
  GitHub repo lives in `sameAs` as a peer surface. (Restores PR
  #2586. Without this, Google credits the GitHub repo as canonical
  for the entity, which is the opposite of what this surface
  exists for.)

Fix 5 — catalog-card horizontal padding unified at 24 px
- featured-card 22 → 24, template-card 20 → 24,
  system-card 18 → 24, source-card 28 → 24.
- For template-card, also moved horizontal padding into the group
  rule exclusively so future siblings join without re-asserting
  margin shorthands. (Restores PR #2600.)

Fix 6 — `_redirects` for the locale-code rename
- This bundle uses `zh` / `zh-tw` / `pt-br` / `es` (the codes Tom's
  i18n.ts ships). The previous OD landing-page used `zh-CN` /
  `zh-TW` / `pt-BR` / `es-ES`. Externally-indexed and inbound-linked
  URLs against the old prefixes now 301 to the new canonical.

Validation
- `pnpm --filter @open-design/landing-page typecheck` — 0 errors
- `pnpm --filter @open-design/landing-page build` — completed
  successfully; 18,204 pages built; sitemap-0.xml is 179 KB
  (well under the 25 MiB Cloudflare Pages limit).

* docs: promote 'open-source alternative to Claude Design' to README H1

Brings the missing README and .gitignore changes from #2469 that the
first wholesale-checkout in this branch missed (the auto-pulled diff
scope was filtered to apps/landing-page/ initially).

What
- Every README.*.md (13 locale variants) now leads with the
  "open-source alternative to Claude Design" tagline as a subtitle to
  the project name in the H1 / first paragraph. This was @pftom's
  brand-positioning commit (`ee851dc`) on the original #2469 branch.
- `.gitignore` adds `growth/**` to keep growth-research scratch out of
  the repo.

Why
- The README is one of the highest-PageRank surfaces a GitHub project
  exposes to Google. Promoting the "alternative to Claude Design"
  framing into the H1/subtitle position makes the project surface for
  exactly the query the SEO work in this PR is trying to capture.
- Without this commit, the replicated #2469 in this branch would still
  rank against the previous H1 ("Open Design") on GitHub crawls,
  letting the SEO win at the LP fall short on the GitHub surface.

This is a strict subset of #2469's content — pure docs, no code,
no behavior change beyond what GitHub renders on the repo overview.

---------

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-22 00:59:11 +08:00
eefaf4504a Revert "Enhance landing page with SEO-focused content and FAQ section (#2469)" (#2603)
This reverts commit 26ee030b4c.

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-22 00:23:37 +08:00
Tom HuangandGitHub 26ee030b4c Enhance landing page with SEO-focused content and FAQ section (#2469)
* Enhance landing page with SEO-focused content and FAQ section

- Updated `.gitignore` to include growth directory.
- Modified `astro.config.ts` to prioritize high-intent landing pages for SEO.
- Added new FAQ styles and layout in `globals.css` for better user experience.
- Implemented FAQ section in `page.tsx`, ensuring it aligns with structured data requirements.
- Created dedicated pages for agents and alternatives to Claude Design, enhancing SEO and user navigation.
- Introduced comparison page for evaluating Open Design against competitors.
- Added favicon links component for consistent branding across all pages.

* Add SVG favicon and update favicon links for improved branding

* Enhance landing page with official source pillars for improved branding and navigation

- Added five canonical "official source" pillars to the homepage, reinforcing key links: official site, GitHub repository, releases, documentation, and Discord community.
- Updated URLs for releases, issues, documentation, and license to streamline access and improve user experience.

* Add locale support and enhance landing page with language switcher

- Introduced locale management with a new i18n module, defining multiple languages for the landing page.
- Implemented a locale switcher in the topbar and header, allowing users to select their preferred language.
- Updated global styles for the locale selector and adjusted layout for better responsiveness.
- Enhanced SEO by ensuring localized content is served based on user selection.
- Added a script for automatic locale detection and persistence in local storage.

* Implement localized routing and enhance navigation with href utility

- Added `stripLocaleFromPath` and `localizedHref` functions to manage locale-based URL paths.
- Updated `localePath` to normalize paths based on the detected locale.
- Refactored links in the header, footer, and main page components to utilize the new `localizedHref` function for improved navigation.
- Introduced locale-aware routing for new pages, ensuring consistent user experience across different languages.
- Enhanced SEO with alternate links for localized content in the sub-page layout.

* Update header component to use new logo format

- Replaced favicon image with a new logo in WebP format for improved performance and quality.
- Ensured consistent branding across the landing page with the updated logo.

* Enhance landing page with localization and new UI components

- Introduced a comprehensive localization schema for content management, allowing for multilingual support across various sections.
- Updated the blog and collection schemas to include internationalization (i18n) fields for better content localization.
- Implemented a new official source strip for improved navigation and branding, linking to key resources like the official site and documentation.
- Enhanced the locale switcher functionality, allowing users to select their preferred language with improved UX.
- Updated styles for the locale switcher and added new components for better responsiveness and accessibility.
- Refactored existing components to utilize localized URLs, ensuring a consistent user experience across different languages.

* Implement comprehensive localization and enhance landing page UI

- Introduced new localization features, including `EXTRA_LOCALIZED_HOME_BODY_COPY` and `EXTRA_LOCALIZED_LANDING_UI_COPY`, to support multilingual content across the landing page.
- Updated `astro.config.ts` to integrate internationalization (i18n) settings for the sitemap, improving SEO for localized content.
- Created new files for home page and info page internationalization, defining structured content for various languages.
- Enhanced the locale switcher functionality with improved UX, allowing users to easily select their preferred language.
- Refactored existing components to utilize localized content and URLs, ensuring a consistent experience across different languages.
- Made CSS adjustments for better responsiveness and accessibility in the UI components.

* Enhance landing page with new design templates and localization improvements

- Added support for design templates in the content management system, allowing for better organization and access to design resources.
- Implemented comprehensive localization for blog topics, enhancing multilingual support across various sections of the landing page.
- Updated the header component to include new product menu items, improving navigation and user experience.
- Refactored CSS for improved responsiveness and accessibility, including a new sticky chrome bar for better navigation.
- Enhanced the locale switcher functionality, ensuring a seamless experience for users selecting their preferred language.

* docs(readme): promote 'open-source alternative to Claude Design' tagline to subtitle across locales
2026-05-21 23:40:58 +08:00
e5bea2c134 feat(landing-page): SEO surfaces, schema upgrades, manifest — cherry-pick from #2469 (#2596)
Pulls the high-value, low-conflict slices of @pftom's #2469 onto
current main. That PR's branch was based on `af63af3` (May 20) and
diverged from a fast-moving main, so its Tier 1 SEO content is shipped
here as a fresh PR rather than rebased.

What's included
- **5 new SEO landing pages**, English-only for v1:
  - `/official/` — brand-authority hub naming every alias the project
    is searched as (Open Design, OpenDesign, open-design, opendesign,
    Open Design AI, OD); Organization JSON-LD with the same alternateName.
  - `/quickstart/` — three-command install path with HowTo JSON-LD,
    requirements, expected output, troubleshooting, next steps.
  - `/agents/` — 17 BYOK adapters across three tiers, ItemList JSON-LD,
    BYOK explainer.
  - `/compare/` — evaluation-stage comparison hub vs Claude Design,
    Figma Make, v0, Lovable/Bolt, Open CoDesign; mandatory honest-limits
    block as FAQPage JSON-LD.
  - `/alternatives/claude-design/` — primary commercial-intent page;
    TL;DR, why-people-search, BYOK explainer, feature-comparison table,
    who-should-pick-which, migration steps, FAQPage JSON-LD.
- **`[locale]/` wrappers** for each of the 5 new pages, generated via
  `PREFIXED_LOCALES` from OD's existing `_lib/i18n.ts`. Each wrapper is
  a thin re-export of the canonical English page; per-locale translations
  are a follow-up.
- **`seo-head.astro` schema upgrades** — `inLanguage` on Article /
  WebSite / Blog JSON-LD, `availableLanguage` on the WebSite block,
  `og:locale:alternate` for non-current locales, hreflang `x-default`
  link.
- **`favicon-links.astro` extracted as a shared component** so every
  page emits the same icon set; new sizes (favicon-16x16, favicon-32x32,
  android-chrome-192x192, android-chrome-512x512) generated from the
  current brand mark; `site.webmanifest` published for PWA / Android
  install affordances.
- **`llms.txt`** restructured to lead with brand-alias declaration and
  list every new official entry point. The Sister Projects section
  introduced in #2566 stays.

What's intentionally NOT included
- @pftom's parallel i18n bundle (`i18n.ts` / `home-page-i18n.ts` /
  `info-page-i18n.ts` / `landing-ui-i18n.ts` / `content-i18n.ts`,
  ~10K lines of locale data). It duplicates and conflicts with OD's
  existing `_lib/i18n.ts` + `_lib/home-copy.ts`. Adapting the new
  pages to OD's existing system instead keeps the codebase on a
  single source of truth; per-locale translations of the new pages
  can land as a separate PR.
- Mass-rewrites of existing pages (homepage, HA, Skills/Systems
  catalogs, blog detail). #2469's branch base predates a number of
  in-flight PRs that touched those files heavily; cherry-picking the
  rewrites would re-litigate already-merged work.
- The `[locale]/<existing-route>/` routing tree for Skills, Systems,
  Templates, etc. Without translations, mirroring 26 routes per locale
  produces duplicate-content signals; that lift belongs with a
  per-locale copy push.

Surface area
- Marketing site only. No `apps/web`, no `apps/daemon`, no contracts,
  no CLI surfaces. No new dependencies. No env vars.
- New favicon assets are static PNGs generated from the canonical
  brand-mark source via `magick`; committed as files, no pipeline.

Validation
- `pnpm --filter @open-design/landing-page typecheck` — 0 errors
- Local dev server: every new English route returns 200; every
  `[locale]/<route>/` variant (e.g. `/zh-CN/agents/`) returns 200;
  `/favicon.ico` and `/site.webmanifest` resolve.
- Local dev server: `seo-head.astro` emits the new `inLanguage`,
  `availableLanguage`, `og:locale:alternate`, and `hreflang=x-default`
  signals; `<FaviconLinks />` renders the consolidated icon set
  including the new manifest link.

Followups
- Translate the 5 new pages and replace the English-content wrappers
  under `[locale]/`.
- Swap `sub-page-layout.astro` to use `<SeoHead />` so the new schema
  upgrades reach all sub-pages, not just the homepage and any future
  pages that opt into SeoHead directly.
- Mass-update the homepage and HA page along the lines of #2469's
  rewrite, but rebased onto current main rather than off `af63af3`.

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-21 23:03:21 +08:00
netteeandGitHub 052f8097de fix(daemon): inject @-mention skills into system prompt (#2552)
* fix(daemon): inject @-mention skills into system prompt

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

* fix(daemon): compose ad-hoc skill mode and aliases

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

* fix(daemon): lazily load and stage ad-hoc skills

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

* test(daemon): assert staged skill files before spawn

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

* fix(daemon): compose skill metadata across @ mentions

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

* test(daemon): cover ad-hoc critique skill policy

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

* fix(daemon): preserve plugin skill composition

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

* fix(daemon): resolve conflicting composed skill surfaces

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

* fix(daemon): preserve primary skill surface

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

* fix(daemon): share resolved critique surface

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)
2026-05-21 22:20:21 +08:00
682a9c9a9a feat(landing-page): group header nav into Product / Library / Learn (#2588)
* feat(landing-page): group header nav into Product / Library / Learn

Reduces the top-level nav from 7 link slots (Product, Skills, Systems,
Templates, Craft, Tutorials, Blog, Contact — plus the locale switcher
and two CTAs) to 3 dropdown groups, eliminating the row-overflow
that forced the brand sub-meta to truncate at narrow desktop widths
and cramming the hamburger panel on tablet.

Why
- The four catalog pages — Skills, Systems, Templates, Craft — share
  the same shape: a list page that links into per-record details.
  They are facets of one library, not competing peer destinations.
  Letting each occupy its own top-row slot was a policy of last
  resort when there were only four; with Product newly added and
  Tutorials newly added since #2452 + #2266, the row no longer fits.
- Tutorials and Blog are both editorial reading surfaces (videos
  vs articles), so they pair naturally under one Learn group.
- Contact in the top nav was always misleading — it links to
  `#contact`, a same-page anchor on the homepage CTA section. The
  footer already lists it. Promoting it to top-row real estate while
  the row was overflowing made no sense.

What
- New `Library` dropdown holds Skills (132) / Systems (150) /
  Templates (111) / Craft (11). Each row inside the panel keeps its
  count badge inline beside the label, plus a one-line blurb so the
  panel reads as a directory rather than an undifferentiated list.
  The trigger highlights `is-active` whenever any of the four facet
  pages is the active route, so users still see "you are inside
  Library" feedback.
- New `Learn` dropdown holds Tutorials and Blog. Same pattern: inline
  blurbs, parent-highlight when either child page is active.
- `Contact` removed from the header. The footer surface still lists
  it; the top-row capacity it occupied is reclaimed.
- `.dropdown-num` CSS class added for the inline count badge inside
  dropdown rows, distinct from the existing `.num` (which floats
  above top-row links as a superscript).

Active-state semantics are preserved — every sub-page that already
passes `active="skills"`, `active="systems"`, etc. keeps working
without changes; the new Library trigger reads those same values
to decide whether to highlight itself.

Surface area
- Marketing site only. No `apps/web`, no `apps/daemon`, no contracts,
  no CLI surfaces. No new dependencies. No i18n keys (existing
  `copy.navSkills` / `copy.navSystems` / `copy.navTemplates` /
  `copy.navCraft` / `copy.navBlog` keep working; "Library" and
  "Learn" are universal English labels that don't need translation
  tables for this iteration).

Validation
- `pnpm --filter @open-design/landing-page typecheck` — 0 errors
- Local dev server at narrow breakpoints (≤1080px) — hamburger panel
  flat-expands the new Library / Learn dropdowns; existing handler
  closes the panel on link click.
- Local dev server at desktop widths (≥1180px) — top row now reads
  Product · Library · Learn, matching the design intent.

* feat(landing-page): polish brand mark — bigger glyph, single-line wordmark

Three small refinements to the header brand block, requested after
the new logo + grouped-nav landed:

1. The "Open Design" wordmark was wrapping to two lines once the new
   nav groups (Product · Library · Learn) sat alongside it on narrow
   desktop widths. The hamburger fallback already hides the entire
   nav at ≤1080px so the wordmark has plenty of horizontal room
   there — wrapping was a layout glitch, not a width constraint.
   Applies `white-space: nowrap` on `.brand` and adds an explicit
   `.brand-name` span so the rule is intent-bearing rather than
   relying on the parent inline-flex behaviour.

2. The brand mark felt undersized against the wordmark's optical
   weight, especially with the new black mark where the negative
   space inside the speech bubble visually shrinks the glyph. Bumps
   the displayed size from 36×36 to 44×44 (+22%) in both the header
   and the footer, so the proportion matches what users see in the
   logo.webp source.

3. Removes the `<span class="brand-meta">` block ("STUDIO Nº 01 ·
   BERLIN / OPEN / EARTH"). It was decorative metadata that earned a
   third of the brand block's horizontal real estate without
   carrying any user-facing function. Drops the JSX, drops the
   styles, drops the now-stale `display:none` overrides at the 1180
   and 880 breakpoints. The same rotated brand-string still lives on
   the side-rail pseudo-elements at the page edges (`.side-rail
   .rail-text`) — that surface is the canonical home for that
   editorial flourish, not the nav.

Surface area
- Marketing site only. `_components/header.tsx`, `page.tsx` (footer
  brand block), and `globals.css`. No `apps/web`, no `apps/daemon`,
  no contracts, no CLI. No new dependencies, no i18n changes.

Validation
- `pnpm --filter @open-design/landing-page typecheck` — 0 errors
- Local dev server: header HTML no longer contains `brand-meta`;
  the brand block on `/` and any sub-page renders the bigger 44px
  glyph + single-line "Open Design" wordmark.

* refactor(landing-page): unbundle Learn dropdown and round the brand mark

Two small follow-ups after looking at the rendered nav:

1. Reverts the Learn dropdown back to two standalone top-row links
   (Tutorials, Blog). Rolling exactly two items into a dropdown adds
   a click without reclaiming any horizontal space — the Library
   grouping is what genuinely cleared the row, and Tutorials + Blog
   can live side by side. Active-state semantics are preserved
   automatically since both pages already pass `active="tutorials"`
   and `active="blog"`.

2. Adds `border-radius: 10px` to the brand-mark image. The source
   PNG is a solid-fill square; clipping the corners gives the mark
   the modern app-icon silhouette (~22% of side length, matching
   the iOS / macOS convention) so it reads as a brand glyph rather
   than a raw screenshot next to the wordmark. Applies to both the
   header and footer brand blocks via the shared `.brand-mark img`
   selector.

Validation
- `pnpm --filter @open-design/landing-page typecheck` — 0 errors
- Local dev server: top row reads `Product ▾ · Library ▾ · Tutorials · Blog`
- Local dev server: brand mark renders with rounded corners on `/`
  and any sub-page (header + footer reuse the same `.brand-mark`
  styling)

---------

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-21 21:15:48 +08:00
7631539808 Add project bulk delete feedback (#2481)
* Add project bulk delete feedback

* Restart bulk delete toast timer

---------

Co-authored-by: Ghxst <200635707+GHX5T-SOL@users.noreply.github.com>
2026-05-21 20:57:19 +08:00
0f1b74ee44 fix(landing-page): canonicalize HA SoftwareApplication on the landing page (#2586)
Follow-up to #2566. That PR introduced `url: HA_URL` plus a
two-element `sameAs` on the SoftwareApplication block, which declared
the GitHub repo as the entity's canonical surface — Google uses `url`
to decide which page to attribute structured-data entities to. The
BreadcrumbList block right below uses the landing page URL, so the
two blocks contradicted each other on what the canonical page was.

Since the goal of #2566 was making "html anything" surface the landing
page in search, canonicalizing on GitHub was an inversion of intent.
This commit switches `url` to the landing page URL and keeps
`HA_URL` only in `sameAs` so the GitHub repo is registered as a peer
reference surface, not the canonical one. The two structured-data
blocks now agree.

Surfaced by Looper (PerishCode) on the #2566 review thread; the fix
landed too late to ride the original PR, hence this small follow-up.

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-21 20:43:18 +08:00
8269b7df90 Keep live artifact preview mounted across tabs (#2482)
Co-authored-by: Ghxst <200635707+GHX5T-SOL@users.noreply.github.com>
2026-05-21 20:38:58 +08:00
c37c691d82 [codex] Add landing page Google Analytics (#2582)
* Add Claude-style design system workflow

* Merge design system workflow into main

* Restore design system workflow UI styles

* Fix design system setup scrolling

* Fix design system setup connector button

* Preserve connector auth link after popup block

* Simplify connected GitHub setup state

* Open generated design system workspace project

* Summarize design system auto prompt in chat

* Add bounded GitHub connector design intake

* Prefer path-scoped GitHub intake tools

* Restore branch GitHub design context intake

* Restore design system review workspace

* Restore design system manager tab

* Let design system workflow routes own details

* Open editable design systems as projects

* Restore design system workspace coverage

* Fix bounded GitHub connector intake

* Hide design system review while generating

* Suppress design system generation questions

* Constrain GitHub design intake to bounded command

* Tolerate oversized GitHub metadata during intake

* Rebuild daemon CLI when sources change

* Fallback when GitHub connector snapshots are rate limited

* Allow GitHub intake without Composio

* Use native GitHub auth for design intake

* Remove design system review group heading

* Improve design system extraction evidence

* Align design system scaffold with Claude output

* Add evidence inventory for design system intake

* Add local design system evidence intake

* Add design system package audit gate

* Allow auditing Claude Design reference packages

* Audit design system package content quality

* Migrate legacy design system artifacts

* Clean migrated design system artifacts

* Require modular design system UI kits

* Reject thin design system UI kits

* Prioritize core design evidence intake

* Require role-based design system UI kits

* Clean stale design system manifest references

* Require representative preserved design assets

* Warn on generic design system visuals

* Enforce design system quality warnings

* Audit connected design system UI kits

* Require mounted design system UI kits

* Require composed design system app shells

* Require runnable JSX design system kits

* Require browser globals for design system components

* Infer design system names from source URLs

* Require source examples in design system packages

* Bind preserved fonts in design system tokens

* Require skill frontmatter in design system packages

* Preserve build icons in design system packages

* Require real assets in brand previews

* Require substantive source examples

* Require product overview in design system README

* Require reusable UI kit README

* Require reusable design system skill docs

* Seed Claude-style UI kit entry contract

* Preserve runtime build assets in design packages

* Audit design system packages after generation

* Audit design system first-run output

* Audit source-backed preview cards

* Align design system UI kit scaffolds

* Materialize design evidence package artifacts

* Show project chat during design system setup

* Hand off design system setup to project chat

* Auto-repair design system audit failures

* Harden design system evidence preservation

* Tighten design system package guidance

* Add targeted design system repair guidance

* Bound design system audit auto repair

* Use connector statuses in design system setup

* Audit design system preview manifests

* Require README preview manifests for design systems

* Add landing page Google Analytics

* Keep GA PR scoped to landing page

* Cover new landing routes with Google Analytics

* Load Google Analytics without static script src

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
2026-05-21 20:38:09 +08:00
0a5598c0e3 feat(landing-page): improve HTML Anything discoverability for SEO (#2566)
Three small additions that close the gap between users searching
"html anything" (with a space) and finding the existing
`/html-anything/` page:

1. JSON-LD `SoftwareApplication.alternateName` is now an array of
   real query variants — `html anything`, `html-anything`,
   `htmlanything`, `HTML Anything Editor`, plus the existing tagline.
   Each entry maps to a query users actually type. Feeding them as
   declared alternates is a stronger signal than relying on Google's
   tokenizer to figure out hyphen ↔ space equivalence on its own.
   Also adds a `sameAs` array linking the canonical landing page and
   the GitHub repo so structured-data crawlers see the entity span
   both surfaces.

2. `llms.txt` now lists the HA landing page in its canonical entry
   points and adds a dedicated "Sister Projects" paragraph that
   spells out the name variants and the export targets. LLM crawlers
   (Claude, Perplexity, ChatGPT browsing) read this file to decide
   what to surface; the previous version made HA invisible to them.

3. The home page footer's Library column now lists "HTML Anything"
   as a peer to Skills / Systems / Templates / Craft, which puts a
   real body-anchor link to `/html-anything/` on `/`. Nav-dropdown
   anchors (the Product menu) carry less SEO weight than an anchor
   in a discoverable content area, so this makes the home actually
   pass link equity to the sub-page. The label is hardcoded English
   intentionally — "HTML Anything" stays unlocalized as a brand name
   on every locale, so threading a new key through all 18 home-copy
   translations would just churn translators.

## Why now

The HA landing page has been live since #2452 but a "html anything"
query (the variant most users type) doesn't surface us, while the
hyphenated form does. Tokenizer equivalence isn't enough on a young
domain — we need declared signals, an inbound link from a
content-area on the highest-PR page, and machine-readable hints for
LLM crawlers. This PR ships all three.

## Test plan

- [x] `pnpm --filter @open-design/landing-page typecheck` — 0 errors
- [ ] After deploy: `curl https://open-design.ai/html-anything/`
      shows `"alternateName":[...]` array in JSON-LD
- [ ] After deploy: `curl https://open-design.ai/llms.txt` includes
      a `## Sister Projects` section
- [ ] After deploy: home page footer Library column lists HTML Anything

## Surface area

- [x] `apps/landing-page` only
- [ ] no `apps/web`, no `apps/daemon`, no contracts, no CLI
- [ ] no new dependencies
- [ ] no skill / design-system / template / craft additions

## Followups (not in this PR)

- Mirror the HA hero banner to R2 + Image Resizing (Core Web Vitals)
- Generate a HA-specific Open Graph card so social previews stay on
  brand (currently reuses the GitHub-hosted banner)
- Add a `SoftwareSourceCode` JSON-LD block to associate the landing
  page with the GitHub repository entity
- Land a Chinese landing page at `/zh-CN/html-anything/` to capture
  CN-locale queries

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-21 19:26:30 +08:00
93036afa2e Fix sketch default color in dark mode (#2488)
Co-authored-by: Ghxst <200635707+GHX5T-SOL@users.noreply.github.com>
2026-05-21 19:18:32 +08:00
Siri-RayandGitHub 3a33a7b475 fix(web): localize quick brief prompt (#2520)
* fix(web): localize quick brief prompt

Generated-By: looper 0.8.1 (runner=worker, agent=codex)

* fix(web): pass locale from design system chat

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

* fix(web): preserve task-type routing options

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

* fix(web): preserve task-type routing options

Generated-By: looper 0.8.1 (runner=fixer, agent=codex)
2026-05-21 19:18:13 +08:00
Marc ChanandGitHub 047d2bdd95 fix(orbit): respect selected app language (#2522)
* fix(orbit): respect selected app language

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

* fix(orbit): respect selected app language

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)
2026-05-21 19:17:53 +08:00
Zellux WangandGitHub f892daae05 Polish connector detail drawer (#2528) 2026-05-21 19:17:31 +08:00
Marc ChanandGitHub 8dfcc545c7 docs(changelog): draft unreleased section (#2539)
* fix(ci): catch nix hash drift before merge

* docs(changelog): draft unreleased section

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

* docs(changelog): add missing unreleased link refs

Generated-By: looper 0.8.1 (runner=fixer, agent=opencode)
2026-05-21 19:17:17 +08:00
Weston HoughtonandGitHub ce3891f20f fix(daemon): honor hostname OD_ALLOWED_ORIGINS for no-Origin same-origin GETs (#2478)
Browsers omit the Origin header on same-origin GET requests per the Fetch
spec, but the no-Origin host-only branch in isLocalSameOrigin only honors
IP-literal entries in OD_ALLOWED_ORIGINS (via ipOnlyExtraOrigins). When a
remote-access proxy (Tailscale Serve, Caddy, etc.) terminates TLS at a
non-loopback hostname listed in OD_ALLOWED_ORIGINS, those legitimate
same-origin GETs to /api/app-config, /api/mcp/servers, etc. get rejected
with "cross-origin request rejected" 403s while the matching POST/PUTs
(which always carry Origin) succeed.

Trust Sec-Fetch-Site: same-origin as a fallback when the Origin header is
absent. The Sec-* prefix is browser-set and cannot be modified by
JavaScript, so a value of "same-origin" attests that the request really
originated from the same origin as the target — a cross-site
<img>/<script> exploit would carry "cross-site" instead, leaving the
existing CSRF defenses intact.

Tests:
- pnpm --filter @open-design/daemon test -- tests/origin-validation.test.ts → 43 passed (5 new)
2026-05-21 19:15:07 +08:00
netteeandGitHub 0108d3a655 fix(web): localize Chinese integrations copy (#2081) (#2563)
Generated-By: looper 0.8.1 (runner=worker, agent=opencode)
2026-05-21 19:07:45 +08:00