Repository navigation
feat(fields): offer the mobile shell's code scanner on text and lookup fields - #29493
Merged
Merged
Conversation
The mobile shell's bridge now carries scan(opts) behind has('scan').
Type it as an optional member (older shells lack it) and call it through
callNative, so a caller gets unavailable, ok (text or null) or failed.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUw36sPe8KXr9ANYMv
Inside the ObjectStack mobile shell, when the bridge announces scan, an editable single-line text field and a lookup field show a scan button. Text takes the decoded value through onChange; a lookup opens its search with it (PeoplePicker gains initialSearch for the search-first variant). A closed scanner changes nothing; a failure shows the error toast. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw36sPe8KXr9ANYMv
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw36sPe8KXr9ANYMv
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw36sPe8KXr9ANYMv
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw36sPe8KXr9ANYMv
Contributor
Author
|
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m 席位核对 · ACCEPT · 2026-11-02T16:45ZHead-sha:
|
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.
Requested by Steedos · project thread
Fixes #29378
Clause-②: yes
Metadata-protocol: none
维护者速读
has('scan')为真时),可编辑的单行文本字段与查找(lookup)字段旁出现一个「扫码」图标按钮;文本字段把解码结果经onChange写入,查找字段用解码结果打开搜索。普通浏览器、旧版壳、只读或禁用字段不显示按钮。scan()(feat(bridge): scan() — native barcode / QR scanner for Console fields mobile#170),Console 端没有调用方,就是「声明了却没人用」的能力;现场用户用手机录入资产标签、条码、单据号时需要它。@object-ui/mobile的NativeHostMethod加'scan'、ObjectStackNative加可选scan?(opts)、新导出NativeScanOptions与nativeScan(opts?);@object-ui/fields的PeoplePicker加可选 propinitialSearch;@object-ui/i18n十个语言包各加fields.scan.button、fields.scan.failed两个键;@object-ui/fields新增依赖@object-ui/mobile(mobile 只依赖 types,无环)。浏览器里按钮不渲染、DOM 不变。回滚即 revert 本 PR。ui/packages/fields/src/widgets/ScanFieldButton.tsx(显示门useFieldScanOffered与失败 toast);ui/packages/fields/src/widgets/LookupField.tsx(handleScanned与lookupScan的门控);ui/packages/mobile/src/nativeHost.ts(nativeScan)。pnpm --dir ui exec vitest run packages/fields/src/widgets/ScanFieldButton.shellScan.test.tsx应 13/13 通过;截图见下方 Verification(390 px,桩桥接)。What changes
@object-ui/mobiletypes the bridge's scan method, mirroring mobiledocs/HYBRID-SHELL.md"Scanning codes":'scan'joinsNativeHostMethod;ObjectStackNativegains an OPTIONALscan?(opts)(a shell older than the scanner does not carry it, and a required member would break typed stubs);NativeScanOptions(formats?: string[]) is exported;nativeScan(opts?)goes throughcallNativeand resolvesunavailable,okwith the decoded text ornull(scanner closed), orfailedwith the shell's code and message. The method is called on the bridge, so the bridge stays itsthis.@object-ui/fieldsgains a dependency on@object-ui/mobile(mobile depends only on@object-ui/types; plugin-grid, list, calendar and timeline already take this edge). An internalScanFieldButtonplususeFieldScanOffered()(not exported from the package entry) readsuseNativeHost(), so a bridge the shell injects late re-renders the field with the button.TextField: the single-line input only (not password, not the multi-row textarea, not disabled or read-only). The decoded text REPLACES the value throughonChange, the same path a keystroke takes, so the host's validation judges it; it is not cut tomaxLength, which would lose scanned data silently.LookupField: the classic popover and the static-option list open with the code in their search box; the search-first variant (picker: 'search') opensPeoplePickerseeded through the new optionalinitialSearchprop, cleared when it closes. Gated like the trigger: no button when disabled, whendependsOnis unmet, in compact (inline cell) mode, or on a classic lookup whose target offers no search.okwith text runs the handler;okwithnullorunavailabledoes nothing;failedraisestoast.errorfrom@object-ui/components(the sonner instance the Console mounts) with the new key as title and the shell's message as description.@object-ui/i18n:fields.scan.button("Scan code") andfields.scan.failed("Could not scan") in all ten locales, plusFIELD_DEFAULTSfor a provider-less render.Metadata-protocol: none.Tests
ScanFieldButton.shellScan.test.tsx(13 cases) with a stubwindow.ObjectStackNative: plain browser andhas('scan')false render no button (text and lookup); read-only, disabled and password render none; the decoded value lands throughonChange;nullchanges nothing and raises no toast;failedraises the toast with the shell message; a lookup's decoded value opens the dropdown with it in the search box and drives the$searchquery;nullleaves a lookup closed; the search-first variant opensPeoplePickerseeded.nativeHost.test.tsxextended:scanjoins the unavailable / has-says-no / method-missing tables;okwith text (formats forwarded, bridge kept asthis),okwithnull, andfailedwith the shell message.scripts/ablation-replace.mjs, source-resolved through the vitest alias): replacingreturn host.has('scan') === true;withreturn (host.has('scan') || true) === true;inScanFieldButton.tsxreds exactly the two older-shell pins (2 failed, 11 passed); restored, blob equal to HEAD andgit diff HEADempty.Acceptance notes
display: contents, so the scan button sits on its own line under the trigger there.check:dual-build-cjs-loadsis NOT MEASURED locally (exit 3, needs a fullpnpm build); it scans the rootpackages/tree only, so no ui package is in its population. CI runs it.Verification
Local runs on head
bf41e3ae(suites, gates), then main merged atc6569d87(main touched none ofui/packages/fields,mobile,i18nor the lockfile; the two scan test files re-ran green there: 2 files, 59 tests).Suites and type-check
turbo run type-checkfor@object-ui/fields,@object-ui/mobile,@object-ui/i18n: 50 tasks successful (each package'stsc --noEmit && tsc -p tsconfig.test.jsonechoed).pnpm --dir ui exec vitest run packages/mobile/ packages/i18n/: Test Files 104 passed, Tests 1567 passed, 13 skipped.pnpm --dir ui exec vitest run packages/fields/: Test Files 294 passed, 1 skipped; Tests 4426 passed, 7 skipped.pnpm --dir ui exec vitest run scripts/: Test Files 169 passed, 2 skipped; Tests 4921 passed.Derived gates
node scripts/pm/dispatch-gates.mjs --commandsderived 54 families; 53 exit 0,pnpm check:dual-build-cjs-loadsexit 3 (prerequisite).--ranreconciliation: 54 accounted for, 53 run, 1 NOT-MEASURED, 0 UNRUN.node scripts/check-sdui-manifest.mjsexit 0.pnpm --dir ui run check:new-line-citations: 0 new citations.Phone-width screenshots (390 px, stub bridge)
A temporary Console Vite entry rendered the real
TextFieldandLookupFieldwith an in-memory data source, then was deleted. Playwright readings: plain browser, 0 scan buttons; shell stub, 2 buttons; after scanning, the text input holdsASSET-0042and the lookup dropdown opens with searchASSET-0042listing only "ASSET-0042 Forklift"; a refused camera shows the toast "Could not scan / Camera permission denied". Files in the project thread:phone-plain-browser.png,phone-shell-buttons.png,phone-shell-after-scan.png,phone-shell-scan-failed.png.Generated by Claude Code