Commit Graph
1166 Commits
Author SHA1 Message Date
qiongyu1999 b4c8072ccf Fix daemon prompt CI assertions 2026-05-19 14:20:49 +08:00
qiongyu1999 4095d8e647 Merge latest main and fix design system CI 2026-05-19 14:12:15 +08:00
Marc ChanandGitHub 4f116d9eaf fix(ci): anchor PR inactivity clock to author responses (#2185)
* fix(ci): anchor PR inactivity clock to author responses

* fix(ci): add dry-run mode to PR inactivity workflow

* fix(ci): read workflow dry-run input from event payload

* fix(ci): log PR inactivity dry-run diagnostics

* fix(ci): accept both review association field names

* fix(ci): log PR 642 feedback payload shapes

* fix(ci): trust PR reviewers by repo permission

* fix(ci): remove temporary inactivity debug logs
2026-05-19 13:59:15 +08:00
PerishFireandGitHub bd48c597b0 chore: pin dependency versions and harden CI caches (#2189)
* chore: pin dependency versions

* ci: enforce pinned dependency specs

* ci: fix pnpm executable invocation
2026-05-19 13:58:27 +08:00
Ethan GuoandGitHub 477dd627b1 feat(daemon): parse DESIGN.md YAML frontmatter for presets (#2110)
* feat(daemon): parse DESIGN.md YAML frontmatter for presets

Falls back to frontmatter name/description/category/surface behind
existing Markdown heuristics; frontmatter `colors` wins over Markdown
swatches.

Closes #1857
Contract: runs/2026-05-18T08-09-54_open-design_issue-feat/contract.md

* test(daemon): cover DESIGN.md frontmatter fallback chain

* fix(daemon): keep Markdown swatch row when frontmatter colors miss slots

Material Design 3 token names (`on-surface`, `outline`) in totality-
festival's frontmatter left fg/support unmatched, regressing its picker
swatches; fall back to Markdown when the frontmatter row would use hard-
coded defaults.

Refs: https://github.com/nexu-io/open-design/pull/2110#discussion_r3258871692
2026-05-19 13:52:11 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
226bc57471 Update docs/assets/github-metrics.svg (#2145)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-19 13:50:05 +08:00
kamiandGitHub 68b0e0258d fix(web): scope daemon transcript to active agent (#2157) 2026-05-19 13:49:34 +08:00
kamiandGitHub b838e94b88 fix(web): expand skill mention picker (#2170) 2026-05-19 13:49:15 +08:00
qiongyu1999 5b0f45e85a Merge origin/main into design system handoff 2026-05-19 13:47:44 +08:00
qiongyu1999 511fcc7557 Fix design system GitHub intake handoff 2026-05-19 13:39:08 +08:00
bluto447andGitHub 6e1ebb97dd docs: add Windows native setup notes to AGENTS.md (#324)
* docs: add Windows native setup notes to AGENTS.md

* docs: address review feedback on Windows native setup notes

- Reframe Node 22 FAQ around the actual user question (no orphaned
  citation); ground the answer in package.json#engines only
- Remove fabricated better-sqlite3 engines field claim; the
  lockfile entry has only resolution/integrity, no engines metadata
- Cross-reference historical Windows issues (#10, #96, #100, #203,
  #315) for fresh-installer context
- Remove tools-dev bullet that duplicated the Local lifecycle section
- Specify Visual Studio Build Tools 2022 or newer

* docs: pin Windows Node install to verified Node 24 (Looper follow-up)

`OpenJS.NodeJS.LTS` floats with whichever line is currently LTS — works
today (Node 24.15.0) but will silently advance to Node 26 in October
2026, conflicting with package.json#engines: node: "~24". WinGet has no
`OpenJS.NodeJS.24` package to pin against. Reword the install bullet to
(a) keep the WinGet command, (b) offer nodejs.org as alternative, (c)
require `node --version` verification, and (d) call out the October
2026 drift explicitly.
2026-05-19 13:23:00 +08:00
PerishFireandGitHub 99b42726b8 Simplify CI PR gate (#2183) 2026-05-19 13:18:41 +08:00
428603cf6c Add local custom agent profiles (#378)
Co-authored-by: fuyizheng3120 <fuyizheng3120@users.noreply.github.com>
2026-05-19 13:13:48 +08:00
kamiandGitHub a83cfe9a0c fix(daemon): preserve Windows chat attachments (#2158) 2026-05-19 12:01:33 +08:00
shangxinyu1andGitHub f650a043d9 test(e2e): align entry coverage with redesigned flows (#2101)
* Migrate entry E2E coverage and split CI

* test(e2e): relax connectors auth error assertions

* ci: route scenario registry changes to extended e2e

* ci: decouple packaged smoke jobs from validate gate

* ci: restore pre-split workflow

* test(e2e): slim critical ui smoke coverage

* test(e2e): move broader entry flows out of critical

* test(e2e): restore entry chrome coverage to ci

* ci: parallelize workspace validation jobs

* test(web): stabilize media palette bridge assertion

* ci: cache e2e playwright browsers
2026-05-19 11:26:40 +08:00
PerishFireandGitHub 4424f08be0 [codex] Add packaged desktop auto-update (#1375)
* Add packaged desktop auto-update

* Handle counted beta nightly update versions

* Refresh desktop auto-update branch for main

* Serialize desktop updater operations

* Refresh auto-update branch for packaged paths
2026-05-19 11:20:05 +08:00
qiongyu1999 7e1b0f3d40 Require README preview manifests for design systems 2026-05-19 11:11:09 +08:00
Joey-nexuandGitHub 56988e406c feat: integrate xAI SuperGrok subscription as a credential source for Grok media + X search (#2134)
* feat(daemon): add xAI OAuth client with PKCE + token storage

Wraps mcp-oauth.ts PKCE primitives for xAI's auth.x.ai OAuth server.
xAI doesn't speak MCP and doesn't expose Dynamic Client Registration,
so issuer / endpoints / client_id / scope / loopback :56121 are
hardcoded constants.

Adds xai-tokens.ts for persistent storage, mirroring mcp-tokens.ts:
atomic write + chmod 0600 + per-dataDir in-memory mutex. Simplified
for the single-token case (no per-server-id map).

Reference: NousResearch/hermes-agent hermes_cli/auth.py:93-100.
PoC reuses Hermes client_id (b1a00492-...); replace before stable
release once Open Design has its own.

Tests: 11 + 20, all green. tsc --noEmit clean. pnpm guard clean.

* feat(daemon): expose xAI Grok models in Hermes runtime fallbackModels

Lists grok-4.3, grok-4.20-reasoning, grok-4.20-non-reasoning, and
grok-4.20-multi-agent-0309 as discoverable Hermes fallback models.
A user who has not installed Hermes yet now sees these xAI options
in the model picker, signalling that `hermes auth add xai-oauth`
(SuperGrok subscription) or XAI_API_KEY unlocks Grok in Open Design
without OD itself implementing OAuth-for-chat.

`fetchModels` (which calls `hermes acp` to enumerate the user's
actually-installed providers) is unchanged; this list only kicks
in when probing fails (e.g. Hermes off PATH).

Reference: xAI × Nous Research grok-hermes integration announcement,
2026-05-15. https://x.ai/news/grok-hermes

* feat(media): route Grok Imagine through xAI OAuth credentials

Adds resolveXAIBearer() — a refresh-aware helper on top of the
xai-tokens.json store written by the daemon's OAuth client. Returns
a fresh access_token, transparently refreshing in-place when the
stored token enters the 120 s expiry skew window.

Wires it into media-config.ts so the existing Grok provider gets the
same OAuth-fallback treatment OD already gives the OpenAI provider:
env keys win, then stored Settings keys, then OD-native xAI OAuth,
then a borrowed Hermes-side xai-oauth token from ~/.hermes/auth.json.
SuperGrok subscribers who already authorized Hermes get OD image /
video generation routed through their subscription with zero extra
setup.

Updates the "no xAI API key" error in renderGrokImage / renderGrokVideo
to point at the new OAuth path so users hitting it know they have a
zero-cost option.

Also exposes mediaConfigDir() so credential helpers next to
media-config.json (like xai-tokens.json) reuse the same precedence:
OD_MEDIA_CONFIG_DIR > OD_DATA_DIR > <projectRoot>/.od.

Tests: 7 new xai-credentials cases (refresh on expiry, refresh
failure, missing refresh_token, response without refresh_token) +
8 new media-config Grok OAuth fallback cases (OD-native, Hermes
borrow, OD vs Hermes precedence, env precedence, stored precedence,
unconfigured, expired-without-refresh). All green; tsc / guard clean.

* feat(media): add xAI Grok TTS provider

Registers grok-tts in the speech model catalog and wires up
renderXAITTS to dispatch (provider=grok, surface=audio, kind=speech)
to https://api.x.ai/v1/tts. xAI exposes a dedicated /tts endpoint
that returns raw audio bytes — distinct from OpenAI's /audio/speech
JSON shape — so TTS gets its own renderer rather than reusing
renderOpenAISpeech.

Credentials route through the same OAuth-aware path as Grok image
and video (PR follow-up to media-config.ts), so a SuperGrok
subscriber gets TTS for free once they have authorized once.

Default request body matches the documented minimal shape
(text / voice_id / language); sample_rate / bit_rate / codec are
left unset so the server applies its mp3 / 24 kHz / 128 kbps
defaults. Plumbing for explicit overrides is left for a later PR
once the agent-facing contract grows the corresponding flags.

Tests: 5 cases covering documented body shape, voice / language
override, env-key fallback, server-error surfacing, and the
no-credentials error. All green; tsc / guard clean.

Reference: https://docs.x.ai/developers/model-capabilities/audio/text-to-speech

* feat(daemon, web): expose xAI OAuth flow in Settings UI

Closes the loop on the Grok integration: a SuperGrok subscriber can
now authorize Open Design directly from Settings → Media Providers →
Grok, with no API key and no Hermes install. After authorizing, image,
video, and TTS routes pick up the bearer through the OAuth fallback
chain added in 'route Grok Imagine through xAI OAuth credentials'.

Daemon side
- xai-oauth-server.ts opens a one-shot HTTP listener on
  127.0.0.1:56121 to receive the OAuth callback. The redirect URI is
  hard-locked to that port because the PoC reuses the Hermes-issued
  client_id. Listener self-closes on first matching callback or after
  a 30 min timeout.
- xai-routes.ts wires three endpoints onto the daemon's HTTP app:
    POST /api/xai/oauth/start       — mint state, open listener,
                                       return authorize URL
    GET  /api/xai/auth/status       — has-token / expiry / in-flight
    POST /api/xai/oauth/disconnect  — wipe stored token, stop listener
- server.ts registers xai-routes alongside the existing mcp-routes.

Web side
- XaiOAuthControl.tsx renders a Sign in / Reconnect / Disconnect
  surface mirroring McpOAuthControl, but polls /api/xai/auth/status
  exclusively because the :56121 callback page lives in a separate
  process and can't postMessage back to the OD UI. SettingsDialog
  embeds it inside the Grok provider row.

Tests: 9 listener cases (bind / state mismatch / replay / favicon /
EADDRINUSE / timeout / explicit error param / one-shot consume /
early stop) + 8 route cases (start mints PKCE URL, second start
replaces in-flight listener, status reports listening + connected,
callback ok stores token, callback error skips storage, disconnect
wipes, cross-origin guard rejects all three endpoints). All 17 +
the 74 from prior commits pass; tsc / web typecheck / pnpm guard
clean.

PoC client_id stays Hermes-issued; user-visible strings are
hardcoded English pending an i18n pass before stable.

* fix(daemon, web): xAI OAuth follow-up — paste-back, X search, UX polish

PoC testing surfaced four real-world rough edges in the Sign in flow
that were not obvious before getting an actual SuperGrok subscription
in front of it. None alter the architecture in 'expose xAI OAuth flow
in Settings UI'; they round it off so the path the user actually walks
matches the one the design assumed.

1. Layout. XaiOAuthControl was a grid item inside .media-provider-body
   and got squeezed into the API-key column. Moves it out of the body
   so the row's flex-column layout gives it the full width — matches
   what every other Settings provider OAuth surface gets.

2. Paste-back. xAI's `auth.x.ai` page often shows a "cannot connect to
   your application" fallback that hands the user a code instead of
   redirecting back to 127.0.0.1:56121, even when the loopback listener
   is reachable (browser DOES quietly redirect in the background, but
   the page lies and shows the manual-paste UI anyway). Adds:
     - POST /api/xai/oauth/complete that takes {state, code} and runs
       completeXAIAuth + setXAIToken + stops the listener.
     - A paste-back input row in XaiOAuthControl that surfaces while
       the dance is in flight; submitting either via Enter or the
       button calls /complete and falls through to the same connected
       state the loopback path lands on.

3. X search. New POST /api/xai/search wraps Grok's native x_search tool
   through the Responses API, gated on the same OAuth-first credential
   chain as Grok image / video / TTS. Body accepts query (required),
   allowed_x_handles, excluded_x_handles, from_date, to_date, model.
   Returns { answer, citations[], model } parsed from the Responses
   payload via two newly exported helpers (extractAnswerText,
   extractUrlCitations).

4. State machine + warning banner. Three issues collapsed into one:
     - Polling that flipped busy → 'idle' the moment the loopback
       listener self-closed disabled the paste-back input even though
       the dance was still recoverable. Removed that branch; awaiting
       state now only ends on connected=true or explicit cancel.
     - paste-input `disabled` was over-eager (`busy !== 'awaiting' &&
       busy !== 'refreshing'`); now it's only blocked while a submit
       is in flight (`busy === 'refreshing'`).
     - Added a heads-up banner inside the awaiting region explaining
       that xAI's "cannot connect" page is a UX bug on their side and
       the OD panel is the source of truth for sign-in success. The
       connected message picks up the cue too: "You can close any
       open xAI browser tabs now."

Tests: +12 cases on top of the existing 17. The complete endpoint
covers happy path, blank-field rejection, and unknown-state error.
The search endpoint covers blank-query rejection, no-credentials 401,
full bearer / x_search-options forwarding with response parsing, and
upstream-error pass-through. Two helper functions get four direct
parser cases. All 29 in the file pass; 225 across the daemon test
suite pass; tsc / web tsc / pnpm guard all clean.

* fix(daemon): satisfy tsconfig.tests.json strictness in xai test files

The CI workspace typecheck step runs tsconfig.tests.json (which extends
tsconfig.json's strict + exactOptionalPropertyTypes settings and adds
the tests/ directory to the include set) — but the local
`tsc -p tsconfig.json --noEmit` I ran while iterating only covered
src/. That gap let two classes of strict-mode errors slip into the
PR's CI:

- `let outcome: CallbackOutcome | null = null` mutated from inside an
  async callback narrowed to `never` after `outcome?.kind` because TS
  doesn't track cross-function mutation. Switched the seven sites in
  xai-oauth-server.test.ts to a `{ current: CallbackOutcome | null }`
  ref object — TS does narrow .current correctly, so `kind` / `error`
  field access stops collapsing to `never`.
- `await r.json()` returns `Promise<unknown>` in the lib.dom typings
  shipped with TS 5.x, so every `body.field` / `status.connected`
  access in xai-routes.test.ts tripped TS18046. Added a one-line
  `jsonOf<T = any>` helper at the top of the file and switched all
  call sites (both `await r.json()` and `.then((r) => r.json())`).
- The cross-origin guard test iterated `for (const [method, path] of
  [...])` — under noUncheckedIndexedAccess that destructures to
  `string | undefined`, which RequestInit.method (a `string` under
  exactOptionalPropertyTypes) won't accept. Hoisted the cases to a
  typed `ReadonlyArray<readonly [string, string]>` so the elements
  stay non-optional.

Behaviour is unchanged; vitest still reports 29/29 across these two
files. tsc -p tsconfig.tests.json --noEmit now passes locally,
matching what CI will run.

* fix(xai-oauth): preserve refresh_token + release :56121 on cancel

Two lifecycle issues Looper flagged on the prior commit:

1. resolveXAIBearer dropped the existing refresh_token whenever the
   refresh response omitted one. RFC 6749 §6 explicitly allows the
   server to skip refresh_token rotation and keep the old one valid;
   xAI's behaviour is currently to rotate, but a future change could
   silently break OD users. With the old code the first refresh
   succeeded but persisted a token with no refresh credential, so the
   next expiry forced the user back through Sign in even though their
   grant was still good. Carries the previous refresh_token forward
   when fresh.refresh_token is absent. Updates the matching
   xai-credentials test to assert the carried-forward value instead of
   the previous (incorrect) "drop it" assertion.

2. The Cancel button in XaiOAuthControl only cleared React-side
   pending state; the daemon's one-shot 127.0.0.1:56121 listener kept
   running for the full 30 min server timeout. /api/xai/auth/status
   would still report listening=true, and that singleton port could
   block the next Sign in (or a Hermes session on the same machine).
   Adds POST /api/xai/oauth/cancel that calls stopActiveListener()
   without touching the stored token (Disconnect is the destructive
   path; this is the narrow "release the port" affordance), wires the
   UI Cancel handler to fire it, and adds two route tests covering
   the listener-stopped-but-token-preserved invariant and the no-op
   behaviour when no listener is in flight.

All 38 xai tests + tsconfig.tests.json typecheck + web typecheck +
pnpm guard pass.

* fix(xai-oauth): close two more lifecycle gaps Looper flagged

Both are non-blocking but cheap and right.

1. window.open used 'noopener=no,noreferrer=no' (carried over from the
   sibling McpOAuthControl), which deliberately KEEPS the auth.x.ai
   tab's window.opener reference back to the Settings tab. Reverse
   tabnabbing risk if the auth page or any redirect target along the
   OAuth chain ever turns hostile, with no upside — the xAI flow
   doesn't use postMessage, the daemon receives the code through the
   :56121 listener (or paste-back), so opener access buys nothing.
   Switched to 'noopener,noreferrer'.

2. PendingAuthCache was constructed with its default 10 min TTL while
   the loopback listener self-closes at 30 min and the UI shows a
   pending state for the same 30 min. After 10 min, a user looking at
   a live paste-back input would hit `xAI OAuth state not found or
   expired` even though everything visible (and the daemon socket)
   still claimed the dance was live. Constructed the cache with
   30 * 60 * 1000 so the PKCE state, the open :56121 socket, and the
   paste-back UI all expire together.

The third inline comment (XaiOAuthControl.tsx:248 — "Cancel only
clears React-side state") was a stale reference: the previous commit
fd04887 wired the Cancel button to fire `cancelInFlightOAuth()` which
hits the new `POST /api/xai/oauth/cancel` endpoint. Looper carried
the old comment forward when re-reviewing the rebased file; no code
change needed.

All 38 xai tests still green; tsconfig.tests.json clean; web tsc
clean; pnpm guard clean.

* fix(xai-oauth): keep loopback listener open on stale-tab callbacks

The one-shot listener marked itself consumed at the top of every
/callback request, then closed itself in the finally block whether
or not the state actually matched. A stray browser tab replaying an
old /callback?state=… (real-world scenario: user re-clicked Sign in
before closing the previous tab) would therefore close the singleton
:56121 listener with a state-mismatch error before the real xAI
redirect could arrive.

Now we only tear the listener down on outcomes that actually
terminate the dance:
  - ok callback (matched state, code present)
  - explicit ?error= from xAI (auth provider terminated; we should
    propagate, not wait for the 30 min timeout). xAI's error
    redirects may or may not echo state, but a stale tab can't
    fabricate ?error= without colluding with the auth server, so
    this branch is safe to consume.

Stale tabs / browser prefetches / malformed redirects still get the
HTTP 400 / "Sign-in failed" page, but the listener stays open and
the matching xAI redirect that arrives next is what closes it.

Tests: replaces the previous "rejects state mismatch with kind=error"
test with the recovery scenario (stale-then-real callbacks both hit
the listener; only the real one fires onCallback). Adds a sibling
case for missing-code / missing-state callbacks. xai-oauth-server
suite is now 10/10; full xai sweep 39/39.

* fix(xai-oauth): scope error-callback consume to matching/missing state

c00252c simplified the consume rule to "any explicit ?error= closes
the listener", which was broader than the stale-tab protection added
in the same commit. A browser history replay of an old
`/callback?error=access_denied&state=stale` would set `consumed`,
fire `onCallback`, and tear down the singleton 127.0.0.1:56121 socket
before the current dance's real callback could land — undoing the
defence the commit was supposed to add.

Tighten the rule so error-callbacks consume only when:
  - the URL carries no state (xAI rejected before issuing one, so
    there's nothing to compare against — safe to terminate), or
  - the carried state matches our expectedState (xAI explicitly
    rejected this dance; propagate immediately rather than wait for
    the 30 min timeout).

An ?error= replay carrying a *different* state is now treated like
the stale success replay above: returns the 400 page to the browser,
keeps the listener live, lets the real callback close it.

Tests: adds two cases — error+wrong-state followed by real success
must still resolve to ok; error+matching-state still consumes the
listener and surfaces the error to onCallback. xai-oauth-server
suite goes 10 → 12; full xai sweep 39 → 41.
2026-05-19 11:10:34 +08:00
qiongyu1999 1fab3161f8 Audit design system preview manifests 2026-05-19 01:28:34 +08:00
qiongyu1999 b821179b8b Use connector statuses in design system setup 2026-05-19 01:28:30 +08:00
qiongyu1999 e1c58c8eb5 Bound design system audit auto repair 2026-05-19 01:26:25 +08:00
qiongyu1999 534bc50948 Add targeted design system repair guidance 2026-05-19 01:22:47 +08:00
qiongyu1999 536741e253 Tighten design system package guidance 2026-05-19 01:19:50 +08:00
qiongyu1999 890038d6d3 Harden design system evidence preservation 2026-05-19 01:17:08 +08:00
qiongyu1999 2f6d00d75d Auto-repair design system audit failures 2026-05-19 01:10:12 +08:00
qiongyu1999 231035db9b Hand off design system setup to project chat 2026-05-19 01:04:43 +08:00
qiongyu1999 06ca48577f Show project chat during design system setup 2026-05-19 00:58:25 +08:00
qiongyu1999 1395d4196f Materialize design evidence package artifacts 2026-05-19 00:54:01 +08:00
qiongyu1999 b851a402b4 Align design system UI kit scaffolds 2026-05-19 00:42:00 +08:00
qiongyu1999 721870f2c0 Audit source-backed preview cards 2026-05-19 00:29:51 +08:00
qiongyu1999 8062b50de1 Audit design system first-run output 2026-05-19 00:24:39 +08:00
qiongyu1999 4d14f4841c Audit design system packages after generation 2026-05-19 00:14:14 +08:00
qiongyu1999 29521ef85f Preserve runtime build assets in design packages 2026-05-19 00:01:52 +08:00
34f66113a0 Implement Home audio essential workflow (#2104)
* fix(web): align Home prompt overlay with textarea so caret lands on click

Picking a chip such as Slide deck or Image loaded a default prompt
into the Home textarea and rendered an overlay with `{{key}}`
placeholders as interactive `<input>` / `<select>` controls. The
overlay controls and the underlying textarea text were laid out
independently:

- Inputs declared `min-width: 8ch` and `Math.max(displayValue.length
  + 1, 10)ch` of width.
- Selects added 18px of right-padding for the dropdown arrow.
- The textarea kept the raw substituted string in a proportional
  font.

The two layouts no longer matched column-for-column, so every slot
shifted the textarea text to the left of where it appeared in the
overlay, compounding across the line. Clicking on visible prose to
position the caret hit a different character offset in the textarea
and subsequent typing or deletion landed in the wrong place.

For example, the Slide deck template

  Create a {{slideCount}}-slide {{deckType}} for {{audience}} about
  {{topic}}. ...

renders with slideCount=10 (~2 ch in the textarea) under a slot
input forced to 10 ch in the overlay — clicking right after the
literal `-slide` placed the caret several characters into `pitch
deck`. The Image / Video / Audio chips with their pre-filled
subject, style, aspect values reproduced the same drift.

Render the inline pills as read-only `<span>`s carrying the exact
substring the textarea shows at that position, mark them
`aria-hidden` so the textarea remains the single labelled control,
and surface every plugin input field — including the ones referenced
inline — in `PluginInputsForm` underneath. Editing flows through the
form, the parent's `updateActiveInputs` already re-renders the
prompt, and the pills stay aligned with the textarea on every
keystroke.

Also drop the now-unused inline helpers (updatePluginInput,
getTemplateInputNames, shouldRenderSlotAsText, inlineFieldType,
fileInputLabel, fileMetadata) and the dead
`.home-hero__prompt-slot-control/input/select/toggle/file/text` CSS
rules.

Verified:
- pnpm --filter @open-design/web typecheck
- vitest run on HomeHero.plugin-picker, HomeHero.rail, and
  HomeView.prefill (29/29 pass; tests updated to reflect the new
  read-only span + always-on form contract)
- Manual click-to-edit on Slide deck and Image chips in the
  pnpm tools-dev web runtime — caret now lands where the user
  clicked.

* Implement home audio essential workflow

* Fix Home media composer review issues

* Guard stale Home media apply results

---------

Co-authored-by: hahaplus <zmjdll@gmail.com>
2026-05-18 23:14:03 +08:00
Lucky PathanandGitHub 15161f685b fix(web): improve replace modal button hover contrast (#2114) 2026-05-18 22:13:09 +08:00
1523defad1 feat(web): add plugin registry detail drawer (#2087)
Co-authored-by: multica-agent <github@multica.ai>
2026-05-18 22:12:16 +08:00
kamiandGitHub 8a629eb999 fix: discover codex models from cli (#2082) 2026-05-18 22:11:51 +08:00
qiongyu1999 1ef5bd9e1a Seed Claude-style UI kit entry contract 2026-05-18 21:15:45 +08:00
qiongyu1999 48fec6c630 Require reusable design system skill docs 2026-05-18 21:11:25 +08:00
qiongyu1999 af8a84789d Require reusable UI kit README 2026-05-18 20:54:52 +08:00
qiongyu1999 35202c32c7 Require product overview in design system README 2026-05-18 20:46:52 +08:00
qiongyu1999 b587913fa0 Require substantive source examples 2026-05-18 20:40:38 +08:00
qiongyu1999 926d31455d Require real assets in brand previews 2026-05-18 20:35:23 +08:00
Nicholas-XiongandGitHub b8c2f9eb82 fix: localize Projects page title (#2077) (#2118)
- Replace hardcoded 'Projects' with t('entry.navProjects')
- Uses existing translation key (already defined in en.ts and zh-CN.ts)
- Page title now displays as '项目' in Chinese
2026-05-18 20:27:48 +08:00
qiongyu1999 328c7f5223 Preserve build icons in design system packages 2026-05-18 20:27:41 +08:00
f7eb82d7a5 feat(design-systems): import design system projects (#2112)
* feat(design-systems): define project manifest contract

* feat(design-systems): add default project manifest

* feat(daemon): consume design system manifests

* feat(design-systems): import local project systems

* feat(design-systems): import from github repositories

---------

Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
2026-05-18 20:20:38 +08:00
qiongyu1999 5bb9083c5d Require skill frontmatter in design system packages 2026-05-18 20:17:49 +08:00
qiongyu1999 f3a591a71d Bind preserved fonts in design system tokens 2026-05-18 20:09:01 +08:00
qiongyu1999 06ebcb99e1 Require source examples in design system packages 2026-05-18 20:01:52 +08:00
qiongyu1999 73e9a6185d Infer design system names from source URLs 2026-05-18 19:55:19 +08:00