Skip to main content

nocobase-ui-builder

NocoBase 2 only; never use in a NocoBase 3 project. INTERNAL CONTINUATION ONLY. Never select this skill from a raw user UI request. Use it only after nocobase-portal-manage has already resolved exactly one enabled Portal and confirmed portalType=no-code, or has proved the runtime has capabilities.multiPortal === false. Portal count or `isDefault` marker alone never enables this skill. It authors NocoBase Modern UI pages, blocks, menu items, fields, actions, layouts, reactions, and AI employee placements through backend flow-surfaces via nb api. Unresolved UI requests go to nocobase-portal-manage first; a sole AI Portal is handed off automatically for source-code implementation. Hand off AI employee lifecycle work to nocobase-ai-employee and explicit DSL, YAML, git, or cli push workflows to nocobase-dsl-reconciler. It does not handle ACL, data modeling, workflow orchestration, browser reproduction, page error postmortems, Portal lifecycle, AI Portal source-code UI, or non-Modern page navigation.

Source facts

Repository
Agents-Store/opencode-plugins
Last source activity
October 3, 2026 at 23:16
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
100 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
nocobase-ui-builder
description
NocoBase 2 only; never use in a NocoBase 3 project. INTERNAL CONTINUATION ONLY. Never select this skill from a raw user UI request. Use it only after nocobase-portal-manage has already resolved exactly one enabled Portal and confirmed portalType=no-code, or has proved the runtime has capabilities.multiPortal === false. Portal count or `isDefault` marker alone never enables this skill. It authors NocoBase Modern UI pages, blocks, menu items, fields, actions, layouts, reactions, and AI employee placements through backend flow-surfaces via nb api. Unresolved UI requests go to nocobase-portal-manage first; a sole AI Portal is handed off automatically for source-code implementation. Hand off AI employee lifecycle work to nocobase-ai-employee and explicit DSL, YAML, git, or cli push workflows to nocobase-dsl-reconciler. It does not handle ACL, data modeling, workflow orchestration, browser reproduction, page error postmortems, Portal lifecycle, AI Portal source-code UI, or non-Modern page navigation.
# Goal - Agent-facing write path is `nb api flow-surfaces <action>` with the raw business payload. - Backend `flow-surfaces` is the authoring compiler: it normalizes compatible payloads and returns aggregate `errors[]` for hard validation failures. - Aggregate authoring `errors[]` is a full repair batch. Use the backend-provided `errorCount`, per-error `path`/`ruleId`/`details`, and `agentInstruction` to repair every listed issue in one payload revision, then retry the same write. Portal navigation errors in [Required Portal Preflight](#required-portal-preflight) are target or implementation-path failures, not payload-repair batches. - Use `nb` as the only public transport. If `nb` is missing or stale, report the blocked command/env state instead of switching transports. - Keep routing intent-first: open one matching quick-route doc first, not the whole directory. - When a quick route already matches, stay on it. Do not enumerate the skill directory just to rediscover docs. - When the task is a partial-match or handoff-only request, answer from this skill's scope boundary directly. Do not inspect runtime, scripts, or helper docs just to justify the handoff. - Treat one user request that spans several pages as ordered single-page runs. When multiple pages share the same `navigation.group.title`, serialize them: the first page resolves the title and captures `routeId`; later pages use that `routeId`, never title-only creation, and concurrent title-only shared-group creates are forbidden. - Whole-page navigation target rules live in [navigation-targets.md](./references/navigation-targets.md): a resolved no-code Portal requires exact `kind: "portal"` plus `routeName` discovery and `navigation.portalUid`; direct Admin/Mobile layout targeting is legacy-only when `capabilities.multiPortal === false`; duplicate same-title non-mobile groups require explicit `routeId`; shared title-only group creates must be serialized. # Mandatory Portal Manager Entry Before applying any other instruction in this skill, load and execute `nocobase-portal-manage` for the same user request and follow its Ordinary UI Build Workflow. Do not start a quick route, inspect UI Builder navigation targets, or make a `flow-surfaces` call first. - If the current request already carries a Portal Manager routing outcome, consume it and do not invoke Portal Manager again. This prevents a no-code dispatch back to UI Builder from looping. - A valid outcome is exactly one selected enabled Portal with its exact `name` and `portalType`, explicit legacy evidence that `capabilities.multiPortal === false`, or Portal Manager's verified legacy Flow Surfaces signature (Portal target discovery absent while core `apply-blueprint` and structured read capability are present). A carried `isDefault` marker is display/readback context only and must not substitute for selection. - `portalType === "no-code"`: continue this skill with the selected Portal context. - `portalType === "ai"`: leave this skill and continue the same request on Portal Manager's AI Portal source-code path without asking the user again. Portal Manager must pull the Portal first when the selected record/info has no local development path. - Zero enabled Portals, multiple unselected Portals, or a missing/unsupported type remain Portal Manager outcomes; do not bypass them inside UI Builder. # Router - Portal-first boundary: Mandatory Portal Manager Entry is the only entry to this router. Exactly one enabled Portal may be selected automatically, but count or default status alone is not admission: continue here only when Portal Manager reports that selected record's `portalType` is exactly `no-code`, or with explicit evidence that `capabilities.multiPortal === false`. A sole AI Portal exits this skill without a `flow-surfaces` write, then immediately continues the same request on Portal Manager's source-code implementation path; Portal Manager pulls the Portal first if no development path exists. Do not ask whether to use the AI Portal or create a no-code Portal. A missing or unsupported `portalType` returns to Portal Manager for resolution without falling back to Admin. - whole-page authoring goes through backend `applyBlueprint`, `nb api flow-surfaces apply-blueprint`, and [whole-page-quick.md](./references/whole-page-quick.md) - AI employee / AI assistant action authoring stays inside whole-page or localized `flow-surfaces` writes; read [ai-employee-actions.md](./references/ai-employee-actions.md) when the request mentions AI employee placement, AI analysis buttons, AI assistants, or AI task reconfiguration. Use `nocobase-ai-employee` first only when the request needs employee discovery, matching, creation, prompt/model/skill/tool configuration, or a lifecycle decision beyond binding an existing visible username. - Dashboard / KPI / overview routing: [dashboard-routing.md](./references/dashboard-routing.md) - Comments and record history route through public Flow Surfaces block types; read [comments.md](./references/blocks/comments.md) or [record-history.md](./references/blocks/record-history.md) when the request mentions comments, discussion threads, history, audit history, or change history - Calendar / schedule / event-view authoring uses `CalendarBlockModel`; read [calendar.md](./references/blocks/calendar.md) when the request mentions 日历, calendar, 排期, 日程, 事件视图, or 排班 - Menu, page-entry, `routeId`, `pageSchemaUid`, layout/workspace scope, and duplicate-page identity decisions use [navigation-targets.md](./references/navigation-targets.md). Visible desktop menu discovery uses `nb api resource list --resource 'desktopRoutes:listAccessible' --no-paginate -j`; do not try unavailable desktop-routes CLI families, `menus`, `catalog`, or `describe-surface` just to find menu items. - localized existing-surface edits go through backend actions such as `compose`, `configure`, `update-settings`, `add-*`, `move-*`, and `remove-*`, plus [local-edit-quick.md](./references/local-edit-quick.md) - localized existing-surface reaction work starts with `get-reaction-meta`, writes through `set*Rules`, and [reaction-quick.md](./references/reaction-quick.md); first-pass whole-page reactions stay in `reaction.items[]` with no live `get-reaction-meta`; artifact-only localized reaction drafts record the planned `get-reaction-meta` probe - partial-match or boundary-only requests go through [boundary-quick.md](./references/boundary-quick.md) first - After that route is clear, if template / reference / `copy` routing is truly in scope, read [template-quick.md](./references/template-quick.md) first and then [templates.md](./references/templates.md) for the full decision matrix. - Do not open [tool-shapes.md](./references/tool-shapes.md) until you are preparing a real nb body. - If the task involves JS `code`, `renderer: "js"`, `jsBlock`, `jsColumn`, `jsItem`, JS actions, charts, or `ctx.*` API questions, read [js.md](./references/js.md) first, then [js-surfaces/index.md](./references/js-surfaces/index.md), then [js-snippets/index.md](./references/js-snippets/index.md), and only then [js-reference-index.md](./references/js-reference-index.md). If that intent includes opening a popup / drawer / dialog / drilldown, also read [popup-openview.md](./references/patterns/popup-openview.md): resolve a template-first popup-capable FlowModel before writing JS. - Whenever a payload or RunJS body will explicitly author `defaultFilter`, `dataScope.filter`, `resource.setFilter()`, request `params.filter`, or any `{ path, operator, value }` condition, first load the `nocobase-utils` skill with topic `filter`, then read [Filter Condition Format](../nocobase-utils/references/filter/index.md) and inspect live metadata for the terminal field. Keep the UI Builder shape required by the host, but choose the operator only from the terminal field's frontend allowlist. Date comparisons use `$dateBefore`, `$dateAfter`, `$dateNotBefore`, or `$dateNotAfter`, never `$lt`, `$lte`, `$gt`, or `$gte`. - Before using a `flow-surfaces` action you have not used yet in the current task, run `nb api flow-surfaces <action> --help` when available. - The only local helper CLI kept by this skill is `nb-template-decision`. Invoke it through `node skills/nocobase-ui-builder/runtime/bin/nb-template-decision.mjs` from this repo root, or through the equivalent absolute path to this skill. Do not probe the bare helper name first. # Required Portal Preflight Complete this gate before any `flow-surfaces` mutation, including whole-page and localized writes: 1. Consume the current-request outcome produced by Mandatory Portal Manager Entry. If it is absent, return to that entry and execute Portal Manager before doing anything else in UI Builder. 2. If the current request has no Portal resolution, or multiple Portals remain unselected, stop without writing and keep the request in `nocobase-portal-manage`. 3. If exactly one enabled Portal was selected, require Portal Manager to report the selected record's `portalType`; the fact that only one Portal exists or that a Portal is marked `isDefault` is never sufficient by itself. 4. Continue UI Builder only when `portalType === "no-code"`. For exactly one Portal with `portalType === "ai"`, exit UI Builder and immediately continue the same build request on Portal Manager's source-code path: locate the selected Portal project, run `nb portal pull <portal>` first if the selected record/info has no development path, implement, and test there. The original build request already authorizes this handoff; do not ask the user to choose between the AI Portal and creating a no-code Portal, and do not stop the overall task merely because UI Builder exited. For a missing or other unsupported type, exit without writing and return to Portal Manager to choose a compatible skill or implementation path; never fall back to Admin. 5. For the confirmed no-code Portal, require a non-empty selected Portal `name`, then resolve a non-empty `navigation.portalUid` exactly as documented in [navigation-targets.md](./references/navigation-targets.md). A missing or ambiguous mapping stops without writing. 6. The legacy Admin/Mobile layout lane requires either explicit `capabilities.multiPortal === false` evidence or Portal Manager's verified legacy Flow Surfaces signature. A merely missing, true, failed, unauthorized, server-error, or unclear capability result is not legacy evidence. 7. Multi-portal whole-page create must include the resolved `navigation.portalUid`, omit `navigation.layoutUid`, and never retry against `admin-layout-model`. A mobile-backed no-code Portal still enters through `navigation.portalUid` and then follows the backend's mobile-root rules. Backend Portal navigation errors are stop/handoff signals, not aggregate authoring payload repairs: - `navigation-portal-type-unsupported`: stop the same-write retry, do not switch to Admin, and hand off to the selected AI Portal source path. - `navigation-portal-selection-required`: stop all writes and ask the user to select a Portal through Portal Manage. - `navigation-portal-not-found`: stop and ask the user to create or inspect the requested Portal through Portal Manage. - `navigation-admin-layout-not-portal-target`: rerun Portal resolution; do not alter the payload to continue writing to Admin. # Required Block Type Error Recovery - When the user requirement or semantic routing has selected `jsBlock`, keep that section as `jsBlock` after authoring errors. Repair the payload shape (`settings.code` or `assets.scripts.<key>.code` plus `block.script`) and retry the same block type. Do not replace it with `table`, `list`, `chart`, `actionPanel`, `gridCard`, `markdown`, or a deferred note just to bypass the error. - When the user requirement or semantic routing has selected `chart`, keep that section as `chart` after authoring errors. Repair the payload shape (`assets.charts.<key>.query`, `assets.charts.<key>.visual`, and `block.chart` for whole-page blueprints, or localized `settings.query/settings.visual`) and retry the same block type. Do not replace it with `table`, `list`, `jsBlock`, `actionPanel`, `gridCard`, `markdown`, or a deferred note just to bypass the error. - An aggregate `errors[]` response for a required `jsBlock` or `chart` means the current payload is invalid; it is not permission to change the required block type. Change block type only when the user changes the requirement or the backend explicitly says that block type is unsupported in the current container. - If a required `jsBlock` or `chart` still cannot be authored after the documented repair attempts, report that specific section as unfinished with the latest error evidence instead of claiming completion through a fallback block. # Hard Rules 1. After Required Portal Preflight succeeds, use `nb api flow-surfaces <action>` directly for nb writes. Send the raw business payload once; do not create wrapper envelopes, `cliBody`, or local helper output as a prerequisite. 2. For a normal single-page request, default to exactly one real tab. Do not add empty tabs or placeholder `markdown` / note / banner blocks unless the user asked for them. 3. Default blueprint `fields[]` entries to simple strings. Upgrade a field entry to an object only when `popup`, `target`, `renderer`, a field-specific `type`, or clear form behavior inferred from live field `description` is required. 4. For page authoring, field truth comes from live collection metadata. Prefer `nb api data-modeling collections get --filter-by-tk <collection> --appends fields -j`; if that command family is unavailable, use `nb api resource list --resource collections --filter '{"name":"<collection>"}' --appends fields -j`. Do not use `data-modeling fields list` / `nb api data-modeling collections fields list` as the authoring truth. Any field used in blueprint `fields[]` must have a non-empty `interface`. Treat collection field `description` as active form-authoring input: use agent/LLM semantic extraction for arbitrary languages, then emit only structured public behavior, optionally as normalized field metadata `descriptionBehavior.{settings,linkage}` before prepare. Clear static required hints become field `settings.required`, low-risk length / range / regex / count constraints become `settings.rules` or `settings.maxCount`, and unambiguous same-form conditional required/disabled/hidden hints become `reaction.items[]` on stable local form targets, with generated local keys materialized only when needed; for backend-generated add/edit popups, put the same behavior under target-scoped `defaults.collections.<collection>.formBehavior.addNew/edit`. When a conditional description names fields by localized UI label/title instead of field `name` (for example `状态` for `status` or `重点变化` for `highlights`), resolve those labels through live metadata and emit an explicit linkage rule; do not rely on backend fallback parsing. Map condition values through live option `value` / localized `label` metadata when available. Helper text such as `settings.extra` is not sufficient coverage for conditional required/disabled/hidden behavior, and such fields must not be marked `implemented` unless a real linkage rule targets the field. Leave ambiguous descriptions as helper text instead of guessing, and do not rely on the NocoBase backend to parse raw descriptions or on deterministic keyword lists for arbitrary-language coverage. For whole-page `applyBlueprint`, recompute the full involved collection set from live metadata on every draft and rebuild `defaults.collections` from scratch instead of reusing stale fragments. For every involved direct collection, emit or allow the backend to materialize `popups.view` / `popups.addNew` / `popups.edit` as stable `{ name, description }` descriptors, and let any `table` block pull that collection into `addNew` threshold evaluation even when the blueprint did not spell out an `addNew` opener. Keep `fieldGroups` and `formBehavior` collection-only on the target collection, and add `defaults.collections.<collection>.fieldGroups` only when one of those fixed generated popup scenes should still have more than 10 effective fields after scene filtering; otherwise let the backend materialize compatible defaults. Before finalizing generated defaults fieldGroups, run one compact self-review verdict (`approve` or `regenerate`) and regenerate at most once using the lowest practical reasoning / no-think mode. If the backend returns aggregate field-group errors, regenerate semantic groups from live metadata and retry once. For association fields, keep every involved relation scope on the same fixed `view` / `addNew` / `edit` trio under `defaults.collections.<sourceCollection>.popups.associations.<associationField>.<action>` with the same `{ name, description }` contract, keyed only by the first relation segment. Do not create per-association or relation-scoped `fieldGroups` / `formBehavior`. Never generate `defaults.blocks`, and never put `blocks`, `fields`, `fieldGroups`, or layout under `defaults.collections.*.popups`. When backend-generated add/edit candidate fields have a non-empty `description`, account for each described generated field either through structured `defaults.collections.<collection>.formBehavior.addNew/edit` output or through sibling `defaults.collections.<collection>.formBehaviorDescriptionReview.fields.<field> = { decision, reasonCode? }`. Use `decision: "implemented"` only when real coverage exists through structured `formBehavior` or applicable `reaction.items[]`; otherwise use `decision: "noUiBehavior"` or `"unsupported"` with a valid `reasonCode`. Do not send old `fields: string[]`, `hasTried`, `formBehavior: {}`, or `null` as no-op escape hatches; `null` review entries are only for generated candidates whose `description` is empty. 5. `layout` belongs only on `tabs[]` or inline `popup`, never on a block object. For `createForm`, `editForm`, and `details`, omit `fieldsLayout` by default and let backend authoring generate the inner grid: ordinary fields are two per row, `divider`, `richText`, and `vditor` fields get full-width rows from live collection metadata. For `filterForm`, prepare may still synthesize the compact three-per-row `fieldsLayout`. Use explicit `fieldsLayout` only when the blueprint must control the inner field grid directly. Omit page/popup `layout` only when that tab/popup has at most one non-filter block; otherwise explicit layout is required. When multiple non-filter blocks share the same tab/popup, each non-template-backed data block needs a title; template-backed blocks are exempt. A single non-filter block may omit its title unless the user explicitly asks for one; if a redundant single-scope data-block title is supplied, backend authoring strips the persisted block chrome title. For low-level `set-layout`, do not reuse public `{ rows: [[{ key, span }]] }` syntax: runtime `rows` is `Record<string, string[][]>`, each cell array stacks live child `uid`s, `[[uidA], [uidB]]` means two columns, and `[[uidA, uidB]]` means one stacked column. 6. For `createForm`, `editForm`, and `details`, once the block contains more than 10 real fields, use explicit `fieldGroups` instead of one flat `fields[]` list. Do not treat manual `divider` items as a substitute, and do not combine `fieldGroups` with `fieldsLayout`. 7. If clicking a shown record or relation record should open details, prefer a field popup. Use a button or action column only when the request explicitly asks for one. 8. Whole-page `navigation` decisions must follow [navigation-targets.md](./references/navigation-targets.md): duplicate same-title non-mobile groups require explicit `navigation.group.routeId`; mobile targets use root navigation and no group. Non-mobile page identity is target plus menu group routeId plus page title: the same group and same title may replace, while a different group, layout, or portal with the same title does not merge, reuse, or auto-replace another page. A selected no-code Portal always uses its exactly mapped `navigation.portalUid` and omits `navigation.layoutUid`; ignore navigation-target defaults, layout targets, and backing `layoutUid` values. Direct Admin/Mobile layout navigation is allowed only in the proven legacy lane. 9. In non-mobile `applyBlueprint create`, any newly created `navigation.group` and any top-level or second-level `navigation.item` must carry one valid Ant Design icon name. For mobile creates, `navigation.item.icon` must carry the page entry icon and no group icon is needed. 10. Shared title-only group creates are serialized: first non-mobile page may resolve/create by `navigation.group.title`; later pages in the same target layout must use the captured `navigation.group.routeId`. 11. `navigation.group.routeId` and desktop-route `id` are navigation locators only. When follow-up localized work or explicit inspection is needed after create/init or successful whole-page `applyBlueprint`, normalize to `pageSchemaUid` for page-level `flow-surfaces get`, and only use live `uid` values returned by `get` / `describe-surface` / create responses for `catalog`, `context`, `get-reaction-meta`, `compose`, `configure`, `add*`, or `remove*`. Never pass a desktop-route `id` as `target.uid`. For artifact-only locator handoffs, keep direct machine-readable fields `navigation.routeId`, `page.pageSchemaUid`, and `liveTargets[].uid`; when no live uid exists yet, use a non-empty placeholder string instead of `null`. 12. Before the first real whole-page `applyBlueprint`, complete Required Portal Preflight, make sure live collection/page/menu metadata used for planning is current, then call `nb api flow-surfaces apply-blueprint --body-file <payload>.json -j`. The backend resolves compatible defaults such as sorting aliases, height modes, popup defaults, field groups, title fields, navigation group title matches, template decisions, and omitted direct data-surface `defaultFilter` groups; explicit invalid default filters still return aggregate `errors[]`. If the backend returns aggregate authoring `errors[]`, repair all listed issues in one pass and retry the raw payload; do not fix only the first error and immediately call `applyBlueprint` again. The four Portal navigation errors above stop this loop. 13. Update action field assignment uses only public `settings.assignValues`. `bulkUpdate` is a collection action under block `actions`; `updateRecord` is a record action under `recordActions`. `assignValues` must be one plain object keyed by fields from the host collection metadata; `{}` is valid and clears assignment values. When a requested action is described as 图标按钮 / 仅图标 / icon button, preserve that visual intent by setting public `settings.icon` and `settings.iconOnly: true`. Submit/update-record workflow binding uses only public `settings.triggerWorkflows` or `configure.changes.triggerWorkflows`; each row is `{ workflowKey, context? }`, `[]` clears bindings, and `null` is invalid. Do not use `add-fields`, raw `flowModels`, `AssignFormGridModel`, `AssignFormItemModel`, or direct internal `stepParams` for these action settings.
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub