diff --git a/CHANGELOG.md b/CHANGELOG.md index bbee02314..c6a804327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **Copilot Skills Architecture Unified** (`skills`): Consolidated 10 parallel ObjectUI Copilot skills (`objectui-auth-permissions`, `objectui-console-development`, `objectui-data-integration`, `objectui-i18n`, `objectui-mobile`, `objectui-plugin-development`, `objectui-project-setup`, `objectui-schema-expressions`, `objectui-sdui-page-builder`, `objectui-testing`) into a single tree-based `skills/objectui/` structure aligned with shadcn/ui best practices. The new architecture features: + - **Single entry point** (`skills/objectui/SKILL.md`): Unified skill manifest with core principles, architecture overview, and indexed references to guides and rules. + - **Global rules** (`skills/objectui/rules/`): Protocol compliance, styling constraints, component composition patterns, and no-touch zones as dedicated markdown files. + - **Domain guides** (`skills/objectui/guides/`): 10 specialized guides (page-builder, plugin-development, schema-expressions, data-integration, project-setup, testing, i18n, mobile, auth-permissions, console-development) for deep domain expertise. + - **Unified evals** (`skills/objectui/evals/`): All evaluation JSON files consolidated from parallel skill evals directories. + - **Updated skills-lock.json**: Now references only `objectui` (local) and `shadcn` (github) skills, removing fragmented cross-skill dependencies. + - This restructuring improves AI agent selection accuracy, reduces maintenance overhead, and aligns with shadcn/ui's single-skill tree model for better ecosystem compatibility. + ### Improved - **Console UI design optimization sweep** (`@object-ui/console`): Comprehensive accessibility and design consistency improvements across all major console interfaces: diff --git a/ROADMAP.md b/ROADMAP.md index e543b4975..9e50d03d5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,11 +1,11 @@ # ObjectUI Development Roadmap -> **Last Updated:** April 1, 2026 +> **Last Updated:** April 14, 2026 > **Current Version:** v0.5.x > **Spec Version:** @objectstack/spec v3.3.0 > **Client Version:** @objectstack/client v3.3.0 > **Target UX Benchmark:** 🎯 Airtable parity -> **Current Priority:** AppShell Navigation Β· Designer Interaction Β· **View Config Live Preview Sync βœ…** Β· Dashboard Config Panel Β· Airtable UX Polish Β· **Flow Designer βœ…** Β· **App Creation & Editing Flow βœ…** Β· **System Settings & App Management βœ…** Β· **Right-Side Visual Editor Drawer βœ…** Β· **Object Manager & Field Designer βœ…** Β· **AI SDUI Chatbot (service-ai + vercel/ai) βœ…** Β· **Unified Home Dashboard βœ…** +> **Current Priority:** AppShell Navigation Β· Designer Interaction Β· **View Config Live Preview Sync βœ…** Β· Dashboard Config Panel Β· Airtable UX Polish Β· **Flow Designer βœ…** Β· **App Creation & Editing Flow βœ…** Β· **System Settings & App Management βœ…** Β· **Right-Side Visual Editor Drawer βœ…** Β· **Object Manager & Field Designer βœ…** Β· **AI SDUI Chatbot (service-ai + vercel/ai) βœ…** Β· **Unified Home Dashboard βœ…** Β· **Unified Copilot Skills Architecture βœ…** --- diff --git a/skills-lock.json b/skills-lock.json index 4007b6cfd..d6f378796 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -1,20 +1,15 @@ { "version": 1, "skills": { - "frontend-design": { - "source": "anthropics/skills", - "sourceType": "github", - "computedHash": "063a0e6448123cd359ad0044cc46b0e490cc7964d45ef4bb9fd842bd2ffbca67" + "objectui": { + "source": "local", + "sourceType": "local", + "path": "./skills/objectui" }, "shadcn": { "source": "shadcn/ui", "sourceType": "github", "computedHash": "873c67922d80775a9fdf596db7964b579f0408c30ec6e3d11989f6055bbec89f" - }, - "skill-creator": { - "source": "anthropics/skills", - "sourceType": "github", - "computedHash": "b122f4d1e91aa1d83027e050b5b37c3156203ee85458d7b1360bc48167c9e02a" } } } diff --git a/skills/objectui/README.md b/skills/objectui/README.md new file mode 100644 index 000000000..ebb5c138c --- /dev/null +++ b/skills/objectui/README.md @@ -0,0 +1,104 @@ +# ObjectUI Copilot Skill + +This directory contains the unified ObjectUI Copilot skill, consolidating all ObjectUI development knowledge into a single tree-based structure aligned with [shadcn/ui](https://github.com/shadcn-ui/ui/tree/main/skills/shadcn) best practices. + +## Structure + +``` +skills/objectui/ +β”œβ”€β”€ SKILL.md # Main entry point - core principles & architecture +β”œβ”€β”€ rules/ # Global non-negotiable constraints +β”‚ β”œβ”€β”€ protocol.md # JSON Protocol compliance rules +β”‚ β”œβ”€β”€ styling.md # Tailwind & Shadcn styling rules +β”‚ β”œβ”€β”€ composition.md # Component composition patterns +β”‚ └── no-touch-zones.md # Protected upstream files +β”œβ”€β”€ guides/ # Domain-specific expertise (10 guides) +β”‚ β”œβ”€β”€ page-builder.md # Schema-driven page building +β”‚ β”œβ”€β”€ plugin-development.md # Creating custom plugins +β”‚ β”œβ”€β”€ schema-expressions.md # Expression syntax & debugging +β”‚ β”œβ”€β”€ data-integration.md # DataSource & API integration +β”‚ β”œβ”€β”€ project-setup.md # Project initialization & config +β”‚ β”œβ”€β”€ testing.md # Testing patterns (Vitest, Storybook, Playwright) +β”‚ β”œβ”€β”€ i18n.md # Internationalization & localization +β”‚ β”œβ”€β”€ mobile.md # Mobile responsiveness & PWA +β”‚ β”œβ”€β”€ auth-permissions.md # Authentication & RBAC +β”‚ └── console-development.md # Console app development +β”œβ”€β”€ evals/ # Evaluation test cases (10 JSON files) +└── agents/ # Agent configurations (empty, reserved for future) +``` + +## Why This Structure? + +### Before (10 Parallel Skills) +- `skills/objectui-auth-permissions/` +- `skills/objectui-console-development/` +- `skills/objectui-data-integration/` +- `skills/objectui-i18n/` +- `skills/objectui-mobile/` +- `skills/objectui-plugin-development/` +- `skills/objectui-project-setup/` +- `skills/objectui-schema-expressions/` +- `skills/objectui-sdui-page-builder/` +- `skills/objectui-testing/` + +**Problems:** +- Agent had to choose between 10+ skills +- Cross-skill knowledge was fragmented +- Maintenance overhead (10 separate manifests) +- Inconsistent with shadcn/ui single-skill model + +### After (1 Tree-Based Skill) +- `skills/objectui/` (single skill) + - `SKILL.md` - unified entry point + - `rules/` - global constraints + - `guides/` - domain expertise + +**Benefits:** +- βœ… Single unified entry point +- βœ… AI agent always knows where to look +- βœ… Global rules apply consistently +- βœ… Aligned with shadcn/ui architecture +- βœ… Easier maintenance and updates + +## Usage + +When working with ObjectUI, the AI agent will: + +1. **Start with `SKILL.md`** - Core principles, architecture, and quick reference +2. **Check `rules/`** - Ensure compliance with non-negotiable constraints +3. **Consult `guides/`** - Get deep domain expertise for specific tasks + +### Example Flow + +**User:** "Help me build a dashboard page with ObjectUI" + +**Agent:** +1. Reads `SKILL.md` β†’ Understands core principles +2. Checks `rules/protocol.md` β†’ Learns JSON schema structure +3. Checks `rules/styling.md` β†’ Learns Tailwind requirements +4. Reads `guides/page-builder.md` β†’ Gets dashboard building patterns +5. Reads `guides/schema-expressions.md` β†’ Learns dynamic expressions + +## Maintenance + +When adding new content: + +- **Core principles or architecture changes** β†’ Update `SKILL.md` +- **New global rule** β†’ Add to `rules/` +- **New domain area** β†’ Add guide to `guides/` +- **New eval test** β†’ Add JSON to `evals/` + +## Migration Notes + +This structure was created on April 14, 2026, consolidating: +- 10 parallel skill directories +- 10 SKILL.md files β†’ 1 SKILL.md + 10 guides +- 10 evals directories β†’ 1 unified evals/ +- Cross-skill references unified into single namespace + +All content preserved and reorganized for better discoverability. + +## Reference + +- **shadcn/ui skill structure**: https://github.com/shadcn-ui/ui/tree/main/skills/shadcn +- **Issue**: [#ζžΆζž„δΌ˜εŒ–-ε°†-objectui-倚-Copilot-skill-εˆεΉΆδΈΊε•-skill-ζ ‘ηŠΆδ½“η³»](https://github.com/objectstack-ai/objectui/issues) diff --git a/skills/objectui/SKILL.md b/skills/objectui/SKILL.md new file mode 100644 index 000000000..e61b94a85 --- /dev/null +++ b/skills/objectui/SKILL.md @@ -0,0 +1,331 @@ +--- +name: objectui +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. +user-invocable: false +--- + +# ObjectUI + +> **A Universal, Server-Driven UI (SDUI) Engine built on React + Tailwind + Shadcn.** + +ObjectUI renders JSON metadata from the `@objectstack/spec` protocol into pixel-perfect, accessible, and interactive enterprise interfaces (Dashboards, Kanbans, CRUDs, Forms, Grids). + +**Repository:** [github.com/objectstack-ai/objectui](https://github.com/objectstack-ai/objectui) + +## Strategic Positioning + +- **The "JSON-to-Shadcn" Bridge:** The only library combining Low-Code speed with Shadcn/Tailwind design quality +- **The "Face" of ObjectStack:** Official renderer for the ecosystem, while remaining **Backend Agnostic** + +## Core Principles + +### 0. English-Only Codebase + +**Context:** This is an international open-source project. + +**Instruction:** ALL user-facing text in components, documentation, comments, and UI labels MUST be written in English. + +**Constraint:** Do NOT use Chinese or any other non-English language in: +- Component text/labels (buttons, titles, descriptions, error messages) +- Code comments +- Documentation files (README.md, docs/*.md) +- Console/log messages + +**Reasoning:** English ensures global accessibility and consistency across the codebase. + +### 1. Strict Adherence to @objectstack/spec + +**Context:** We are the implementation of a standard protocol. + +**Instruction:** All component schemas, JSON structures, and data types MUST strictly follow definitions in `@objectstack/spec`. + +**Constraint:** Do not invent new schema properties. If the spec says `columns`, do not use `fields`. + +**Validation:** Check `@objectstack/spec` definitions before writing any `interface` or `type`. + +### 2. Protocol Agnostic (The Universal Adapter) + +**Context:** Users might fetch data from a legacy SOAP API or a local JSON file. + +**Instruction:** Never hardcode `objectql.find()`. Use the DataSource Interface. + +**Pattern:** Inject dataSource via the root ``. + +### 3. Documentation Driven Development + +**Context:** Code without docs is dead code. + +**Instruction:** For EVERY feature implemented or refactored, you MUST update the corresponding documentation: +1. Package `README.md` +2. `content/docs/guide/*.md` + +**Definition of Done:** The task is not complete until the documentation reflects the new code/architecture. + +### 4. "Shadcn Native" Aesthetics + +**Identity:** We are essentially "Serializable Shadcn". + +**Instruction:** When implementing a component (e.g., Card), strictly follow Shadcn's DOM structure (CardHeader, CardTitle, CardContent). + +**Constraint:** ALWAYS expose `className` in the schema props. Allow users to inject `bg-red-500` via JSON to override default styles. + +### 5. The Action System (Interactivity) + +**Concept:** A static UI is useless. The JSON must define behavior. + +**Pattern:** Actions are defined as data, not functions. + +**Example JSON:** +```json +{ + "events": { + "onClick": [ + { "action": "validate", "target": "form_1" }, + { "action": "submit", "target": "form_1" }, + { "action": "navigate", "params": { "url": "/success" } } + ] + } +} +``` + +**Implementation:** The `@object-ui/core` package acts as an Event Bus to dispatch these actions. + +### 6. Layout as Components + +**Concept:** Layouts are just components that render children. + +**Instruction:** Treat Grid, Stack, Container as first-class citizens. + +**Responsiveness:** Layout schemas must support responsive props (e.g., `cols: { sm: 1, md: 2, lg: 4 }`). + +### 7. Type Safety over Magic + +- **No `any`:** Use strict Generics. +- **Registry:** Use a central ComponentRegistry to map strings (`"type": "button"`) to React components. +- **No `eval()` or dynamic imports** for security. + +### 8. The "No-Touch" Zones (Shadcn Purity) + +**Protected Path:** `packages/components/src/ui/**/*.tsx` + +**Rule:** You are FORBIDDEN from modifying the logic or styles of files in this directory. + +**Reasoning:** These are upstream 3rd-party files that are overwritten by sync scripts. + +**Workaround:** If a user asks to change the behavior of Button or Dialog: +1. Do NOT edit `src/ui/button.tsx`. +2. Create or Edit a wrapper in `packages/components/src/custom/`. +3. Import the primitive from `@/ui/...` and wrap it. + +## Tech Stack (Strict Constraints) + +- **Core:** React 18+ (Hooks), TypeScript 5.0+ (Strict) +- **Styling:** Tailwind CSS (Utility First) + - βœ… REQUIRED: Use `class-variance-authority` (cva) for component variants + - βœ… REQUIRED: Use `tailwind-merge` + `clsx` (`cn()`) for class overrides + - ❌ FORBIDDEN: Inline styles (`style={{}}`), CSS Modules, Styled-components +- **UI Primitives:** Shadcn UI (Radix UI) + Lucide Icons +- **State Management:** Zustand (for global store), React Context (for scoped data) +- **Testing:** Vitest + React Testing Library +- **Docs:** Storybook (Component isolation) + +## Architecture & Monorepo Topology + +You manage a strict PNPM Workspace. + +| Package | Role | Responsibility | πŸ”΄ Strict Constraints | +|---|---|---|---| +| `@object-ui/types` | The Protocol | Pure JSON Interfaces (ComponentSchema, ActionSchema). | ZERO dependencies. No React code. | +| `@object-ui/core` | The Engine | Schema Registry, Validation, Expression Evaluation (`visible: "${data.age > 18}"`). | No UI library dependencies. Logic Only. | +| `@object-ui/components` | The Atoms | Shadcn Primitives (Button, Badge, Card) & Icons. | Pure UI. No business logic. | +| `@object-ui/fields` | The Inputs | Standard Field Renderers (Text, Number, Select). | Must implement FieldWidgetProps. | +| `@object-ui/layout` | The Shell | Page Structure (Header, Sidebar, AppShell). | Routing-aware composition. | +| `@object-ui/plugin-*` | The Widgets | Complex Views (Grid, Kanban, Map, Charts). | Heavy dependencies allowed here only. | +| `@object-ui/react` | The Runtime | ``, useRenderer, useDataScope. | Bridges Core and Components. | +| `@object-ui/data-*` | The Adapters | Connectors for REST, ObjectQL, GraphQL. | Isolate ALL fetch logic. | + +### Architectural Strategy (Strict) + +**❌ Do NOT create a package for every component.** + +**βœ… Group by Dependency Weight:** + +1. **Atoms (@object-ui/components):** Shadcn Primitives. Zero heavy 3rd-party deps. +2. **Fields (@object-ui/fields):** Standard Inputs. +3. **Layouts (@object-ui/layout):** Page Skeletons. +4. **Plugins (@object-ui/plugin-*):** Heavy Widgets (>50KB) or specialized libraries (Maps, Editors, Charts). + +## The JSON Protocol Specification (The "DNA") + +You must enforce a strict JSON structure. Every node in the UI tree follows this shape: + +```typescript +// @object-ui/types +interface UIComponent { + /** The unique identifier for the renderer registry (e.g., 'input', 'grid', 'card') */ + type: string; + + /** Unique ID for DOM accessibility and event targeting */ + id?: string; + + /** Visual properties (mapped directly to Shadcn props) */ + props?: Record; + + /** Data binding path (e.g., 'user.address.city') */ + bind?: string; + + /** Styling overrides (Tailwind classes) */ + className?: string; + + /** Dynamic Behavior */ + hidden?: string; // Expression: "${data.role != 'admin'}" + disabled?: string; // Expression + + /** Event Handlers */ + events?: Record; // onClick -> [Action1, Action2] + + /** Layout Slots */ + children?: UIComponent[]; +} +``` + +## Quick Reference + +### When to Use Specific Guides + +The guides in `guides/` provide deep domain expertise: + +- **Page Building & Schema Design** β†’ [guides/page-builder.md](./guides/page-builder.md) +- **Creating Custom Plugins** β†’ [guides/plugin-development.md](./guides/plugin-development.md) +- **Expression Syntax & Debugging** β†’ [guides/schema-expressions.md](./guides/schema-expressions.md) +- **Data Fetching & DataSource** β†’ [guides/data-integration.md](./guides/data-integration.md) +- **New Project Setup** β†’ [guides/project-setup.md](./guides/project-setup.md) +- **Testing Components & Schemas** β†’ [guides/testing.md](./guides/testing.md) +- **Multi-Language Support** β†’ [guides/i18n.md](./guides/i18n.md) +- **Mobile & Responsive Design** β†’ [guides/mobile.md](./guides/mobile.md) +- **Auth, Roles & Permissions** β†’ [guides/auth-permissions.md](./guides/auth-permissions.md) +- **Console Development** β†’ [guides/console-development.md](./guides/console-development.md) + +### Critical Global Rules + +The rules in `rules/` define non-negotiable constraints: + +- **JSON Protocol Compliance** β†’ [rules/protocol.md](./rules/protocol.md) +- **Styling & Tailwind Usage** β†’ [rules/styling.md](./rules/styling.md) +- **Component Composition Patterns** β†’ [rules/composition.md](./rules/composition.md) +- **No-Touch Zones (Shadcn Upstream)** β†’ [rules/no-touch-zones.md](./rules/no-touch-zones.md) + +## Implementation Patterns + +### Pattern A: The Component Registry (Extensibility) + +How do we let users add their own "Map" component? + +```typescript +// packages/core/src/registry.ts +export type ComponentImpl = React.FC<{ schema: any; ... }>; + +const registry = new Map(); + +export function registerComponent(type: string, impl: ComponentImpl) { + registry.set(type, impl); +} + +export function resolveComponent(type: string) { + return registry.get(type) || FallbackComponent; +} +``` + +### Pattern B: The Renderer Loop (Recursion) + +How to render a tree? + +```typescript +// packages/react/src/SchemaRenderer.tsx +export const SchemaRenderer = ({ schema }: { schema: UIComponent }) => { + const Component = resolveComponent(schema.type); + const { isHidden } = useExpression(schema.hidden); + + if (isHidden) return null; + + return ( + + {schema.children?.map(child => ( + + ))} + + ); +}; +``` + +## AI Workflow Instructions + +### On "Create New Component" (e.g., 'DataTable') + +1. **Type Definition:** Update `@object-ui/types`. Define `DataTableSchema` (columns, sorting, pagination). +2. **Shadcn Mapping:** Look at shadcn/ui/table. Create `DataTableRenderer` in `@object-ui/components`. +3. **Data Scope:** Use `useDataScope()` to get the array data. Do not fetch data inside the component. +4. **Registration:** Register `"type": "table"` in the core registry. + +### On "Action Logic" (e.g., 'Open Modal') + +1. **Define Schema:** Add `OpenModalAction` interface to types. +2. **Implement Handler:** Add the logic to the ActionEngine in `@object-ui/core`. +3. **Visuals:** Ensure the component triggering it calls `useActionRunner()`. + +### On "Documentation" + +1. **JSON First:** Always show the JSON configuration first. +2. **Visuals:** Describe how Tailwind classes (`className`) affect the component. +3. **Storybook:** Suggest creating a `.stories.tsx` file for every new component. + +## Debugging & Browser Simulation Strategy + +When debugging the simulated browser environment (e.g., `apps/console` in mock mode), strict adherence to the official toolchain is required. + +### Rule #1: Official MSW Integration + +- **Startup:** Use `@objectstack/plugin-msw` to initialize the mock API server. Do NOT write custom fetch interceptors or manual mock servers unless absolutely necessary. +- **Configuration:** Ensure the `MSWPlugin` is configured with the correct `baseUrl` (e.g., `/api/v1`) to match the client's expectations. + +### Rule #2: Client Data Fetching + +- **Data Access:** Always use `@objectstack/client` for data fetching. Do not use raw `fetch` or `axios` directly in components. +- **Alignment:** Verify that the client's `baseUrl` matches the mock server's configuration. + +### Rule #3: Upstream Fixes First + +- **Principle:** If you encounter a bug or limitation in the official packages (`@objectstack/*`): + - **Action 1:** Do NOT rely solely on local workarounds (monkey-patching) in the app. + - **Action 2:** Prompt the user to modify the source code in the official packages (if available in the workspace) or report the issue. + - **Reasoning:** We prioritize fixing the core engine over patching individual apps. + +## Common Mistakes to Avoid + +- Writing large bespoke React JSX trees before schema definition +- Hardcoding API calls directly inside visual renderers +- Introducing package coupling (e.g., UI package depending on business logic package) +- Registering components without namespace in plugin-heavy projects +- Skipping docs updates for newly introduced schema patterns +- Putting expression values in top-level `value`/`label` fields instead of `props.*` +- Missing Shadcn CSS variables β€” components render but look completely unstyled +- Forgetting `@source` directives in Tailwind config β€” utility classes not generated for ObjectUI packages + +## Fast Triage Playbook for Ambiguous Requests + +If the request is underspecified: + +1. Infer likely page category (list/detail/form/dashboard) +2. Produce a minimal viable schema first +3. Mark assumptions clearly +4. Provide one conservative and one advanced variant + +This keeps momentum while inviting focused user feedback. + +--- + +**You are the Architect. Build the Engine.** diff --git a/skills/objectui-auth-permissions/evals/evals.json b/skills/objectui/evals/auth-permissions.json similarity index 100% rename from skills/objectui-auth-permissions/evals/evals.json rename to skills/objectui/evals/auth-permissions.json diff --git a/skills/objectui-console-development/evals/evals.json b/skills/objectui/evals/console-development.json similarity index 100% rename from skills/objectui-console-development/evals/evals.json rename to skills/objectui/evals/console-development.json diff --git a/skills/objectui-data-integration/evals/evals.json b/skills/objectui/evals/data-integration.json similarity index 100% rename from skills/objectui-data-integration/evals/evals.json rename to skills/objectui/evals/data-integration.json diff --git a/skills/objectui-i18n/evals/evals.json b/skills/objectui/evals/i18n.json similarity index 100% rename from skills/objectui-i18n/evals/evals.json rename to skills/objectui/evals/i18n.json diff --git a/skills/objectui-mobile/evals/evals.json b/skills/objectui/evals/mobile.json similarity index 100% rename from skills/objectui-mobile/evals/evals.json rename to skills/objectui/evals/mobile.json diff --git a/skills/objectui-plugin-development/evals/evals.json b/skills/objectui/evals/plugin-development.json similarity index 100% rename from skills/objectui-plugin-development/evals/evals.json rename to skills/objectui/evals/plugin-development.json diff --git a/skills/objectui-project-setup/evals/evals.json b/skills/objectui/evals/project-setup.json similarity index 100% rename from skills/objectui-project-setup/evals/evals.json rename to skills/objectui/evals/project-setup.json diff --git a/skills/objectui-schema-expressions/evals/evals.json b/skills/objectui/evals/schema-expressions.json similarity index 100% rename from skills/objectui-schema-expressions/evals/evals.json rename to skills/objectui/evals/schema-expressions.json diff --git a/skills/objectui-sdui-page-builder/evals/evals.json b/skills/objectui/evals/sdui-page-builder.json similarity index 100% rename from skills/objectui-sdui-page-builder/evals/evals.json rename to skills/objectui/evals/sdui-page-builder.json diff --git a/skills/objectui-testing/evals/evals.json b/skills/objectui/evals/testing.json similarity index 100% rename from skills/objectui-testing/evals/evals.json rename to skills/objectui/evals/testing.json diff --git a/skills/objectui-auth-permissions/SKILL.md b/skills/objectui/guides/auth-permissions.md similarity index 100% rename from skills/objectui-auth-permissions/SKILL.md rename to skills/objectui/guides/auth-permissions.md diff --git a/skills/objectui-console-development/SKILL.md b/skills/objectui/guides/console-development.md similarity index 100% rename from skills/objectui-console-development/SKILL.md rename to skills/objectui/guides/console-development.md diff --git a/skills/objectui-data-integration/SKILL.md b/skills/objectui/guides/data-integration.md similarity index 100% rename from skills/objectui-data-integration/SKILL.md rename to skills/objectui/guides/data-integration.md diff --git a/skills/objectui-i18n/SKILL.md b/skills/objectui/guides/i18n.md similarity index 100% rename from skills/objectui-i18n/SKILL.md rename to skills/objectui/guides/i18n.md diff --git a/skills/objectui-mobile/SKILL.md b/skills/objectui/guides/mobile.md similarity index 100% rename from skills/objectui-mobile/SKILL.md rename to skills/objectui/guides/mobile.md diff --git a/skills/objectui-sdui-page-builder/SKILL.md b/skills/objectui/guides/page-builder.md similarity index 100% rename from skills/objectui-sdui-page-builder/SKILL.md rename to skills/objectui/guides/page-builder.md diff --git a/skills/objectui-plugin-development/SKILL.md b/skills/objectui/guides/plugin-development.md similarity index 100% rename from skills/objectui-plugin-development/SKILL.md rename to skills/objectui/guides/plugin-development.md diff --git a/skills/objectui-project-setup/SKILL.md b/skills/objectui/guides/project-setup.md similarity index 100% rename from skills/objectui-project-setup/SKILL.md rename to skills/objectui/guides/project-setup.md diff --git a/skills/objectui-schema-expressions/SKILL.md b/skills/objectui/guides/schema-expressions.md similarity index 100% rename from skills/objectui-schema-expressions/SKILL.md rename to skills/objectui/guides/schema-expressions.md diff --git a/skills/objectui-testing/SKILL.md b/skills/objectui/guides/testing.md similarity index 100% rename from skills/objectui-testing/SKILL.md rename to skills/objectui/guides/testing.md diff --git a/skills/objectui/rules/composition.md b/skills/objectui/rules/composition.md new file mode 100644 index 000000000..70abe1475 --- /dev/null +++ b/skills/objectui/rules/composition.md @@ -0,0 +1,297 @@ +# Component Composition Rules + +> **Critical:** Follow Shadcn composition patterns strictly for consistency and accessibility. + +## Rule: Use Full Card Composition + +**βœ… CORRECT:** +```typescript + + + Customer Summary + Active users this month + + + {/* main content */} + + + + + +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Don't dump everything in CardContent + + +

Customer Summary

+

Active users this month

+ {/* content */} + +
+
+``` + +## Rule: Overlays Need Accessible Titles + +Dialog, Sheet, and Drawer **always need a Title** for accessibility. + +**βœ… CORRECT:** +```typescript + + + Confirm Delete + {/* content */} + + + +// If visually hidden: +Settings +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Missing title - accessibility violation + + +

Are you sure?

+
+
+``` + +## Rule: Items Inside Their Group + +**βœ… CORRECT:** +```typescript + + + + + + + Profile + Settings + + + +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Items directly in Content without Group + + Apple + +``` + +## Rule: TabsTrigger Must Be Inside TabsList + +**βœ… CORRECT:** +```typescript + + + Account + Password + + ... + ... + +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Triggers directly in Tabs + + Account + ... + +``` + +## Rule: Avatar Always Needs Fallback + +**βœ… CORRECT:** +```typescript + + + {user.initials} + +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Missing fallback - shows nothing if image fails + + + +``` + +## Rule: Use Existing Components, Not Custom Markup + +**Callouts use Alert:** +```typescript +// βœ… Correct + + Error + Something went wrong. + + +// ❌ Wrong +
+ Error +

Something went wrong.

+
+``` + +**Empty states use Empty:** +```typescript +// βœ… Correct +} + title="No results found" + description="Try adjusting your search" +/> + +// ❌ Wrong +
+ +

No results found

+
+``` + +**Use Separator instead of hr:** +```typescript +// βœ… Correct + + +// ❌ Wrong +
+
+``` + +**Use Skeleton for loading:** +```typescript +// βœ… Correct + + +// ❌ Wrong +
+``` + +**Use Badge instead of styled spans:** +```typescript +// βœ… Correct +New + +// ❌ Wrong +New +``` + +## Rule: Toast via Sonner + +**βœ… CORRECT:** +```typescript +import { toast } from 'sonner'; + +toast.success('Item saved'); +toast.error('Failed to save'); +toast.info('Processing...'); +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Don't build custom toast systems +showNotification({ type: 'success', message: 'Saved' }); +``` + +## Rule: Button Loading State + +**βœ… CORRECT:** +```typescript + +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Button has no isPending/isLoading prop + +``` + +## Rule: Icons in Buttons Use data-icon + +**βœ… CORRECT:** +```typescript + + + +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ No data-icon attribute + + +// ❌ Manual sizing classes on icons + +``` + +**Why:** Components handle icon sizing via CSS. No manual `size-4` or `w-4 h-4` needed. + +## Rule: Pass Icons as Objects, Not String Keys + +**βœ… CORRECT:** +```typescript +import { CheckIcon } from 'lucide-react'; + + +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Don't use string lookups + +``` + +## Rule: Registry-Based Component Resolution + +For schema-driven rendering, use ComponentRegistry, not switch/case: + +**βœ… CORRECT:** +```typescript +const Component = ComponentRegistry.get(schema.type); +return ; +``` + +**❌ FORBIDDEN:** +```typescript +// ❌ Don't use switch statements +switch (schema.type) { + case 'button': return +} +``` + +### Step 2: Create a wrapper in `custom/` + +**βœ… CORRECT:** +```typescript +// packages/components/src/custom/enhanced-button.tsx +import { Button } from '../ui/button'; + +export function EnhancedButton({ loading, children, ...props }) { + return ( + + ); +} +``` + +### Step 3: Export from package index + +```typescript +// packages/components/src/index.ts +export { Button } from './ui/button'; // Original primitive +export { EnhancedButton } from './custom/enhanced-button'; // Wrapper +``` + +### Step 4: Register the wrapper if needed + +```typescript +// For schema-driven rendering +import { ComponentRegistry } from '@object-ui/core'; +import { EnhancedButton } from '@object-ui/components'; + +ComponentRegistry.register('enhanced-button', EnhancedButton); +``` + +## Directory Structure + +``` +packages/components/src/ +β”œβ”€β”€ ui/ # πŸ”’ NO-TOUCH ZONE - Shadcn upstream +β”‚ β”œβ”€β”€ button.tsx +β”‚ β”œβ”€β”€ card.tsx +β”‚ β”œβ”€β”€ dialog.tsx +β”‚ └── ... +β”œβ”€β”€ custom/ # βœ… SAFE ZONE - Custom wrappers +β”‚ β”œβ”€β”€ enhanced-button.tsx +β”‚ β”œβ”€β”€ data-card.tsx +β”‚ └── ... +β”œβ”€β”€ icons/ # βœ… SAFE ZONE - Icon components +└── index.ts # βœ… SAFE ZONE - Package exports +``` + +## Exception: CSS-Only Modifications + +If you need to add CSS rules for state-dependent behavior (e.g., Tailwind 4 sidebar compatibility), you may add them to: + +``` +packages/components/src/index.css +``` + +**Example (allowed):** +```css +/* Tailwind 4 sidebar state rules */ +.sidebar-menu-button-icon-mode { + /* Custom CSS for icon mode */ +} +``` + +**But NEVER modify the TypeScript/JSX in `ui/` files.** + +## Sync Script Reference + +The sync script that overwrites these files is located at: + +``` +scripts/shadcn-sync.js +``` + +This script fetches the latest components from shadcn/ui and overwrites local `ui/` files. + +## Summary + +| Action | Allowed? | Location | +|--------|----------|----------| +| Modify Button logic | ❌ NO | `ui/button.tsx` | +| Create ButtonWithIcon | βœ… YES | `custom/button-with-icon.tsx` | +| Modify Card styles | ❌ NO | `ui/card.tsx` | +| Create DataCard wrapper | βœ… YES | `custom/data-card.tsx` | +| Add CSS rules | βœ… YES | `index.css` | +| Fix TypeScript errors in ui/ | ❌ NO | Report upstream or use workaround | + +**When in doubt:** Create a wrapper in `custom/` instead of editing `ui/` files. diff --git a/skills/objectui/rules/protocol.md b/skills/objectui/rules/protocol.md new file mode 100644 index 000000000..8ee989fd7 --- /dev/null +++ b/skills/objectui/rules/protocol.md @@ -0,0 +1,151 @@ +# JSON Protocol Rules + +> **Critical:** All ObjectUI schemas MUST strictly follow `@objectstack/spec` definitions. + +## Rule: Expression Evaluation Boundaries + +### Fields That ARE Evaluated + +SchemaRenderer evaluates these fields automatically: + +| Field | Evaluation Type | Return Type | Example | +|---|---|---|---| +| `props.*` | Template (`${}`) | Preserves original type | `"props": { "count": "${items.length}" }` β†’ number | +| `content` | Template (`${}`) | string | `"content": "Total: ${data.total}"` | +| `hidden` | Condition | boolean | `"hidden": "${data.role !== 'admin'}"` | +| `hiddenOn` | Condition | boolean | `"hiddenOn": "data.status === 'draft'"` | +| `visible` | Condition | boolean | `"visible": "${data.isActive}"` | +| `visibleOn` | Condition | boolean | `"visibleOn": "data.permissions.canView"` | +| `disabled` | Condition | boolean | `"disabled": "${form.isSubmitting}"` | +| `disabledOn` | Condition | boolean | `"disabledOn": "!data.hasPermission"` | + +**Precedence rule:** `visible` takes priority over `hidden`. + +### Fields That are NOT Evaluated + +These top-level schema fields are passed as raw strings: + +- `value` β€” use `props.value` instead +- `label` β€” use `props.label` instead +- `description` β€” use `props.description` instead +- `title` β€” use `props.title` instead +- `className` β€” always a static Tailwind class string +- `id` β€” always a static string +- `type` β€” component type identifier +- `bind` β€” data scope path (resolved by `useDataScope`, not by expressions) + +## Rule: Component Schema Structure + +Every UI component node MUST follow this shape: + +```typescript +interface UIComponent { + type: string; // Required: component type identifier + id?: string; // Optional: unique identifier + props?: Record; // Optional: component properties + bind?: string; // Optional: data binding path + className?: string; // Optional: Tailwind CSS classes + hidden?: string; // Optional: visibility expression + disabled?: string; // Optional: disabled expression + events?: Record; // Optional: event handlers + children?: UIComponent[]; // Optional: nested components +} +``` + +## Rule: No Schema Property Invention + +**❌ FORBIDDEN:** Adding custom properties not defined in `@objectstack/spec`. + +**Example violation:** +```json +{ + "type": "grid", + "fields": [...], // ❌ spec uses "columns" + "customProp": "value" // ❌ not in spec +} +``` + +**βœ… CORRECT:** +```json +{ + "type": "grid", + "props": { + "columns": [...] // βœ… follows spec + } +} +``` + +## Rule: Type Preservation in Expressions + +When the entire string is a single `${expression}`, the result preserves its type: + +```json +"${data.count}" // β†’ returns number 42, not string "42" +"${data.isActive}" // β†’ returns boolean true, not string "true" +"Count: ${data.count}" // β†’ returns string "Count: 42" (mixed template) +``` + +## Rule: Data Binding Path Resolution + +The `bind` field is NOT expression-evaluated. It's a path string resolved by `useDataScope()`: + +```json +{ + "type": "data-table", + "bind": "customers", // Resolved as dataSource.customers + "props": { + "columns": [...] + } +} +``` + +**Nested paths work:** `"bind": "app.settings.users"` resolves `dataSource.app.settings.users`. + +## Rule: Action Event Structure + +Events must be defined as arrays of action definitions: + +```json +{ + "events": { + "onClick": [ + { "action": "validate", "target": "form_1" }, + { "action": "submit", "target": "form_1" }, + { "action": "navigate", "params": { "url": "/success" } } + ] + } +} +``` + +**❌ DO NOT** use function references or inline callbacks in JSON schemas. + +## Rule: Layout Responsiveness + +Layout components must support responsive properties: + +```json +{ + "type": "grid", + "props": { + "cols": { "sm": 1, "md": 2, "lg": 4 } // βœ… Responsive config + } +} +``` + +## Rule: Expression Security + +The expression parser blocks dangerous patterns: + +**❌ BLOCKED:** +- `eval()` +- `Function()` +- `setTimeout()`, `setInterval()` +- `import()`, `require()` +- `process.*`, `global.*` +- `window.*`, `document.*` +- `__proto__`, `constructor`, `prototype` + +**βœ… SAFE GLOBALS:** +- `Math` β€” `${Math.round(price)}` +- `JSON` β€” `${JSON.stringify(obj)}` +- `parseInt`, `parseFloat`, `isNaN`, `isFinite` diff --git a/skills/objectui/rules/styling.md b/skills/objectui/rules/styling.md new file mode 100644 index 000000000..3f055388f --- /dev/null +++ b/skills/objectui/rules/styling.md @@ -0,0 +1,240 @@ +# Styling Rules + +> **Critical:** ObjectUI uses Tailwind CSS exclusively. No inline styles, CSS modules, or styled-components. + +## Rule: Use Tailwind Utility Classes Only + +**βœ… CORRECT:** +```json +{ + "type": "card", + "className": "col-span-12 lg:col-span-4 p-6" +} +``` + +**❌ FORBIDDEN:** +```typescript +// Never do this in ObjectUI + +``` + +## Rule: Use `cn()` for Class Merging + +When combining classes programmatically, always use the `cn()` utility: + +```typescript +import { cn } from '@object-ui/components'; + +function MyComponent({ className, ...props }) { + return ( +
+ {/* content */} +
+ ); +} +``` + +**Why:** `cn()` uses `tailwind-merge` + `clsx` to properly handle class conflicts (e.g., `p-4` vs `p-6`). + +## Rule: Use Semantic Color Tokens + +**βœ… CORRECT:** +```typescript +className="bg-primary text-primary-foreground" +className="bg-secondary text-secondary-foreground" +className="bg-muted text-muted-foreground" +className="bg-destructive text-destructive-foreground" +className="border-border" +``` + +**❌ FORBIDDEN:** +```typescript +className="bg-blue-500 text-white" // ❌ Hard-coded color +className="bg-[#3b82f6]" // ❌ Arbitrary value +``` + +**Why:** Semantic tokens support theming and dark mode automatically. + +## Rule: Use Component Variants (CVA) + +For component variations, use `class-variance-authority`: + +```typescript +import { cva } from 'class-variance-authority'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center rounded-md', // base + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground', + destructive: 'bg-destructive text-destructive-foreground', + outline: 'border border-input bg-background', + }, + size: { + default: 'h-10 px-4 py-2', + sm: 'h-9 px-3', + lg: 'h-11 px-8', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + } +); +``` + +## Rule: Expose `className` in Schema Props + +**Every component must accept `className` in its schema props** to allow JSON-level style overrides: + +```json +{ + "type": "card", + "className": "bg-red-500", // βœ… User can override styles + "props": { + "title": "Alert" + } +} +``` + +## Rule: Responsive Classes + +Use Tailwind's responsive prefixes in schemas: + +```json +{ + "type": "grid", + "className": "grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" +} +``` + +## Rule: Dark Mode with Semantic Tokens + +**❌ DO NOT** manually add `dark:` variants: + +```typescript +// ❌ Wrong +className="bg-white dark:bg-gray-900" +``` + +**βœ… CORRECT:** +```typescript +// βœ… Semantic tokens handle dark mode automatically +className="bg-background text-foreground" +``` + +## Rule: No Manual Z-Index + +**❌ DO NOT** manually set `z-index` on overlay components: + +```typescript +// ❌ Wrong + +``` + +**βœ… CORRECT:** +```typescript +// βœ… Dialog, Sheet, Popover, etc. handle their own stacking context + +``` + +## Rule: Spacing with `gap-*` not `space-*` + +**βœ… CORRECT:** +```typescript +className="flex flex-col gap-4" +className="grid grid-cols-3 gap-6" +``` + +**❌ FORBIDDEN:** +```typescript +className="space-y-4" // ❌ Deprecated +className="space-x-6" // ❌ Use gap instead +``` + +## Rule: Use `size-*` for Equal Dimensions + +**βœ… CORRECT:** +```typescript +className="size-10" // width and height both 40px +``` + +**❌ FORBIDDEN:** +```typescript +className="w-10 h-10" // ❌ Redundant +``` + +## Rule: Use `truncate` Shorthand + +**βœ… CORRECT:** +```typescript +className="truncate" +``` + +**❌ FORBIDDEN:** +```typescript +className="overflow-hidden text-ellipsis whitespace-nowrap" +``` + +## Rule: Tailwind 4 CSS Variables Setup + +For ObjectUI components to render correctly, third-party projects must include this CSS setup: + +```css +@import "tailwindcss"; + +/* Scan ObjectUI packages so Tailwind generates their utility classes */ +@source "../../node_modules/@object-ui/components/src/**/*.tsx"; +@source "../../node_modules/@object-ui/fields/src/**/*.tsx"; +@source "../../node_modules/@object-ui/layout/src/**/*.tsx"; +@source "../../node_modules/@object-ui/react/src/**/*.tsx"; + +/* Map Shadcn CSS variables to Tailwind 4 color tokens */ +@theme { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + +/* Light mode CSS variables (Shadcn defaults) */ +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --radius: 0.625rem; +} +``` + +**Without this setup, ObjectUI components will render but look completely unstyled.**