- description
- Use before editing any file under the registry components or kits, or a component's drag, drop, handle menu or indicator UI. Implements Plate React component families, copied registry UI and kit wiring with exact browser proof.
- name
- plate-ui
- metadata
- {"skiller":{"source":".agents/rules/plate-ui.mdc"}}
# Plate UI
Use the `shadcn` skill for CLI, upstream docs, and generic shadcn/ui rules.
Use `vercel-react-best-practices` for selected implementation tactics.
`docs/vision/plate.md` (Plugin and component doctrine) owns Plate React and
component law: component shape, family topology, extraction boundaries,
cross-platform layering and React performance. This skill owns the method that
applies it: open-code preservation, the base/live kit split, registry wiring
and browser proof.
## Repo Surfaces
- `apps/www/src/registry/components/editor` — provider-neutral copied editor
features, live/static components, and app-owned kit values
- `apps/www/src/registry/bases/*` — provider-specific source variants that
install over the same flat editor targets
- `apps/www/src/registry/registry-*.ts` — registry metadata and dependencies
- `packages/*` — upstream semantic owners; package edits route to
`plate-plugins`
## Routing Gate
Ownership follows the Routing table in `AGENTS.md`.
Continue here whenever a Plate package or registry task makes a React,
component, hook, provider, store, primitive, or composition decision. Package
implementation and proof still route to `plate-plugins`; plugin builders,
schema law, and application typing remain outside this skill.
## Principles
1. **Preserve open code.** A shadcn-derived component should still look like source code a user can own, read, diff, and tweak.
2. **Registry wiring is part of authorship.** A component is not done until kits, examples, and style deps are coherent.
3. **Examples teach the install shape.** Do not remove an explicit feature
plugin, kit, renderer binding, or dependency merely because an aggregate
application editor installed by the `editor-kit` registry item also includes
it. Deliberate repetition can make copied code and feature ownership
transparent.
## Registry
### Registry Wiring → [registry.md](./rules/registry.md)
- Update `registry-features.ts`, `registry-editor.ts`, and
`registry-examples.ts` together.
### Registry Changelog
- User-visible registry UI, kit, example, metadata, style dependency, copied-code
install shape, or generated registry changelog changes need a registry
changelog entry or a concrete N/A reason.
- `changeset`'s registry reference owns the schema, scaffold, generation and
verification. Do not duplicate the entry contract here.
## Styling And Vercel Tactics
Use stable kebab-case `data-slot` for component identity and `data-state` for
interaction and visual state. Keep Tailwind v4 syntax canonical: slash opacity,
parenthesized CSS variables, suffix `!`, and canonical utilities.
`vercel-react-best-practices` provides selected implementation tactics. It
does not decide Plate public APIs, extraction, file ownership, or provider
visibility.
- Load exact rule files for the problem; never import the whole catalog as
doctrine.
- `rerender-split-combined-hooks` may justify splitting internal calculations
or effects. It never requires multiple public hooks or files.
- Context-interface, lifted-state, compound-component, and provider patterns
need real independent composition. They do not automatically earn a public
provider or store.
- Performance advice may optimize an accepted Plate shape; it cannot replace
the component law in Vision.
## Key Patterns
```tsx
// Good: simple component owns its own renderer behavior directly.
function MediaImage(props: EditorElementProps<typeof ImagePlugin>) {
const selected = useSelected();
return <EditorElement {...props} data-selected={selected || undefined} />;
}
// Good: one reusable family controller coordinates complex siblings.
const tableResize = useTableResizeController();
// Good: host-owned app code outside copied registry UI uses its inferred editor.
const api = editor.api.comments;
// Good: copied suggestion UI uses its owning descriptor.
const suggestionApi = editor.plugin(SuggestionPlugin).api;
// Good: feature-owned cross-feature integration checks the optional portal.
const optionalSuggestion = editor.plugin(SuggestionPlugin);
if (optionalSuggestion.installed) {
optionalSuggestion.api.createIdentity();
}
// Good: package core owns commands/state, platform UI composes locally.
const canToggleBold = bridgeState.canToggleBold;
const onPress = () => editor.plugin(BoldPlugin).update.toggle();
return <Button disabled={!canToggleBold} onClick={onPress}>Bold</Button>;
// Good: base/live split stays explicit.
export const BaseFootnoteKit = [
BaseFootnoteReferencePlugin.configure({
component: FootnoteReferenceElementStatic,
}),
BaseFootnoteDefinitionPlugin.configure({
component: FootnoteDefinitionElementStatic,
}),
];
export const FootnoteKit = [
FootnoteReferencePlugin.configure({
component: FootnoteReferenceElement,
slots: { afterEditable: FootnoteCombobox },
}),
FootnoteDefinitionPlugin.configure({ component: FootnoteDefinitionElement }),
];
// Good: runtime-neutral policy is shared by both editor presets.
export const MarkdownKit = [
MarkdownPlugin.configure({ initialState: { remarkPlugins: [remarkGfm] } }),
];
// Bad: package hook exists only to feed one shadcn component's local UI.
const state = useSingleComponentOnlyState();
return <Popover open={state.open}>...</Popover>;
// Bad: React-only package hook that mainly returns renderer glue.
const { dialogTitle, menuItems, onOpenChange, popoverOpen } =
useToolbarMenuState();
```
## Workflow
1. Start with the `shadcn` skill. Run the normal `shadcn` docs/search workflow first.
2. Search Plate for the closest analog in
`apps/www/src/registry/components/editor`, the applicable
`apps/www/src/registry/bases/*` variant, and the relevant `packages/*`.
3. Decide ownership before writing code: package ownership requires a durable
contract independent of the renderer (`docs/vision/plate.md`).
4. Decide the three layers before coding:
- semantic core
- platform adapter
- local open-code UI
5. Choose the component-family shape before writing state/effects:
- direct component by default
- one terminal `use<Family>` only for a real semantic controller
- one private context only when sibling coordination needs it
6. Apply the React checks before writing state/effects:
- can this be derived during render?
- should this stay in an event handler?
- am I subscribing to more editor state than the UI actually renders?
7. Build the component as open code first.
8. When plugin wiring changes, audit the affected field and its registry peers
for local structural mirrors, state `Pick` aliases, callback return
annotations, and casts that replace package ownership or contextual
inference. Fix the canonical package contract instead of preserving a local
adapter.
9. Extract only the boundaries that pass that ownership test.
10. Share runtime-neutral kits; wire base/live renderer kits and registry deps
only where the renderer ownership actually differs.
## Audit References
- [component-audit.md](./references/component-audit.md) — concrete good patterns and anti-patterns from this repo
عرض على GitHub