Repository navigation
feat(console): an action's confirm and param dialogs name their dismiss button (cancelText drawn in place of Cancel) - #28983
Conversation
…ext on the dismiss button Claude-Session: https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg Co-Authored-By: Claude <noreply@anthropic.com>
…s bundles for cancelText; changeset Claude-Session: https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg Co-Authored-By: Claude <noreply@anthropic.com>
…unts the action form's cancelText row Claude-Session: https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 177 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6bd81c6aeb45ce93617c163f60d4112426cd1ac3 && git checkout 6bd81c6aeb45ce93617c163f60d4112426cd1ac3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 00d0668d380046198dfc9151858b88df463bfd9b f566aa2e1c0e895ca3b7d23a2de9626244c2915a && git checkout -B drift-repro 00d0668d380046198dfc9151858b88df463bfd9b && git merge --no-ff f566aa2e1c0e895ca3b7d23a2de9626244c2915a
node scripts/docs-audit/affected-docs.mjs --json 00d0668d380046198dfc9151858b88df463bfd9b
|
|
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu 席位核对 · ACCEPT · 2026-11-02T06:13ZHead-sha:
|
Requested by Steedos · project thread
Fixes #28879
Refs #28519
Clause-②: yes (widening)
Metadata-protocol: none
File surface: the card's Wanted 1-4 list (claim 6568809663), plus two mechanical consequences the seat added in amendment 6568954960 (
ui/packages/types/src/ui-action.ts, and the oneRecordDetailViewuseObjectLabel()destructure line outsideparamCollectionHandler).维护者速读
cancelText;没声明时仍是「Cancel」。翻译包里的_actions.ACTION.cancelText优先,其次是作者写的多语言文本,最后是作者写的字符串。设置 → 邀请里的「Cancel Invitation」确认框,取消按钮现在写「Keep invitation」(中/日/西已翻译)。Studio 的动作表单可以填这个键,os i18n extract会为带confirmText或参数的动作生成它的翻译位。cancelText已在 feat(spec): an action's confirm and param dialogs can name their dismiss button —cancelText#28859 写进 spec,但控制台一直画「Cancel」;动作本身就叫「取消……」时,两个按钮分不清哪个是保留。cancelText的动作完全不变。回滚即 revert 本 PR。ui/packages/core/src/actions/ActionRunner.ts的confirmOptions(把cancelText交给确认框);ui/packages/react/src/hooks/useActionTextLocalizer.ts新增的cancelText解析;packages/spec/liveness/action.json的cancelText行。pnpm --dir ui exec vitest run packages/core/src/actions/__tests__/ActionRunner.confirmCancelText-28879.test.ts packages/app-shell/src/views/ActionConfirmDialog.cancelText-28879.test.tsx packages/app-shell/src/views/ActionParamDialog.cancelText-28879.test.tsx,预期 3 个文件全绿。What changes
The confirm path.
ActionDefdeclarescancelText?: SpecActionInput['cancelText'](derived) andACTION_DEF_KEYSlists it.useActionTextLocalizerresolves it only when the action declares one: the authoredI18nLabelis collapsed to the active language, then offered as the fallback touseObjectLabel().actionCancelText, so the bundle'sobjects.OBJECT._actions.ACTION.cancelText(globalActions.ACTION.cancelTextfor an object-less action) wins.ActionRunner#confirmOptionshands the resolved non-empty string to the confirm handler asoptions.cancelText, whichActionConfirmDialogalready draws overactionConfirm.cancel. The runner has no language, so it passes on a string only; a surface that skips the localizer keeps the host's "Cancel".The param path.
ParamDialogStategainscancelText?: string. Both param-collection handlers (useConsoleActionRuntime#paramCollectionHandler,RecordDetailView#paramCollectionHandler) resolve it exactly as they resolvesubmitText, andActionParamDialogdraws it on the dismiss button,actionDialog.cancelwhen absent or empty.The resolver.
@object-ui/i18nuseObjectLabel().actionCancelText, the sibling ofactionSubmitTexton the sameactionSuffixesnode rule (a bound action never readsglobalActions). The member-count pin inuseObjectLabel-identity-5564.test.tsxgoes from 29 to 30.Types and forwarding.
UIActionSchema.cancelText?: SpecAction['cancelText'](amendment 6568954960).action:button,action:icon,action:groupandaction:menuforwardcancelTextto the runner;action-forward-precedence.test.tsxlists it aftersubmitText.os i18n extract(pushActionCancelText). ScaffoldscancelTextunder an action's translation root for an action that declaresconfirmTextor a non-emptyparams, the populationtranslateActionoverlays it onto, and no slot for any other action.First consumer.
sys_invitation'scancel_invitationdeclarescancelText: 'Keep invitation'. The platform-objects bundles regenerate (node scripts/check-i18n-bundles.mjs --write); zh-CN 保留邀请, ja-JP 招待を保持, es-ES Conservar invitación are hand-translated, as is the new Studio action-form row.Spec rows.
liveness/action.jsoncancelTextflipsplanned→livewith the read points above and dropsauthorWarn/authorHint;state-counts/action.mdregenerates. Theaction/cancelTextomit row leavesmetadata-form-zod-reconciliation.test.ts, andaction.form.tsgains a plaincancelTextrow ("Cancel button label"), not gated, because the parse refuses the key on an action that opens neither dialog. "(declared ahead of its console reader: no renderer shows it yet)" leaves theActionSchema.cancelTextdescribe and "no renderer shows it yet" leaves the translation slot's;content/docs/references/**regenerates. Inactions.mdxboth "The console does not draw it yet" sentences flip, thecancelTextone and the stalesubmitTextone.Why
ActionSchema.cancelTextlanded spec-first in #28859 with no reader, so an author who wrote it still saw "Cancel". On Setup → Invitations, Cancel Invitation asked with two buttons reading Cancel and Cancel Invitation.Tests
ActionRunner.confirmCancelText-28879.test.ts(5): the bag carriescancelTextbeside the label; a label-less action still hands it on; control, no key without it;''hands nothing; dismissing stops the run.ActionConfirmDialog.cancelText-28879.test.tsx(3) andActionParamDialog.cancelText-28879.test.tsx(4): the label is drawn, the fallback is Cancel (also for''on the param dialog), and the labelled button dismisses.useActionTextLocalizer.cancelText-28879.test.tsx(6): authored string, bundle first, inline map collapsed, the global node for an object-less action, nothing for an undeclared key even with a bundle entry, a nameless action's map collapsed.useConsoleActionRuntime.cancelText-28879.test.tsx(5) andRecordDetailView.paramCancelText-28879.test.tsx(5), one per param reader.useObjectLabel-actionCancelText-28879.test.tsx(5): the object node, the global node, no global read for a bound action (with a control), the miss answers.RecordDetailView.paramRuntimeParity-6431.test.tsx:cancelTextjoinsDIALOG_READS, and the two runtimes write the same value.packages/cli/test/i18n-extract-action-cancel-text.test.ts(7): top-level, global and inline actions; the seed-less slot; no slot withoutconfirmTextorparams; bundle seed and coverage gating; one fixture throughActionSchema→ extract →ObjectTranslationDataSchema.useObjectLabelpartially gain anactionCancelTextpassthrough (sevenuseConsoleActionRuntime.*files), andobject-lifecycle-panel-echo-decisions.test.tscounts the new action-form row (698 → 699).Acceptance notes
ui/packages/types/src/ui-action.ts(the renderers'schema.cancelTextdoes not type-check without it), and inRecordDetailView.tsxtheuseObjectLabel()destructure gainsactionCancelText(the handler cannot call the hook). Nothing else inRecordDetailView.tsxmoves.cancelTextis not interpolated: it is a button label, likesubmitText.metadata-admin/MetadataTypeActions.tsx, a thirdActionParamDialogconsumer (Setup metadata-type actions), passes neithersubmitTextnorcancelText, so its buttons keep Confirm / Cancel. The card names the two business-action handlers only. Noted, not filed.Verification
Readings at head
f566aa2e(after mergingorigin/mainat00d0668d) unless a line says otherwise.Derived gates: 112 derived, 111 run exit 0, 1 NOT MEASURED
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 112 families ate389f7f7(the pre-merge head; the merge brought nothing that overlaps the diff). Each ran with stdin closed and its exit captured before any pipe.dispatch-gates.mjs --ran: 112 accounted, 111 run, 1 NOT-MEASURED:pnpm check:dual-build-cjs-loadsexit 3 (PREREQUISITE NOT MET, needs a whole-workspace build), left to CI.check:skill-examplesandcheck:i18n-coveragefirst exited 3 on unbuilt prerequisites; after buildingclient-reactand the i18n-coverage closure both exit 0 (check-i18n-coverage: OK (14 config(s), 426 baselined untranslated string(s), none new)).f566aa2e:node scripts/check-sdui-manifest.mjsexit 0;node scripts/check-i18n-bundles.mjsexit 0;pnpm --filter @objectstack/spec check:generated"All 14 generated artifacts are up to date" after a spec rebuild; uicheck:new-line-citations0 new;check-nul-bytesexit 0.Tests
f566aa2e: the 18 new or touched test files plusRecordDetailView.childWriteRefreshesParent-12244.test.tsx(the one overlap with main) → 19 files, 100 passed.e389f7f7:packages/core/src/actions/,packages/i18n/,packages/components/src/renderers/action/, everyuseActionTextLocalizer*,useConsoleActionRuntime*,ActionParamDialog*,ActionConfirmDialog*andRecordDetailView*test → 330 files, 3779 passed, 13 skipped.packages/types/→ 386 files, 10456 passed.pnpm --dir ui exec vitest run scripts/→ 169 passed, 2 skipped, exit 0 (includescheck:action-forward-parity).@object-ui/app-shell,components,core,i18n,react,types) → 70/70 tasks, exit 0.vitest run src/ui/ src/system/→ 238 files, 8155 passed;scripts/liveness/→ 12 files, 359 passed;typecheckexit 0.vitest run --project unit test/i18n-extract→ 12 files, 96 passed;typecheckexit 0. The integration tier is declared to CI.test→ 108 files, 1503 passed;typecheckexit 0.scripts/ablation-replace.mjs, committed first): deleting thecancelTextspread inconfirmOptionsredsActionRunner.confirmCancelText-28879(2 failed | 3 passed); replacingstate.cancelText || t('actionDialog.cancel')witht('actionDialog.cancel')redsActionParamDialog.cancelText-28879(2 failed | 2 passed). Both restored, blob == HEAD,git diff HEADempty.Human Minimum
packages/specholds no runtime logic. (Only a describe, a form row, a liveness row and a test ledger row change there.)pnpm lintis CI's run.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg
Generated by Claude Code