Repository navigation
Commit 58b277c
fix(app-shell): package sheet asks in-app before Delete, Discard and Duplicate; Studio returns to its landing after a delete (objectui#11784) (#11831)
Fixes #11784
Clause-②: no
The package sheet (`PackageDetailSheet`,
`packages/app-shell/src/views/metadata-admin/PackagesPage.tsx`) no
longer uses a browser dialog for *Delete app*, *Discard changes* or
*Duplicate*. After a delete, Studio now returns to its landing instead
of opening another package. Implemented by the os-dev agent dispatched
by the `domain:ui#3` seat, session
`https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8`.
This PR supersedes draft objectui#11829. The sheet change is the same:
its net diff was applied to a fresh branch cut from `main`, so that no
commit here names a model in a trailer. The seat retires objectui#11829.
## What changed
### The package sheet
- **Delete app** opens one in-app `AlertDialog`:
- a radio between "Delete the structure, keep the records" and "Delete
the structure and the data", with nothing preselected;
- the package name (the one the sheet's title shows) typed to arm the
destructive button;
- a Cancel that sends nothing.
"Keep the records" sends `DELETE /api/v1/packages/ID?keepData=true`; the
other choice sends no query (the spec's `keepData` in
`package-api.zod.ts`: preserve object tables, remove metadata only).
Before, two `window.confirm` calls ran in a row, and the second one's
Cancel sent `?keepData=true`, so Cancel still deleted the structure.
- **Discard changes (N)** asks first, naming the count. Only its confirm
calls `/discard-drafts`.
- **Duplicate** replaces `window.prompt` with an inline form built from
the Studio landing's own pieces, imported rather than copied:
`PackageIdInput` (same sanitizer and format hint), `PACKAGE_ID_RE` (one
id rule) and `duplicatePackage` from `studio-design/packages-io.ts`.
`BuilderLanding.tsx` is not edited. Because the call is now
`duplicatePackage`, a 200 whose duplicate verdict is `success: false`
shows as an error, not as "Package duplicated": the sheet's old reader
looked only at the envelope's `success`.
- Rows: `deleteConfirm`, `deleteKeepData` and `duplicatePrompt` are
removed from both `en` and `zh` designer tables (nothing else read
them). The new `engine.packages.detail.*` rows are paired. The duplicate
form reuses the landing's `engine.studio.landing.dup*` placeholders and
default name, so the sheet's default copy name is now "NAME copy" (it
was "NAME (copy)").
### Studio after a delete (the host)
Where Studio goes after a delete is the host's decision, not the
sheet's: `PackageSwitcher`'s `onManageChanged` in
`views/studio-design/StudioDesignSurface.tsx`. Only its eviction target
changes:
- **Other packages remain:** Studio now goes to the Studio landing,
`/studio` (the console's index route in `StudioRoute.tsx`; `HomePage`
spells it the same way). It used to open `list[0]`, whichever package
the list started with, which is the card's "Studio lands on another
package (showcase)".
- **No package left at all:** unchanged. Studio still goes to
`useHomePath()`'s declared home. That arm is a recovery exit governed by
objectui#7373, and the source scan
`homeRecoveryRedirectsFollowDeclaration-7373.test.ts` pins `const
homePath = useHomePath()` at this site. The two "nothing left" deletion
pins stay unchanged, as controls.
No sheet prop or callback was added. The console's Packages page hosts
the same sheet with `onChanged` set to a list reload, so the host change
cannot reach it. A new pin drives a delete there and shows it reloads in
place and never navigates to `/studio`.
No new export, prop, type member or language-pack key.
`PackageDetailSheet` keeps its props, and the rows live in app-shell's
module-local designer table, which the package root does not re-export.
## Tests
`PackagesPage.confirmFlows-11784.test.tsx` drives the real sheet through
a fetch stub that records every request.
- **Delete:**
- Cancel sends no DELETE, however the question is asked.
- Cancel after choosing a mode and typing the name sends none.
- The button stays disabled until a mode is chosen and the name matches.
The package id is not accepted as the name.
- "Structure only" sends `?keepData=true`; "structure and data" sends no
query.
- A refused DELETE keeps the sheet open and shows the refusal.
- Reopening the dialog starts over.
- **Discard:** the dialog names the count, Cancel sends nothing, and
confirm discards once.
- **Duplicate:**
- The typed id and name are sent.
- An id that `PACKAGE_ID_RE` refuses never reaches the server.
- Cancel sends nothing.
- A false verdict on a 200 is shown as an error.
- **No native dialogs:** spies show no `window.confirm` or
`window.prompt` call on the three paths.
- **Console Packages page:** a delete reloads the list, closes the
sheet, and stays on the page (it never goes to `/studio`).
In `StudioDesignSurface.packageDeletionInference.test.tsx`, the case
"REAL deletion, a sibling survives" now asserts the Studio landing. It
cites this card and triage comment 6041966855 ("return to `/studio`
after a delete"). Every other case is unchanged: two failed-refresh
cases, two "nothing left" cases and "package still there".
**Ablations** (one-time checks, not kept). Each was run with the fix
committed, through objectstack's `scripts/ablation-replace.mjs`. In
both, the anchor hit once and the blob moved; afterwards the restored
blob equals HEAD and `git diff HEAD` is empty.
1. **Sheet.** The old two-confirm delete was put back in place of the
dialog opener. The "Cancel deletes nothing" pin went red on the defect
itself: it received `DELETE /api/v1/packages/com.acme.crm?keepData=true`
after the spy answered OK, then Cancel. 7 of 12 pins went red; the
Discard and Duplicate pins, which the mutation does not reach, stayed
green. This was measured on the old branch, with the same sheet bytes.
2. **Host.** The old `list[0]` target was put back. The restated sibling
pin went red: the location was `/studio/app.other/interfaces`, the
sibling package. The other 5 cases stayed green (1 failed, 5 passed).
## Gates
All on head `e896089`:
- `turbo run build --filter=@object-ui/app-shell^... --concurrency=2`
(28 tasks), then `pnpm --filter @object-ui/app-shell type-check` (`tsc
--noEmit && tsc -p tsconfig.test.json`): exit 0.
- `pnpm exec vitest run` on 11 files: `Test Files 11 passed (11)`,
`Tests 297 passed (297)`. The files:
- both pin files above;
- `homeRecoveryRedirectsFollowDeclaration-7373`;
- `StudioDesignSurface.manageSnapshotRefresh` and
`StudioDesignSurface.packageUserMessage`;
- `PackagesPage.envelopeUserMessage`;
- `PackageIdInput` and `BuilderLanding.orgScope-11553`;
- the three i18n gate suites (`check-i18n-designer-table-parity`,
`check-i18n-dead-keys`, `check-i18n-en-drift`).
- The whole `packages/app-shell/src/views/studio-design/` directory:
`Test Files 94 passed (94)`, `Tests 553 passed (553)`.
- `eslint` on the five touched source and test files, with the repo's
own config: 0 errors.
- `StudioDesignSurface.tsx` has 14 warnings at base and at head.
- `PackagesPage.tsx` has 18 warnings at base and 17 at head.
- No type-aware rules are configured, so the diff cannot move a verdict
on an untouched file.
- `check:control-bytes`, `check:test-path-roots`,
`check:changeset-claims`, `check:pending-changeset-literals`,
`check:i18n-keys`, `check:i18n-drift`, `check:i18n-designer-parity`,
`check:new-line-citations`, `check-changeset-presence`,
`check-changeset-no-major`: exit 0 each.
- `check:i18n-dead-keys` (a report) lists no `engine.packages.detail.*`
key.
- The full `vitest run packages/app-shell/` was not re-run on this
branch; CI runs it. It last ran green on objectui#11829's branch with
the same sheet diff: `Test Files 1059 passed | 1 skipped (1060)`.
## Acceptance notes
- Not this card: the other native `confirm` / `prompt` / `alert` calls
under `app-shell/src/views` stay as they are. That includes
`adoptOrphans` on this same sheet
(`engine.packages.detail.adoptConfirm`). A `git grep` for
`window.confirm(` / `confirm(` style calls under
`packages/app-shell/src/views` (tests excluded) also finds:
- `RuntimeDraftBar.tsx`
- `MetadataTypeActions.tsx`
- `ResourceEditPage.tsx` (three)
- `ResourceHistoryPage.tsx`
- `datasource/DatasourceResourcePage.tsx`
- `StudioDesignSurface.tsx` (two)
That grep is not exhaustive; the card counts by its own reading.
- objectstack-ai/objectstack#22090 (package *Revert* 404) and its
wording are untouched.
- Already there before this PR, not changed here: rendering the sheet
with pending drafts logs React's "div cannot be a descendant of p"
warning (the `Badge` inside the *Pending changes* paragraph).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8aebc6f commit 58b277c
6 files changed
Lines changed: 742 additions & 40 deletions
File tree
- .changeset
- packages/app-shell/src/views
- metadata-admin
- studio-design
| 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 | + | |
0 commit comments