Skip to content

Commit fbe344b

Browse files
feat(spec)!: the action blocks spell the destructive variant danger, the word ActionSchema.variant uses (#26966)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} --> _Requested by **Yibo Wang** · [project thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBQfjnJWXwANqb5TscsSvrHR)_ Fixes #26779 Clause-②: no (narrowing) Metadata-protocol: narrowed Skills-docs: follow-up owed — skills/objectui/evals/ (`ui/content/docs/guide/layout.md` now teaches `variant: 'danger'` on an `action:button`; `check:doc-json` and `page-width-padding-refusal-11318` judge that fence against the rows, but no eval exercises an action block's variant) ## 维护者速读 - 改了什么:四个 action 区块(`action:button` / `action:icon` / `action:group` / `action:menu` 及其成员)的 `variant` 把 `destructive` 改名为 `danger`,与 `ActionSchema.variant` 同一个拼法;旧值保存时拒收并给出改名提示,存量页面由 ADR-0087 转换自动改写。 - 为什么改:同一个危险动作在声明里写 `danger`、在区块里写 `destructive`,两套方言让每个渲染器都要翻译一次;协议只留一个拼法(分诊裁决 6448425754)。 - 风险与回滚:作者写的 `variant: 'destructive'` 区块在下次保存时被拒(收窄,带迁移处方);存量 `page` 行在加载时被转换改写。回滚 = revert 本 PR。 - 先看:`packages/spec/src/ui/component.zod.ts`(`ACTION_BLOCK_VARIANTS` 与 `actionBlockVariant`)、`packages/spec/src/conversions/registry.ts`(`pageActionBlockVariantDestructiveToDanger`)、`ui/packages/components/src/renderers/action/runner-variant.ts`。 - 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run src/conversions/page-action-block-variant-destructive-to-danger.test.ts`,预期 35 个用例全绿(六行接受 `danger`、拒收 `destructive` 并给改名提示、存量行被改写)。 - 需要你做的:无,席位落地。 ## What changes **Spec (`packages/spec/src/ui/component.zod.ts`).** The five rows that read `BUTTON_PRIMITIVE_VARIANTS` (`ActionButtonPropsSchema`, `ActionIconPropsSchema`, the shared group/menu member shape, `ActionGroupPropsSchema`, `ActionMenuPropsSchema`) now read `ACTION_BLOCK_VARIANTS = default | danger | outline | secondary | ghost | link`, plus `primary` on the three rows that took it before. `BUTTON_PRIMITIVE_VARIANTS` had no other reader on `main` `8074feb6`, so it is replaced, not kept beside. Each row builds its enum through `actionBlockVariant(subject, extra)`, whose error map answers `destructive` with the rename onto `danger` (naming the key the author wrote) and leaves every other miss to the enum's own message. That is the established enum-value retirement shape (`FORM_VIEW_LAYOUT_RETIRED`, `OBJECT_FORM_LAYOUT_RETIRED`); it adds no `.refine()`, so no dropped-refinements row. The `.describe()` texts say `danger` and what it does. **Data at rest.** ADR-0087 D2 conversion `page-action-block-variant-destructive-to-danger` (`toMajor: 18`, `retiredFromLoadPath: true`, `retiredAfter: '18.2.0'`, order 77). It rewrites `properties.variant` on the four block nodes and `variant` on each `action:group` / `action:menu` member, at every position `mapPageComponents` visits. It also visits `action:bar`, renaming the bar's own `properties.variant` and the `variant` of each `actions` / `systemActions` member. The bar draws its members through the block renderers with `member.variant || properties.variant`, so both spellings would reach a renderer, and the bar has no props row to refuse `destructive` at save (round 3, after the delta review `6464021995`). Every other component (`element:button` keeps the primitive's `destructive`) and every other value is left as stored. D3 entry `ui-page-action-block-variant-destructive-refused`. One JOIN row in `retired-key-removal-version.pin.test.ts` (six refusals; the pin derives `@objectstack/spec 18` and `--from 17` from `retiredAfter`). **ui renderers.** `runner-variant.ts` now holds two closed maps and neither reads `destructive`: `toRunnerVariant` forwards `danger | primary | secondary | ghost | link` as they are (and nothing for `default` / `outline`), and `toButtonVariant` draws `danger` as the primitive's `destructive` and `primary` as `default`. The four renderers draw through `toButtonVariant`, and the group/menu dropdown items paint red on `danger`. The designer inputs of `action:button` and `action:icon` list `danger` in place of `destructive`, and `sdui.manifest.json` is regenerated (two enum values). `UIActionSchema.variant` follows the rows by reference, so its doc comment is rewritten. **The #26776 bridge is removed, not kept.** It existed because stored and authored blocks said `destructive` while the runner reads `danger`. After this PR the rows refuse `destructive` at save, a page's next save meets the component-props gate, and every data-at-rest seam (`applyConversionsToStoredItem` for stored `page` rows, the artifact door inside its declared-floor window, `os migrate meta --write` for sources) replays the conversion, retired entries included. So no stored page reaches a renderer with `destructive`, and AGENTS.md Prime Directive 12 leaves no room for a consumer-side alias kept "just in case". **Authored values converted.** No `examples/**`, `apps/**`, `packages/platform-objects/**`, `skills/**` or `content/docs/**` source writes `variant: 'destructive'` on an action block (measured with a `git grep` over `variant` + `destructive` at `8074feb6`; every hit there is a React `Button`/`Alert` primitive or a `type: 'button'` component). The authored ones are in `ui/`: the record detail views' code-built Delete entry for the `⋯` menu (`RecordDetailView.tsx`, `plugin-detail` `DetailView.tsx`), the `action-bar.tsx` doc example, and the test fixtures below. **VS Code extension.** `ui/packages/vscode-extension/src/providers/SchemaValidator.ts` judges the action block variants against the block rows (`danger` accepted, `destructive` answered with the rename; `primary` on the rows that take it). The `button` component's list, which is the Button primitive's and keeps `destructive`, is untouched. The package is private, so it needs no changeset. **Changeset** `.changeset/26779-action-block-variant-danger.md`: `@objectstack/spec`, `@object-ui/components`, `@object-ui/types`, `@object-ui/plugin-detail` minor, with the FROM → TO prescription, both declaration lines and the `registered` ADR-0087 marker, plus a paragraph that supersedes the variant sentences of the pending #26665 entry. ## Tests - New `packages/spec/src/conversions/page-action-block-variant-destructive-to-danger.test.ts` (35 cases): the six rows accept `danger`; refuse `destructive` with the rename at `variant` / `actions.0.variant`; CONTROL, another miss keeps the enum message; the stored-row seam rewrites all four nodes and a member alone (copy-on-write for the other); six controls left as stored; canonical page returns the same reference; idempotent; the authoring funnel does not replay it. - Updated fixtures to the new spelling: spec `component-report-items-action-members-typed.pin.test.ts` (two members), ui `action-group-menu-inputs-11168.test.tsx` (two members, same assertions: `bg-destructive` and `text-destructive`), `action-variant-confirm-26665.test.tsx` (the four blocks confirm through the destructive button on `danger`; new `toButtonVariant` rows; CONTROL rows that `destructive` is no longer read), and one `pins:` sentence in `registry-inputs-spec-parity.test.ts`. ## Acceptance notes - **Claim wording.** The claim asks for "an `aliases` prescription". `strictObject`'s `aliases` maps unknown KEYS to known ones; a renamed VALUE has no key to alias, so the prescription lives in the enum's error map, the shape the other enum-value retirements use. - **File surface beyond the claim, each forced:** `RecordDetailView.tsx` and `plugin-detail` `DetailView.tsx` (their Delete entry would lose its red row once the menus paint on `danger`), `@object-ui/types` `ui-action.ts` (doc comment of the by-reference type), `sdui.manifest.json` + `scripts/sdui-manifest.record.json` (the registry-change regeneration; the record's `objectuiWorkspaceVersion` moves 18.1.0 → 18.2.0 because main's record trailed the version label), the D3 entry and the JOIN row (both red without them, as on the locale retirement). - **Unchanged on purpose.** The page-header tests still give the host's `sys_delete` fixture `variant: 'destructive'`: the header judges destructiveness by `variant === 'danger' || name === 'sys_delete'`, and that fixture is the by-name control. `ActionPreview.tsx` previews an `ActionSchema`, not a block. - **Unreleased #26665 changeset.** `.changeset/26665-action-block-destructive-confirm.md` (not yet released) describes the bridge this PR removes ("`danger` stays refused there"). It is left byte-identical to the merge base, because editing another card's pending note is the DELIBERATE CORRECTION class that keeps `check-empty-changeset` red until a person confirms it. Instead, this PR's changeset carries a paragraph that explicitly replaces that entry's variant sentences in the same release and keeps its confirm-dialog behaviour (seat 1 ruling). - **Not measurable from here:** stored deployed rows and hotcrm sources. The conversion covers stored `page` rows and built artifacts in their window; a hotcrm source with a `destructive` block meets the refusal on its next `os validate`. ## Verification Readings on the pre-merge head `22846fae` unless marked; `main` was then merged (`17ecbb46`, 28 commits, none touching a file this PR edits) and the spec artefacts and spec tests below were re-run on the merged head. <details><summary>Re-measure before any edit (main 8074feb)</summary> A `safeParse` probe over the six rows: `action:button`, `action:icon`, `action:group`, the group member, `action:menu` and the menu member each REFUSE `danger` ("Invalid option: expected one of default|destructive|outline|secondary|ghost|link…") and ACCEPT `destructive`. The premise held on all six. `BUTTON_PRIMITIVE_VARIANTS` had five readers, all action block rows, and no other. </details> <details><summary>Spec</summary> - `pnpm --filter @objectstack/spec build`, then `check:generated`: 13/14 up to date, `check:docs` stale; `--fix` rewrote `content/docs/references/ui/component.mdx`, then 14/14 up to date. On the merged head `17ecbb46`: build + `check:generated`, all 14 up to date, nothing to commit. - `pnpm --filter @objectstack/spec typecheck`: exit 0. `pnpm --filter @objectstack/spec test`: 739 files, 22139 tests passed (1 todo). Lock VERDICT command-exit 0. - Merged head: `vitest run src/conversions src/migrations src/shared/retired-key-removal-version.pin.test.ts src/ui/component-report-items-action-members-typed.pin.test.ts`: 29 files, 1054 tests passed. </details> <details><summary>ui</summary> - Closure build `--filter '@object-ui/app-shell^...' --filter '@object-ui/plugin-detail^...' --filter @objectstack/formula --filter @objectstack/client`: VERDICT command-exit 0. - `vitest run packages/components/src/renderers/action` + the three `page-header-*` tests + `apps/console/src/__tests__/registry-inputs-spec-parity.test.ts`: 40 files, 921 tests passed. - `type-check` on `@object-ui/types`, `@object-ui/components`, `@object-ui/plugin-detail`, `@object-ui/app-shell`: all four Done, VERDICT command-exit 0. - Reverse check of the by-reference type, against the rebuilt spec `.d.ts`: a scratch file assigning `'destructive'` and `'danger'` to `UIActionSchema['variant']` reds only the first (`TS2322: Type '"destructive"' is not assignable to type '"default" | "link" | "primary" | "secondary" | "danger" | "ghost" | "outline" | undefined'`); the file was then deleted. - `node scripts/gen-sdui-manifest-node.mjs`: 106 components; the diff is the two `destructive` → `danger` input enum values. </details> <details><summary>Derived gates and dogfood</summary> - `dispatch-gates.mjs --commands`: 116 families. Run serially: 114 exit 0, `spec check:skill-examples` and `check:dual-build-cjs-loads` exit 3 (prerequisite not built). After the dogfood closure build both exit 0 (267 prose examples type-check; dual-build floors met). `--ran` reconciliation: 116 derived, 116 accounted for. - Dogfood (`--filter '@objectstack/dogfood^...'` build, then `pnpm --filter @objectstack/dogfood test`, one lock): VERDICT command-exit 1. Test Files 1 failed, 339 passed, 3 skipped (343); Tests 2 failed, 2752 passed, 20 skipped (2774). Both failures are in `test/packaged-action-disabled-dispatch-live.dogfood.test.ts` (acceptance 0 and acceptance 5/2). Run alone on the same head it passes 8/8, the load flake already recorded for that file; this diff touches no action-disable path. - `node scripts/check-nul-bytes.mjs`: exit 0. No exported symbol is renamed or removed (`BUTTON_PRIMITIVE_VARIANTS` was module-private), so `check-platform-checklist` was not owed. </details> ## Human Minimum Each item is one rule from [CONTRIBUTING.md › Human Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum), which links the AGENTS.md text that states it. - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. - [x] 2. Every name I added uses the casing for its kind. - [ ] 3. No formatter ran; `pnpm lint` passes. (No formatter ran. `pnpm lint` is CI's run.) - [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_01WGufzeYWDZN6LkXji5ziLF --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8d79bbd commit fbe344b

24 files changed

Lines changed: 734 additions & 93 deletions
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@object-ui/components': minor
4+
'@object-ui/types': minor
5+
'@object-ui/plugin-detail': minor
6+
---
7+
8+
feat(spec)!: the action blocks spell the destructive variant `danger`, the word `ActionSchema.variant` uses
9+
10+
An action's variant was spelled in two dialects. `ActionSchema.variant`, the declaration the action runner dispatches, takes `primary | secondary | danger | ghost | link`. The four action blocks (`action:button`, `action:icon`, `action:group`, `action:menu`) and the group/menu members took the Button primitive's `default | destructive | outline | secondary | ghost | link` (plus `primary` on the button, the icon and the members) and refused `danger`. So one destructive action was `danger` as a declaration and `destructive` as a block, and each block renderer translated between the two.
11+
12+
The block rows now take `danger` in place of `destructive`: `default | danger | outline | secondary | ghost | link`, plus `primary` where it was accepted before. `danger` draws the destructive button (a red item in a dropdown or `action:menu`) and asks a `confirmText` question through the destructive confirm button. `destructive` is refused at `variant` with the rename. Other components that draw the Button primitive directly (`element:button`, toasts, alerts, badges and the like) keep `destructive`, the primitive's own word, and are not touched.
13+
14+
The four block renderers forward `danger` to the runner as it is and draw it as the primitive's `destructive`. They no longer read `destructive`: stored pages are rewritten by the conversion below, so no renderer keeps a bridge for the old word. `UIActionSchema.variant` (`@object-ui/types`) follows the rows by reference. The record detail views' Delete entry in the `⋯` menu is spelled `danger` too.
15+
16+
This replaces the variant sentences of the #26665 entry in this release: the action blocks no longer translate `destructive` to `danger` at their forward to the runner, and `danger` is the spelling they accept (`UIActionSchema.variant` included). That entry's confirm-dialog behaviour is unchanged: a destructive block with a `confirmText` asks through the destructive confirm button, now when it is declared `variant: 'danger'`.
17+
18+
Stored rows and older built artifacts: the ADR-0087 D2 conversion `page-action-block-variant-destructive-to-danger` rewrites `variant: 'destructive'` to `'danger'` on the four blocks' `properties.variant` and on each `action:group` / `action:menu` member's `variant`, at every position a page nests a component (regions, slots, nested containers). It renames an `action:bar`'s own `variant` and the `variant` of each `actions` / `systemActions` member as well. The bar has no props row, so nothing refuses `destructive` on it, but it draws each member through the block renderers, which no longer read that word. A stored `page` row is rewritten when it is rehydrated, so it keeps drawing the destructive button and confirming through the destructive confirm button. The conversion is retired from the load path, so a source that still writes `destructive` on a block is refused, not rewritten. The D3 entry `ui-page-action-block-variant-destructive-refused` carries the judgment for `os migrate meta`.
19+
20+
Clause-②: no (narrowing) — the action block rows refuse `destructive`, a value they used to accept. `danger` takes its place on the same rows.
21+
22+
Metadata-protocol: narrowed (`variant: 'destructive'` is refused on the action blocks and their members; write `danger`)
23+
24+
<!-- adr-0087: registered page-action-block-variant-destructive-to-danger, ui-page-action-block-variant-destructive-refused -->
25+
26+
**BREAKING** for an author who wrote `variant: 'destructive'` on an action block.
27+
28+
**`destructive` → `danger`.**
29+
30+
- **Before.** `{ type: 'action:button', properties: { label: 'Purge', actionType: 'script', variant: 'destructive' } }` parsed; `variant: 'danger'` was refused.
31+
- **Now.** `destructive` is refused at `variant` (or at `actions.N.variant` on a group or menu member) with a message naming the rename.
32+
- **Fix.** Write `variant: 'danger'` on the block or member. Nothing else changes: it draws the same destructive button. `os migrate meta --from 17 --write` applies the rename to your sources.

‎content/docs/references/ui/component.mdx‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const result = ActionButtonPropsSchema.parse(data);
3232
| **label** | `string` | optional | Button text. A literal string, placed as-is — localize through the translation bundle entry for this component id, not an inline locale map |
3333
| **icon** | `string` | optional | Lucide icon name drawn left of the label, resolved through the shared action-icon resolver (an unknown name draws no icon) |
3434
| **actionType** | `string` | optional | Executor the action runner dispatches to — built in: `script`, `url`, `modal`, `flow`, `api`, `form`; a handler registered under another name is dispatched too. `navigation`, the runner's alias of `url`, is refused: write `url`. Write it here, not as `type`: on a page component `type` is the component itself |
35-
| **variant** | `Enum<'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Button variant — the Button primitive's vocabulary, plus `primary`, which renders as `default` (renderer default: `default`) |
35+
| **variant** | `Enum<'default' \| 'danger' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Button variant — the Button primitive's vocabulary with its destructive arm spelled `danger`, the action runner's word, plus `primary`, which renders as `default` (renderer default: `default`). `danger` draws the destructive button and asks a `confirmText` question through the destructive confirm button |
3636
| **size** | `Enum<'default' \| 'sm' \| 'lg' \| 'icon' \| 'md'>` | optional | Button size — the Button primitive's vocabulary, plus `md`, which renders as `default` (renderer default: `default`) |
3737
| **visible** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Visibility predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope, evaluated against the row the host binds; the button is not rendered when it is FALSE, and a predicate that fails to evaluate hides it. Omit for always-visible |
3838
| **disabled** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Disabled predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope; the button is shown but cannot be pressed while it is TRUE. Omit for never-disabled |
@@ -78,7 +78,7 @@ const result = ActionButtonPropsSchema.parse(data);
7878
| **location** | `Enum<'list_toolbar' \| 'list_item' \| 'record_header' \| 'record_more' \| 'record_related' \| 'record_section'>` | optional | Render only the members whose `locations` include this location. Omit to render every member |
7979
| **label** | `string` | optional | Dropdown trigger text (renderer default: `Actions`). Inline mode renders no group label. A literal string — localize through the translation bundle entry for this component id |
8080
| **icon** | `string` | optional | Lucide icon name on the dropdown trigger. Inline mode renders no group icon |
81-
| **variant** | `Enum<'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'>` | optional | Button variant for the dropdown trigger and for every inline member that sets none — the Button primitive's vocabulary (renderer default: `outline`) |
81+
| **variant** | `Enum<'default' \| 'danger' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'>` | optional | Button variant for the dropdown trigger and for every inline member that sets none — the Button primitive's vocabulary with its destructive arm spelled `danger`, the action runner's word (renderer default: `outline`) |
8282
| **size** | `Enum<'default' \| 'sm' \| 'lg' \| 'icon'>` | optional | Button size for the dropdown trigger and for every inline member that sets none — the Button primitive's vocabulary |
8383
| **visible** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Visibility predicate for the whole group — a boolean, a CEL string, or a `{ dialect, source }` envelope, evaluated against the row the host binds. Omit for always-visible |
8484

@@ -90,7 +90,7 @@ const result = ActionButtonPropsSchema.parse(data);
9090
| **label** | `string` | optional | The item's text. A literal string, placed as-is — localize through the translation bundle entry for this component id |
9191
| **icon** | `string` | optional | Lucide icon name drawn before the label, resolved through the shared action-icon resolver (an unknown name draws no icon) |
9292
| **type** | `string` | optional | Executor the action runner dispatches to — built in: `script`, `url`, `modal`, `flow`, `api`, `form`; a handler registered under another name is dispatched too. `navigation`, the runner's alias of `url`, is refused: write `url`. A member is an action entry, so its executor is `type` (on an `action:button` node it is `actionType`) |
93-
| **variant** | `Enum<'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Item variant — the Button primitive's vocabulary, plus `primary` (drawn as `default`). An inline `action:group` button draws it (falling back to the group's `variant`); a dropdown or `action:menu` item draws `destructive` red and every other variant plainly |
93+
| **variant** | `Enum<'default' \| 'danger' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Item variant — the Button primitive's vocabulary with its destructive arm spelled `danger`, the action runner's word, plus `primary` (drawn as `default`). An inline `action:group` button draws it (falling back to the group's `variant`); a dropdown or `action:menu` item draws `danger` red and every other variant plainly. `danger` asks a `confirmText` question through the destructive confirm button |
9494
| **visible** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Visibility predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope, evaluated against the row the host binds; the item is not drawn when it is FALSE, and a predicate that fails to evaluate hides it. Omit for always-visible |
9595
| **disabled** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Disabled predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope; the item is drawn but cannot be pressed while it is TRUE, and a predicate that fails to evaluate disables it. Omit for never-disabled |
9696
| **tags** | `Enum<'separator-before'>[]` | optional | Item tags — `separator-before` draws a divider above the item in a dropdown or menu (not above the first item); no other tag is drawn |
@@ -127,7 +127,7 @@ const result = ActionButtonPropsSchema.parse(data);
127127
| **label** | `string` | optional | Accessible label and tooltip text (and its first letter stands in when no icon resolves). A literal string — localize through the translation bundle entry for this component id |
128128
| **icon** | `string` | optional | Lucide icon name, resolved through the shared action-icon resolver (renderer registration default `play`) |
129129
| **actionType** | `string` | optional | Executor the action runner dispatches to — built in: `script`, `url`, `modal`, `flow`, `api`, `form`; a handler registered under another name is dispatched too. `navigation`, the runner's alias of `url`, is refused: write `url`. Write it here, not as `type` |
130-
| **variant** | `Enum<'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Button variant — the Button primitive's vocabulary, plus `primary`, which renders as `default` (renderer default: `ghost`) |
130+
| **variant** | `Enum<'default' \| 'danger' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Button variant — the Button primitive's vocabulary with its destructive arm spelled `danger`, the action runner's word, plus `primary`, which renders as `default` (renderer default: `ghost`). `danger` draws the destructive button and asks a `confirmText` question through the destructive confirm button |
131131
| **visible** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Visibility predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope, evaluated against the row the host binds; the icon is not rendered when it is FALSE. Omit for always-visible |
132132
| **disabled** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Disabled predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope; the icon is shown but cannot be pressed while it is TRUE. Omit for never-disabled |
133133
| **description** | `string` | optional | Action description — the tooltip text when there is no `label`, and forwarded to the runner for the parameter dialog |
@@ -168,7 +168,7 @@ const result = ActionButtonPropsSchema.parse(data);
168168
| **actions** | `{ name?: string; label?: string; icon?: string; type?: string; … }[]` | optional | The menu's actions, in order — each an action object the menu draws and runs itself (`name`, `label`, `icon`, `type`, `target`, `visible`, `disabled`, `tags`, …); a member's executor is its `type` |
169169
| **label** | `string` | optional | Trigger text and accessible label; omit for an icon-only trigger labelled "More actions". A literal string — localize through the translation bundle entry for this component id |
170170
| **icon** | `string` | optional | Lucide icon name on the trigger (renderer default: the horizontal ellipsis) |
171-
| **variant** | `Enum<'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'>` | optional | Trigger button variant — the Button primitive's vocabulary (renderer default: `ghost`) |
171+
| **variant** | `Enum<'default' \| 'danger' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'>` | optional | Trigger button variant — the Button primitive's vocabulary with its destructive arm spelled `danger`, the action runner's word (renderer default: `ghost`) |
172172
| **size** | `Enum<'default' \| 'sm' \| 'lg' \| 'icon'>` | optional | Trigger button size — the Button primitive's vocabulary (renderer default: `icon`) |
173173
| **visible** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Visibility predicate for the whole menu — a boolean, a CEL string, or a `{ dialect, source }` envelope, evaluated against the row the host binds; a predicate that fails to evaluate hides it. Omit for always-visible |
174174

@@ -180,7 +180,7 @@ const result = ActionButtonPropsSchema.parse(data);
180180
| **label** | `string` | optional | The item's text. A literal string, placed as-is — localize through the translation bundle entry for this component id |
181181
| **icon** | `string` | optional | Lucide icon name drawn before the label, resolved through the shared action-icon resolver (an unknown name draws no icon) |
182182
| **type** | `string` | optional | Executor the action runner dispatches to — built in: `script`, `url`, `modal`, `flow`, `api`, `form`; a handler registered under another name is dispatched too. `navigation`, the runner's alias of `url`, is refused: write `url`. A member is an action entry, so its executor is `type` (on an `action:button` node it is `actionType`) |
183-
| **variant** | `Enum<'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Item variant — the Button primitive's vocabulary, plus `primary` (drawn as `default`). An inline `action:group` button draws it (falling back to the group's `variant`); a dropdown or `action:menu` item draws `destructive` red and every other variant plainly |
183+
| **variant** | `Enum<'default' \| 'danger' \| 'outline' \| 'secondary' \| 'ghost' \| 'link' \| 'primary'>` | optional | Item variant — the Button primitive's vocabulary with its destructive arm spelled `danger`, the action runner's word, plus `primary` (drawn as `default`). An inline `action:group` button draws it (falling back to the group's `variant`); a dropdown or `action:menu` item draws `danger` red and every other variant plainly. `danger` asks a `confirmText` question through the destructive confirm button |
184184
| **visible** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Visibility predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope, evaluated against the row the host binds; the item is not drawn when it is FALSE, and a predicate that fails to evaluate hides it. Omit for always-visible |
185185
| **disabled** | `boolean \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Disabled predicate — a boolean, a CEL string, or a `{ dialect, source }` envelope; the item is drawn but cannot be pressed while it is TRUE, and a predicate that fails to evaluate disables it. Omit for never-disabled |
186186
| **tags** | `Enum<'separator-before'>[]` | optional | Item tags — `separator-before` draws a divider above the item in a dropdown or menu (not above the first item); no other tag is drawn |

0 commit comments

Comments
 (0)