feat: workspace fence toggle (workspaceFence pref + one-click off in error surfaces)

- New persisted built-in pref workspaceFence (default true) under the
  files card's secondary settings modal; host reads it per request via
  the settings seam (fenceEnabledOf — absent/malformed stays ON).
- ensureWorkspacePath/ensureWorkspaceWritePath gain a fence param:
  disarmed skips the containment assert but keeps realpath resolution;
  wired through fs.tree/fs.read/fs.write, the media + html routes, and
  the upload path (writeWorkspaceUpload fence input).
- Editor load failures and file-tree level errors show the localized
  reason plus a one-click global off (FenceErrorNotice) that flips the
  pref, adopts it into the store, and auto-retries the failed operation
  (reloadSeq / retryDir) — the raw wire text is never shown.
- GitView's two client-side pre-checks (hidden open action + no-op
  defense) respect the pref; store threaded through TreePanel/FileTree.
- i18n: 4 new keys across all 21 dictionaries; tests: fence-off route
  coverage, prefs parsing, schema default, editor toggles list;
  docs: guide §8/§8.1 + plans/2026-08-29 design record.
This commit is contained in:
Menghuan1918
2026-08-29 15:49:19 +08:00
parent 2088ef6891
commit a9353ce627
44 changed files with 391 additions and 38 deletions
+3
View File
@@ -10,6 +10,7 @@ import { createElement } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { act } from 'react-dom/test-utils'
import { GitView } from '../src/client/GitView.tsx'
import { createSidebarStore } from '../src/client/state.ts'
import { api, type GitLogEntry, type GitStatusResult, type GitWorktree } from '../src/client/api.ts'
;(globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true
@@ -72,6 +73,7 @@ describe('GitView linked-worktree consistency', () => {
await act(async () => {
root.render(createElement(GitView, {
scope: { sessionId: 'session', cwd: MAIN },
store: createSidebarStore(),
onOpenFile: () => {},
onOpenDiff: () => {},
visible: false,
@@ -129,6 +131,7 @@ describe('GitView linked-worktree consistency', () => {
await act(async () => {
root.render(createElement(GitView, {
scope: { sessionId: 'session', cwd: MAIN },
store: createSidebarStore(),
onOpenFile: () => {},
onOpenDiff: () => {},
visible: false,