The setter updater contract previously said "must be pure" only in the sense of "don't mutate prev / return a new value" (the isEqual short-circuit footgun); it did not cover side effects inside the updater.
Document that updaters must also be side-effect-free: don't smuggle a derived value out (e.g. into an enclosing-scope variable) to drive post-write work, and don't rely on how often or when the updater runs. To react to what changed, derive it from the value transition in a useEffect that watches the value.
Deliberately does not promise single synchronous invocation, to keep the setter free to batch/retry/defer later. Updates the CacheSetStateAction type doc, the useCache @remarks (canonical reference for all three hooks), and cache-usage.md.
Store the working directory on each CLI tool instead of per provider, and update the page, hook, and tests to use the shared tool directory. Also simplify the config editor to only show supported tools, refresh endpoint labeling, hide Cherry AI providers from metadata, and normalize Cherry provider keys to the lowercase prefix used by the generated config files.
Now that the renderer cache hooks resolve functional updaters against the latest stored value, replace the hand-rolled ref + `typeof === 'function'` wrappers (TabsProvider, TranslatePage, GlobalSearchPanel) and the snapshot-based read-modify-write call sites (recent-items in AppShell/HomePage/AgentPage, emoji recents, message selection, recall-test history) with `setX(prev => ...)`. Each updater derives from `prev`, and the callbacks drop the cache value from their dependency arrays.
The mini-app keep-alive sites (hide / cleanup / sync) close the read-modify-write race where an app opened concurrently during a status mutation's await was clobbered by a stale snapshot. Fixes#16460.
Update the two local cache mocks (GlobalSearchPanel / RecallTestPanel tests) to resolve functional updaters like the real hook.
useCache, useSharedCache and usePersistCache setters now accept a React-style functional updater `(prev) => next` in addition to a concrete value. The updater resolves `prev` from the latest stored value at write time rather than the render-time snapshot, making read-modify-write correct across an `await` — the root cause of the keep-alive overwrite race behind #16460.
`prev` is typed shallow-readonly (`ReadonlyValue<T>`), so mutating it in place and returning the same reference — which the CacheService `isEqual` short-circuit would otherwise swallow silently — is a compile error. Concrete-value calls are unchanged, so existing consumers keep compiling.
The renderer useCache mock mirrors the functional branch with the same default fallback; docs and hook tests updated. Consumer call-site adoption lands separately.
`utils/export.ts` was misclassified: a 1215-line export workflow (toast,
popups, IPC, network, module state, preference reads) living in the pure
`utils/` bucket. It imported `getMessageTitle` from `MessagesService` while
`MessagesService` imported `getTitleFromString` back from it — a direct
circular dependency, and a failure of the renderer "util = pure / service =
runtime logic" type axis.
Split it along the type axis:
- `utils/export.ts` keeps only pure, zero-`@data` transforms
(getTitleFromString, processCitations, messageToPlainText,
messagesToPlainText) and is now a true leaf, so the cycle is broken.
- New `services/ExportService.ts` holds the runtime/behavior layer: markdown
rendering that reads export preferences, the Notion/Yuque/Obsidian/Joplin/
Siyuan/Notes exporters, and the export-in-progress mutex.
- `git mv utils/copy.ts -> services/CopyService.ts` (it depends on the moved
topic exporters).
- Lift the Obsidian popup out of `exportNote` up into the notes menu hook
(`useNotesMenu.handleObsidianExport`) so the service no longer imports a
component.
Consumer imports and test mocks are repointed accordingly, and the export
test suite is split into pure (`utils/__tests__/export.test.ts`) and behavior
(`services/__tests__/ExportService.test.ts`) files. No behavior change.
Drop @tanstack/react-query in favor of SWR (already the renderer's
data-fetching library via DataApi) and remove the dependency. Redesign
each fetch's semantics instead of mirroring the old behavior:
- Notes file content (useNotesQuery): event-driven via the chokidar
watcher + save invalidation, re-reading on file switch. Drop the focus
refetch (redundant with the watcher) and the contradictory 30s
staleTime. Invalidate through the bound mutate; remove the dead
refetchFileContent.
- Citation panel/tooltip: switch to useSWRImmutable and share an
xOembedKey so the tooltip reuses the panel's cached oEmbed. Hide the
preview snippet on fetch failure (fetchWebContent degrades to
noContent) and drop the inert retry.
- Window roots (main/settings/subWindow): remove QueryClientProvider;
SWR's global cache needs no provider.
Rewrite the citation tests on real SWR + mocked fetch utils and add
useNotesQuery tests.
Switch code-CLI state from named configs to provider-keyed configs, and update the page, hooks, and edit panel to manage enabled providers instead of ad hoc config entries. Also align provider filtering/reordering, refresh i18n text, and make Codex prefer the responses endpoint when available.
Standardizes CLI tool typing by renaming `codeCLI` to `CodeCli` and updating all usages across main, renderer, shared presets/types, and tests. This also aligns enum members to uppercase constant-style names, fixes related imports (`shell-env`, code providers, platform utils), and removes a couple of unused imports to keep references consistent after the rename.
Apply the native CLI config before marking it active, so save and enable flows only switch state after injection succeeds. Also moved the launch action to the page header, removed the redundant directory shortcut UI, and cleaned up the code-edit panel to use shared codeCLI constants.
Refactors Codex advanced settings from raw file editors to toggle pills (goal mode, remote compaction, common config) with new i18n labels. `injectCliConfig` now writes Codex API keys to `~/.codex/auth.json` (preserving existing auth fields), sets `requires_openai_auth` in `config.toml`, applies/clears goal mode and remote compaction flags correctly, and extends OpenCode generation to read reasoning and context/output limits from `configBlob.env`. Tests were expanded to cover the new Codex auth/config merge behavior and OpenCode option mapping.
Shift native CLI config writing for Claude Code, Codex, and OpenCode into the renderer at enable-time, and remove the old main-process config writer path. Also updates the code CLI UI to match the new flow, fixes sequential config writes in `useCodeCli`, and adds coverage for the renderer-side injection behavior.
Replace the old `advanced` payload with a tool-specific `config` blob, and apply provider credentials at launch time instead of persisting them in preferences. The code-CLI page now uses extracted config panels and hooks for per-tool metadata, terminal loading, and binary actions, while Claude/Codex/OpenCode/Hermes/OpenClaw each get dedicated config handling. Also updates the shared config schema and localized labels.
Split the code page into focused modules, move the CLI tool catalog and version types into shared page files, and track install/upgrade state per tool so each card updates independently. Also reworked the config editor into smaller panel primitives with environment-backed advanced fields, added the Claude attribution toggle, and removed obsolete hero/gallery components and asset references.
Reworks the CLI config panel into a structured editor with tool parameters, raw JSON editing, and model-role overrides for Claude. Also improves model/provider lookup and updates localized strings for the new UX.
Renderer half of the code-cli refactor (stacked on the main-process PR).
- Rebuild Code CLI page with sidebar, config cards, provider cards, config edit panel, version status
- Multi-config per CLI tool UI (named configs)
- Drop the standalone OpenClaw page (OpenClaw is now managed under Code CLI); remove OpenClawPage, UpdateButton, route, sidebar entry
- Update useCodeCli hook + store + i18n for the new config model
Part 2 of 2. Depends on the main-process PR; base will move to main after it merges.
Note: 11 web typecheck errors remain from rebasing the code-cli renderer onto latest main's UI/preference types — to be resolved before flipping this PR out of draft.
Signed-off-by: Pleasurecruise <3196812536@qq.com>
Ensure provider switches leave no stale config entries and add Hermes support/tests. Claude: remove managed ANTHROPIC_* env keys and managed top-level keys when switching configs. Codex: drop stale 'Cherry-*' provider tables and strip managed top-level keys so previous named configs don't leave residue. Hermes: preserve non-managed fields on upsert, support contextLength/maxTokens, add unit tests, and merge/write YAML config atomically. OpenCode: minor cleanup. Shared presets: rename packageName -> miseTool and update tool specifiers (npm:/pipx:).