Skip to content

fix(deps): #3184 可合并版 —— focus-scope 栈驱逐竞态补丁,解冲突 + 补丁存废说明 - #3200

Merged
os-zhuang merged 3 commits into
mainfrom
claude/pr-3184-fix-hdhtl4
Aug 2, 2026
Merged

fix(deps): #3184 可合并版 —— focus-scope 栈驱逐竞态补丁,解冲突 + 补丁存废说明#3200
os-zhuang merged 3 commits into
mainfrom
claude/pr-3184-fix-hdhtl4

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

接续 #3184(claude/lookup-search-new-record-a5cbae)。原 PR 的诊断与补丁都是对的、CI 也全绿,但它落后 main 15 个提交且 mergeable_state 已变成 dirty(packages/components/package.jsonpnpm-lock.yaml 冲突),合不进去。本 PR 是把同一修复重新落到当前 main 之上的可合并版本,并补了两处仓库规约上的欠账。

原始问题与根因分析见 #3183 / #3184,此处不再重复。

本 PR 相对 #3184 的改动

1. 解冲突

2. patches/README.md(新增)

这是本仓第一次引入 pnpm patchedDependencies,而「为什么打这个补丁、什么时候可以删」原本只存在于 PR 描述里 —— 合并之后就消失了,下一个升级 radix 的人无从判断。README 记下:上游 issue、删除条件(上游在 effect 重跑时取消挂起的驱逐)、pnpm patch / patch-commit 的编辑方式,以及「版本一变、补丁失配就直接让 pnpm install 硬失败」这一刻意为之的响亮失败 —— 逼人工确认上游是否真修了,而不是把补丁悄悄迁移过去。

3. 中文串改英文

AGENTS.md 准则 #-1 要求代码注释与文档一律英文,而 changeset 会进 CHANGELOG.md 并随 npm 发布。两处:changeset 正文里的 "新建记录时 lookup 无法搜索"、测试文件顶部注释里的 "lookup 无法搜索"。语义不变,只换语言。

验证

在当前 main 之上的 worktree 里全量跑过:

  • 补丁确实生效 —— 安装后 node_modules 里的 dist/index.mjsdist/index.js 都带上了 __pendingStackRemove
  • 回归测试是「有承重的」——双向验证过:补丁在位时 dialog-popover-focus-scope.test.tsx 3/3 绿;把 node_modules 里的补丁 patch -R 掉换成原版 radix 后,第一个用例必红,失败形态正是线上症状 —— document.activeElement 是「选择…」触发按钮而不是搜索框;补丁装回去立刻转绿。
  • 全量测试pnpm vitest run:814 文件 / 9539 用例通过,0 失败(1 文件、25 用例为既有 skip)。这一条是关键 —— 补丁改的是全仓每一个 Dialog / Popover / DropdownMenu 的焦点栈行为,不只是 lookup 那一处。
  • 类型检查pnpm type-check:78/78 包通过。
  • ESLint:新增测试文件干净。

关于补丁本身(复核结论)

复核过 focusScope 的对象标识:它是 React.useRef({...}).current,在同一组件实例的 re-render / effect 重跑之间稳定,且实例之间互不相同 —— 所以把挂起的 timeout 挂在 focusScope 上,只会取消该实例自己的驱逐。真实卸载路径不受影响:组件没了,没有重跑去 clearTimeout,延时回调照常派发 focusScope.autoFocusOnUnmount 并把 scope 移出栈。另外 focusScopesStack.add 内部本来就 arrayRemove 去重,重跑重复 add 不会在栈里留下重影。

备注

🤖 Generated with Claude Code

https://claude.ai/code/session_01NUhj4LiXcXGT93m3683KFK


Generated by Claude Code

baozhoutaoand others added 3 commits August 2, 2026 00:31
产线 console 里,新建/编辑模态中 lookup 快捷下拉的搜索框和 Record Picker
表格弹窗都无法输入:焦点在 focusout 阶段就被外层 Dialog 的 FocusScope
同步拽回触发按钮,列表永不过滤("新建记录时 lookup 无法搜索")。
根因是 @radix-ui/react-focus-scope@1.1.16(上游最新 rc 同样存在)的竞态:
栈管理 effect 的 cleanup 用 setTimeout(0) 延迟执行
focusScopesStack.remove(scope)。当该 effect 因 container ref 抖动在组件
未卸载时重跑,顺序变成"重跑先 add → 旧 cleanup 的延时 remove 后执行",
刚加回去的 scope 被永久踢出栈——dialog 的陷阱监听器还活着,却再也没有
弹层能 pause 它。vite dev 不触发该抖动,所以只有产线 bundle 复现。
pnpm patch:effect 重跑(scope 存活)时取消挂起的延时驱逐;真实卸载路径
(autofocus-on-unmount + 栈移除)不变。回归测试用 asChild 子元素换 key
确定性复现竞态:未打补丁必红。已在 rc.1 后端 + 修复后产线 console 真机
验证下拉与表格弹窗搜索均恢复。
Closes#3183
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…htl4
# Conflicts:
#	packages/components/package.json
#	pnpm-lock.yaml
合并 main 后的补充:
- 新增 patches/README.md —— 本仓首次引入 pnpm patchedDependencies,而「为什么打、
什么时候删」此前只写在 PR 描述里,合并后即消失。README 记下上游 issue、删除条件
(上游在 effect 重跑时取消挂起的驱逐)、编辑/删除补丁的操作,以及「版本号一变补丁
失配就直接让 pnpm install 失败」这一刻意为之的响亮失败。
- changeset 与测试文件顶部注释里的中文改为英文 —— AGENTS.md 准则 #-1 要求代码注释与
文档(changeset 会进 CHANGELOG,随 npm 发布)一律英文。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUhj4LiXcXGT93m3683KFK
@vercel

vercelBot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 2, 2026 12:26pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-Bu28QwyZ.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.47KB3.09KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.12KB3.41KB
auth (LoginForm.js)17.86KB5.29KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.43KB2.09KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)476.12KB104.47KB
core (index.js)2.25KB0.80KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)136.20KB34.74KB
fields (index.js)223.45KB54.66KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.46KB0.96KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)5.37KB1.72KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)3.26KB1.44KB
layout (index.js)38.47KB10.67KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.05KB1.53KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)44.90KB12.35KB
plugin-charts (index.js)60.53KB17.12KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)111.87KB28.82KB
plugin-designer (index.js)210.51KB42.50KB
plugin-detail (index.js)224.52KB54.98KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)111.39KB26.93KB
plugin-gantt (index.js)162.26KB39.53KB
plugin-grid (index.js)185.05KB49.03KB
plugin-kanban (index.js)47.82KB13.18KB
plugin-list (index.js)104.88KB25.32KB
plugin-map (index.js)16.80KB5.24KB
plugin-markdown (index.js)13.65KB4.67KB
plugin-report (index.js)40.48KB10.57KB
plugin-timeline (index.js)25.76KB7.32KB
plugin-tree (index.js)8.34KB2.82KB
plugin-view (index.js)83.54KB20.39KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)2.46KB1.21KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang marked this pull request as ready for review August 2, 2026 12:39
@os-zhuang
os-zhuang added this pull request to the merge queueAug 2, 2026
Merged via the queue into main with commit f6e8d78Aug 2, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/pr-3184-fix-hdhtl4 branch August 2, 2026 12:39
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@os-zhuang@baozhoutao@claude