Repository navigation
feat(settings): 工作区背景新增「壁纸市场」(Wallhaven SFW 图库浏览/下载/应用) - #626
Merged
xintaofei merged 17 commits intoSep 6, 2026
Merged
Conversation
The market shipped three things that misbehave in ordinary use, plus a tail of smaller gaps. All are fixed here; the contract change between the Rust summary and its TypeScript mirror is why this is one commit. Concurrent downloads shared one staging file. The grid disabled only the card being downloaded, so a second wallpaper could be picked mid-download, and both writers then fought over `background.img.tmp`: the second `File::create` truncates what the first is still writing, and whichever `rename` lands second fails with ENOENT. On APFS that reproduced as "download failed" on a download that had actually succeeded, 58 times out of 60 interleavings. Writers now stage into `background.img.<pid>.<seq>.tmp` — rename stays atomic, so overlapping writers are a clean last-writer-wins and neither can truncate the other — and the dialog serializes downloads behind a synchronous ref, because two clicks in one frame both read the state guard before it re-renders. Crash debris is swept on a 10-minute age gate, which a live writer's file can never trip. Roughly one wallpaper in fifteen could never be applied. Downloads are capped at 16 MiB / 40 Mpx; measured against the default browse view, 5.0% of listings exceed the byte cap and 1.7% the pixel cap — one or two dead cards per page, each answering a click with "please retry" for a condition retrying cannot change. The listing already reports `file_size`, so the summary now carries it alongside the dimensions, and those cards are disabled up front, badged, and told the offending number against the ceiling. Real failures now show the backend's own message instead. The host allowlist stopped at the first URL. reqwest's default policy follows ten hops across hosts, and `fetch_asset` hands the body back to the caller — so a redirect off wallhaven made this a reader of whatever the host can reach, which in server mode may be a private network. Every hop is now re-checked, the way `forge` and `remote_proxy` re-check theirs. Also: the validate-and-write tail moved off the async runtime, matching every other command in `commands::background`; the 30 s total deadline became a read-stall timeout plus per-request deadlines, since 16 MiB in 30 s demanded 4.4 Mbit/s from exactly the networks this proxy exists for; the source-URL key joined the cross-window storage listener, so a second window stops claiming a replaced wallpaper is "in use"; the thumbnail cache is LRU-bounded; the search debounce no longer resets the page on its mount tick, which used to snap you back to page 1 if you paged within 300 ms of opening; paging follows the page wallhaven actually served; the card's accessible name says what it does and the focus ring shows it; and three i18n keys nothing referenced were dropped. `validate_background`'s doc claimed its callers only ever pass files the user picked off their own disk. The market broke that premise, so the doc now says what still holds on that path and what does not.
A push-back that failed reported "git push failed — pushing to a fork
needs its author to allow edits from maintainers on the pull request" on a
pull request where maintainer edits were already allowed. Neither half of
that sentence was earned.
The reason was thrown away one frame earlier. `classify_remote_git_error`
files git's whole stderr under `detail` and leaves `message` as the bare
"git push failed"; `AppCommandError` renders as `{message}` alone, so the
`to_string()` in the delivery path dropped the only part that said
anything. Flatten with the detail instead — the delivery error string is
the sole channel to a human here, and it was carrying no information.
The advice was a fixed suffix, appended to *every* fork push failure. It
described the most common cause, which reads as a finding rather than the
guess it was, and sends a reader to check a setting that may well be
correct. It now depends on what git actually said: a permission refusal
keeps the maintainer-edits hint, a non-fast-forward gets told the branch
moved, and anything unrecognised gets no advice — an unhelpful message
costs a moment, a confident wrong one costs an afternoon.
Permission is matched before staleness, because a forge that refuses a
push outright can also print "rejected", and mistaking that for an
out-of-date branch would send someone rebasing for nothing.
The pull request's branch moved on while this task was open (its author merged main into it again at 88cf455), so the delivery push was no longer a fast-forward. Merging it back in is what makes the push-back apply.
Surfacing git's stderr made the failure legible, but it also put another program's output into a string that is persisted on the task row and rendered in the UI. A push refused by a server-side hook can answer with a banner, and all of it would have been stored. Keep a prefix — git leads with the line that identifies the failure and follows with hints — and cut on a character boundary, since branch names and hook banners arrive in any encoding and slicing mid-codepoint would panic on the failure path itself.
… credentials
Two follow-ups from review of the push-back diagnosis.
Bare "403" and "401" were matched anywhere in git's output, so a push
refused for being out of date on a branch like `fix/403-page` — or in a
repository named after an issue — would have been called a permission
problem and answered with fork advice. Match the phrasing that actually
carries a status code instead ("error: 403", "http 403", "status code
403"), which a ref name cannot supply on its own.
The other is a consequence of newly surfacing git's stderr. The account's
token never travels in a URL — it goes through GIT_ASKPASS — but
`web_origin` returns a self-hosted `server_url` verbatim, so a user who
typed credentials into their own forge address would have git echo them
back into a string that is persisted and rendered. Blank the userinfo out
of the detail before it goes anywhere.
Collaborator
|
codeg work task |
Collaborator
|
我来处理这个错误check |
`File::open` is read-only, and Windows backs `set_modified` with `SetFileTime`, whose handle must carry `FILE_WRITE_ATTRIBUTES` — `GENERIC_READ` does not include it. So the sweep test backdated its fixture happily on Unix, where `futimens` gates an explicit timestamp on the file's ownership rather than the descriptor's access mode, and failed the windows-latest server cell with "Access is denied".
xintaofei
added a commit
that referenced
this pull request
Sep 6, 2026
Diffs now show images as pictures, the repository panel speaks Gitea, and workspace backgrounds get a wallpaper market. Desktop notifications become a real setting, and backup & restore stops losing custom-agent conversations. ## New - **A wallpaper market for the workspace background** — browse the public Wallhaven SFW gallery from Settings → Appearance, and one click downloads and applies a wallpaper. (#626, @user-A100) - **Image files now diff as before/after pictures**, with a swipe slider, in the workspace diff tab, the commit dialog, the push workspace and the unstash dialog. - **The repository panel now works against Gitea and Forgejo**, with a Gitea accounts section under Settings → Version Control. - **Desktop notifications are now configurable** in Settings → General: a master switch, when to notify, whether to hide contents, and one switch per event. - **A deleted conversation can be restored from the session picker** — turn on "Include deleted", and importing one brings the original conversation back with its history intact. - **The status bar reports the codeg-mcp service** and lets you switch its tool groups — delegation, live feedback, ask-a-question, session lookup, automations and work tasks — right in the popover. - **Claude now marks where its context was compacted**, live and after reopening, saying whether it was triggered manually or automatically (claude-agent-acp 0.75.1). - **Every restore is snapshotted first and can be rolled back**, with real progress, a cancel button and a result panel. ## Improved - **Updated bundled agents:** CodeBuddy 2.146.0, Qoder 1.1.45, Gemini CLI 0.58.0, OpenClaw 2026.9.2, and the Claude adapter (claude-agent-acp 0.75.1). - **Settings → General now opens with the notification and sound sections folded**, instead of filling the page before anything below them. - **Refreshed model catalogs** — Codex gains `gpt-6-astra`, OpenCode grows to 213 providers and 7,562 models, and gpt-6 sessions finally show a context meter. ## Fixed - **A backup no longer loses custom ACP agents' conversations** — their message text was packed by neither the backup nor the restore path, so a round trip left every conversation in place and empty. - **OpenCode, Hermes, Cursor and Antigravity session databases now survive a backup round trip whole**, instead of losing their newest messages and being restored beside a stale write-ahead log. - **Forking a Claude conversation from a branch an earlier fork left behind now lands on the reply you picked**, instead of quietly falling back to the end of the conversation (claude-agent-acp 0.75.1). - **A failed task push-back now reports git's own reason**, instead of always blaming the fork author for not allowing maintainer edits. Thanks to @user-A100 for contributing to this release. ----------------------------- # 发布版本 0.30.4 差异对比现在能把图片当图片看,仓库面板支持了 Gitea,工作区背景也多了一个「壁纸市场」。 桌面通知成为一项完整的设置,备份恢复也不再弄丢自定义智能体的会话内容。 ## 新增 - **工作区背景新增「壁纸市场」**——在「设置 → 外观」中浏览 Wallhaven 公开图库(仅 SFW),点一下即完成下载并应用。(#626,@user-A100) - **图片文件的差异现在以「改动前 / 改动后」两张图呈现**,支持滑杆对比,工作区差异标签页、提交对话框、推送工作区和取出储藏对话框均已接入。 - **仓库面板支持 Gitea 与 Forgejo**,「设置 → 版本控制」下也新增了 Gitea 账号区块。 - **桌面通知现在可以自行配置**:「设置 → 通用」中新增总开关、通知时机、是否隐藏通知内容,以及逐事件开关。 - **已删除的会话可以从会话导入面板恢复**——打开「包含已删除」开关后再导入,原来那条会话连同历史记录一起回来。 - **状态栏新增 codeg-mcp 服务指示**,委托、实时反馈、向你提问、会话查询、定时自动化、工作任务这几组工具都能在弹层里直接开关。 - **Claude 的上下文压缩现在会在会话里留下标记**,实时可见、重开仍在,并写明这次是手动还是自动触发(随 claude-agent-acp 0.75.1 到位)。 - **每次恢复都会先给当前数据打快照并支持回滚**,同时补上了真实进度、取消按钮和结果面板。 ## 改进 - **内置智能体版本更新:** CodeBuddy 2.146.0、Qoder 1.1.45、Gemini CLI 0.58.0、OpenClaw 2026.9.2,以及 Claude 适配器(claude-agent-acp 0.75.1)。 - **「设置 → 通用」中的通知与提示音两个区块现在默认折叠**,不必先翻过它们才能看到下面的内容。 - **内置模型目录更新**——Codex 新增 `gpt-6-astra`,OpenCode 增至 213 家供应商、7562 个模型,gpt-6 系列会话也终于有了上下文用量显示。 ## 修复 - **备份不再丢失自定义 ACP 智能体的会话内容**——这些消息正文备份和恢复两条路径都没打包,导致还原后会话还在、内容全空。 - **OpenCode、Hermes、Cursor、Antigravity 的会话数据库现在能完整经受一次备份还原**,不再丢掉最新的消息、也不会被还原到一份过期的预写日志旁边。 - **从上一次分叉留下的旧分支上分叉 Claude 会话,现在会落在你选中的那条回复上**,不再不声不响地退回会话末尾(随 claude-agent-acp 0.75.1 到位)。 - **任务回推失败时会显示 git 给出的真实原因**,不再一律回一句「需要 PR 作者允许维护者编辑」。 感谢 @user-A100 为本次发布做出的贡献。
xyalter
pushed a commit
to lesscodex/codeg
that referenced
this pull request
Oct 31, 2026
…ectory (spacering-net#626) ## Problem When the agent's working directory is deleted while it runs, Codex fails every config load with a generic message (IJAI-2923, IJAI-3685): - `config/read` → `-32603 failed to resolve feature override precedence: No such file or directory (os error 2)` - `thread/start` → `-32600 failed to load configuration: No such file or directory (os error 2)` Codex loads project config from its process working directory, which it inherits from codex-acp, so the error says nothing about the real cause. ## Change New `src/CodexErrorExplanations.ts`: a single list of rules that every app-server request error goes through in `CodexAppServerClient.sendRequest`. A rule only rewrites the message; the error class, code and data stay, so `CodexThreadErrors` and app-server recovery work as before. - **Deleted working directory**: the agent records its working directory at start and checks whether the process still points at the directory at that path (a directory recreated at the same path counts as deleted). The message becomes: `Codex cannot read its configuration: its working directory <path> was deleted or replaced after the agent started. Restart the agent.` - **Config files hint**: moved here from `CodexAcpServer.handleError`, so it now applies to every request that fails to load config, not only session new/fork. "feature override precedence" is reworded to "failed to load configuration". Errors that require logout are skipped. `handleError` keeps only the logout action, via `requiresLogout()` from the module. ### Wire shape note Session new/fork config errors previously reached the client as `data: "<message>\n\nCheck…"` (a string, because the text was passed as the `data` argument of `RequestError.internalError`). They now take the SDK's default path like every other internal error: `data: {details: "<message>\n\nCheck…"}`. The logout error keeps its string `data`.
xyalter
pushed a commit
to lesscodex/codeg
that referenced
this pull request
Oct 31, 2026
🤖 I have created a release *beep* *boop* --- ## [2.3.0](agentclientprotocol/codex-acp@v2.2.2...v2.3.0) (2026-10-26) ### Features * add ACP v2 support again ([spacering-net#620](agentclientprotocol/codex-acp#620)) ([9e1690f](agentclientprotocol/codex-acp@9e1690f)) * mark session list rows of worktrees Codex made (agentWorktree) ([spacering-net#631](agentclientprotocol/codex-acp#631)) ([aa02d43](agentclientprotocol/codex-acp@aa02d43)) * narrow models to what the providers/set gateway lists ([spacering-net#627](agentclientprotocol/codex-acp#627)) ([3f555f9](agentclientprotocol/codex-acp@3f555f9)) * report the model of each native subagent session ([spacering-net#678](agentclientprotocol/codex-acp#678)) ([51005a6](agentclientprotocol/codex-acp@51005a6)) * return initial ACP v2 session commands ([spacering-net#652](agentclientprotocol/codex-acp#652)) ([5e9b7c5](agentclientprotocol/codex-acp@5e9b7c5)) * support AIR sessionIndex extension ([spacering-net#590](agentclientprotocol/codex-acp#590)) v2 ([spacering-net#622](agentclientprotocol/codex-acp#622)) ([2fd758c](agentclientprotocol/codex-acp@2fd758c)) * support append-mode ACP system prompts ([spacering-net#653](agentclientprotocol/codex-acp#653)) ([5993a12](agentclientprotocol/codex-acp@5993a12)) ### Bug Fixes * disable Codex's Visualize skill, whose output ACP clients show as raw text ([spacering-net#667](agentclientprotocol/codex-acp#667)) ([0ba94f5](agentclientprotocol/codex-acp@0ba94f5)) * display the selected wire provider as JetBrains Air Gateway ([spacering-net#589](agentclientprotocol/codex-acp#589)) ([cc54abf](agentclientprotocol/codex-acp@cc54abf)) * explain Codex config load errors caused by a deleted working directory ([spacering-net#626](agentclientprotocol/codex-acp#626)) ([a636804](agentclientprotocol/codex-acp@a636804)) * explain invalid_encrypted_content errors after a provider change ([spacering-net#668](agentclientprotocol/codex-acp#668)) ([a77829f](agentclientprotocol/codex-acp@a77829f)) * hold a fork back from the session list until its session/fork response is out ([spacering-net#642](agentclientprotocol/codex-acp#642)) ([59974c7](agentclientprotocol/codex-acp@59974c7)) * include subagent edits in the agent file-change report ([spacering-net#632](agentclientprotocol/codex-acp#632)) ([7d07123](agentclientprotocol/codex-acp@7d07123)) * leave codex exec runs out of the session list without sessionIndex ([spacering-net#639](agentclientprotocol/codex-acp#639)) ([df75d2b](agentclientprotocol/codex-acp@df75d2b)) * migrate to ACP SDK 2.0 ([spacering-net#649](agentclientprotocol/codex-acp#649)) ([c8fd166](agentclientprotocol/codex-acp@c8fd166)) * publish the commands when the skills change ([spacering-net#643](agentclientprotocol/codex-acp#643)) ([a62b568](agentclientprotocol/codex-acp@a62b568)) * report structured ACP v2 failures ([spacering-net#651](agentclientprotocol/codex-acp#651)) ([5377bfe](agentclientprotocol/codex-acp@5377bfe)) * show a Codex subagent's follow-up turn in a new subagent session ([spacering-net#634](agentclientprotocol/codex-acp#634)) ([7c761c7](agentclientprotocol/codex-acp@7c761c7)) * synchronize ACP v2 foreground state ([spacering-net#650](agentclientprotocol/codex-acp#650)) ([986955a](agentclientprotocol/codex-acp@986955a)) * update codex to 0.164.0 ([spacering-net#666](agentclientprotocol/codex-acp#666)) ([e227c0e](agentclientprotocol/codex-acp@e227c0e)) * wait for the compact turn to finish ([spacering-net#654](agentclientprotocol/codex-acp#654)) ([ced4c34](agentclientprotocol/codex-acp@ced4c34)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #624
功能概述 / What
在 设置 → 外观 → 工作区背景 中新增「壁纸市场」:浏览 Wallhaven 公开图库(
purity=100SFW 强制),点击缩略图即由后端下载、校验并立即应用为当前背景。backgrounds/marketplace.rs(新):搜索(purity=100 硬编码、分类映射 all/general/anime/people→111/100/010/001)、缩略图代理、下载。全部 URL 经 https +wallhaven.cc/子域 白名单校验(拒绝 userinfo),Content-Type 限 jpeg/png/webp,响应体双重上限(搜索 4 MiB / 缩略图 4 MiB / 原图 16 MiB),下载复用既有validate_background+write_background_atomic—— 与本地选图同一条安全路径background_market_search / _asset / _download三组:commands/background.rs的_core+ tauri command、web/handlers/background.rsAxum handler、router/lib.rs 注册,Tauri 与 standalone-server 双模式同构lib/workspace-background-market.tstransport 绑定(camelCase 镜像);useProxiedBackgroundThumb(模块级 asset 缓存 + per-consumer blob URL,镜像宠物市场同款 hook)downloadMarketWorkspaceBackground(后端落盘 → bump 版本戳 → 跨窗口重读盘,与本地选图共用失效广播);workspaceBgSourceUrl「使用中」标记(localStorage,本地图/移除时清除)workspace-background-market-dialog.tsx:搜索(300ms 防抖)+ 分类 + 缩略图网格 + 分页 + 加载/空/错误可重试三态 + 「使用中」徽标;入口为工作区背景区「壁纸市场」按钮AppearanceSettings.workspaceBackground.market.*10 locale 同步(messages.test.ts键集校验通过)设计取舍 / Why
pets/marketplace.rs→ commands → transport):与仓库既有市场类功能同构,无新依赖(reqwest/base64 均已在 Cargo.toml)。backgrounds::MAX_BG_BYTES,市场图与本地选图共享同一张安全网,避免出现第二套上限。th.wallhaven.cc(与宠物市场代理 codex-pets.net 同因)。验证 / How verified
自动化(本机全部实际执行):
cargo test --no-default-features --bin codeg-server --lib✅(含 marketplace 8 个新单测:URL 白名单/分类映射/payload 解析/去窄化)cargo clippy --all-targets --features test-utils -- -D warnings✅、cargo clippy --no-default-features --bin codeg-server --lib -- -D warnings✅cargo check(tauri)与cargo check --no-default-features --bin codeg-server✅pnpm vitest run --maxWorkers=1:397 文件 / 5620 测试全部通过(Task 4/5 两次全量)✅npx tsc --noEmit✅、eslint 对全部改动文件 ✅手动 smoke(桌面 dev 构建)✅:市场打开/搜索/分类/下载应用/「使用中」徽标/分页/本地图覆盖清除标记。
English summary: Adds a "Wallpaper Market" to Settings → Appearance → Workspace Background, backed by the key-free Wallhaven public search API. SFW is hard-coded (
purity=100), all URLs pass an https + wallhaven.cc allowlist, search/asset/download are proxied host-side, and downloads go through the exact same validation + atomic write as a manual background pick. Dual-mode (Tauri + standalone server), 10-locale i18n, full test suite green.