Commit graph

1477 commits

Author SHA1 Message Date
lefarcen
7312c64580
ci(landing): split landing deploy into staging gate + manual production (#2994)
* ci(landing): split landing deploy into staging gate + manual production

A merge to `main` previously published the landing page straight to
production (open-design.ai) via `landing-page-deploy`. There was no
buffer to review the rendered site, so a bad merge was live instantly.

Split deploys across two Cloudflare Pages projects so production is only
ever reached by an explicit human action:

- `landing-page-staging` (push to main) -> staging project
  `open-design-landing-staging` -> staging.open-design.ai.
- `landing-page-production` (manual workflow_dispatch only) -> production
  project `open-design-landing` -> open-design.ai. Only this workflow
  names the production project; gate it with required reviewers on the
  `production` GitHub environment.
- `landing-page-ci` now also deploys a per-PR preview into the staging
  project (`--branch=pr-<n>`) for same-repo branches and comments the URL.
  Fork PRs (no secrets / read-only token) skip the deploy and keep just
  the build validation. Path filters already scope this to landing edits.

Decouple search-engine indexing from staging:

- `blog-indexing-on-deploy` now triggers on `landing-page-production`
  (not every main push), so the test environment is never submitted to
  Google/IndexNow.
- It diffs from a new `blog-indexed-prod` tag (the last indexed prod
  commit) instead of `HEAD^`, and force-advances the tag after a
  successful run, so a manual promotion bundling several merged posts
  indexes all of them rather than only the last commit.

Staging and PR-preview builds drop `PUBLIC_GA_MEASUREMENT_ID` so test
traffic does not pollute the production GA property.

* ci(landing): keep staging + PR previews out of the search index

staging.open-design.ai mirrors production and is exposed via cert
transparency logs, so search engines can discover it. Indexing the
mirror competes with open-design.ai for the same content.

Emit `<meta name="robots" content="noindex, nofollow">` whenever
OD_LANDING_NOINDEX=1, and set that flag on the staging and PR-preview
builds (production leaves it unset and stays indexable). noindex is
used rather than a robots.txt Disallow so crawlers can still fetch the
page and read both the tag and the canonical, which already points at
the production origin.

* fix(landing): make staging noindex actually take effect

The previous commit read `process.env.OD_LANDING_NOINDEX` directly in
`seo-head.astro`, but `.astro` frontmatter is transformed by Vite and
does not see process.env, so the meta never rendered. Two fixes:

- Inject the flag as the compile-time constant `__OD_LANDING_NOINDEX__`
  via `vite.define` in astro.config.ts (config runs in Node and can read
  process.env); SeoHead consumes that constant.
- The homepage (`index.astro`) and `og.astro` build their own <head> and
  never use SeoHead, so a per-component meta can miss pages. Add an
  `astro:build:done` integration that appends a catch-all
  `/*  X-Robots-Tag: noindex, nofollow` to the Cloudflare Pages `_headers`
  on staging/preview builds, covering every response (homepage, assets,
  any custom-head page) at the HTTP layer. Production builds leave
  `_headers` untouched.

Verified: build with OD_LANDING_NOINDEX=1 emits the _headers block and
the SeoHead <meta>; build without the flag emits neither; astro check
clean.

* fix(landing): address review — pin prod checkout to main, defer index pointer

Two blockers from review:

- landing-page-production: workflow_dispatch can be launched from any ref
  via the Actions "Use workflow from" dropdown, so an operator could ship
  an arbitrary branch to open-design.ai. Pin the checkout to `ref: main`
  so the deployed artifact always equals reviewed main.

- blog-indexing-on-deploy: the `blog-indexed-prod` pointer was advanced
  right after sitemap submission, before Inspect / Search Analytics /
  Render status / Open status PR. A failure in any of those still moved
  the pointer, so the next production run skipped those posts. Move the
  advance to the very end, gated on `success()`, so a failure leaves the
  tag in place and the range is re-processed next run (submissions are
  idempotent).

* fix(landing): gate production promotion to the main ref only

Follow-up to the production-path review note: pinning checkout to main
fixed the deployed content, but the workflow was still dispatchable from
any ref, which records a non-main production run and would dodge
blog-indexing's `workflow_run` `branches: [main]` filter. Gate the whole
job on `github.ref == 'refs/heads/main'` so a dispatch from any other
branch/tag is skipped outright.
2026-05-26 14:05:04 +00:00
open-design-bot
6618780812 chore(card): generated card 2026-05-26 14:01:21 +00:00
open-design-bot
a7bafe78f7 chore(events): append pr_merged 2026-05-26 14:01:19 +00:00
open-design-bot
22d7830943 chore(contributors): +30pts for @jinmeihong0201-gif (PR #3012) 2026-05-26 14:01:17 +00:00
ashleytheash
f27fbfb3f1 chore(test): restore contributor data after email pipeline verification 2026-05-26 21:10:10 +08:00
open-design-bot
2e45a49a8d chore(card): generated card 2026-05-26 13:05:40 +00:00
open-design-bot
5ebced04f8 chore(events): append issue_opened_accepted 2026-05-26 13:05:38 +00:00
open-design-bot
c6c98d11a5 chore(contributors): @ashleytheash opened issue #3008 2026-05-26 13:05:37 +00:00
ashleytheash
9d5343a7de chore(test): reset for end-to-end resend send verification 2026-05-26 21:05:30 +08:00
open-design-bot
997a07e57d chore(card): generated card 2026-05-26 13:04:30 +00:00
open-design-bot
1bb91282b2 chore(events): append pr_merged 2026-05-26 13:04:28 +00:00
open-design-bot
51c46201fb chore(contributors): +30pts for @lefarcen (PR #2993) 2026-05-26 13:04:27 +00:00
lefarcen
a0ea9bdaf3
ci: make agent PR exploration manual only (#2993)
* Make agent PR explore manual dispatch only

* chore: retrigger PR checks

* chore: retrigger CI after Actions recovery
2026-05-26 12:59:58 +00:00
open-design-bot
faa77a940d chore(card): generated card 2026-05-26 12:59:04 +00:00
open-design-bot
371cf483e3 chore(events): append issue_opened_accepted 2026-05-26 12:59:03 +00:00
open-design-bot
c97d817ad8 chore(contributors): @ashleytheash opened issue #3007 2026-05-26 12:59:01 +00:00
ashleyashli
a922d077bf chore(test): reset contributors before resend-key end-to-end test 2026-05-26 20:58:01 +08:00
open-design-bot
2c968dd528 chore(card): generated card 2026-05-26 12:54:38 +00:00
open-design-bot
80149127e2 chore(events): append issue_opened_accepted 2026-05-26 12:54:37 +00:00
open-design-bot
7f2abf6b67 chore(contributors): @ashleytheash opened issue #3006 2026-05-26 12:54:35 +00:00
ashleyashli
31ac82b4e7 chore(test): reset contributors to test spark welcome flow end-to-end 2026-05-26 20:53:07 +08:00
open-design-bot
05ecfc335f chore(events): append issue_opened_accepted 2026-05-26 12:49:33 +00:00
open-design-bot
248f0e320a chore(contributors): @ashleytheash opened issue #3005 2026-05-26 12:49:32 +00:00
open-design-bot
687814a5ba chore(card): generated card 2026-05-26 12:48:56 +00:00
open-design-bot
b2da15bcca chore(events): append issue_opened_accepted 2026-05-26 12:48:54 +00:00
open-design-bot
5f72423261 chore(contributors): @ashleyashli opened issue #3004 2026-05-26 12:48:53 +00:00
ashleyashli
0bad8c8591 chore(test): temporarily remove ashleyashli to re-trigger spark welcome flow 2026-05-26 20:48:38 +08:00
open-design-bot
6d3786024f chore(card): generated card 2026-05-26 12:44:52 +00:00
open-design-bot
b1d518a39d chore(events): append issue_opened_accepted 2026-05-26 12:44:50 +00:00
open-design-bot
23f1b6b1ec chore(contributors): @ashleyashli opened issue #3003 2026-05-26 12:44:49 +00:00
open-design-bot
243dcab605 chore(card): generated card 2026-05-26 12:41:52 +00:00
open-design-bot
89d55b64d7 chore(events): append issue_opened_accepted 2026-05-26 12:41:50 +00:00
open-design-bot
9cc7460b9f chore(contributors): @ashleytheash opened issue #3002 2026-05-26 12:41:49 +00:00
chaoxiaoche
fce444bcab
Consolidate chat comments preview on main (#2906)
* feat(web): queue chat sends

* feat(web): render code comment directives

* feat(web): add preview comments and manual edits

* fix(web): polish shared chrome controls

* fix(web): align queued send loading state

* feat(web): open primary project artifacts

* fix(web): keep queued sends and tests aligned

* fix(web): restore docked comment tools layout

* fix(web): align preview comment toolbar

* fix(web): place local cli beside handoff

* fix(web): move agent menu beside handoff

* fix(web): make project instructions a direct header action

* fix(web): compact handoff and toolbar labels

* fix(web): clarify handoff menu and annotation label

* fix(web): restore compact cursor handoff trigger

* fix(web): align agent menu trigger with handoff

* fix(web): add draw toolbar close action

* fix(web): move inspect editing into edit mode

* fix(web): avoid reserving comment sidebar in annotation mode

* fix(web): float preview comments panel

* fix(web): keep edit canvas full width

* fix(web): polish preview annotation tools

* fix(web): highlight active preview comments

* fix(web): open comments panel after annotation save

* fix(web): polish comment handoff controls

* fix(web): remove palette preview tool

* fix(web): simplify draw annotation toolbar

* fix(web): restore queued tasks into composer

* fix(web): restore queued send strip styling

* fix(web): hide internal comment target ids

* fix(web): align manual edit panel header

* test(web): cover visual interaction contracts

* fix(web): address PR feedback regressions

* fix(web): preserve artifact chrome state

* fix(daemon): restore project raw file routes

---------

Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
Co-authored-by: mrcfps <mrc@powerformer.com>
2026-05-26 10:31:19 +00:00
lefarcen
b5bf28060b
Add sandboxed agent PR exploration (#2604) 2026-05-26 07:52:42 +00:00
Jane
ceb636aa1b
feat(landing-page): plugin detail page interactive preview + share dialog (#2958)
* feat(landing-page): plugin detail page interactive preview + share dialog

The new `/plugins/<manifest-id>/` detail page that shipped in #2926
landed without the two affordances PR #2679 added to the legacy
`/skills/<slug>/` and `/templates/<slug>/` pages: a click-to-expand
iframe of the live artifact, and a share dialog with brand-keyword
copy plus four-channel jump buttons (X / LinkedIn / Reddit /
Facebook). This restores both, sourced from the bundled-plugin
manifest under `plugins/_official/<bucket>/<slug>/open-design.json`.

## Interactive preview

Three preview-type behaviours, gated on `od.preview.type`:

  - `video` (Cloudflare Stream URLs already in the manifest) —
    inline `<video controls poster=...>` with the playable MP4 as
    `<source>`. Detail-page row is unchanged from #2926; controls
    double as the open-full affordance.
  - `html` (a local `example.html` referenced by `od.preview.entry`,
    only the `examples/` bucket today) — `<details>` toggle wraps the
    poster image as the summary; clicking opens a sandboxed
    `<iframe>` that loads the entry HTML lazily, with an
    "Open in new tab ↗" pill in the frame's top-right corner so the
    artifact can be inspected at full screen.
  - `image` or no entry — static `<img>` (existing behaviour).

`copy-example-html.ts` is extended to mirror the local entry and any
`./assets/...` siblings to `out/plugins/<manifest-id>/<entry>` so the
iframe URL resolves on Cloudflare Pages instead of SPA-falling-back to
the homepage. The four examples carrying sibling-asset references
(flowai-live-dashboard-template, trading-analysis-dashboard-template,
open-design-landing, open-design-landing-deck) all render in-place.

## Share dialog

Same `<dialog data-share-dialog>` markup the legacy detail pages use,
so the global click handlers in `header-enhancer.astro`
(`data-share-open` / `data-share-copy` / `data-copy-link`) wire up
the open / copy actions automatically — no extra client bundle. Four
platform jumps (X / LinkedIn / Reddit / Facebook) plus a Copy-text /
Copy-link pair, with a single English template for now (the new
`/plugins/...` routes only generate English pages; localisation can
land alongside the i18n catch-all follow-up).

## Bundled in

- The `copy-example-html.ts` sibling-assets fix from open PR #2880.
  Without it the existing `/skills/<slug>/` iframe still 404s on
  Cloudflare Pages for after-hours-editorial-template and the four
  others; bundling it here means the same script handles both
  sources in one pass and sidesteps two PRs touching identical
  helper code.

* fix(plugins): remove dangling preview.entry from example-hyperframes

The hyperframes example folder ships a SKILL.md (it's an instruction
manual for using the HyperFrames HTML format) but no runnable
`example.html`. The manifest still claimed `preview.type: html` /
`preview.entry: ./example.html`, which made the marketing site try
to iframe a non-existent file and forced the preview pipeline into
its `Path 3` fallback card — leaving the catalog row visually
inconsistent with the eleven sibling `video-template-hyperframes-*`
plugins that have real Cloudflare-Stream poster URLs.

Drop the preview block entirely so the manifest stops promising a
demo it can't deliver. The landing-page detail row continues to
render the typographic fallback card (sourced from title /
description / mode), which is now the honest representation:
"this is an instruction skill, not a renderable template".

* fix(landing-page): address PR #2958 review feedback on plugin preview pipeline

Two blocking issues called out in code review:

1) `bundled-plugins.ts` exposed `previewEntryUrl` for every manifest
   that declared `preview.type: "html"`, even when the entry file
   wasn't shipped. Several first-party manifests fall in this state
   (example-design-brief's `./brief-preview.html`, example-x-research,
   example-pptx-html-fidelity-audit, example-hatch-pet,
   example-last30days, example-guizang-ppt, example-replit-deck,
   example-live-artifact, example-html-ppt, example-dcf-valuation).
   The detail page then rendered a click-to-expand iframe and popout
   link to a file that copy-example-html.ts had skipped, so the
   iframe URL SPA-fell-back to the homepage on Cloudflare Pages.

   `entryRelativeUrl()` now `existsSync()`-checks the resolved local
   path before returning a URL. When the file's missing the detail
   page falls through to the static thumbnail branch, exactly like
   plugins that ship no preview entry at all.

2) `copy-example-html.ts` recognised only `(src|href|poster)="./assets/..."`
   and then bulk-copied the entry's sibling `assets/` folder, so it
   missed two real ref shapes: bare-relative (`href="assets/styles.css"`,
   `src="assets/deck-stage.js"` under example-html-ppt-zhangzara-pin-and-paper)
   and cross-folder (`src="../open-design-landing/assets/hero.png"`
   under example-open-design-landing-deck).

   Replaced the heuristic with a generic walker that:
   - Parses every relative ref in the entry HTML
     (`(src|href|poster|srcset|data-src)=` plus `url(...)`), splitting
     srcset on whitespace/commas so multi-URL attrs are honoured.
   - Resolves each ref against `dirname(entrypointSrc)` for the source
     and against `dirname(iframeAbsPath)` for the destination —
     identical to how a browser resolves the same ref against the
     iframe URL. Files outside the source root or the iframe root
     are dropped.
   - Recurses into copied HTML / CSS / JS / SVG so multi-step chains
     (entry → assets/template.html → assets/fonts/foo.woff) don't
     strand intermediate files.
   - Tracks visited *destinations* rather than sources, so a single
     source that legitimately needs to land at two different out-paths
     (same-folder copy at /plugins/example-X/assets/foo.png AND a
     cross-folder copy at /plugins/open-design-landing/assets/foo.png
     for sibling decks that use `../open-design-landing/assets/foo.png`)
     gets both copies.

Verified manually:
- /plugins/example-html-ppt-zhangzara-pin-and-paper/assets/styles.css
  and assets/deck-stage.js → 200 (bare-relative)
- /plugins/open-design-landing/assets/hero.png and assets/about.png
  → 200 (cross-folder destination, no manifest-id prefix because
  iframe URL `..` collapses the prefix)
- /plugins/example-design-brief/ renders the static thumbnail only,
  no click-to-expand iframe (broken entry guard)
- /plugins/example-flowai-live-dashboard-template/assets/template.html
  → 200 (existing same-folder behaviour preserved)

Build now reports `copied 266 entry files + 65 referenced files`,
where the 65 includes both the same-folder `./assets/...` payloads
the previous heuristic captured and the bare-relative + cross-folder
shapes it didn't.

---------

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-26 07:43:46 +00:00
Yuhao Chen
3655230571
fix(web): align draw note enter action (#2971) 2026-05-26 07:36:34 +00:00
Marc Chan
d5659d82d4
chore(nix): streamline pnpm deps hash maintenance (#2919)
* chore(nix): streamline pnpm deps hash maintenance

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

* fix(ci): satisfy actionlint in nix hash autofix

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

* fix(ci): allow nix hash autofix on fork PRs

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

* fix(ci): follow up nix hash review

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

* fix(ci): tolerate nix hash bot token failures

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)
2026-05-26 07:35:38 +00:00
Yuhao Chen
fb1e0c819f
fix(plugins): reject symlinked plugin assets (#2036)
* fix(plugins): reject symlinked plugin assets

* test(plugins): cover asset directory symlink escapes

* fix(plugins): reject symlinked asset path segments
2026-05-26 07:03:22 +00:00
kami
9e8d01ee22
fix(daemon): fail disallowed connector tool selections (#2006)
* fix(daemon): fail disallowed connector tool selections

* fix(daemon): harden connector tool error parsing
2026-05-26 07:03:10 +00:00
蓝宙
b5b975769a
fix Windows folder import from project modal (#2863)
Co-authored-by: Lanzhou3 <217479610+Lanzhou3@users.noreply.github.com>
2026-05-26 06:52:07 +00:00
Yuhao Chen
1770789fa0
fix(web): hide recent project pagination chrome (#2966) 2026-05-26 06:47:48 +00:00
ashleytheash
dbdf3b9637
feat(landing): add /share-out redirect to track X share button clicks (#2969)
* feat(landing): add /share-out redirect for X share button click tracking

Adds a Cloudflare Pages Function at /share-out/:eventId that records each
click of the "Share on X" button surfaced in the contributor card comments
on GitHub, then 302-redirects to the original twitter.com / x.com intent
URL (passed via ?to=, host-allowlisted).

Together with the existing /share/:eventId function this gives us both
sides of the X funnel without an X API key:

  - /share-out/:eventId  -> GitHub user clicked the X button   (funnel step 1)
  - /share/:eventId      -> someone on X clicked the posted tweet (funnel step 2)

Per-event KV storage is optional (SHARE_OUT_CLICK_EVENTS). When no KV is
bound the function falls back to console.log; aggregate counts are visible
in Cloudflare Pages analytics with no extra setup.

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

* chore: retrigger CI

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

---------

Co-authored-by: ashley li <ashleyli@ashleydeMacBook-Air-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 15:13:59 +08:00
nettee
6c6ee44e07
docs(media): clarify custom providers and ComfyUI status (#479) (#2942)
Closes #479

Generated-By: looper 0.9.0 (runner=worker, agent=opencode)
2026-05-26 06:22:02 +00:00
Marc Chan
125dcd0174
fix(ci): run fork visual reports from trusted code (#2935)
* fix: run fork visual reports from trusted code

* fix: auto-approve strict web visual capture

* fix: address visual report review feedback

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

* fix: propagate visual report storage failures

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

* fix: validate PR screenshots before upload

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

* fix: validate visual PR identity before comment

* fix: harden fork visual report validation

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

* fix: address remaining fork visual report review feedback

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

* fix: handle stale fork visual report lookup

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

* fix: allow stale fork visual report fallback

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
2026-05-26 06:17:04 +00:00
PerishFire
f4d53486ec
fix(web): warn BYOK users that API mode has no file-edit tools (#2943)
The BYOK chat path streams model text directly through the provider API and
does not run the agent-runtime scaffolding that wires up Read/Write/Edit
tools — so a model that "decides" to edit a file just emits HTML or code
back into the chat. When users switch from Local CLI (Claude Code) to
BYOK with an Anthropic key and then ask the agent to keep adjusting a
design, nothing on disk changes and the failure mode is silent.

Until the BYOK tool loop is implemented (#313 / #699 / #719), surface a
clear notice in the BYOK panel of Settings that explains the limitation
and points users at Local CLI mode for file edits.

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)

Co-authored-by: libertecode <libertecode@proton.me>
2026-05-26 06:16:14 +00:00
hahalolo
1aa72b6d09
fix(docker): fix container startup crash due to missing OD_API_TOKEN (#2928)
* fix(docker): fix container startup crash due to missing OD_API_TOKEN

* fix(docker): forward OD_API_TOKEN to fix docker container boot loop

* fix(docker): enforce non-empty OD_API_TOKEN for docker-compose

* fix(deploy): automate OD_API_TOKEN generation in installer and close compose loop

* docs(readme): guide manual deployment users to configure OD_API_TOKEN

* docs(readme): align working directory paths for manual deployment instructions

* docs(readme): align working directory paths for manual deployment instructions

* docs(readme): restore git clone context for first-time users
2026-05-26 06:15:48 +00:00
PerishFire
bfcafc81fd
feat(pack): add Windows portable zip target alongside NSIS installer (#2937)
Adds a new `--to zip` (and `--to all`) tools-pack Windows build target that
produces a portable `.zip` from the cached `win-unpacked` tree using the
bundled 7z. The zip lays files at the archive root so users can extract it
anywhere and launch `Open Design.exe` without going through the NSIS
installer, addressing the no-install download request.

Release plumbing is updated to publish the portable zip and its sha256
beside the existing installer on R2 for beta, preview, and stable channels
(default on, gated by `WINDOWS_INCLUDE_ZIP`/`WIN_INCLUDE_ZIP`). The
electron-updater `latest.yml` feed continues to point only at the
installer; the zip is a manual-download convenience and is intentionally
excluded from the in-app updater.

Closes #1121

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)

Co-authored-by: libertecode <libertecode@proton.me>
2026-05-26 06:14:44 +00:00
Prantik Medhi
709fc0a497
fix: keep raw HTML source out of artifact chat prose (#2940)
* fix: keep raw HTML source out of chat prose for artifacts

* test: cover artifact chat prose rule
2026-05-26 06:14:28 +00:00
MrBeanDev
fc53feccd9
feat(web): add Aider and Trae CLI brand icons, refresh Pi mark (#2956)
Some checks failed
visual-baseline / Capture visual baselines (push) Waiting to run
actionlint / Lint GitHub Actions workflows (push) Failing after 1s
ci / Detect CI change scopes (push) Successful in 0s
landing-page-ci / Validate landing page (push) Failing after 1s
landing-page-deploy / Deploy landing page (push) Has been skipped
nix-check / build (push) Failing after 2s
ci / Validate Nix flake (push) Has been skipped
ci / Preflight (push) Failing after 1s
ci / Workspace unit tests (push) Failing after 1s
ci / Daemon workspace tests (push) Failing after 1s
ci / Web workspace tests (push) Failing after 2s
ci / Browser tests (push) Failing after 2s
ci / Build workspaces (push) Failing after 1s
ci / Validate workspace (push) Failing after 0s
ci / Runtime trace (push) Has been skipped
AgentIcon fell back to an initial-letter pill for Aider and Trae CLI
because neither id was registered in ICON_EXT and no asset shipped. Add
the bundled brand marks so both agents render their real logo:

- aider.png — Aider's published avatar, downscaled to 96px (~2.6KB).
- trae-cli.png — Trae's app icon, downscaled to 96px (~2.3KB). Keyed on
  the `trae-cli` runtime id so the file and ICON_EXT entry match exactly.

Both vendors only publish rasterised marks, so they follow the existing
PNG-fallback path used for Devin.

Pi shipped a stale single-glyph silhouette in MONO_ICONS. Replace it
with the current dark-tile mark (white glyph on #09090b) and drop it
from MONO_ICONS — the tile has baked colors, so CSS-mask rendering with
currentColor would flatten it to a solid square. It now renders through
<img> like the other color-baked brands.

Adds AgentIcon test coverage for all three.
2026-05-26 06:14:15 +00:00