Commit Graph
1023 Commits
Author SHA1 Message Date
Ethan GuoandGitHub 0d2c87242f fix(web): snapshot pending before reload so expired-auth cancel actually fires (#1972)
reloadConnectorStatuses commits its prune setState (and runs the connectorAuthorizationPendingRef mirror effect) between the two awaits in onFocus, so cancelStaleAuthorizations was reading a post-prune ref and never identified anyone as stuck. Capture the snapshot inside onFocus before reloadConnectorStatuses runs, and pass it explicitly so the expired-auth daemon cancel actually fires.

Refs #1909 #1354
2026-05-17 23:13:53 +08:00
d36ceacf3a feat(web): surface saved Project instructions for review and retrieval (#1933)
Saved Project instructions had no post-save read-back surface. The
header control was a bare pencil icon that opened an editor and closed
on Save, so after returning to the workspace a user could not confirm
what was stored, revisit it, or tell whether it was still active
(#1822).

Make the saved state discoverable: once instructions exist, the header
affordance becomes a persistent "Project instructions" chip. Opening it
shows a read-only review panel with a preview of the saved text and an
"Active - included in every message" status, plus a reopen-to-edit
action. Saving lands back on the review panel so the stored value is
read back immediately. The empty state keeps the pencil and opens the
editor directly.

Persistence and project-level system-prompt injection are unchanged.

Closes #1822

Co-authored-by: multica-agent <github@multica.ai>
2026-05-17 23:07:25 +08:00
647433ccef Fix Inspect preview transport flash (#1967)
* Fix inspect preview transport flash

Co-authored-by: multica-agent <github@multica.ai>

* Keep inactive srcdoc transport lazy

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-05-17 22:30:37 +08:00
8562de404a fix: horizontal overflow pushes chat panel and toolbar off-screen when iframe preview exceeds viewport width (#1930)
Co-authored-by: 郑惠 <14549727+felicia-study@user.noreply.gitee.com>
2026-05-17 20:56:52 +08:00
mar2181andGitHub 1456cc4c14 feat(community-pets): add dentist mascot (#1924)
A friendly stylized chibi dentist in white scrubs and surgical mask.
8x9 atlas (1536x1872 webp) covering idle, run-left/right, waving,
jumping, failed, waiting, running, review states.
2026-05-17 20:45:53 +08:00
张东明andGitHub e93583c550 fix(web): remove overscroll-behavior:none blocking scroll in New Project panel (#1945)
`overscroll-behavior: none` on `.newproj-body` prevented trackpad and
mouse wheel scrolling on macOS — the panel only scrolled when hovering
directly over the native scrollbar track. Removing the property restores
normal scroll behavior.

Fixes #1942
2026-05-17 20:28:22 +08:00
laihenyiandGitHub 53104466d6 docs(i18n): sync zh-TW README with English version (#1919)
* docs(i18n): sync zh-TW README with English version

- Add missing Docker, Nix, and Stay in the loop sections
- Add Desktop data migration guide (OD_LEGACY_DATA_DIR + manual copy)
- Update badge bar with Latest release and Follow @nexudotio badges
- Update At-a-glance table: BYOK endpoint, design systems 72→129,
  Media generation row, [ads] reference
- Update OD_DATA_DIR row from "not supported" to full instructions
- All translations follow Taiwan Traditional Chinese conventions
  and TRANSLATIONS.md rules

* fix(i18n): link QUICKSTART.zh-TW.md instead of QUICKSTART.md in zh-TW README
2026-05-17 20:25:20 +08:00
cd56b5e576 fix(cli): plugin publish reads manifest version when the stored row reads as the 0.0.0 pre-handshake sentinel (#1903)
Closes #1765

`od plugin publish --to open-design` builds the registry-submission
body from the daemon's `/api/plugins/:id` row. The row carries a
stored `version` plus the full manifest. For project-local plugins —
`generated-plugin/`, snapshots, freshly imported folders — the
stored `version` reads as `'0.0.0'` until the registry handshake
runs and stamps the real version. The manifest's `version`, however,
is the author's real value the whole time.

Before this patch, the publish flow consumed `row.version` directly
and printed `version: 0.0.0` into the registry submission body even
when the plugin's `open-design.json` declared `0.1.0` or any other
non-sentinel version.

Mirror `apps/daemon/src/plugins/marketplaces.ts:298,328` and prefer
the manifest version when the stored row reads as the pre-handshake
sentinel. The fallback chain is now:

  1. row.version, when it exists and isn't '0.0.0'
  2. row.manifest.version, when it exists and is non-empty
  3. row.version (even if '0.0.0'), preserving today's behavior for
     plugins that genuinely have no manifest data
  4. '0.0.0' literal, when nothing else is available

The fallback chain is conservative: it never substitutes a higher
version that the author didn't already declare somewhere — it just
prefers the manifest over the stored sentinel.

Validation:
- `pnpm --filter @open-design/daemon test -- tests/plugins-publish.test.ts` → green
- `pnpm --filter @open-design/daemon typecheck` → green

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-17 20:24:41 +08:00
kamiandGitHub 30ad8b8ac3 improve privacy consent modal: policy link, clearer CTAs, mobile layout (#1921)
The first-run consent banner had no link to a privacy policy, an
affirmative button ("Help improve") that didn't read as a consent
choice, and a fixed bottom-right card that crowded content on phones.

- Add a "Read the privacy policy" link (external-link icon, accent,
  underlined) above the actions, plus a root PRIVACY.md it points to
  documenting the telemetry behaviour the modal discloses.
- Rename the CTAs to "Share usage data" / "Don't share" so both name
  the action; they stay equal-prominence per the EDPB/GDPR comment.
- Stretch the banner to a bottom-edge bar under 540px with a
  safe-area inset so it clears mobile browser chrome.
- Add PrivacyConsentModal tests; sync the new i18n key to every
  locale and update the consent-label assertion in App.connectors.

Refs #1756
2026-05-17 20:24:15 +08:00
Ethan GuoandGitHub 9cc1fb28f3 fix(web): apply Tweaks palette shift to :root CSS custom properties (#1952)
Walk document.styleSheets so chromatic custom properties on :root/html/body/:host (including @media-nested rules) are hue-shifted via inline setProperty, not just the values returned by getComputedStyle.

Fixes #1393
References: PR #1643 (open, in review) binds the Tweaks toolbar toggle to artifact `.tw-panel` panels via a new `injectTweaksBridge` in `srcdoc.ts` — adjacent surface but different problem. Our fix extends `injectPaletteBridge` to also shift `:root` CSS custom-property colors, which #1643 does not touch.
2026-05-17 20:16:57 +08:00
Ethan GuoandGitHub b7fa1d9286 fix(web): recover stuck Composio connector when auth is cancelled (#1909)
* fix(web): recover stuck Composio connector when auth is cancelled

When a user closes the Composio system-browser auth window without completing the flow, the connector card stayed in its loading state forever because nothing told the daemon the request was abandoned.

The window-focus refresh now awaits the status poll and, for every connector still in connectorAuthorizationPending whose freshly fetched status is not 'connected', issues cancelConnectorAuthorizationRequest and clears the matching local pending/error UI state. The connected guard is read from the just-returned statuses so a postMessage success that lands just before focus is not racy.

Fixes #1354

* fix(web): gate Composio focus auto-cancel on TTL + cancel response

Require expiresAt to have elapsed before auto-cancelling a pending
Composio auth on focus, and preserve the spinner on a failed cancel
(matching the manual Cancel handler) so the UI never disagrees with
the daemon.

Refs https://github.com/nexu-io/open-design/pull/1909#discussion_r3254152045
Refs https://github.com/nexu-io/open-design/pull/1909#discussion_r3254152046
2026-05-17 20:15:38 +08:00
Ethan GuoandGitHub 324eca27ea feat(web): add manual removal for captured Pod components (#1951)
* feat(web): add manual removal for captured Pod components

Adds `removePodMember` helper and a per-chip × in `BoardComposerPopover`; leaves `comments.ts` untouched (avoid-zone from #1127).

Closes #802
Contract: runs/2026-05-16T08-08-52_open-design_issue-feat/contract.md

* style(web): hide Pod chip × until chip hover

Swaps the unicode × for the existing `Icon name="close"` SVG so the
hit target stays centered, and fades the button in only on chip
hover / keyboard focus for a quieter resting state.

* fix(web): auto-close Pod composer when last chip is removed

Removing the last chip leaves a stale anchor; close so Send cannot attach to elements no longer visible.

* refactor(web): extract BoardComposerPopover and Pod-member reducer

Moves the popover to its own module and lifts the chip-removal reducer into a pure `applyPodMemberRemoval` so unit tests exercise the real code path and the popover's export is no longer test-only.

* fix(web): rebuild Pod anchor when a member is removed

Without this, the popover keeps the original union bbox / selector / label after each chip removal, so a subsequent Send to chat anchors the comment to elements no longer in the Pod.

* fix(web): render every captured chip and scroll on overflow

The previous slice(0, 6) cap left chips beyond the sixth invisible and undeletable. Render the full list inside a 132px-tall scrollable strip.
2026-05-17 20:13:56 +08:00
Ethan GuoandGitHub 10c62bf6e4 fix(web): warn before editing a built-in skill creates a shadow (#1850)
Clicking Edit on a built-in skill silently issued PUT /api/skills/:id,
which the daemon stores as a user-owned shadow and then hides the
built-in entry from the Settings list. From the user's perspective the
row they just edited disappears with no explanation.

Arm an inline override-warning banner on Edit for source==='built-in'
skills, mirroring the existing inline delete-confirm pattern. The edit
form only opens after the user explicitly confirms. User skills bypass
the warning and edit directly. No daemon or listSkills contract change.

Fixes #1378
2026-05-17 18:30:45 +08:00
Ethan GuoandGitHub a6288cec3f fix(web): allow editing existing routines from RoutinesSection (#1884)
The Routines list only exposed Run now / Pause / History / Delete, so any
change to a routine required deleting and recreating it. The daemon already
serves PATCH /api/routines/:id; only the web entry point was missing.

Add an Edit button on each routine row that pre-populates the create form
from the stored schedule and target via a new formFromRoutine helper, and
branch the submit handler on editingId so it PATCHes /api/routines/:id
with the updated fields instead of POSTing a new routine.

Fixes #1373
2026-05-17 15:02:54 +08:00
Yuhao ChenandGitHub fb57ef2fc3 Improve DeepSeek TUI diagnostics (#1940) 2026-05-17 15:01:10 +08:00
张东明andGitHub bac56415a2 fix(web): surface daemon error messages for invalid folder imports (#1923)
* fix(web): surface daemon error messages for invalid folder imports

importFolderProject() was swallowing non-2xx responses by returning
null, so the UI could only show a generic "Open folder failed: <path>"
message even though the daemon already returns specific errors like
"cannot import the filesystem root" or "folder not found".

Parse the daemon error body and throw so the panel displays the actual
reason. Also show feedback for empty path input instead of silently
returning.

Fixes #1186

* test(web): update folder import test to match new error propagation

The existing test expected a generic "Open folder failed: <path>"
message from a boolean return. Update to match the new behavior where
the daemon's error message is thrown and displayed directly.
2026-05-17 15:00:49 +08:00
Nicholas-XiongandGitHub 330a93116d fix: prevent horizontal overflow in split layout when iframe content exceeds viewport width (#1935)
Fixes #1929

Problem:
- When viewing HTML artifacts in preview mode, if iframe content width exceeds
  the available workspace width, the entire .split grid container expands
  beyond the viewport
- This pushes the left chat panel and top toolbar buttons outside the visible area
- Users cannot access chat or toolbar when viewing wide preview content

Root Cause:
- .split uses grid-template-columns: 460px 8px minmax(400px, 1fr)
- The third column minmax(400px, 1fr) expands to fit content
- No max-width or overflow constraint on .split container
- When iframe content is wider than viewport, grid expands to accommodate it
- Parent containers don't constrain the grid width

Solution:
- Add max-width: 100% to .split to constrain it to parent width
- Add overflow: hidden to prevent content from pushing layout
- Grid columns now stay within viewport bounds
- Only the iframe/preview area scrolls internally

Changes:
- Modified: apps/web/src/index.css
  - Added max-width: 100% to .split (line 1491)
  - Added overflow: hidden to .split (line 1492)

Impact:
- ✅ Fixes horizontal overflow when iframe content exceeds viewport width
- ✅ Chat panel stays visible and accessible
- ✅ Toolbar buttons remain in their intended positions
- ✅ Layout stays fixed to viewport width
- ✅ Only preview area scrolls internally
- ✅ No breaking changes to existing layouts
- ✅ Works with all viewport sizes

Testing:
1. Open any project and generate an HTML artifact (e.g., todo app)
2. Open the artifact in preview mode (desktop viewport)
3. Verify the layout stays within viewport width
4. Verify chat panel remains visible on the left
5. Verify toolbar buttons remain accessible at the top
6. Verify only the preview iframe scrolls horizontally if content is wide
7. Test with various artifact widths (narrow, normal, extra wide)
8. Test resize handle still works correctly

Before Fix:
- Wide iframe content pushes entire layout beyond viewport
- Chat panel disappears off-screen to the left
- Toolbar buttons disappear off-screen
- Horizontal scrollbar appears on entire page
- Poor user experience with wide content

After Fix:
- Layout stays fixed to viewport width
- Chat panel always visible
- Toolbar always accessible
- Only iframe scrolls internally
- Clean, predictable layout behavior
2026-05-17 14:57:33 +08:00
Nicholas-XiongandGitHub d54d3412d6 fix: wire onRenameProject through EntryShell to DesignsTab (#1882)
Fixes #1790

## Problem

Project rename does not take effect in Open Design 0.8.0. After entering
a new name and clicking OK, the project keeps its old name.

## Root Cause

The onRenameProject callback was broken in the prop chain:

App.tsx ──(handleRenameProject)──▶ EntryView ✅
EntryView ──(onRenameProject missing)──▶ EntryShell ❌
EntryShell ──(onRename not in Props)──▶ DesignsTab ❌
DesignsTab.commitRename() → onRename?.() → silent no-op ❌

EntryView received onRenameProject from App but never forwarded it to
EntryShell. Since EntryShell is what renders DesignsTab, DesignsTab
never received the onRename callback. Because the prop is typed optional
(onRename?:), the confirm handler's onRename?.(id, trimmed) call
silently no-ops — no error, no rename.

This was introduced when EntryShell was added to the 0.8.0 codebase as
an intermediate shell layer and the rename prop was missed during the
handoff.

## Solution

Wire the callback through the missing links:

1. EntryView.tsx: Pass onRenameProject={onRenameProject} to EntryShell
2. EntryShell.tsx:
   - Add onRenameProject to Props interface
   - Destructure it in function params
   - Pass onRename={onRenameProject} to DesignsTab

Now the callback chain is complete and project renames work correctly.

## Testing

Verified that:
- Project rename dialog accepts new name
- Clicking OK successfully renames the project
- Project list reflects the new name immediately
- No console errors or warnings

## Impact

3 lines changed across 2 files. Low risk — only restores the missing
prop forwarding, does not change any rename logic or validation.
2026-05-17 14:57:11 +08:00
7766582f0b chore(ci): scope nix-check workflow permissions to contents:read (#1870)
The other workflows under .github/workflows declare explicit
`permissions:` blocks that scope their GITHUB_TOKEN to the minimum
required (contents: read for build-only flows). `nix-check.yml` was
the lone outlier and inherited the repository's default token
permissions instead.

Add `permissions: { contents: read }` to align with the rest of the
workflow suite and follow GitHub's least-privilege workflow guidance.
No behavior change: the job only reads the repo, runs `nix flake
check`, and uploads a logs artifact on failure (which uses an action
that already declares its own permissions internally).

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-17 11:28:18 +08:00
e64f1d8497 fix(desktop): export PDF saves to a file instead of the OS print dialog (#1920)
* fix(desktop): export PDF saves to a file instead of the OS print dialog

The `od:print-pdf` IPC handler called `webContents.print()`, which
opens the printer-first macOS system print dialog. That handler is the
destination of the renderer's `window.__odDesktop.printPdf()` bridge,
so on desktop "Export PDF" felt like a print flow rather than a file
export — from both the PreviewModal share menu (which always uses this
path) and the FileViewer share menu (whose daemon-route fallback lands
here too).

Route the handler through a direct Save-as-PDF flow instead: a native
Save dialog, then `webContents.printToPDF()` straight to the chosen
file — the same shape `exportPdfFromHtml` already uses for the
daemon-backed export path. The flow is extracted into
`savePrintReadyDocumentAsPdf` behind a structural `PrintReadyPdfTarget`
surface that has no `print()` method, so the regression cannot be
reintroduced.

Fixes #1774

Co-authored-by: multica-agent <github@multica.ai>

* fix(desktop): preserve PDF page sizing in print bridge

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-05-17 11:26:08 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
5b7a2fdb2f Update docs/assets/github-metrics.svg (#1931)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-17 11:25:46 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
3c621131d2 docs(readme): refresh contributors wall (#1932)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-17 11:24:28 +08:00
Marc ChanandGitHub 6bf865a43b fix(ci): avoid duplicate nix-check runs on PR branches (#1917)
* fix(ci): avoid duplicate nix-check runs on PR branches

* fix(ci): keep nix-check on main pushes

Restore the nix-check push trigger for main-only updates while still avoiding duplicate PR-branch runs.

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
2026-05-16 23:38:50 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
cfd361af3a Update docs/assets/github-metrics.svg (#1915)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-16 22:45:00 +08:00
Marc ChanandGitHub 1f728ac8e3 fix(ci): only run docker image workflow for release tags (#1916) 2026-05-16 22:32:33 +08:00
440de58e10 docs(packaging): clarify Intel Mac ZIP support (#1874)
Co-authored-by: li9292 <li9292@li9292s-MacBook-Air.local>
2026-05-16 22:27:33 +08:00
Marc ChanandGitHub d6515643ef fix(ci): use open-design-bot for metrics PRs (#1910)
* fix(ci): use open-design-bot for metrics PRs

* fix(ci): restore metrics workflow app scopes

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
2026-05-16 21:52:37 +08:00
Yuhao ChenandGitHub 2e1dd497aa fix(landing): expose blog RSS feed alias (#1859) 2026-05-16 21:52:11 +08:00
laihenyiandGitHub 22e02446ef docs(i18n): add RTL_LOCALES UI guidance and fix Spanish es-ES alignment (#1908)
Three follow-up fixes for TRANSLATIONS.md from review of #1333:
- P1: Document RTL_LOCALES registration in index.tsx for UI runtime RTL
  (detailed steps, Arabic section, and FAQ — previously only covered
  Markdown auto-RTL)
- P2: Align Spanish terminology section with the repo's actual es-ES code
  (was recommending neutral/LatAm while dismissing Spain terms)
- Minor: Add "(if that file exists)" caveat to Quick Start link translation
  example, matching the detailed section

Refs: #1333, #195
2026-05-16 21:45:15 +08:00
e92f91fb06 fix(contracts): tighten LiveArtifactSsePayload.refreshStatus to LiveArtifactRefreshStatus (#1871)
The SSE `live_artifact` event payload typed `refreshStatus` as
`string | undefined`, but the underlying REST API (`LiveArtifact`) and
every consumer treat it as the canonical `LiveArtifactRefreshStatus`
union (`'never' | 'idle' | 'running' | 'succeeded' | 'failed'`).
Daemon emitters already pass `artifact.refreshStatus`, which is
typed as the enum at the source.

The old type let new union members drift between REST and SSE
unnoticed: a daemon adding a new refresh state for the REST API
could ship out via SSE as a bare `string`, and web/CLI consumers
switching on the union would silently fall through.

Tighten the SSE field to the same enum. The field stays optional
to preserve forward/backward compatibility with daemons that omit
it on legacy events, but the type now narrows to the canonical
members. Runtime behavior is unchanged — this is a type-only fix.

Validation: contracts build/test, web typecheck, daemon typecheck.

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-16 21:39:39 +08:00
2d1a7bd727 fix(daemon): isLocalSameOrigin honors OD_ALLOWED_ORIGINS in reverse-proxy deployments (#1902)
Closes #1868

When the daemon runs behind a reverse proxy (Nginx, Caddy, Traefik),
the Host header the daemon observes is the proxy's upstream address,
not the browser-visible origin the user typed in. As a result the
host check inside isLocalSameOrigin rejected requests whose Origin
header was *explicitly listed* in OD_ALLOWED_ORIGINS — defeating the
documented escape hatch for proxy deployments.

Add a single early-return in isLocalSameOrigin: if a non-empty
Origin header exactly matches an entry in OD_ALLOWED_ORIGINS, trust
it. The configuredAllowedOrigins parser already requires the
origin to be a well-formed http(s) URL and rejects unsupported
schemes, so the allow-list is a maintainer-controlled set of
trusted origins.

The existing host-based loopback / private-LAN path remains the
default for no-Origin and unlisted-Origin requests; this change is
strictly additive for the explicit allow-list case.

Validation:
- New tests in apps/daemon/tests/origin-validation.test.ts cover
  the nginx-upstream case (Host is a container IP, Origin is the
  allow-listed proxy origin) plus three negative cases: foreign
  Origin still rejected, no-Origin behavior preserved, trailing
  slash mismatch rejected.
- pnpm --filter @open-design/daemon test -- tests/origin-validation.test.ts → green
- pnpm guard → green

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-16 21:34:29 +08:00
Nicholas-XiongandGitHub a766048354 fix: Localize skill validation messages in Chinese and all other languages (#1690)
* fix: Localize skill validation messages in Chinese and all other languages

Fixes #1377

Skill creation form validation messages now appear in the user's selected
language instead of always showing English.

**Before:**
- App language set to Chinese
- Leave skill name or body empty
- Validation shows: 'Skill name is required.' (English)
- Mixed-language experience

**After:**
- App language set to Chinese
- Leave skill name or body empty
- Validation shows: '技能名称为必填项。' (Chinese)
- Consistent language experience

**Implementation:**
- Added 'settings.skillsNameRequired' translation key (18 languages)
- Added 'settings.skillsBodyRequired' translation key (18 languages)
- Updated SkillsSection.tsx to use t() instead of hardcoded strings
- Covers: en, zh-CN, zh-TW, de, fr, es-ES, pt-BR, ru, ja, ko, ar, fa, id, pl, hu, uk, tr, th

* fix: Remove line number prefixes from i18n locale files

The previous commit accidentally included line numbers from read_file output
in the locale files, causing TypeScript compilation errors.

This commit removes all line number prefixes (e.g., '1400|') from the
skillsNoFiles entries in all 17 language files.

* fix: Escape apostrophes in Ukrainian skill validation messages

Fix TypeScript syntax error caused by unescaped apostrophes in Ukrainian
locale strings for skill name and body validation messages.
2026-05-16 16:30:58 +08:00
9fa657d7e2 docs: sync Windows troubleshooting link across locale READMEs (#1875)
PR #1546 (`docs: add Windows launcher note`) added a pointer to
`docs/windows-troubleshooting.md` to the English README, right
between the environment requirements paragraph and the
"For desktop/background startup, …" paragraph. The 12 locale
READMEs were not updated at the time, so anyone arriving via
the language switcher would not see the Windows native-setup
path or the double-click launcher hint.

This commit adds an equivalent line to each locale README in
that locale's voice, at the same anchor position. No new asset
is introduced: every link points to the same English
`docs/windows-troubleshooting.md` file (no translated doc yet),
matching how other locale-specific tips already cross-reference
English docs in this repo.

Locales touched: ar, de, es, fr, ja-JP, ko, pt-BR, ru, tr, uk,
zh-CN, zh-TW.

Note: README.ko.md happens to also be missing the "For
desktop/background startup, …" paragraph that the other 11
locales already have. That gap is out of scope for this PR and
should be handled separately when ko translation parity is
revisited.

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-16 16:28:50 +08:00
Yuhao ChenandGitHub 2f553941d3 fix(runtime): auto-annotate imported HTML elements for Tweaks selection (#892) (#1169)
* fix(runtime): auto-annotate imported HTML elements for Tweaks selection (#892)

* fix(runtime): always annotate missing od-ids and broaden selector (#892)

- Remove the conditional gate so annotateMissingOdIds runs for every
  srcdoc, not just comment/inspect bridges. This fixes the persistence
  regression where saved Inspect tweaks reference synthesized data-od-id
  selectors that vanish when the bridge is rebuilt without annotations.

- Expand the selector to cover div-based imported HTML (div[class],
  div[id]), headings, buttons, and links so Picker/Pods work on
  common anonymous wrapper markup.

Addresses review feedback from lefarcen and mrcfps.

* fix(runtime): narrow div selector, skip iframe/object/embed, add tests (#892)

- Replace broad div[class] with direct-child combinators only under
  semantic containers, body, and [id] elements to avoid layout noise
- Add iframe, object, embed to the skip list alongside script/style
- Add tests for: direct-child divs, nested div skip, always-on behavior,
  skip list with id attributes, div children of [id] elements
- Format selector as array join and skip tags as Set for readability

* fix(runtime): let annotateManualEditSourcePaths coexist with data-od-id (#892)

annotateMissingOdIds now runs unconditionally, so elements like main/h1
get data-od-id before annotateManualEditSourcePaths runs. The old skip
condition (has data-od-id) incorrectly prevented source-path marking on
those elements. Change the guard to skip only when the element already
has data-od-source-path, allowing both attributes to coexist.
2026-05-16 16:17:36 +08:00
Marc ChanandGitHub fd82384b5e fix(ci): dispatch metrics branch validation (#1878)
* fix(ci): dispatch metrics branch validation

* fix(ci): dispatch metrics branch validation

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

* fix(ci): dispatch metrics branch validation

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

* fix(ci): dispatch metrics branch validation

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

* fix(ci): dispatch metrics branch validation

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

* fix(ci): dispatch metrics branch validation

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

* fix(ci): dispatch metrics branch validation

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
2026-05-16 16:15:44 +08:00
ff6f673f05 docs: enhance TRANSLATIONS.md with comprehensive contributor guide (#1333)
* docs: enhance TRANSLATIONS.md with comprehensive contributor guide

Addresses #195

Transform TRANSLATIONS.md from a maintainer-focused technical document
into a practical, contributor-friendly guide that saves hours of
reverse-engineering for new locale contributors.

Changes:
- Add Quick Start section with 5-step checklist
- Expand supported languages table to 18 locales with coverage status
- Add detailed step-by-step guide for adding UI dictionaries
- Include translation best practices (what to translate vs preserve)
- Add regional terminology glossary (zh-CN/zh-TW, pt-BR/pt-PT, etc.)
- Add comprehensive testing checklist (5 verification steps)
- Add PR submission guidelines with template
- Add FAQ section covering 15 common questions
- Include code examples and practical commands throughout

Key improvements:
- Reduces time from 8 hours to ~2 hours for new locale PRs
- Highlights commonly forgotten steps (updating all README switchers)
- Provides clear guidance on technical terms, brand names, and RTL
- Includes automated validation commands

The guide now covers the complete workflow from starting a translation
to submitting a PR, with practical examples at every step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: Fix TRANSLATIONS.md with real Dict keys and test guidance

P1 fixes:
1. Replace fake dictionary keys (nav.home, nav.gallery, button.create)
   with real Dict keys from en.ts (common.create, common.cancel,
   settings.language, entry.tabDesigns, entry.tabTemplates)
2. Add note that Dict type enforces key matching, invented keys fail
3. Add explicit test fixture update steps for EXPECTED_LOCALES and
   LOCALE_LABEL assertions in locales.test.ts

This addresses all P1 blockers from @lefarcen and @Siri-Ray reviews.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:12:45 +08:00
张东明andGitHub f0ec0e54de docs(landing-page): fix dead skill references in AGENTS.md (#1901)
PR #955 moved `skills/open-design-landing/` to
`design-templates/open-design-landing/` but AGENTS.md was not updated,
leaving 5 references pointing at a path that no longer exists.

Update all references to the current `design-templates/` location.

Fixes #1891
2026-05-16 16:08:24 +08:00
3944ef34b8 feat(scripts): add informational i18n coverage report (#1896)
Adds `scripts/i18n-coverage-report.ts` (wired via
`pnpm i18n:coverage`) that reports per-locale key-coverage drift
against English. The output looks like:

  Locale (key total = 1578 on en)
  en       English                keys=1578 missing=   0 ...
  zh-CN    简体中文                  keys=1578 missing=   0 coverage=100%
  th       ภาษาไทย               keys=1261 missing= 317 coverage=80%
  ...

Why this is useful right now:

- The existing `scripts/i18n-check.ts` validates *structural*
  consistency (locale registration, README switcher alignment, core
  doc link references) and exits non-zero on failure. It does not
  surface content-coverage drift.
- The test suite enforces strict English-parity only for Indonesian
  (`id`) — other locales pass CI even when an English key was added
  without a matching translation. Today the average locale is missing
  ~140–300 keys vs English on `main`.

This script is purely informational — exit code stays 0. It gives
contributors and release managers a fast way to see which locale
needs translation work without breaking PRs until the locale catches
up. Issue #1894 covers the policy question of *whether* strict
enforcement should extend beyond Indonesian (e.g., to a tier-1 locale
set); this script is the substrate for that policy conversation, not a
substitute for it.

Refs #1894.

Validation:
- `pnpm i18n:coverage` runs end-to-end against the 19 locale files
  under `apps/web/src/i18n/locales/`, exit 0.
- `pnpm guard` green.
- `pnpm exec tsc -p scripts/tsconfig.json --noEmit` green.

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-16 16:06:25 +08:00
MJ-thunderandGitHub 104e10f5e0 add perplexity design system (#1747)
* add perplexity design system

* fix accent row in light surface color table
2026-05-16 16:00:37 +08:00
Yuhao ChenandGitHub 0e61313347 fix(prompts): stabilize discovery brand answers (#1861) 2026-05-16 15:50:52 +08:00
4767c531c1 docs: sync README inventory badges to actual repo counts (skills 31→131, design-systems 72→149) (#1899)
The 12 locale READMEs (+ root English README) each carry two
shields.io inventory badges:

  https://img.shields.io/badge/design%20systems-72-orange
  https://img.shields.io/badge/skills-31-teal

The actual counts on `main` today (verified):

  find skills -maxdepth 2 -name SKILL.md | wc -l        → 131
  find design-systems -maxdepth 2 -name DESIGN.md | wc -l → 149

This commit updates the badge URLs to reflect the actual inventory.
This is the conservative "inventory-only" shape proposed in #1890 —
prose strings like "72 out of the box" or "X systems available" are
left alone in this PR because they carry historical / launch
context the badge URLs don't, and the right shape there is a
maintainer call. The badges themselves are unambiguously a current
inventory signal, so they're safe to sync without taking sides on
the policy question.

`README.fr.md` is intentionally skipped: it already replaced both
numeric badges with `catalogue` generic placeholders, so the
French README needs no update.

Refs #1890.

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-16 15:49:24 +08:00
Sam MorreelandGitHub 70e5ea6f70 Removed - sentinel for Qwen CLI (#1332)
* Removed - sentinel for Qwen CLI

* Addressed reviewer's comments

* Removed IDE config file

* Removed IDE file
2026-05-16 12:07:32 +08:00
57372a7519 docs(ko): add desktop/background startup paragraph to README.ko (#1876)
README.ko.md is missing the "For desktop/background startup,
fixed-port restarts, and media generation dispatcher checks
(OD_BIN, OD_DAEMON_URL, apps/daemon/dist/cli.js), see
QUICKSTART.md" paragraph that the other 11 locale READMEs and
the English README all carry. Korean readers landing on the
README through the language switcher do not see this pointer
and have to discover the operational env vars by reading the
English QUICKSTART unprompted.

Add an equivalent paragraph in Korean at the same anchor
position the other 11 locales use — between the environment
requirements paragraph and the "첫 번째 로드 시:" enumeration.
The link target is `QUICKSTART.md` (English), matching how
ru/uk/es/tr/ar already cross-reference the English QUICKSTART
when no locale-specific QUICKSTART exists yet.

No code changes; one file, two lines added.

Co-authored-by: nicejames <nicejames@gmail.com>
2026-05-16 11:47:38 +08:00
Yuhao ChenandGitHub 26502ea124 test(web): decouple memory preview icon assertion (#1863) 2026-05-16 11:37:34 +08:00
mehmet turacandGitHub 7eb1e7dcfe fix(daemon): wrap Claude connection smoke stdin (#1844) 2026-05-16 11:33:40 +08:00
open-design-bot[bot]GitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
ea7d98084e docs(readme): refresh contributors wall (#1867)
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-16 11:27:08 +08:00
epicandGitHub 1896c699a8 fix(web): use wrapper+icon pattern for agent model select to fix duplicate chevrons (#1831)
Native macOS browser sometimes renders its own disclosure indicator even
with appearance:none, stacking it on top of the CSS background-image SVG
chevron. Apply the same orbit-template-select-wrap pattern: clear the
background-image via explicit wrapper class, use an absolutely-positioned
Icon component for a single, controlled chevron. Affects both the model
picker and reasoning picker selects in the agent-model-row.
2026-05-16 00:29:53 +08:00
9a64fccdc0 fix(security): resolve hostname before approving external API base URLs (#1176)
* fix(security): resolve hostname before approving external API base URLs

Before this change the daemon-side base-URL validator only inspected the
literal hostname string. A public DNS record that points at an internal
address ('internal.example.com' → 10.0.0.5) passed validation, and the
daemon would issue the upstream request anyway — turning the BYOK proxy
into an SSRF primitive against internal infrastructure.

Add a small companion ('validateBaseUrlResolved') that runs the existing
sync check, resolves the hostname with 'dns.lookup({ all: true })', and
re-applies the block-list against every resolved address. Wire it into
the wrapper the daemon already uses ('validateExternalApiBaseUrl'), so
every proxy/finalize handler picks it up without further edits.

Carve-outs match the existing sync validator:

- Loopback hostnames skip DNS (Ollama-style local LLMs still work,
  including '*.localhost' / 'lvh.me'-style names that resolve to 127.0.0.1
  per RFC 6761).
- IP literals are already vetted by the sync pass; no need to re-resolve.
- DNS resolver errors fall through to the existing fetch error path —
  a transient ENOTFOUND should not turn into a 403.

The 6 callers that previously consumed the sync result now 'await' the
async wrapper. All call sites are already inside async route handlers.

Vitest coverage in apps/daemon/tests/connection-test.test.ts covers:
sync rejection passthrough, loopback / IP-literal short-circuits,
private IPv4 and IPv6 resolution, dual-stack with one private record,
public→public passes (api.openai.com), '*.localhost' resolved→loopback,
and resolver-error fallback.

Detected by: aeon (manual review + semgrep + osv + trufflehog).
Class: CWE-918 (SSRF) — DNS-based bypass.

* fix(security): cover remaining daemon baseUrl fetch surfaces + Ollama redirects

Addresses PR #1176 review feedback (lefarcen / mrcfps): the resolved-IP
wrapper only covered the proxy/finalize routes, leaving three adjacent
SSRF gaps open.

- testProviderConnection (/api/test/connection provider mode): switch
  from sync validateBaseUrl to await validateBaseUrlResolved so a
  hostname that resolves to a private IP is rejected before the daemon
  POSTs the smoke prompt upstream.
- listProviderModels (/api/provider/models): same swap. Import the
  DNS-aware helper from ./connectionTest.js since it carries the dns
  binding the daemon owns; contracts stays pure.
- Ollama proxy stream fetch: align with the other four proxy routes
  by setting redirect: 'error', so a validated public host cannot 3xx
  the daemon to a private/internal URL after the pre-fetch check.

Regression coverage:
- POST /api/provider/models — DNS spy returns 10.0.0.5 for a synthetic
  hostname; route must respond { ok: false, kind: 'forbidden' } and
  must not invoke upstream fetch.
- POST /api/test/connection provider mode — same shape.
- /api/proxy/ollama/stream — fetch mock asserts redirect: 'error' on
  the upstream Ollama call.

The existing /api/provider/models timeout test now stubs dnsPromises
so it doesn't race the probe timer against real DNS.

---------

Co-authored-by: aeon <aeon@aaronjmars.com>
2026-05-15 23:12:52 +08:00
mehmet turacandGitHub 6b15236843 fix(web): distinguish expanded memory preview action (#1813) 2026-05-15 23:08:30 +08:00
Abhisek DasandGitHub 3f56395e2d fix(web): restore Settings subtab-pill hover contrast in dark theme (#1815)
The .subtab-pill button hover rule hard-coded `rgba(255,255,255,0.6)` as the
hover background. In light theme this composites to a near-white wash that
sits cleanly under `--text`, but in dark theme it overlays a translucent
white on the dark panel and produces a bright, near-white surface beneath
the same light `--text`, dropping contrast to ~1.87 (well below WCAG AA 4.5).

The reporter (#1795, on 0.6.0) called this out on five Settings surfaces.
Four of them — BYOK / Appearance / Notifications / protocol-chip — were
incidentally fixed by the c5d77a03 dark-palette refresh because they use
the .seg-control / .seg-btn hover path, not the .subtab-pill path. The
Pet → pet source tabs (Built-in / Custom / Community) still reproduce on
0.7.0 because they use .subtab-pill, which never got a theme-aware hover.

Switch the hover background to `var(--bg-muted)`, an existing token that
mirrors `--bg-panel`'s active state shape (one step lighter than
`--bg-subtle`) and is defined per-theme. Composited contrast becomes
~9.4 in dark and ~14.3 in light against `--text`, comfortably above AA.

The same .subtab-pill rule is shared by RoutinesSection and DesignsTab, so
they pick up the fix as a side effect of fixing the source class.

Anchored by a Playwright regression test under e2e/ui/ that asserts WCAG
AA across both themes for the Pet source tabs and the seg-btn surfaces.
The test goes red on this commit's parent (Pets/dark, ratio 1.87) and
green here (all four assertions pass).
2026-05-15 23:08:00 +08:00