Repository navigation
Commit fed3a51
fix(app-shell): a ⌘K record hit opens under the app that lists its object (#28964)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbWpUA7fw8QiGG9YiDDSgEUC)_
Fixes #28890
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:⌘K
搜到的记录,现在在「列出该对象的应用」里打开:当前应用列出它就留在当前应用;否则去第一个列出它的可打开应用;都没有才留在当前应用。
- **为什么改**:没有对象导航的应用(如 cloud
的「账户」应用)会搜全部对象,搜到的环境记录却按「账户」应用拼链接,打开后左栏和应用切换器都不对。
- **风险与回滚**:只改记录命中的链接,当前应用列出该对象时行为不变;回滚即 revert 本 PR。
- **先看**:`ui/packages/app-shell/src/chrome/CommandPalette.tsx` 的
`recordHitAppSegment` 与 `appListsObject`;其次新测试文件。
- **两分钟验证**:`cd ui && pnpm exec vitest run
packages/app-shell/src/chrome/CommandPalette.recordHitApp-28890.test.tsx`,预期
4 个用例全绿。
- **需要你做的**:无,席位落地。
## What changes
`CommandPalette.tsx` built every record hit's link from the palette's
own app (`baseUrl`). An app with no object navigation searches every
object (the scope at `CommandPalette.tsx` `searchableObjectNames`), so
its hits named objects it does not list, and each one opened under that
app with the wrong sidebar and switcher.
A record hit now opens under:
1. the palette's own app, when it lists the hit's object (unchanged
behaviour);
2. otherwise the first app the user can open that lists it:
`filterActiveApps` (active, not hidden), in metadata order, addressed by
`appAddressSegment`;
3. otherwise the palette's own app.
"Lists" means a `type: 'object'` entry naming the object at any depth of
the app's `navigation` or any `areas[].navigation`, among the entries
the member is shown there: `navEntriesShownToMember` under the palette's
gates, walked with `navigationNames`. That is the way
`activityObjectRoster` reads every app's navigation.
No new resolver module. On `main` at `9a9823d9` no console helper
resolves an app from an object: `resolveHostAppSegment` (the card's
named example) takes no object and answers "the app the user is in, else
the first active app", and every other record-link producer uses the
current app or `resolveHostAppSegment`. The two private functions added
here compose the existing helpers (`filterActiveApps`,
`appAddressSegment`, `navEntriesShownToMember`, `navigationNames`); only
the "does this app list this object" predicate is new, and it stays
private to the palette.
## Tests
`ui/packages/app-shell/src/chrome/CommandPalette.recordHitApp-28890.test.tsx`
mounts the real palette and `CommandPaletteProvider`, chooses the record
hit, and reads the address off the router:
- from a component-only app, the hit opens under the first app that
lists the object (skipping an app that lists nothing and a hidden app
that lists it), addressed by its package id;
- an app that lists the object only in an `areas[].navigation` counts as
listing it;
- CONTROL: from an app that lists the object, the hit stays there, even
with an earlier app that also lists it;
- CONTROL: when no openable app lists the object, the hit stays under
the palette's app.
Ablation (`scripts/ablation-replace.mjs`, mutation landed and restored
to the HEAD blob): with the hit's base forced back to `baseUrl`, the two
branch cases go red (`Received:
"/apps/account/sys_environment_28890/record/E1"`) and the two controls
stay green.
## Acceptance notes
- Acceptance 1 is measured with a local reproduction (a component-only
app and an object another app lists), not on the cloud control plane;
the cloud repository was not needed.
- Out of scope, per the ruling: the search scope (an app with no object
navigation searches every object) is left as it is. It is what lets such
an app find these records at all, and this PR only changes where they
open.
- A hit whose object is listed by several other apps opens under the
first in metadata order, never a "best" one.
## Verification
Local readings, `objectstack-ai/objectstack`. Derived gates on
`af0d5914` (the fix and changeset, before the main merge); tests,
type-check and the key gates re-run on the final head `938ba76b` (main
merged at `a3640933`, plus a type-only commit).
<details><summary>Derived gates: 34 derived, 34 run, 0 NOT-MEASURED, all
exit 0</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 34 families for the 3 changed paths
(199 changed lines, under 3000). Every one exited 0, including `node
scripts/check-sdui-manifest.mjs` and its `--self-test`, the changeset
gates (run with this body as the event), `pnpm
check:cross-package-test-inputs`, `pnpm check:test-source-alias`, `pnpm
check:type-check-coverage` / `-debt`, and `pnpm --dir ui exec vitest run
scripts/` (169 files passed, 2 skipped). `dispatch-gates --ran`
reconciled: 34 run, 0 NOT-MEASURED, 0 UNRUN.
On `938ba76b`: `check-sdui-manifest`, `check-changeset-no-major --base
origin/main`, `check-empty-changeset --base origin/main`,
`check:nul-bytes`, `check:type-check-debt`, `check:type-check-coverage`,
`check:undeclared-dep-imports`: all exit 0.
</details>
<details><summary>Tests and type-check</summary>
- New file: 4/4 passed.
- Every test file that names `CommandPalette` (`git grep -l`, 71 files:
66 under `ui/packages`, 5 under `ui/apps/console`): 609 tests passed, on
`af0d5914`. All 23 files in `ui/packages/app-shell/src/chrome/` (144
tests) passed again on `938ba76b`.
- `turbo run type-check --filter=@object-ui/app-shell` (`tsc --noEmit`
and `tsc -p tsconfig.test.json`): exit 0; both re-run on `938ba76b`,
exit 0.
- `eslint` on the two touched files: 0 errors, and no warning the file
did not already have on `main`.
- The whole app-shell suite is left to CI's Test UI.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jTYffwZcQvQX8pCSso)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QsR2jTYffwZcQvQX8pCSso
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 65a8d4f commit fed3a51
3 files changed
Lines changed: 201 additions & 1 deletion
File tree
- .changeset
- ui/packages/app-shell/src/chrome
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| 58 | + | |
| 59 | + | |
58 | 60 | | |
59 | 61 | | |
60 | 62 | | |
| |||
115 | 117 | | |
116 | 118 | | |
117 | 119 | | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
118 | 167 | | |
119 | 168 | | |
120 | 169 | | |
| |||
196 | 245 | | |
197 | 246 | | |
198 | 247 | | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
199 | 253 | | |
200 | 254 | | |
201 | 255 | | |
| |||
483 | 537 | | |
484 | 538 | | |
485 | 539 | | |
486 | | - | |
| 540 | + | |
487 | 541 | | |
488 | 542 | | |
489 | 543 | | |
| |||
0 commit comments