Skip to content

feat(app-shell): author a nav entry's landing, badge, order and visible condition in the Studio inspector - #26977

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-tkq6t8
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-tkq6t8

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread
Fixes #26740

维护者速读

  • 改了什么:Studio「界面」支柱的导航项检查器,现在能编辑导航项本就声明的键:对象项的落点(默认、命名视图 viewName、记录 recordId 加 recordMode、筛选 filters),以及 badge/badgeVariant、order、visible。没有新增任何键。
  • 为什么改:这些键 spec 早已声明、运行时也会兑现,但 Studio 里只能看不能改(visible 只读,其余根本没有入口),作者只能去 Setup 或手改 JSON。
  • 风险与回滚:只动 StudioNavItemInspector 一个组件,外加把 FiltersEditor 原样搬到新文件 NavFiltersEditor.tsx;i18n 只增 11 行 studio 键。回滚就是 revert 本 PR。
  • 先看:StudioDesignSurface.tsx 里 chooseLanding 与 onVisibleLint 两个处理函数;前者保证 spec 互斥的落点键不会同时写入,后者保证有 CEL 错误的条件不会被写进草稿。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/StudioDesignSurface.navItemInspectorKeys-26740.test.tsx,19 条应全绿。
  • 需要你做的:评审即可;无需决定。三条顺路观察写在 Acceptance notes,均未立卡。

What changes

The Studio Interfaces pillar's nav-item inspector (StudioNavItemInspector) now authors every key NavigationItemSchema declares on the entry's member that the ruling names, and nothing the member does not declare:

  • Landing of an object entry, as one choice: the object's default, a named view (viewName, picked from the package's views of that object through usePackageNavTargets), a record (recordId plus recordMode), or a filtered slice (filters, through the same rows editor Setup's AppNavInspector mounts). Choosing a landing removes the other landings' keys, because objectNavTargetExclusivity refuses filters beside viewName/recordId; choosing a record also removes runAction, which the spec refuses beside recordId. The landing is read off the keys the entry holds (deriveObjectTargetMode, the runtime's precedence).
  • badge and badgeVariant: a text field and a picker over the four variants the spec declares. The variant table is keyed by the spec-derived type, so a variant the spec adds or drops stops the file compiling.
  • order: a number field, written as a number; an emptied field removes the key.
  • visible: now edited in CelPredicateField, the control the Data pillar and the page-block inspector already use for predicates, with the roots the console binds for a nav entry (current_user, plus data only on an entry that declares dataSource). Text reaches the entry only while its lint finds no error, so the nav autosave can never send an unparseable condition; until then the entry keeps its previous condition and the field says "Not saved yet". An envelope keeps its envelope (only its source is edited). An envelope with no CEL source is still shown read-only, as before.
  • Every emptied field removes its key; nothing writes ''.

Supporting changes:

  • FiltersEditor is moved out of AppNavInspector.tsx into metadata-admin/inspectors/NavFiltersEditor.tsx as a pure move (the only change is export), so both inspectors mount the same editor.
  • 11 new engine.inspector.appNav.* rows in the studio namespace (en, zh). No row in metadata-admin/i18n.ts is added or changed.
  • Changeset: @object-ui/i18n patch.

Clause-②: no
Metadata-protocol: none

Why

The spec has declared these keys all along and the running menu honours them, but the Studio inspector offered no way to set them: visible was shown read-only and the rest had no field. Studio builders had to leave for Setup or hand-edit JSON.

Tests

New file StudioDesignSurface.navItemInspectorKeys-26740.test.tsx, 19 tests:

  • Each declared key is authored, written in the spec's spelling, and the resulting entry parses: named view (and the running menu's link goes to that view), record, filters, badge and variant, order (a number), visible.
  • A key the member does not declare is not offered: a page entry has no landing; a separator offers order only; an object entry with no object has no landing; each landing shows only its own fields.
  • A landing change removes the keys the spec refuses beside it (record to filters, filters to record including runAction, back to default).
  • visible round-trips: shown and left as authored; an envelope keeps its envelope; a condition with a lint error is not written and the hint shows, then the fix is written; emptying removes the key; a non-CEL envelope is read-only.
  • order moves an entry in the running menu (NavigationRenderer).

Four existing tests change. Three now name the Label textbox they query, because the inspector gained a second textbox (Badge): navItemInspectorLabel-11196, navItemInspectorLabelMaps-11148, navItemTypes-11790. interfacesNavTree-12455 now asserts the visible value in the editable field, and its control case uses a separator.

Ablations, one per control, each through scripts/ablation-replace.mjs (anchor hit once, blob changed, then restored with blob equal to HEAD). Every one turned the suite red:

Control Mutation Red
landing picker does nothing 7
landing clears other keys key removal loop dropped 3
view picker does nothing 1
record id field does nothing 2
record mode picker does nothing 1
filters editor commit does nothing 1
badge field does nothing 1
badge variant picker does nothing 1
order field does nothing 2
order as a number writes the string 2
visible lint-clean text not written 4
visible lint gate erroring text written too 1
badge offered only where declared shown on every member 1

Acceptance notes

Observations from reading the code; none reproduced through a public door, none filed:

  • The Studio rail and canvas list entries in array order and do not sort by order, while the running menu does (byNavOrder). Once entries carry order, a drag reorder in the rail can be overridden at runtime.
  • Setup's AppNavInspector derives its target mode from the keys present, so choosing "record" or "view" there is not held until a value is entered.
  • Setup's clearedTargetPatch does not remove runAction when the target becomes a record.

Design note: the dispatch asks that visible use Studio's predicate control and validation. The Studio's other blocking predicates block the host autosave through onBlockingIssuesChange; wiring that here would edit the host outside this card's file surface. Instead the inspector writes the condition only when its lint is clean, which gives the same guarantee (no erroring predicate is ever saved) and says so on the field.

Not added, because the ruling does not list them: requiredPermissions, requiresObject, requiresService, dataSource.

Verification

On head 13df6681 (main merged in; the merge touched none of this PR's files):

  • New tests: StudioDesignSurface.navItemInspectorKeys-26740.test.tsx 19/19 pass.
  • Neighbouring Studio nav and AppNavInspector tests: 15 files, 124 tests pass. Before the merge: every metadata-admin/inspectors/ test plus the deep-link and nav-surface tests, 198 files, 2650 pass, 1 skipped.
  • @object-ui/app-shell and @object-ui/i18n type-check exit 0; @object-ui/i18n tests 89 files, 1373 pass, 13 skipped.
  • ESLint on every touched file: 0 errors.
  • dispatch-gates --commands derived 34 gate families; all 34 ran and exit 0, and dispatch-gates --ran reconciles 34 derived, 34 run, 0 not measured. That includes node scripts/check-sdui-manifest.mjs and pnpm check:nul-bytes.
  • One gate, pnpm --dir ui exec vitest run scripts/, first went red (2 tests in check-readme-exports.test.ts) because the local plugin-detail dist predated the merge that added RecordPageColumns. After rebuilding @object-ui/plugin-detail it ran 178 files, 5065 pass, exit 0. This PR does not touch that package.
  • 13 ablations, one per control, all red, each restored with blob equal to HEAD (table under Tests).

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (packages/spec not touched.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; ESLint on every touched file reports 0 errors.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: not applicable.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the maintainer summary above is the requested Chinese digest).

Generated by Claude Code

…le condition in the Studio inspector

The Interfaces nav-item inspector offers the keys the spec's nav members
declare beyond the target: an object entry's landing (named view, record
with open mode, or URL-filtered slice), the shared base's badge and badge
variant, order, and the visible condition, authored in the CEL editor
every Studio predicate uses and written only once it lints clean.

The filters rows editor moves unchanged out of AppNavInspector into its
own module so both nav inspectors mount it.

Claude-Session: https://claude.ai/code/session_01JQwxbXKcuJdVEkbLwwWgKt
Co-authored-by: Claude <noreply@anthropic.com>
… and visible fields

Claude-Session: https://claude.ai/code/session_01JQwxbXKcuJdVEkbLwwWgKt
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Oct 27, 2026
…d of a local copy

The nav inspector declared its own isPlainRecord, which the spec already
exports with the same body; check:spec-symbols refuses a hand-written
spec-named symbol.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbXKcuJdVEkbLwwWgKt
…boboxes in two older tests

The inspector now also has a Badge textbox, a Badge style picker and a
condition field. railLabels found the Label field as the only textbox,
and sharedSelect resolved the inspector's comboboxes against a fixed list
of names, so both now name what they mean.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbXKcuJdVEkbLwwWgKt
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · seat domain:ui#5 · session_01JQwxbXGpVJHZTXXerMoY4C · head 0c67d377 · 2026-10-28T00:45Z

席位意见:Studio 导航项检查器补齐 spec 已声明的键:对象条目的落点(视图 viewName、记录 recordId/recordMode、filters)、badge/badgeVariant、order 和 visible。每个键只在声明它的成员上出现,没有新键;visible 用 Studio 现有的 CEL 谓词控件,只有 lint 无错误时才写入草稿。

  1. Shape: Fixes #26740; 13 files, +804/−126: StudioDesignSurface.tsx (the inspector, import lines and three module helpers directly above it), FiltersEditor moved verbatim out of AppNavInspector.tsx into the new metadata-admin/inspectors/NavFiltersEditor.tsx and imported back, one new test file (344 lines), six existing tests now name their fields, studio namespace en +14 / zh +11, and a patch changeset for @object-ui/i18n. No row in metadata-admin/i18n.ts.
  2. Premise measured: the seat read the source diff. The landing picker reuses OBJECT_TARGET_MODES / OBJECT_MODE_FIELDS / deriveObjectTargetMode; changing it removes the keys objectNavTargetExclusivity refuses beside the new one (and runAction for a record). The view picker is usePackageNavTargets(…, 'view') filtered to the bound object. badgeVariant values come from a table keyed by the spec-derived type; order is written as a number and an emptied field removes the key. visible keeps the entry's form (bare string or envelope source). Dev: 19/19 new tests, each authored key checked with NavigationItemSchema.safeParse; 13 ablations, all red, restored.
  3. Gates / check-runs: PR check-runs on 0c67d377: 43 success, 10 skipped, 0 failure; Lint & Repo Gates, TypeScript Type Check and Check Changeset success. Two reds on earlier heads were the dev's own: a local isPlainRecord duplicating @objectstack/spec/data's (check:spec-symbols; now imported, d0f6a1e8), and two older Studio tests that counted inspector fields (0c67d377, test-only). Dev's local run: 34 derived, 34 run, --ran reconciled.
  4. Accepted deviations: the declared pure move of FiltersEditor; helpers above the inspector; six existing tests edited to name fields the inspector now has more of; visible is held back while lint reports an error instead of blocking host autosave (no host edit outside the file surface); a merge of origin/main to clear a STALE TREE.
  5. Out of scope: (1) the Studio rail and AppNavCanvas ignore order while the runtime sorts by byNavOrder: confirmed by grep, filed Studio Interfaces: the nav rail and canvas list entries in array order and ignore order, while the running menu sorts by it #26979. (2) Setup AppNavInspector does not hold a picked record/view mode until a value is typed, and (3) Setup clearedTargetPatch keeps runAction on a record target: Acceptance notes, unreproduced inferences in ui seat 2's Setup area, passed to that seat. Skills-docs: none — Studio inspector controls for already-declared keys.
  6. Landing: not Tier H (no governed path, +930 lines); ready for landing (ready + auto-merge).

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 28, 2026 00:46
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 28, 2026 00:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 7eb613c Oct 28, 2026
54 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-tkq6t8 branch October 28, 2026 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants