Skip to content

fix(app-shell): a Studio selection unfolds a hidden right panel at every width - #26824

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-51d5nn
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-51d5nn

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #26737

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Studio 里右侧面板被隐藏时,选中一个元素(自动化里的流程节点、界面里的区块、数据里的字段)现在会把右侧面板展开,并直接显示该元素的属性。以前只有手机宽度会自动展开,桌面宽度下选中后配置哪里都不显示。
  • 为什么改:QA 实测 900×850 下隐藏右栏后点节点,没有抽屉也没有侧栏,用户必须重新找到右上角的切换按钮才能编辑选中的步骤。
  • 风险与回滚:低。只改了右栏宿主的 showProps 一处行为,不动任何元数据和 spec。注意它推翻了旧测试钉住的「隐藏的右栏在选中时保持隐藏」,三个设计面板一起生效。回滚即 revert 本 PR。
  • 先看:StudioDesignSurface.tsx 里 showSlotProps 的新定义;StudioDesignSurface.pillarsClaimSlot-12725.test.tsx 里替换掉的那条用例。
  • 两分钟验证:在 Studio 打开一个流程,点右上角图标隐藏右栏,再点任意节点,预期右栏展开并显示该节点的配置。
  • 需要你做的:无,席位落地。

What changes

The Studio right slot's handle (StudioRightSlotHandle) documents showProps as "An element was selected: show its properties". The host implemented it as "switch the slot to properties, and open it only on a phone" (if (isMobile) setSlotOpenMobile(true)), so at a desktop width a selection made while the builder had hidden the panel set the mode and drew the properties nowhere. In Automations the classic folded-row drawer is the other path, and it is drawn only outside the slot (configFolded && !inSlot), so neither path showed the node's configuration.

showSlotProps now unfolds the slot at every width, through the same setSlotOpen that the top-bar toggle and the slot shortcut use, so the shown state is remembered under the Studio key as a click on the toggle remembers it. On a phone nothing changes.

The function is now held in useState for the surface's life, reading the latest setSlotOpen through a ref, instead of a useCallback keyed on isMobile. The three pillars that call it run their effect on the selection key and on this function. With the old memo, a re-created callback (a breakpoint change, or React discarding the memo, ui/AGENTS.md #10) re-ran those effects, and with this change that would reopen a panel the builder had just hidden over the same selection. Now a pillar asks again only on a new selection.

Why

A builder who hides the right panel on a mid-size screen could not edit the selected step without finding the toggle again (QA run studio-qa-2026-10-27, id U9).

Tests

  • StudioDesignSurface.pillarsClaimSlot-12725.test.tsx (Automations): the case "a hidden slot stays hidden on a selection, as on Interfaces" is replaced. At a desktop width (no md match), the builder hides the slot from the toggle, selects a node, and the slot is visible in properties mode with that node's inspector, the toggle pressed and the Studio key 1, with no drawer. A second case: hidden again over the same selection, the slot stays hidden, also when the same node is picked again, and another node unfolds it.
  • StudioDesignSurface.rightSlot-12725.test.tsx (Interfaces): hiding the slot and selecting a block unfolds it on the block's properties.
  • Ablation (one-off, not kept): with the fix committed, node scripts/ablation-replace.mjs deleted setSlotOpenRef.current(true); (anchor 1 → 0, blob 15f41dcf → dd54dbc7). The two files ran red: Tests 3 failed, 25 passed (28), exactly the three new cases. Restored: blob equals HEAD, git diff HEAD empty.

Acceptance notes

  • This reverses a pinned behaviour. objectui#12725 phase 2 pinned "a hidden slot stays hidden on a selection, as on Interfaces". The card, its triage and the claim ask for the opposite (a selection shows its configuration at every width), so this PR replaces that case rather than keeping both. The maintainer's words that header quotes for phase 2 (「属性好需要考虑选中每个元素的时候也要显示」) ask for properties to show on every selection.
  • All three slot pillars change together. The fix is in the slot host, not in the Automations pillar, so a block selected in Interfaces and a field selected in Data unfold a hidden panel too. A flow-only fix would need either a second handle method or a drawer inside the slot layout, and the slot layout deliberately draws no drawer (the pinned case "a row too narrow for the classic aside opens no drawer"). The classic layout's folded-row drawer is unchanged.
  • The unfold is remembered, as the toggle and the shortcut remember it: after a selection unfolds the panel, a reload shows it.

Verification

All readings at head 367db7d0 (base c8583f80), under scripts/pm/os-verify-lock.sh.

Touched package: @object-ui/app-shell
  • Dependency closure and type-check: pnpm exec turbo run type-check --filter=@object-ui/app-shell --concurrency=2, 64 tasks successful, VERDICT command-exit 0. Re-run at 367db7d0: pnpm --filter @object-ui/app-shell type-check (tsc --noEmit and tsc -p tsconfig.test.json), VERDICT command-exit 0.
  • The two touched test files: Test Files 2 passed (2), Tests 28 passed (28).
  • Narrowed app-shell run: every test file naming StudioDesignSurface, useStudioRightSlot or StudioRightSlot (169 files, StudioDesignSurface.flowCanvasChip-11665 left out: seat 1 holds it on test(app-shell): StudioDesignSurface.flowCanvasChip-11665 reads the zh tooltip before the lazy studio catalogue lands, so Test UI goes red at random #26692): Test Files 169 passed (169), Tests 1333 passed (1333), VERDICT command-exit 0. The rest of app-shell is left to CI's Test UI shards.
  • ESLint with ui/eslint.config.js on the three touched files: 0 errors, and no warning on the changed lines.
Derived gates
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 34 commands for the 4 changed paths; all 34 exit 0, each code captured before any pipe, among them check:nul-bytes, check:test-source-alias, check:cross-package-test-inputs, check:type-check-coverage, check:type-check-debt, check-sdui-manifest, check-empty-changeset and check-changeset-no-major. pnpm --dir ui exec vitest run scripts/: Test Files 178 passed, 2 skipped (180), Tests 5065 passed, 2 skipped.
  • --ran reconciled: "34 derived famil(ies) accounted for — 34 run, 0 NOT-MEASURED". pnpm check:changeset-fixed (roster under .changeset) also ran, exit 0.
  • pnpm --dir ui run check:new-line-citations: 0 new cross-file line-address citations.
  • Families CI runs over the whole tree (type-check lanes, lint, artifact rosters) are CI's; the PR's check-runs are the reading.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (lint is CI's)
  • 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: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jRkRJ1Po9y5UgWCsns


Generated by Claude Code

…ery width

With the right panel hidden, selecting a flow node (or an Interfaces block,
or a Data field) set the slot to its properties but reopened it only on a
phone, so at a desktop width the selection's configuration was drawn
nowhere. The slot's showProps now unfolds it at every width, as the slot
toggle and the slot shortcut do. It is held in state for the surface's
life, so a pillar asks again only on a new selection, never because the
callback was re-created.

Claude-Session: https://claude.ai/code/session_01QsR2jRkRJ1Po9y5UgWCsns
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Oct 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat note · domain:ui#4 · 2026-10-27T17:55Z · head 367db7d05ecf9af4b830621b219387c26a3aad48

Test UI (3/16) failed on one test, packages/app-shell/src/views/metadata-admin/inspectors/inspectorStrings.i18n-10804.test.tsx ("zh: the unknown reference reads engine.flowRef.unknownWithSuggestion"): engine.inspector.flowNode.kind read the English Node under zh-CN. The other 1960 tests in the shard passed.

This is not this PR's failure. The diff touches only the slot host's showSlotProps in StudioDesignSurface.tsx, two existing slot tests and a changeset; nothing in the flow-node inspector strings, and main is green on the same test (CI run 40550550159). PR #26780 (merged) moved the engine.inspector.flowNode.* rows into @object-ui/i18n's lazily loaded studio catalogue, and this test reads a zh row without awaiting it, so it fails when no earlier test in its worker loaded the catalogue. The fix is #26692's PR #26800 (seat 1, not yet merged), which makes this file await loadStudioCatalogue('zh'). It is another seat's test-only fix outside this card's surface, so it is not ported; once #26800 merges, this branch merges main if still red.

The seat re-runs the failed jobs once. A second failure counts as real.


Generated by Claude Code

…spector string cases

The engine.inspector.flowNode rows live in the lazily fetched studio zh
catalogue; under this branch's Test UI shard order the file ran before
anything loaded it and read the English fallback. Ported byte-identically
from the same fix on the branch claude/project-thread-998cn0 so the two
merge cleanly.

Claude-Session: https://claude.ai/code/session_01QsR2jRkRJ1Po9y5UgWCsns
Co-authored-by: Claude <noreply@anthropic.com>
…chip cases

Same shard-order cause as the inspector string test: the zh "API name"
title lives in the lazily fetched studio catalogue. Ported byte-identically
from the same fix on the branch claude/project-thread-998cn0 so the two
merge cleanly.

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

Copy link
Copy Markdown
Contributor Author

Seat note · domain:ui#4 · 2026-10-27T18:33Z

Test UI (15/16) on 084cf97e is red on actionKeys.pin.test.ts › "lists every key the spec ActionSchema declares", missing: ['disabledReason']. The red is not from this PR. #26786 merged at 13dc413f and added the key to the spec's ActionSchema. Main's own Test UI (9/16) at aeb9cf0d is red on the same assertion. It is filed as #26849 (p0), and that fix is dispatched now. This PR does not carry the fix. Once #26849 merges, the thread merges main and the seat re-reads CI. Every other check on this head is green or skipped.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · seat domain:ui#4 · session_01QsR2jTfvjNTaR29ehLk26T · 2026-10-27T19:57Z · head 79ad8d57628e832c913e4a6e1067b9fee266fb09

  1. Shape: draft, base main, first line Fixes #26737. Clause-②: no and Metadata-protocol: none match the claim (6448230140), in the body and in the changeset (@object-ui/app-shell patch). Four files, inside the surface: StudioDesignSurface.tsx (the slot host's showSlotProps, about 15 lines), two existing tests beside it (pillarsClaimSlot-12725, rightSlot-12725), one root changeset. The two test(app-shell): flowCanvasChip-11665 awaits the lazy studio zh catalogue before reading the tooltip #26800 test ports dropped out of the diff once main carried test(app-shell): flowCanvasChip-11665 awaits the lazy studio zh catalogue before reading the tooltip #26800 (see 4). No packages/spec/**, server or new key.

  2. Premise, measured: the seat read the diff. On main, showSlotProps set the props mode and reopened the slot only when isMobile, so at a desktop width a selection with the slot hidden drew its configuration nowhere. Now it calls the host's setSlotOpen(true), which already branches on isMobile and remembers the desktop state under DOCK_STUDIO_EXPANDED_STORAGE_KEY as the toggle does. The callback is created once (useState initialiser, a ref to the current setSlotOpen), so pillars ask on a new selection only, never because the callback was re-created (ui/AGENTS.md chore: version packages #10). Hiding the slot again over the same selection keeps it hidden; only another selection unfolds it.

  3. Gates, pinned to the head: 52 check-runs on the head: 43 success, 9 skipped, 0 failure, 0 pending. · check-governed-merges --pr 26824: NOT governed, 0 of 4 paths hit the register (82 changed lines). mergeable_state: clean.

  4. Accepted deviations:

  5. Out of scope: none. Skills-docs: is not owed.

  6. Prose face, checked sentence by sentence against the diff: each changeset claim matches a hunk and a test:

    • a new selection unfolds a hidden panel at every width, on the selection's properties (Automations node, Interfaces block);
    • the shown state is remembered as from the toggle (session storage '1', toggle aria-pressed);
    • hidden again over the same selection, it stays hidden until another selection.

    Ablation of the unfold call turned exactly the 3 new cases red; restored.

Landing: no contract review is owed (no published schema, no governed text, Clause-②: no). The seat marks the PR ready and arms auto-merge. On MERGED, Fixes #26737 closes the card.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 19:58
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 19:58
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 6090bb7 Oct 27, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-51d5nn branch October 27, 2026 20:25
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/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio Automations: with the right panel hidden, selecting a node shows its configuration nowhere — the config drawer only reopens on mobile

2 participants