Repository navigation
Commit d8be368
feat(app-shell): Studio's action New menu opens on common actions, with the blank action under Advanced (part of objectui#11861) (#11977)
Part of #11861 — the actions entry only. The automations entry stays for
a later claim, so the card stays open.
Clause-②: no
## What changes
In an object's **Actions** view, **New** used to add one action straight
away: "New action", an update of this record with no field values yet
(objectui#11820's skeleton). It now opens a menu, as the Validations
view's does since objectui#11931:
| Preset | What it writes | When it is written |
|---|---|---|
| **Change a picklist field** | `operation: 'update'`, `params: [{ field
}]` on the object's first author picklist, labelled "Change Status" for
a Status field. The click asks for the new value and the platform writes
it, as the caller. | At once. Disabled, saying "Needs a picklist field
on this object.", when there is none the author may change. |
| **Run a flow** | `type: 'flow'` | Held until the editor's **Flow
name** is given |
| **Open a web page** | `type: 'url'` | Held until the editor's **URL**
is given |
| **Open a page in a dialog** | `type: 'modal'` | Held until the
editor's **Modal / page name** is given |
The blank action is folded under **Advanced**, still named "New action",
and it writes exactly what New wrote before (the same object literal,
untouched). objectui#11820's `newActionPin` now clicks Advanced, then
New action; it has no assertion changes.
Every preset starts from the blank action's seed: a fresh name, the "New
action" label, this object, and `record_header` placement.
## A preset that needs a target waits for it
The spec refuses a `url`, `flow`, `modal`, `form` or `api` action
without a `target`, and only the author knows which flow, address or
page they mean. Such a preset is therefore **held in the panel**, the
way a new validation rule waits for its condition (objectui#11820):
- It is listed with "Not saved — needs “Flow name”".
- The detail pane shows the Data pillar's held line
(`engine.studio.held.line`, objectui#11786): "Not saved yet: the action
“New action” needs “Flow name”. Your changes are kept here and saved
once it is filled in."
- The target input is the **action editor's own**
(`ActionDefaultInspector`, not edited). For a flow or a page it is a
picker of the app's names, and free text when there are none.
- The first patch that gives the action a target writes it to the object
draft. From then on it is an ordinary action.
- **Delete** drops it unsent.
- It stays on the object it was started on, and it never shows on
another object's list.
- Known cost, the same as the validation hold: leaving the view or the
object first drops it, and Publish does not wait for it.
**Which presets wait is decided by the action as it stands, not by a
second list.** `heldInputKey` reads `TARGET_INPUT_KEYS`, the
target-requiring types mapped to the editor's catalogue key for each
target input. The keys are reused rows, so no new row was added for
them. If the author switches a held action's Type to one without a
target, the next patch writes it.
⛔ No preset writes a script (objectui#11921's ruling A).
## Why these four, and what is not offered
- **The objectui#11820 skeleton moved under Advanced** rather than
becoming the first preset (the claim allows either). With an empty
`patch` and no `params`, the platform answers a click with a refusal.
"Change a picklist field" is the update a click can run as soon as it is
written.
- **"Open a form" is not offered.** The spec's form target is the form
view's name, written as the object name, a dot, and the view key. The
editor's form picker lists `view` metadata names, and I did not verify
that those are such keys. "Open a page in a dialog" targets a page,
which the picker lists by name.
- **"Call an API endpoint" is not offered.** It needs both an endpoint
and a request contract. It stays reachable from the blank action's Type
control.
## The spec, measured rather than claimed
The runtime does not import `ActionSchema`. That keeps the schema out of
the console's first load, for the same reason objectui#11921 writes out
its body-key table. The two facts the module writes out are each
re-derived from the spec in the new pin,
`ObjectActionsPanel.presets-11861.test.tsx`:
- **Types that need a target.** For every `ActionType`, the action is
held exactly when `ActionSchema` refuses it at `target`, and with a
target nothing is held and nothing is refused there.
- **Field-name grammar for a param.** For eight names (`status`, `st`,
`s`, `Status`, `_status`, `status_2`, `2status`, `sta-tus`), the
picklist is offered exactly when the spec accepts it as
`params.0.field`.
It also pins the class. Every preset in `ACTION_PRESETS` ends as an
action that `ActionSchema` and `ObjectSchema` accept, the parse the
object-draft save applies. For held presets that means after the author
types the target into the **real** registered `ActionDefaultInspector`.
The rest of the pin covers:
- the menu shape: 3 to 5 presets, and Advanced folded on every opening;
- the exact written shapes;
- picking a flow through the editor's picker;
- Delete;
- the object scoping;
- the disabled preset, which never takes a `system`, hidden or read-only
picklist.
## Ablations
Run on head `67e28442c` through objectstack's
`scripts/ablation-replace.mjs` in WRAP mode. Each anchor hit exactly
once and moved 1 to 0. The blob changed, and each restore was proven:
blob equal to HEAD, and `git diff HEAD` empty. The control run before
them was 26 of 26 passing in the new pin.
| Guard removed | Pin result | What went red |
|---|---|---|
| The hold: `heldInputKey(fresh) === null` replaced by `true`, so every
preset is written at once | 6 failed, 20 passed | the three held
presets' spec parse; "Run a flow" is held; Delete drops it unsent; it
stays on its object |
| `!f.system` in the picklist choice | 2 failed, 24 passed | "Change"
asks for Status, not the platform's Sync State; the disabled row |
| `!f.readonly` | 1 failed, 25 passed | the disabled row: the read-only
Stage was taken |
| the param-field grammar check | 5 failed, 21 passed | `s`, `Status`,
`_status`, `2status`, `sta-tus` offered though the spec refuses them |
| the held list's object filter | 1 failed, 25 passed | a held action
shows on another object |
| the `api` row of `TARGET_INPUT_KEYS` | 1 failed, 25 passed | the `api`
parity case against `ActionSchema` |
The first round of the same six, on `ace58cbf1`, read identically.
## Gates
Every gate below ran on head `67e28442c` (`git rev-parse --short HEAD`
after the last commit). The branch is cut from `main` at `d7e9e9ab6`.
**Tests**, under the container's verify lock, with repo-root `pnpm exec
vitest run`:
- **The named set: 23 files, 348 tests passed.** It is the new pin, the
objectui#11820 pin, every test naming `ObjectActionsPanel`,
`ActionDefaultInspector` or `actionPresets`, the column-identity
ratchet, and the three tests that read the designer table's source.
- **The whole `studio-design` directory: 147 files, 961 tests passed.**
- Lock verdict: `VERDICT command-exit 0`.
**Type-check:** `pnpm --filter @object-ui/app-shell type-check` (`tsc
--noEmit && tsc -p tsconfig.test.json`) exited 0, after the dependency
closure was built under the lock (`Tasks: 28 successful, 28 total`).
`--listFiles` on the test project lists the new pin, the objectui#11820
pin and `actionPresets.ts`.
**Lint:** `pnpm exec eslint` on the 5 touched TS files: 0 errors, 0
warnings. The first commit drew two
`react-hooks/preserve-manual-memoization` warnings; the second commit
removed them.
**Other gates**, each with its own verdict line:
- `check:i18n-keys`: 0. "Every in-scope call-site key resolves against
the en pack …"
- `check:i18n-designer-parity`: 0. "Every en row has a zh row, and every
shared row carries the same placeholders."
- `check:i18n-drift`: 0. "No en value changed in this range."
- `check:i18n-dead-keys`, `check:changeset-claims`,
`check:pending-changeset-literals`, `check:unreferenced-sources`,
`check:spec-symbols`, `check:esm-specifiers`,
`check:vi-mock-specifiers`, `check:vi-mock-inherit`,
`check:vi-mock-override-shape`, `check:test-path-roots`,
`check:handler-key-reads`, `check:metadata-write-doors`: all 0.
- `check-changeset-presence`: 0. "5 source file(s) of 1 released
package(s) changed, and this change declares 1 changeset(s):
.changeset/11861-action-presets.md."
- `check-changeset-no-major`: 0.
- `check:new-line-citations`: 0. "0 new citation(s)".
- `check:control-bytes`: 0.
**Not measured:**
- `check:eager-locale-catalogues` and the eager-closure budget.
Prerequisite not met: no console build here. CI's Bundle Analysis owns
both.
- A real-browser pass. There is no objectstack backend serving Studio in
this container; the behaviour is pinned in happy-dom with the real
action editor.
## Docs-driven rule
The Actions row of `content/docs/guide/console.md` does not describe
**New** today, and that file is outside this claim's file list, so it is
not edited here. Suggested sentence to append to that row: "The **New**
menu opens on common actions in plain words (*Change a picklist field*,
*Run a flow*, *Open a web page*, *Open a page in a dialog*); a flow,
web-page or page action is saved once it has its target. The blank
action is under **Advanced**." The `packages/app-shell/README.md` does
not describe this menu.
## First-load bytes
The new designer-table rows are eager, as objectui#11937 measured, and
the PM's note put the headroom at about 1.0 KB. So the copy is minimal:
**10 new keys** (en and zh), down from 15 in the first commit.
- *Advanced* reuses the Validations menu's own row
(`engine.studio.rules.advanced`).
- The blank action keeps its existing name row
(`engine.studio.actions.newLabel`).
- The three presets that wait for a target carry no description line.
Their labels say what they do, and the held line names the input once
picked.
- The held line itself reuses `engine.studio.held.line`. The input names
reuse the editor's own target-label rows.
As a local proxy, `gzip -9` of `metadata-admin/i18n.ts` grows by 322 B
against the merge-base. That figure includes one source comment, which
the build strips. The console build was not run locally, so **the budget
bot's reading on this PR is the measurement**. If it reads over the
ceiling, this PR stays a draft.
## Acceptance notes
- These are read-only observations, not filed (carrier: none for both):
- The action editor's **Type** control writes `{ type }` alone.
Switching an existing action to `url`, `flow`, `modal`, `form` or `api`
composes a draft that `ActionSchema` refuses at `target`, and the Data
pillar's hold (objectui#11786) does not cover actions. Measured only
through the spec; not through a public entry.
- The editor's `form` target picker lists `view` metadata names. Whether
those are form view names in the spec's dotted form is unverified.
- No focus move into the editor's target input after a held preset. The
Validations hold focuses its own condition group, but here the input
belongs to the registered editor, and the held line names it instead.
Session: `https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU`
---
_Generated by [Claude
Code](https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0ab2b98 commit d8be368
6 files changed
Lines changed: 787 additions & 23 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3337 | 3337 | | |
3338 | 3338 | | |
3339 | 3339 | | |
| 3340 | + | |
| 3341 | + | |
| 3342 | + | |
| 3343 | + | |
| 3344 | + | |
| 3345 | + | |
| 3346 | + | |
| 3347 | + | |
| 3348 | + | |
| 3349 | + | |
| 3350 | + | |
3340 | 3351 | | |
3341 | 3352 | | |
3342 | 3353 | | |
| |||
6463 | 6474 | | |
6464 | 6475 | | |
6465 | 6476 | | |
| 6477 | + | |
| 6478 | + | |
| 6479 | + | |
| 6480 | + | |
| 6481 | + | |
| 6482 | + | |
| 6483 | + | |
| 6484 | + | |
| 6485 | + | |
| 6486 | + | |
6466 | 6487 | | |
6467 | 6488 | | |
6468 | 6489 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
| 49 | + | |
48 | 50 | | |
| 51 | + | |
| 52 | + | |
49 | 53 | | |
50 | 54 | | |
51 | 55 | | |
| |||
0 commit comments