|
| 1 | +--- |
| 2 | +name: objectui |
| 3 | +description: Universal Server-Driven UI (SDUI) Engine for building JSON-driven React interfaces with Shadcn design quality. Use this skill for all ObjectUI development tasks including schema-driven page building, plugin development, component integration, testing, auth/permissions, data integration, i18n, mobile responsiveness, project setup, and console development. Triggers on any mention of ObjectUI, SchemaRenderer, JSON schemas, SDUI, metadata-driven UIs, or Object Stack ecosystem work. |
| 4 | +user-invocable: false |
| 5 | +--- |
| 6 | + |
| 7 | +# ObjectUI |
| 8 | + |
| 9 | +> **A Universal, Server-Driven UI (SDUI) Engine built on React + Tailwind + Shadcn.** |
| 10 | +
|
| 11 | +ObjectUI renders JSON metadata from the `@objectstack/spec` protocol into pixel-perfect, accessible, and interactive enterprise interfaces (Dashboards, Kanbans, CRUDs, Forms, Grids). |
| 12 | + |
| 13 | +**Repository:** [github.com/objectstack-ai/objectui](https://github.com/objectstack-ai/objectui) |
| 14 | + |
| 15 | +## Strategic Positioning |
| 16 | + |
| 17 | +- **The "JSON-to-Shadcn" Bridge:** The only library combining Low-Code speed with Shadcn/Tailwind design quality |
| 18 | +- **The "Face" of ObjectStack:** Official renderer for the ecosystem, while remaining **Backend Agnostic** |
| 19 | + |
| 20 | +## Core Principles |
| 21 | + |
| 22 | +### 0. English-Only Codebase |
| 23 | + |
| 24 | +**Context:** This is an international open-source project. |
| 25 | + |
| 26 | +**Instruction:** ALL user-facing text in components, documentation, comments, and UI labels MUST be written in English. |
| 27 | + |
| 28 | +**Constraint:** Do NOT use Chinese or any other non-English language in: |
| 29 | +- Component text/labels (buttons, titles, descriptions, error messages) |
| 30 | +- Code comments |
| 31 | +- Documentation files (README.md, docs/*.md) |
| 32 | +- Console/log messages |
| 33 | + |
| 34 | +**Reasoning:** English ensures global accessibility and consistency across the codebase. |
| 35 | + |
| 36 | +### 1. Strict Adherence to @objectstack/spec |
| 37 | + |
| 38 | +**Context:** We are the implementation of a standard protocol. |
| 39 | + |
| 40 | +**Instruction:** All component schemas, JSON structures, and data types MUST strictly follow definitions in `@objectstack/spec`. |
| 41 | + |
| 42 | +**Constraint:** Do not invent new schema properties. If the spec says `columns`, do not use `fields`. |
| 43 | + |
| 44 | +**Validation:** Check `@objectstack/spec` definitions before writing any `interface` or `type`. |
| 45 | + |
| 46 | +### 2. Protocol Agnostic (The Universal Adapter) |
| 47 | + |
| 48 | +**Context:** Users might fetch data from a legacy SOAP API or a local JSON file. |
| 49 | + |
| 50 | +**Instruction:** Never hardcode `objectql.find()`. Use the DataSource Interface. |
| 51 | + |
| 52 | +**Pattern:** Inject dataSource via the root `<SchemaRendererProvider dataSource={...} />`. |
| 53 | + |
| 54 | +### 3. Documentation Driven Development |
| 55 | + |
| 56 | +**Context:** Code without docs is dead code. |
| 57 | + |
| 58 | +**Instruction:** For EVERY feature implemented or refactored, you MUST update the corresponding documentation: |
| 59 | +1. Package `README.md` |
| 60 | +2. `content/docs/guide/*.md` |
| 61 | + |
| 62 | +**Definition of Done:** The task is not complete until the documentation reflects the new code/architecture. |
| 63 | + |
| 64 | +### 4. "Shadcn Native" Aesthetics |
| 65 | + |
| 66 | +**Identity:** We are essentially "Serializable Shadcn". |
| 67 | + |
| 68 | +**Instruction:** When implementing a component (e.g., Card), strictly follow Shadcn's DOM structure (CardHeader, CardTitle, CardContent). |
| 69 | + |
| 70 | +**Constraint:** ALWAYS expose `className` in the schema props. Allow users to inject `bg-red-500` via JSON to override default styles. |
| 71 | + |
| 72 | +### 5. The Action System (Interactivity) |
| 73 | + |
| 74 | +**Concept:** A static UI is useless. The JSON must define behavior. |
| 75 | + |
| 76 | +**Pattern:** Actions are defined as data, not functions. |
| 77 | + |
| 78 | +**Example JSON:** |
| 79 | +```json |
| 80 | +{ |
| 81 | + "events": { |
| 82 | + "onClick": [ |
| 83 | + { "action": "validate", "target": "form_1" }, |
| 84 | + { "action": "submit", "target": "form_1" }, |
| 85 | + { "action": "navigate", "params": { "url": "/success" } } |
| 86 | + ] |
| 87 | + } |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +**Implementation:** The `@object-ui/core` package acts as an Event Bus to dispatch these actions. |
| 92 | + |
| 93 | +### 6. Layout as Components |
| 94 | + |
| 95 | +**Concept:** Layouts are just components that render children. |
| 96 | + |
| 97 | +**Instruction:** Treat Grid, Stack, Container as first-class citizens. |
| 98 | + |
| 99 | +**Responsiveness:** Layout schemas must support responsive props (e.g., `cols: { sm: 1, md: 2, lg: 4 }`). |
| 100 | + |
| 101 | +### 7. Type Safety over Magic |
| 102 | + |
| 103 | +- **No `any`:** Use strict Generics. |
| 104 | +- **Registry:** Use a central ComponentRegistry to map strings (`"type": "button"`) to React components. |
| 105 | +- **No `eval()` or dynamic imports** for security. |
| 106 | + |
| 107 | +### 8. The "No-Touch" Zones (Shadcn Purity) |
| 108 | + |
| 109 | +**Protected Path:** `packages/components/src/ui/**/*.tsx` |
| 110 | + |
| 111 | +**Rule:** You are FORBIDDEN from modifying the logic or styles of files in this directory. |
| 112 | + |
| 113 | +**Reasoning:** These are upstream 3rd-party files that are overwritten by sync scripts. |
| 114 | + |
| 115 | +**Workaround:** If a user asks to change the behavior of Button or Dialog: |
| 116 | +1. Do NOT edit `src/ui/button.tsx`. |
| 117 | +2. Create or Edit a wrapper in `packages/components/src/custom/`. |
| 118 | +3. Import the primitive from `@/ui/...` and wrap it. |
| 119 | + |
| 120 | +## Tech Stack (Strict Constraints) |
| 121 | + |
| 122 | +- **Core:** React 18+ (Hooks), TypeScript 5.0+ (Strict) |
| 123 | +- **Styling:** Tailwind CSS (Utility First) |
| 124 | + - ✅ REQUIRED: Use `class-variance-authority` (cva) for component variants |
| 125 | + - ✅ REQUIRED: Use `tailwind-merge` + `clsx` (`cn()`) for class overrides |
| 126 | + - ❌ FORBIDDEN: Inline styles (`style={{}}`), CSS Modules, Styled-components |
| 127 | +- **UI Primitives:** Shadcn UI (Radix UI) + Lucide Icons |
| 128 | +- **State Management:** Zustand (for global store), React Context (for scoped data) |
| 129 | +- **Testing:** Vitest + React Testing Library |
| 130 | +- **Docs:** Storybook (Component isolation) |
| 131 | + |
| 132 | +## Architecture & Monorepo Topology |
| 133 | + |
| 134 | +You manage a strict PNPM Workspace. |
| 135 | + |
| 136 | +| Package | Role | Responsibility | 🔴 Strict Constraints | |
| 137 | +|---|---|---|---| |
| 138 | +| `@object-ui/types` | The Protocol | Pure JSON Interfaces (ComponentSchema, ActionSchema). | ZERO dependencies. No React code. | |
| 139 | +| `@object-ui/core` | The Engine | Schema Registry, Validation, Expression Evaluation (`visible: "${data.age > 18}"`). | No UI library dependencies. Logic Only. | |
| 140 | +| `@object-ui/components` | The Atoms | Shadcn Primitives (Button, Badge, Card) & Icons. | Pure UI. No business logic. | |
| 141 | +| `@object-ui/fields` | The Inputs | Standard Field Renderers (Text, Number, Select). | Must implement FieldWidgetProps. | |
| 142 | +| `@object-ui/layout` | The Shell | Page Structure (Header, Sidebar, AppShell). | Routing-aware composition. | |
| 143 | +| `@object-ui/plugin-*` | The Widgets | Complex Views (Grid, Kanban, Map, Charts). | Heavy dependencies allowed here only. | |
| 144 | +| `@object-ui/react` | The Runtime | `<SchemaRenderer>`, useRenderer, useDataScope. | Bridges Core and Components. | |
| 145 | +| `@object-ui/data-*` | The Adapters | Connectors for REST, ObjectQL, GraphQL. | Isolate ALL fetch logic. | |
| 146 | + |
| 147 | +### Architectural Strategy (Strict) |
| 148 | + |
| 149 | +**❌ Do NOT create a package for every component.** |
| 150 | + |
| 151 | +**✅ Group by Dependency Weight:** |
| 152 | + |
| 153 | +1. **Atoms (@object-ui/components):** Shadcn Primitives. Zero heavy 3rd-party deps. |
| 154 | +2. **Fields (@object-ui/fields):** Standard Inputs. |
| 155 | +3. **Layouts (@object-ui/layout):** Page Skeletons. |
| 156 | +4. **Plugins (@object-ui/plugin-*):** Heavy Widgets (>50KB) or specialized libraries (Maps, Editors, Charts). |
| 157 | + |
| 158 | +## The JSON Protocol Specification (The "DNA") |
| 159 | + |
| 160 | +You must enforce a strict JSON structure. Every node in the UI tree follows this shape: |
| 161 | + |
| 162 | +```typescript |
| 163 | +// @object-ui/types |
| 164 | +interface UIComponent { |
| 165 | + /** The unique identifier for the renderer registry (e.g., 'input', 'grid', 'card') */ |
| 166 | + type: string; |
| 167 | + |
| 168 | + /** Unique ID for DOM accessibility and event targeting */ |
| 169 | + id?: string; |
| 170 | + |
| 171 | + /** Visual properties (mapped directly to Shadcn props) */ |
| 172 | + props?: Record<string, any>; |
| 173 | + |
| 174 | + /** Data binding path (e.g., 'user.address.city') */ |
| 175 | + bind?: string; |
| 176 | + |
| 177 | + /** Styling overrides (Tailwind classes) */ |
| 178 | + className?: string; |
| 179 | + |
| 180 | + /** Dynamic Behavior */ |
| 181 | + hidden?: string; // Expression: "${data.role != 'admin'}" |
| 182 | + disabled?: string; // Expression |
| 183 | + |
| 184 | + /** Event Handlers */ |
| 185 | + events?: Record<string, ActionDef[]>; // onClick -> [Action1, Action2] |
| 186 | + |
| 187 | + /** Layout Slots */ |
| 188 | + children?: UIComponent[]; |
| 189 | +} |
| 190 | +``` |
| 191 | + |
| 192 | +## Quick Reference |
| 193 | + |
| 194 | +### When to Use Specific Guides |
| 195 | + |
| 196 | +The guides in `guides/` provide deep domain expertise: |
| 197 | + |
| 198 | +- **Page Building & Schema Design** → [guides/page-builder.md](./guides/page-builder.md) |
| 199 | +- **Creating Custom Plugins** → [guides/plugin-development.md](./guides/plugin-development.md) |
| 200 | +- **Expression Syntax & Debugging** → [guides/schema-expressions.md](./guides/schema-expressions.md) |
| 201 | +- **Data Fetching & DataSource** → [guides/data-integration.md](./guides/data-integration.md) |
| 202 | +- **New Project Setup** → [guides/project-setup.md](./guides/project-setup.md) |
| 203 | +- **Testing Components & Schemas** → [guides/testing.md](./guides/testing.md) |
| 204 | +- **Multi-Language Support** → [guides/i18n.md](./guides/i18n.md) |
| 205 | +- **Mobile & Responsive Design** → [guides/mobile.md](./guides/mobile.md) |
| 206 | +- **Auth, Roles & Permissions** → [guides/auth-permissions.md](./guides/auth-permissions.md) |
| 207 | +- **Console Development** → [guides/console-development.md](./guides/console-development.md) |
| 208 | + |
| 209 | +### Critical Global Rules |
| 210 | + |
| 211 | +The rules in `rules/` define non-negotiable constraints: |
| 212 | + |
| 213 | +- **JSON Protocol Compliance** → [rules/protocol.md](./rules/protocol.md) |
| 214 | +- **Styling & Tailwind Usage** → [rules/styling.md](./rules/styling.md) |
| 215 | +- **Component Composition Patterns** → [rules/composition.md](./rules/composition.md) |
| 216 | +- **No-Touch Zones (Shadcn Upstream)** → [rules/no-touch-zones.md](./rules/no-touch-zones.md) |
| 217 | + |
| 218 | +## Implementation Patterns |
| 219 | + |
| 220 | +### Pattern A: The Component Registry (Extensibility) |
| 221 | + |
| 222 | +How do we let users add their own "Map" component? |
| 223 | + |
| 224 | +```typescript |
| 225 | +// packages/core/src/registry.ts |
| 226 | +export type ComponentImpl = React.FC<{ schema: any; ... }>; |
| 227 | + |
| 228 | +const registry = new Map<string, ComponentImpl>(); |
| 229 | + |
| 230 | +export function registerComponent(type: string, impl: ComponentImpl) { |
| 231 | + registry.set(type, impl); |
| 232 | +} |
| 233 | + |
| 234 | +export function resolveComponent(type: string) { |
| 235 | + return registry.get(type) || FallbackComponent; |
| 236 | +} |
| 237 | +``` |
| 238 | + |
| 239 | +### Pattern B: The Renderer Loop (Recursion) |
| 240 | + |
| 241 | +How to render a tree? |
| 242 | + |
| 243 | +```typescript |
| 244 | +// packages/react/src/SchemaRenderer.tsx |
| 245 | +export const SchemaRenderer = ({ schema }: { schema: UIComponent }) => { |
| 246 | + const Component = resolveComponent(schema.type); |
| 247 | + const { isHidden } = useExpression(schema.hidden); |
| 248 | + |
| 249 | + if (isHidden) return null; |
| 250 | + |
| 251 | + return ( |
| 252 | + <Component |
| 253 | + schema={schema} |
| 254 | + className={cn(schema.className)} |
| 255 | + {...schema.props} |
| 256 | + > |
| 257 | + {schema.children?.map(child => ( |
| 258 | + <SchemaRenderer key={child.id} schema={child} /> |
| 259 | + ))} |
| 260 | + </Component> |
| 261 | + ); |
| 262 | +}; |
| 263 | +``` |
| 264 | + |
| 265 | +## AI Workflow Instructions |
| 266 | + |
| 267 | +### On "Create New Component" (e.g., 'DataTable') |
| 268 | + |
| 269 | +1. **Type Definition:** Update `@object-ui/types`. Define `DataTableSchema` (columns, sorting, pagination). |
| 270 | +2. **Shadcn Mapping:** Look at shadcn/ui/table. Create `DataTableRenderer` in `@object-ui/components`. |
| 271 | +3. **Data Scope:** Use `useDataScope()` to get the array data. Do not fetch data inside the component. |
| 272 | +4. **Registration:** Register `"type": "table"` in the core registry. |
| 273 | + |
| 274 | +### On "Action Logic" (e.g., 'Open Modal') |
| 275 | + |
| 276 | +1. **Define Schema:** Add `OpenModalAction` interface to types. |
| 277 | +2. **Implement Handler:** Add the logic to the ActionEngine in `@object-ui/core`. |
| 278 | +3. **Visuals:** Ensure the component triggering it calls `useActionRunner()`. |
| 279 | + |
| 280 | +### On "Documentation" |
| 281 | + |
| 282 | +1. **JSON First:** Always show the JSON configuration first. |
| 283 | +2. **Visuals:** Describe how Tailwind classes (`className`) affect the component. |
| 284 | +3. **Storybook:** Suggest creating a `.stories.tsx` file for every new component. |
| 285 | + |
| 286 | +## Debugging & Browser Simulation Strategy |
| 287 | + |
| 288 | +When debugging the simulated browser environment (e.g., `apps/console` in mock mode), strict adherence to the official toolchain is required. |
| 289 | + |
| 290 | +### Rule #1: Official MSW Integration |
| 291 | + |
| 292 | +- **Startup:** Use `@objectstack/plugin-msw` to initialize the mock API server. Do NOT write custom fetch interceptors or manual mock servers unless absolutely necessary. |
| 293 | +- **Configuration:** Ensure the `MSWPlugin` is configured with the correct `baseUrl` (e.g., `/api/v1`) to match the client's expectations. |
| 294 | + |
| 295 | +### Rule #2: Client Data Fetching |
| 296 | + |
| 297 | +- **Data Access:** Always use `@objectstack/client` for data fetching. Do not use raw `fetch` or `axios` directly in components. |
| 298 | +- **Alignment:** Verify that the client's `baseUrl` matches the mock server's configuration. |
| 299 | + |
| 300 | +### Rule #3: Upstream Fixes First |
| 301 | + |
| 302 | +- **Principle:** If you encounter a bug or limitation in the official packages (`@objectstack/*`): |
| 303 | + - **Action 1:** Do NOT rely solely on local workarounds (monkey-patching) in the app. |
| 304 | + - **Action 2:** Prompt the user to modify the source code in the official packages (if available in the workspace) or report the issue. |
| 305 | + - **Reasoning:** We prioritize fixing the core engine over patching individual apps. |
| 306 | + |
| 307 | +## Common Mistakes to Avoid |
| 308 | + |
| 309 | +- Writing large bespoke React JSX trees before schema definition |
| 310 | +- Hardcoding API calls directly inside visual renderers |
| 311 | +- Introducing package coupling (e.g., UI package depending on business logic package) |
| 312 | +- Registering components without namespace in plugin-heavy projects |
| 313 | +- Skipping docs updates for newly introduced schema patterns |
| 314 | +- Putting expression values in top-level `value`/`label` fields instead of `props.*` |
| 315 | +- Missing Shadcn CSS variables — components render but look completely unstyled |
| 316 | +- Forgetting `@source` directives in Tailwind config — utility classes not generated for ObjectUI packages |
| 317 | + |
| 318 | +## Fast Triage Playbook for Ambiguous Requests |
| 319 | + |
| 320 | +If the request is underspecified: |
| 321 | + |
| 322 | +1. Infer likely page category (list/detail/form/dashboard) |
| 323 | +2. Produce a minimal viable schema first |
| 324 | +3. Mark assumptions clearly |
| 325 | +4. Provide one conservative and one advanced variant |
| 326 | + |
| 327 | +This keeps momentum while inviting focused user feedback. |
| 328 | + |
| 329 | +--- |
| 330 | + |
| 331 | +**You are the Architect. Build the Engine.** |
0 commit comments