Skip to content

fix(app-shell): give inline lookup action params a real record picker (#3405)#2786

Merged
os-zhuang merged 1 commit into
mainfrom
fix/action-param-inline-lookup-3405
Jul 22, 2026
Merged

fix(app-shell): give inline lookup action params a real record picker (#3405)#2786
os-zhuang merged 1 commit into
mainfrom
fix/action-param-inline-lookup-3405

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

UI half of objectstack-ai/objectstack#3405. Spec half: objectstack-ai/objectstack#3406

问题

动作参数弹窗里内联声明的 lookup 参数,永远渲染成纯文本框,提示「粘贴 xxx 的记录 ID(UUID)」,还附一句「可视化选择器即将上线」。

真机场景(PLAT-DEF-005,天顺 EHR 质检派工):质检主管点【指派】/【转派】要选质检员,得先跑到别处把人的 UUID 复制出来再粘进去,对真人基本不可用。同一套引用字段在新建/编辑记录弹窗里是正常的搜索式选择器。

不是用户没配。 租户写的是 { name: 'inspector', type: 'lookup', reference: 'sys_user' },意图清清楚楚,被丢了两次:spec schema 当未知键 strip 掉,本仓的 RawActionParam 也不认这个键。

根因在 paramToField.ts

if (LOOKUP_WIDGET_TYPES.has(type) && !param.referenceTo) type = 'text';

referenceTo 唯一的来源是 resolveActionParams()字段引用分支(从对象字段的 reference_to ?? reference 抄)。内联分支的返回对象里根本没有这一项,所以内联 picker 100% 必然降级,与作者怎么写无关。

那句「即将上线」还是过时的:选择器早就有了(字段引用式走的就是 <LookupField>),这句话把「你这个写法拿不到选择器」谎报成「平台还没做」,把用户和排查的人一起带沟里。

改动

  • resolveActionParam() 把内联 reference 映射成 referenceTo —— 内联分支、缺字段兜底分支都透传,字段引用分支上作为覆盖(与其他所有内联值覆盖已解析字段的行为一致)。
  • text 降级保留为兜底,但 dev 期 console.warn 点名是哪个参数。配合 spec 侧的解析期校验,走到这条路意味着元数据坏了,不再是「元数据不全」的正常路径。
  • lookupPlaceholder / lookupHelpText 不再说「即将上线」,改成指明该参数未配置引用对象。10 个语种(en / zh / ja / ko / de / fr / es / pt / ru / ar)全改。

验证

真机(framework examples/app-showcase :5311 + 本分支 console dev :5312 指过去,中文界面):

  • 弹窗里「Related account」渲染出搜索式选择器,不是 UUID 文本框
  • GET /api/v1/data/showcase_account?top=50 拉列表;输入「华宁」→ ?search=… 服务端过滤命中「华宁科技」;Contoso 同理
  • 选中后 chip 显示名称,并 GET /api/v1/data/showcase_account/{id} 按记录 ID 回填
  • 全程不接触 UUID

测试:@object-ui/app-shell 89 用例 + @object-ui/i18n 106 用例通过,tsc --noEmit 干净。新增覆盖内联 / 字段引用 / 内联覆盖 / 缺字段兜底四条路径,以及降级时的 warn。

合并顺序

本 PR 单独合入是安全的(reference 缺席时行为不变)。但要让内联 picker 真正可用,需要 objectstack-ai/objectstack#3406 先落地并发版 —— 否则 reference 在服务端就被 strip 了,到不了这里。

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Jul 22, 2026 12:26pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.0 KB 350 KB
Entry file index-CMftY9-r.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.19KB 2.96KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 21.70KB 4.21KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 33.74KB 8.53KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 1.83KB 0.79KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.86KB 0.85KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 450.13KB 97.87KB
core (index.js) 1.86KB 0.63KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 125.29KB 31.48KB
fields (index.js) 211.66KB 51.78KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 25.17KB 5.80KB
i18n (useSafeTranslation.js) 2.87KB 1.28KB
layout (index.js) 38.45KB 10.67KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 4.42KB 1.27KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 1.77KB 0.77KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 5.90KB 2.15KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.46KB 1.03KB
permissions (evaluator.js) 4.00KB 1.23KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.50KB 0.70KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 45.37KB 12.48KB
plugin-charts (index.js) 46.90KB 13.26KB
plugin-chatbot (index.js) 179.53KB 42.79KB
plugin-dashboard (index.js) 108.70KB 27.99KB
plugin-designer (index.js) 210.92KB 42.69KB
plugin-detail (index.js) 214.78KB 52.42KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 102.82KB 24.94KB
plugin-gantt (index.js) 162.33KB 39.53KB
plugin-grid (index.js) 174.13KB 45.90KB
plugin-kanban (index.js) 47.45KB 12.99KB
plugin-list (index.js) 98.59KB 23.27KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 37.81KB 9.98KB
plugin-timeline (index.js) 25.37KB 7.20KB
plugin-tree (index.js) 8.36KB 2.81KB
plugin-view (index.js) 85.64KB 20.85KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.55KB 0.67KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 3.19KB 1.38KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 18.70KB 6.09KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.00KB 0.55KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 2.16KB 0.94KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 0.77KB 0.41KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 2.02KB 0.95KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.26KB 1.96KB
types (system-fields.js) 2.39KB 1.17KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.75KB 0.46KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

…er (#3405)

An action param declared inline as `{ name: 'inspector', type: 'lookup',
reference: 'sys_user' }` always rendered as a plain text input asking the user
to paste a record id (UUID). A QC supervisor assigning an inspector had to go
find that person's UUID by hand, while the very same reference field picks
records by name in the create/edit dialog.

`paramToField()` degrades a picker param to text when it has no `referenceTo`
target, and `referenceTo` was only ever populated on the field-backed branch of
`resolveActionParams()`. The inline branch dropped the authored `reference` key
entirely — as did the spec schema, which stripped it as an unknown key without
an error — so an inline picker could never reach `<LookupField>` no matter how
it was authored. The user's config was correct and silently discarded.

- `resolveActionParam()` maps an inline `reference` onto `referenceTo`: on the
  inline branch, on the missing-field fallback branch, and as an override on
  the field-backed branch (matching how every other inline value overrides the
  resolved field).
- The text degradation warns in dev naming the offending param. With
  `@objectstack/spec` now rejecting a targetless inline picker at parse time,
  reaching that path means the metadata is broken, not merely partial.
- The fallback's placeholder and help text no longer claim "a picker is coming
  soon" — it shipped long ago, and that copy misreported a dropped config as a
  missing feature. All 10 locales now say the param has no reference object
  configured.

Verified against the framework showcase (examples/app-showcase) driven in a
browser: the inline `p_account` param renders the searchable picker, queries
`/api/v1/data/showcase_account`, filters server-side on typed input (incl. the
CJK-named account), and resolves the selection by record id.

Refs objectstack-ai/objectstack#3405

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit 0b3be01 into main Jul 22, 2026
14 checks passed
@os-zhuang
os-zhuang deleted the fix/action-param-inline-lookup-3405 branch July 22, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants