Repository navigation
Commit fbc1656
fix(app-shell): show Studio nav entries in the menu's order, and let a canvas drag write order (#27039)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ882oEE1FPo1aN5yzd2Gp32G)_
Fixes #26979
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:Studio「界面」左栏导航树与导航编辑画布,改为按 `order` 排列,与运行时菜单一致;画布拖拽排序时必要时写入被拖条目的
`order`。
- 为什么改:运行时菜单按 `order` 排序,Studio 却按数组顺序显示,设置了 `order`
的条目在设计器和发布后的菜单里位置不同,拖拽也可能看不出效果。
- 风险与回滚:只动 app-shell(私有包)三处源码加测试;没有设置 `order` 的应用显示与拖拽结果不变;回滚即还原本 PR。
- 先看:`AppNavCanvas.tsx` 的 `moveItem` 与
`inMenuOrder`;`StudioDesignSurface.tsx` 的 `NavTree`;新测试块
`railNavOrder-26979`。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/studio-design/StudioDesignSurface.railLabels.test.tsx
-t railNavOrder`,预期 4 个测试全部通过。
- 需要你做的:无,席位落地。
## What changes
- The Studio Interfaces rail (`NavTree`) and the nav canvas
(`AppNavCanvas`) list every level through one new helper, `inMenuOrder`
(exported from `AppNavCanvas.tsx`): `order` ascending, an absent `order`
as `0`, ties in authored order. That is the running menu's rule,
`byNavOrder` in `@object-ui/layout`, which the sidebar applies at the
top level and inside each group. Selection ids, renames and removals
still address the authored array index.
- A canvas drop rewrites the root array in the new shown order and
writes the moved entry's `order` only when its current weight no longer
falls between its new neighbours'; it then takes its neighbour's weight
and the stable sort keeps it where it was dropped. At most one `order`
write per drop, and none for an app whose entries carry no `order`.
- `NavNode` (`navSurface.ts`) and the canvas's `RawNav` declare the
spec's `order?: number`, type only.
### Why this shape (the card's two options, measured)
- The menu sorts by `order` at every level, stably. A drag that only
rewrote `order` (shape b) cannot fix entries whose `order` was written
by the API or the inspector before any drag: the rail and canvas would
still list array order. So both lists sort (shape a), and then a drag
has to write `order` or it does nothing visible. Normalising the array
to the shown order lets one neighbour-weight write place the entry,
which is the fewest `order` writes that keeps the builder equal to the
menu.
- `byNavOrder` is not imported: `navOrder.ts` is internal to
`@object-ui/layout` and publishing it would widen that package's entry
point. Per the seat's claim amendment on the card, Studio states the
same rule locally and a test renders `NavigationRenderer` beside the
rail and the canvas over the same entries, so the two rules cannot drift
without a red test.
- `AppNavCanvas` is shared with Setup's app editor (`AppPreview.tsx`),
which mounts the same component: the sorted listing and the drop
behaviour are the same there.
## Tests
`StudioDesignSurface.railNavOrder-26979` in
`StudioDesignSurface.railLabels.test.tsx` renders the real
`InterfacesPillar` rail, `AppNavCanvas` and `NavigationRenderer`:
- entries with `order` [3, 1, 2] plus a group whose children carry [2,
1] read in one order on all three;
- a drag in the canvas leaves all three agreeing, and only the moved
entry changes (it gains `order`; every other entry is the same object);
- control: entries without `order` keep array order, and a drag among
them writes no `order`.
Ablations (each self-restoring through `scripts/ablation-replace.mjs`,
blob equal to HEAD after restore):
| mutation | result |
|---|---|
| `inMenuOrder` stops sorting | 2 of 4 red (agreement, drag) |
| the drop's `order` write removed | 1 of 4 red (drag) |
| `NavTree` lists array order | 2 of 4 red (agreement, drag) |
The first attempt at the sort ablation was refused by the tool (its
replacement text already occurred in the file), so it did not run; it
was rerun with a distinct replacement.
## Acceptance notes
- Not changed, outside the file surface: on load the Interfaces pillar
opens the first resolvable leaf of the authored array (`firstLeaf` in
the load effect), which can now differ from the rail's first row when
`order` reorders the top. Observation only.
- No changeset: the diff touches only `@object-ui/app-shell`, which is
`private: true`.
## Verification
All local readings on head `42f2d1b4` unless noted. Exit codes captured
before any pipe.
<details><summary>Derived gate families (24): all exit 0, reconciled
with dispatch-gates --ran</summary>
`node scripts/pm/dispatch-gates.mjs --commands` derived 24 families for
this diff; each ran on `42f2d1b4` and exited 0. `--ran` answers: "24
derived famil(ies) accounted for — 24 run, 0 NOT-MEASURED". The set
includes `node scripts/check-sdui-manifest.mjs` (and its `--self-test`),
`node scripts/check-comment-mask-corpus.mjs`, `pnpm --dir ui exec vitest
run scripts/`, `pnpm check:cross-package-test-inputs`, `pnpm
check:test-source-alias`, `pnpm check:type-check-coverage`, `pnpm
check:type-check-debt` and `pnpm check:nul-bytes`.
</details>
<details><summary>Package tests and extra families (on 54fb22e; the
later commit adds one comment and one lint directive)</summary>
- `pnpm --dir ui exec vitest run
packages/app-shell/src/views/studio-design/`: Test Files 243 passed
(243), Tests 1761 passed (1761).
- `pnpm --dir ui exec vitest run
packages/app-shell/src/views/metadata-admin/previews/
packages/app-shell/src/views/metadata-admin/studio-locale.i18n.test.tsx`:
Test Files 148 passed (148), Tests 1678 passed (1678). Covers Setup's
`AppPreview` mount of the canvas.
- `pnpm --filter @object-ui/app-shell type-check`: exit 0.
- `pnpm --dir ui run check:spec-symbols`: exit 0. `pnpm --dir ui run
check:new-line-citations`: 0 new citations, exit 0.
- eslint on the four changed files: 0 errors; on `42f2d1b4` the new
export adds no warning (it carries the same directive as
`navPayloadOf`).
- On `42f2d1b4`: `StudioDesignSurface.railLabels.test.tsx` whole file,
Tests 60 passed (60).
</details>
Not run locally, CI-owned: the repo-wide `pnpm lint`, `test:repo`, and
the full UI suite.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbWATiRKGB7bP2VvNAG
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbWATiRKGB7bP2VvNAG)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 693a729 commit fbc1656
4 files changed
Lines changed: 276 additions & 12 deletions
File tree
- ui/packages/app-shell/src/views
- metadata-admin/previews
- studio-design
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
11 | 15 | | |
12 | 16 | | |
13 | 17 | | |
| |||
41 | 45 | | |
42 | 46 | | |
43 | 47 | | |
44 | | - | |
| 48 | + | |
45 | 49 | | |
46 | 50 | | |
47 | 51 | | |
| |||
60 | 64 | | |
61 | 65 | | |
62 | 66 | | |
| 67 | + | |
| 68 | + | |
63 | 69 | | |
64 | 70 | | |
65 | 71 | | |
| |||
254 | 260 | | |
255 | 261 | | |
256 | 262 | | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
257 | 288 | | |
258 | 289 | | |
259 | 290 | | |
| |||
276 | 307 | | |
277 | 308 | | |
278 | 309 | | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
279 | 314 | | |
| 315 | + | |
280 | 316 | | |
281 | 317 | | |
282 | 318 | | |
| |||
341 | 377 | | |
342 | 378 | | |
343 | 379 | | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
344 | 393 | | |
345 | 394 | | |
346 | 395 | | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
347 | 399 | | |
348 | | - | |
349 | | - | |
350 | | - | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
351 | 410 | | |
352 | 411 | | |
353 | 412 | | |
354 | 413 | | |
355 | 414 | | |
356 | 415 | | |
357 | 416 | | |
358 | | - | |
| 417 | + | |
359 | 418 | | |
360 | 419 | | |
361 | 420 | | |
| |||
394 | 453 | | |
395 | 454 | | |
396 | 455 | | |
397 | | - | |
| 456 | + | |
398 | 457 | | |
399 | 458 | | |
400 | 459 | | |
| |||
405 | 464 | | |
406 | 465 | | |
407 | 466 | | |
408 | | - | |
| 467 | + | |
409 | 468 | | |
410 | 469 | | |
411 | 470 | | |
| |||
423 | 482 | | |
424 | 483 | | |
425 | 484 | | |
426 | | - | |
| 485 | + | |
427 | 486 | | |
428 | 487 | | |
429 | 488 | | |
| |||
484 | 543 | | |
485 | 544 | | |
486 | 545 | | |
487 | | - | |
| 546 | + | |
488 | 547 | | |
489 | 548 | | |
490 | 549 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
67 | 68 | | |
68 | 69 | | |
69 | 70 | | |
| |||
1417 | 1418 | | |
1418 | 1419 | | |
1419 | 1420 | | |
| 1421 | + | |
| 1422 | + | |
| 1423 | + | |
| 1424 | + | |
| 1425 | + | |
| 1426 | + | |
| 1427 | + | |
| 1428 | + | |
| 1429 | + | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
| 1449 | + | |
| 1450 | + | |
| 1451 | + | |
| 1452 | + | |
| 1453 | + | |
| 1454 | + | |
| 1455 | + | |
| 1456 | + | |
| 1457 | + | |
| 1458 | + | |
| 1459 | + | |
| 1460 | + | |
| 1461 | + | |
| 1462 | + | |
| 1463 | + | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
| 1481 | + | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
| 1494 | + | |
| 1495 | + | |
| 1496 | + | |
| 1497 | + | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + | |
| 1501 | + | |
| 1502 | + | |
| 1503 | + | |
| 1504 | + | |
| 1505 | + | |
| 1506 | + | |
| 1507 | + | |
| 1508 | + | |
| 1509 | + | |
| 1510 | + | |
| 1511 | + | |
| 1512 | + | |
| 1513 | + | |
| 1514 | + | |
| 1515 | + | |
| 1516 | + | |
| 1517 | + | |
| 1518 | + | |
| 1519 | + | |
| 1520 | + | |
| 1521 | + | |
| 1522 | + | |
| 1523 | + | |
| 1524 | + | |
| 1525 | + | |
| 1526 | + | |
| 1527 | + | |
| 1528 | + | |
| 1529 | + | |
| 1530 | + | |
| 1531 | + | |
| 1532 | + | |
| 1533 | + | |
| 1534 | + | |
| 1535 | + | |
| 1536 | + | |
| 1537 | + | |
| 1538 | + | |
| 1539 | + | |
| 1540 | + | |
| 1541 | + | |
| 1542 | + | |
| 1543 | + | |
| 1544 | + | |
| 1545 | + | |
| 1546 | + | |
| 1547 | + | |
| 1548 | + | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
| 1556 | + | |
| 1557 | + | |
| 1558 | + | |
| 1559 | + | |
| 1560 | + | |
| 1561 | + | |
| 1562 | + | |
| 1563 | + | |
| 1564 | + | |
| 1565 | + | |
| 1566 | + | |
| 1567 | + | |
| 1568 | + | |
| 1569 | + | |
| 1570 | + | |
| 1571 | + | |
| 1572 | + | |
| 1573 | + | |
| 1574 | + | |
| 1575 | + | |
| 1576 | + | |
| 1577 | + | |
| 1578 | + | |
| 1579 | + | |
| 1580 | + | |
| 1581 | + | |
| 1582 | + | |
| 1583 | + | |
| 1584 | + | |
| 1585 | + | |
| 1586 | + | |
| 1587 | + | |
| 1588 | + | |
| 1589 | + | |
| 1590 | + | |
| 1591 | + | |
| 1592 | + | |
| 1593 | + | |
| 1594 | + | |
| 1595 | + | |
| 1596 | + | |
| 1597 | + | |
| 1598 | + | |
| 1599 | + | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
| 1603 | + | |
| 1604 | + | |
| 1605 | + | |
| 1606 | + | |
| 1607 | + | |
| 1608 | + | |
| 1609 | + | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
1420 | 1618 | | |
1421 | 1619 | | |
1422 | 1620 | | |
| |||
0 commit comments