From 3d0848cd1fb66514dbe7e6934988f5f7a805703b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:11:38 +0000 Subject: [PATCH 1/6] Initial plan From 3e1a99a7775b7ce298edadf99641a757fd811db4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:23:19 +0000 Subject: [PATCH 2/6] feat: add playground app with split-view editor and example schemas Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/playground/.gitignore | 24 + apps/playground/README.md | 69 +++ apps/playground/index.html | 13 + apps/playground/package.json | 32 ++ apps/playground/postcss.config.js | 6 + apps/playground/src/App.tsx | 195 ++++++++ apps/playground/src/data/examples.ts | 650 +++++++++++++++++++++++++++ apps/playground/src/index.css | 69 +++ apps/playground/src/main.tsx | 10 + apps/playground/tailwind.config.js | 61 +++ apps/playground/tsconfig.json | 25 ++ apps/playground/vite.config.ts | 19 + package.json | 4 + pnpm-lock.yaml | 103 +++++ pnpm-workspace.yaml | 1 + 15 files changed, 1281 insertions(+) create mode 100644 apps/playground/.gitignore create mode 100644 apps/playground/README.md create mode 100644 apps/playground/index.html create mode 100644 apps/playground/package.json create mode 100644 apps/playground/postcss.config.js create mode 100644 apps/playground/src/App.tsx create mode 100644 apps/playground/src/data/examples.ts create mode 100644 apps/playground/src/index.css create mode 100644 apps/playground/src/main.tsx create mode 100644 apps/playground/tailwind.config.js create mode 100644 apps/playground/tsconfig.json create mode 100644 apps/playground/vite.config.ts diff --git a/apps/playground/.gitignore b/apps/playground/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/apps/playground/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/apps/playground/README.md b/apps/playground/README.md new file mode 100644 index 000000000..55d1b6a7b --- /dev/null +++ b/apps/playground/README.md @@ -0,0 +1,69 @@ +# Object UI Playground + +A live, interactive playground to showcase Object UI's schema-driven rendering capabilities. + +## Features + +- **Split View Editor**: Monaco Editor (VS Code style) on the left, live preview on the right +- **Real-time Rendering**: See your changes instantly as you edit JSON schemas +- **Example Gallery**: Curated examples organized by category (Primitives, Layouts, Forms) +- **Responsive Preview**: Toggle between desktop, tablet, and mobile viewports +- **Copy Schema**: One-click copy to clipboard for easy integration +- **Error Highlighting**: Clear JSON syntax error messages + +## Examples Included + +### Primitives +- Simple page layouts +- Input component states (required, disabled, email) +- Button variants (destructive, outline, ghost, etc.) + +### Layouts +- Responsive grid layouts +- Analytics dashboard with KPI cards +- Tabs component demonstration + +### Forms +- User registration form with various input types +- Grid-based form layouts + +## Running the Playground + +From the monorepo root: + +```bash +pnpm install +pnpm --filter @apps/playground dev +``` + +Or from this directory: + +```bash +pnpm install +pnpm dev +``` + +The playground will be available at `http://localhost:5174` + +## Building for Production + +```bash +pnpm build +``` + +## Purpose + +This playground serves as: + +1. **Product Demo**: Show what Object UI can do without any backend +2. **Learning Tool**: Help developers understand schema structure +3. **Testing Ground**: Experiment with different configurations +4. **Documentation**: Live, interactive examples are better than static code samples + +## Key Selling Points Demonstrated + +- ✅ **Tailwind Native**: Edit `className` properties and see instant results +- ✅ **Schema-Driven**: Everything is pure JSON - no JSX needed +- ✅ **Responsive**: Built-in responsive grid layouts +- ✅ **Complete**: From simple buttons to complex dashboards +- ✅ **Standalone**: No backend required - works with any data source diff --git a/apps/playground/index.html b/apps/playground/index.html new file mode 100644 index 000000000..ea85be41d --- /dev/null +++ b/apps/playground/index.html @@ -0,0 +1,13 @@ + + + + + + + Object UI - Live Playground + + +
+ + + diff --git a/apps/playground/package.json b/apps/playground/package.json new file mode 100644 index 000000000..b428766a7 --- /dev/null +++ b/apps/playground/package.json @@ -0,0 +1,32 @@ +{ + "name": "@apps/playground", + "private": true, + "license": "MIT", + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@monaco-editor/react": "^4.6.0", + "@object-ui/core": "workspace:*", + "@object-ui/react": "workspace:*", + "@object-ui/components": "workspace:*", + "lucide-react": "^0.469.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^5.1.1", + "autoprefixer": "^10.4.23", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.19", + "typescript": "~5.9.3", + "vite": "^7.2.4" + } +} diff --git a/apps/playground/postcss.config.js b/apps/playground/postcss.config.js new file mode 100644 index 000000000..2e7af2b7f --- /dev/null +++ b/apps/playground/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/apps/playground/src/App.tsx b/apps/playground/src/App.tsx new file mode 100644 index 000000000..b525f14ec --- /dev/null +++ b/apps/playground/src/App.tsx @@ -0,0 +1,195 @@ +import React, { useState, useEffect } from 'react'; +import Editor from '@monaco-editor/react'; +import { SchemaRenderer } from '@object-ui/react'; +import '@object-ui/components'; +import { examples, exampleCategories, ExampleKey } from './data/examples'; +import { Monitor, Tablet, Smartphone, Copy, Check } from 'lucide-react'; + +type ViewportSize = 'desktop' | 'tablet' | 'mobile'; + +export default function Playground() { + const [selectedExample, setSelectedExample] = useState('dashboard'); + const [code, setCode] = useState(examples['dashboard']); + const [schema, setSchema] = useState(null); + const [jsonError, setJsonError] = useState(null); + const [viewportSize, setViewportSize] = useState('desktop'); + const [copied, setCopied] = useState(false); + + // Real-time JSON parsing + useEffect(() => { + try { + const parsed = JSON.parse(code); + setSchema(parsed); + setJsonError(null); + } catch (e) { + setJsonError((e as Error).message); + // Keep previous schema on error + } + }, [code]); + + const handleExampleChange = (key: ExampleKey) => { + setSelectedExample(key); + setCode(examples[key]); + }; + + const handleCopySchema = async () => { + try { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy:', err); + } + }; + + const viewportStyles: Record = { + desktop: 'w-full', + tablet: 'max-w-3xl mx-auto', + mobile: 'max-w-md mx-auto' + }; + + return ( +
+ {/* 1. Sidebar: Example Selector */} + + + {/* 2. Middle: Code Editor */} +
+
+

JSON Schema

+ +
+ + {jsonError && ( +
+ JSON Error: {jsonError} +
+ )} + +
+ setCode(val || '')} + options={{ + minimap: { enabled: false }, + fontSize: 14, + lineNumbers: 'on', + scrollBeyondLastLine: false, + automaticLayout: true, + tabSize: 2, + }} + theme="vs-light" + /> +
+
+ + {/* 3. Right: Live Preview */} +
+
+

Preview

+ + {/* Viewport Size Toggles */} +
+ + + +
+
+ +
+
+
+ {schema && !jsonError ? ( + + ) : ( +
+ {jsonError ? ( +
+

Invalid JSON

+

Fix the syntax error to see the preview

+
+ ) : ( +

Select an example to get started

+ )} +
+ )} +
+
+
+
+
+ ); +} diff --git a/apps/playground/src/data/examples.ts b/apps/playground/src/data/examples.ts new file mode 100644 index 000000000..8c393c104 --- /dev/null +++ b/apps/playground/src/data/examples.ts @@ -0,0 +1,650 @@ +/** + * Predefined JSON schema examples for the Object UI Playground + * Organized by category to showcase different capabilities + */ + +export const examples = { + // A. Basic Primitives - Showcase Shadcn component wrapping + 'input-states': `{ + "type": "div", + "className": "space-y-6 max-w-md", + "body": [ + { + "type": "div", + "className": "space-y-2", + "body": [ + { + "type": "text", + "content": "Input Component States", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "Demonstrating various input states and configurations", + "className": "text-muted-foreground" + } + ] + }, + { + "type": "input", + "label": "Regular Input", + "id": "regular", + "placeholder": "Enter your name" + }, + { + "type": "input", + "label": "Required Field", + "id": "required", + "required": true, + "placeholder": "This field is required" + }, + { + "type": "input", + "label": "Disabled Input", + "id": "disabled", + "disabled": true, + "value": "Cannot edit this" + }, + { + "type": "input", + "label": "Email Input", + "id": "email", + "inputType": "email", + "placeholder": "user@example.com" + } + ] +}`, + + 'button-variants': `{ + "type": "div", + "className": "space-y-6 max-w-2xl", + "body": [ + { + "type": "div", + "className": "space-y-2", + "body": [ + { + "type": "text", + "content": "Button Variants", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "Different button styles using Shadcn variants", + "className": "text-muted-foreground" + } + ] + }, + { + "type": "div", + "className": "space-y-4", + "body": [ + { + "type": "div", + "className": "flex flex-wrap gap-2", + "body": [ + { + "type": "button", + "label": "Default" + }, + { + "type": "button", + "label": "Destructive", + "variant": "destructive" + }, + { + "type": "button", + "label": "Outline", + "variant": "outline" + }, + { + "type": "button", + "label": "Secondary", + "variant": "secondary" + }, + { + "type": "button", + "label": "Ghost", + "variant": "ghost" + }, + { + "type": "button", + "label": "Link", + "variant": "link" + } + ] + }, + { + "type": "div", + "className": "space-y-2", + "body": [ + { + "type": "text", + "content": "Tailwind Native: Custom Styling", + "className": "text-sm font-semibold" + }, + { + "type": "button", + "label": "Purple Custom Button", + "className": "bg-purple-500 hover:bg-purple-700 text-white" + } + ] + } + ] + } + ] +}`, + + // B. Complex Layouts - The killer feature + 'grid-layout': `{ + "type": "div", + "className": "space-y-6", + "body": [ + { + "type": "div", + "className": "space-y-2", + "body": [ + { + "type": "text", + "content": "Responsive Grid Layout", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "Complex nested grid with responsive breakpoints", + "className": "text-muted-foreground" + } + ] + }, + { + "type": "div", + "className": "grid gap-4 md:grid-cols-2 lg:grid-cols-3", + "body": [ + { + "type": "card", + "className": "md:col-span-2", + "title": "Wide Card", + "description": "This card spans 2 columns on medium screens", + "body": { + "type": "div", + "className": "p-6 pt-0", + "body": { + "type": "text", + "content": "Try resizing your browser to see the responsive behavior!" + } + } + }, + { + "type": "card", + "title": "Regular Card", + "body": { + "type": "div", + "className": "p-6 pt-0", + "body": { + "type": "text", + "content": "Standard card" + } + } + }, + { + "type": "card", + "title": "Card 1", + "body": { + "type": "div", + "className": "p-6 pt-0", + "body": { + "type": "text", + "content": "Content 1" + } + } + }, + { + "type": "card", + "title": "Card 2", + "body": { + "type": "div", + "className": "p-6 pt-0", + "body": { + "type": "text", + "content": "Content 2" + } + } + }, + { + "type": "card", + "title": "Card 3", + "body": { + "type": "div", + "className": "p-6 pt-0", + "body": { + "type": "text", + "content": "Content 3" + } + } + } + ] + } + ] +}`, + + 'dashboard': `{ + "type": "div", + "className": "space-y-6", + "body": [ + { + "type": "div", + "className": "flex items-center justify-between", + "body": [ + { + "type": "div", + "className": "space-y-1", + "body": [ + { + "type": "text", + "content": "Analytics Dashboard", + "className": "text-2xl font-bold tracking-tight" + }, + { + "type": "text", + "content": "Overview of your project performance and metrics.", + "className": "text-sm text-muted-foreground" + } + ] + }, + { + "type": "div", + "className": "flex items-center gap-2", + "body": [ + { + "type": "button", + "label": "Download", + "variant": "outline", + "size": "sm" + }, + { + "type": "button", + "label": "Create Report", + "size": "sm" + } + ] + } + ] + }, + { + "type": "div", + "className": "grid gap-4 md:grid-cols-2 lg:grid-cols-4", + "body": [ + { + "type": "card", + "className": "shadow-sm hover:shadow-md transition-shadow", + "body": [ + { + "type": "div", + "className": "p-6 pb-2", + "body": { + "type": "text", + "content": "Total Revenue", + "className": "text-sm font-medium text-muted-foreground" + } + }, + { + "type": "div", + "className": "p-6 pt-0", + "body": [ + { + "type": "text", + "content": "$45,231.89", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "+20.1% from last month", + "className": "text-xs text-muted-foreground mt-1" + } + ] + } + ] + }, + { + "type": "card", + "className": "shadow-sm hover:shadow-md transition-shadow", + "body": [ + { + "type": "div", + "className": "p-6 pb-2", + "body": { + "type": "text", + "content": "Subscriptions", + "className": "text-sm font-medium text-muted-foreground" + } + }, + { + "type": "div", + "className": "p-6 pt-0", + "body": [ + { + "type": "text", + "content": "+2,350", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "+180.1% from last month", + "className": "text-xs text-muted-foreground mt-1" + } + ] + } + ] + }, + { + "type": "card", + "className": "shadow-sm hover:shadow-md transition-shadow", + "body": [ + { + "type": "div", + "className": "p-6 pb-2", + "body": { + "type": "text", + "content": "Sales", + "className": "text-sm font-medium text-muted-foreground" + } + }, + { + "type": "div", + "className": "p-6 pt-0", + "body": [ + { + "type": "text", + "content": "+12,234", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "+19% from last month", + "className": "text-xs text-muted-foreground mt-1" + } + ] + } + ] + }, + { + "type": "card", + "className": "shadow-sm hover:shadow-md transition-shadow", + "body": [ + { + "type": "div", + "className": "p-6 pb-2", + "body": { + "type": "text", + "content": "Active Now", + "className": "text-sm font-medium text-muted-foreground" + } + }, + { + "type": "div", + "className": "p-6 pt-0", + "body": [ + { + "type": "text", + "content": "+573", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "+201 since last hour", + "className": "text-xs text-muted-foreground mt-1" + } + ] + } + ] + } + ] + }, + { + "type": "card", + "className": "shadow-sm", + "title": "Recent Activity", + "description": "Your latest component interactions", + "body": { + "type": "div", + "className": "p-6 pt-0 space-y-2", + "body": [ + { + "type": "text", + "content": "Schema updated successfully", + "className": "text-sm" + }, + { + "type": "text", + "content": "New component rendered at 10:42 AM", + "className": "text-sm text-muted-foreground" + } + ] + } + } + ] +}`, + + 'tabs-demo': `{ + "type": "div", + "className": "space-y-6", + "body": [ + { + "type": "text", + "content": "Tabs Component", + "className": "text-2xl font-bold" + }, + { + "type": "tabs", + "defaultValue": "account", + "className": "w-full", + "items": [ + { + "value": "account", + "label": "Account", + "body": { + "type": "card", + "title": "Account Settings", + "description": "Make changes to your account here.", + "body": { + "type": "div", + "className": "p-6 pt-0 space-y-4", + "body": [ + { + "type": "input", + "label": "Name", + "id": "name", + "value": "Pedro Duarte" + }, + { + "type": "input", + "label": "Username", + "id": "username", + "value": "@peduarte" + }, + { + "type": "button", + "label": "Save changes" + } + ] + } + } + }, + { + "value": "password", + "label": "Password", + "body": { + "type": "card", + "title": "Password", + "description": "Change your password here.", + "body": { + "type": "div", + "className": "p-6 pt-0 space-y-4", + "body": [ + { + "type": "input", + "label": "Current password", + "id": "current", + "inputType": "password" + }, + { + "type": "input", + "label": "New password", + "id": "new", + "inputType": "password" + }, + { + "type": "button", + "label": "Update password" + } + ] + } + } + }, + { + "value": "notifications", + "label": "Notifications", + "body": { + "type": "card", + "title": "Notifications", + "description": "Configure how you receive notifications.", + "body": { + "type": "div", + "className": "p-6 pt-0", + "body": { + "type": "text", + "content": "Notification settings will be displayed here." + } + } + } + } + ] + } + ] +}`, + + // C. Form with simple structure (no data linkage for now as that requires runtime state) + 'form-demo': `{ + "type": "div", + "className": "max-w-2xl space-y-6", + "body": [ + { + "type": "div", + "className": "space-y-2", + "body": [ + { + "type": "text", + "content": "User Registration Form", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "A comprehensive form demonstrating various input types", + "className": "text-muted-foreground" + } + ] + }, + { + "type": "card", + "className": "shadow-sm", + "body": { + "type": "div", + "className": "p-6 space-y-6", + "body": [ + { + "type": "div", + "className": "grid gap-4 md:grid-cols-2", + "body": [ + { + "type": "input", + "label": "First Name", + "id": "firstName", + "required": true, + "placeholder": "John" + }, + { + "type": "input", + "label": "Last Name", + "id": "lastName", + "required": true, + "placeholder": "Doe" + } + ] + }, + { + "type": "input", + "label": "Email Address", + "id": "email", + "inputType": "email", + "required": true, + "placeholder": "john.doe@example.com" + }, + { + "type": "input", + "label": "Password", + "id": "password", + "inputType": "password", + "required": true, + "placeholder": "••••••••" + }, + { + "type": "div", + "className": "flex items-center justify-end gap-2", + "body": [ + { + "type": "button", + "label": "Cancel", + "variant": "outline" + }, + { + "type": "button", + "label": "Create Account" + } + ] + } + ] + } + } + ] +}`, + + 'simple-page': `{ + "type": "div", + "className": "space-y-4", + "body": [ + { + "type": "text", + "content": "Welcome to Object UI", + "className": "text-3xl font-bold" + }, + { + "type": "text", + "content": "The Universal, Schema-Driven Rendering Engine", + "className": "text-xl text-muted-foreground" + }, + { + "type": "div", + "className": "flex gap-2 mt-4", + "body": [ + { + "type": "button", + "label": "Get Started" + }, + { + "type": "button", + "label": "Learn More", + "variant": "outline" + } + ] + } + ] +}` +}; + +export type ExampleKey = keyof typeof examples; + +export const exampleCategories = { + 'Primitives': ['simple-page', 'input-states', 'button-variants'], + 'Layouts': ['grid-layout', 'dashboard', 'tabs-demo'], + 'Forms': ['form-demo'] +}; diff --git a/apps/playground/src/index.css b/apps/playground/src/index.css new file mode 100644 index 000000000..d924e2434 --- /dev/null +++ b/apps/playground/src/index.css @@ -0,0 +1,69 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + --radius: 0.5rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/playground/src/main.tsx b/apps/playground/src/main.tsx new file mode 100644 index 000000000..cc14d8376 --- /dev/null +++ b/apps/playground/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App'; + +createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/apps/playground/tailwind.config.js b/apps/playground/tailwind.config.js new file mode 100644 index 000000000..5a8596424 --- /dev/null +++ b/apps/playground/tailwind.config.js @@ -0,0 +1,61 @@ +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: ["class"], + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + "../../packages/components/src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + } + } + } + }, + plugins: [require("tailwindcss-animate")], +} diff --git a/apps/playground/tsconfig.json b/apps/playground/tsconfig.json new file mode 100644 index 000000000..df2545fa1 --- /dev/null +++ b/apps/playground/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/apps/playground/vite.config.ts b/apps/playground/vite.config.ts new file mode 100644 index 000000000..b521469a9 --- /dev/null +++ b/apps/playground/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@object-ui/components': path.resolve(__dirname, '../../packages/components/src'), + '@object-ui/core': path.resolve(__dirname, '../../packages/core/src'), + '@object-ui/react': path.resolve(__dirname, '../../packages/react/src'), + '@': path.resolve(__dirname, '../../packages/components/src'), + } + }, + server: { + port: 5174 + } +}); diff --git a/package.json b/package.json index af59a1d75..558f4ca13 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,15 @@ "workspaces": [ "packages/*", "examples/*", + "apps/*", "docs" ], "scripts": { "dev": "pnpm --filter prototype dev", "start": "pnpm --filter prototype preview", + "playground:dev": "pnpm --filter @apps/playground dev", + "playground:build": "pnpm --filter @apps/playground build", + "playground:preview": "pnpm --filter @apps/playground preview", "build": "pnpm -r build", "test": "pnpm -r test", "docs:dev": "pnpm --filter object-ui-docs dev", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da91e8c72..55266b828 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,6 +69,55 @@ importers: specifier: ^4.0.17 version: 4.0.17(@types/node@24.10.8)(@vitest/ui@4.0.17)(happy-dom@20.1.0)(jiti@1.21.7)(jsdom@27.4.0) + apps/playground: + dependencies: + '@monaco-editor/react': + specifier: ^4.6.0 + version: 4.7.0(monaco-editor@0.55.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@object-ui/components': + specifier: workspace:* + version: link:../../packages/components + '@object-ui/core': + specifier: workspace:* + version: link:../../packages/core + '@object-ui/react': + specifier: workspace:* + version: link:../../packages/react + lucide-react: + specifier: ^0.469.0 + version: 0.469.0(react@18.3.1) + react: + specifier: 18.3.1 + version: 18.3.1 + react-dom: + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) + devDependencies: + '@types/react': + specifier: 18.3.12 + version: 18.3.12 + '@types/react-dom': + specifier: 18.3.1 + version: 18.3.1 + '@vitejs/plugin-react': + specifier: ^5.1.1 + version: 5.1.2(vite@7.3.1(@types/node@24.10.8)(jiti@1.21.7)) + autoprefixer: + specifier: ^10.4.23 + version: 10.4.23(postcss@8.5.6) + postcss: + specifier: ^8.5.6 + version: 8.5.6 + tailwindcss: + specifier: ^3.4.19 + version: 3.4.19 + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: ^7.2.4 + version: 7.3.1(@types/node@24.10.8)(jiti@1.21.7) + docs: devDependencies: vitepress: @@ -1113,6 +1162,16 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@monaco-editor/loader@1.7.0': + resolution: {integrity: sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==} + + '@monaco-editor/react@4.7.0': + resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: 18.3.1 + react-dom: 18.3.1 + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2111,6 +2170,9 @@ packages: '@types/react@18.3.12': resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -2730,6 +2792,9 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dompurify@3.2.7: + resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==} + electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} @@ -3270,6 +3335,11 @@ packages: mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + marked@14.0.0: + resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==} + engines: {node: '>= 18'} + hasBin: true + mdast-util-to-hast@13.2.1: resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} @@ -3330,6 +3400,9 @@ packages: mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + monaco-editor@0.55.1: + resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -3775,6 +3848,9 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -4940,6 +5016,17 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} + '@monaco-editor/loader@1.7.0': + dependencies: + state-local: 1.0.7 + + '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@monaco-editor/loader': 1.7.0 + monaco-editor: 0.55.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5961,6 +6048,9 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.2.3 + '@types/trusted-types@2.0.7': + optional: true + '@types/unist@3.0.3': {} '@types/use-sync-external-store@0.0.6': {} @@ -6650,6 +6740,10 @@ snapshots: dom-accessibility-api@0.6.3: {} + dompurify@3.2.7: + optionalDependencies: + '@types/trusted-types': 2.0.7 + electron-to-chromium@1.5.267: {} embla-carousel-react@8.6.0(react@18.3.1): @@ -7283,6 +7377,8 @@ snapshots: mark.js@8.11.1: {} + marked@14.0.0: {} + mdast-util-to-hast@13.2.1: dependencies: '@types/hast': 3.0.4 @@ -7350,6 +7446,11 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.2 + monaco-editor@0.55.1: + dependencies: + dompurify: 3.2.7 + marked: 14.0.0 + mrmime@2.0.1: {} ms@2.1.3: {} @@ -7767,6 +7868,8 @@ snapshots: stackback@0.0.2: {} + state-local@1.0.7: {} + std-env@3.10.0: {} string-argv@0.3.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dd8cb5185..76e94287c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ packages: - 'packages/*' - 'examples/*' + - 'apps/*' - 'docs' From 6892bd8d39db11b5859f6396f2649a4b49d82f75 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:29:00 +0000 Subject: [PATCH 3/6] fix: replace Monaco Editor with textarea for better compatibility Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/playground/package.json | 3 +-- apps/playground/src/App.tsx | 27 ++++++++++++--------------- pnpm-lock.yaml | 12 ++++++++++++ 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/apps/playground/package.json b/apps/playground/package.json index b428766a7..54562eb57 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -11,10 +11,9 @@ "preview": "vite preview" }, "dependencies": { - "@monaco-editor/react": "^4.6.0", + "@object-ui/components": "workspace:*", "@object-ui/core": "workspace:*", "@object-ui/react": "workspace:*", - "@object-ui/components": "workspace:*", "lucide-react": "^0.469.0", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/apps/playground/src/App.tsx b/apps/playground/src/App.tsx index b525f14ec..3fb4db246 100644 --- a/apps/playground/src/App.tsx +++ b/apps/playground/src/App.tsx @@ -1,9 +1,8 @@ import React, { useState, useEffect } from 'react'; -import Editor from '@monaco-editor/react'; import { SchemaRenderer } from '@object-ui/react'; import '@object-ui/components'; import { examples, exampleCategories, ExampleKey } from './data/examples'; -import { Monitor, Tablet, Smartphone, Copy, Check } from 'lucide-react'; +import { Monitor, Tablet, Smartphone, Copy, Check, Code2 } from 'lucide-react'; type ViewportSize = 'desktop' | 'tablet' | 'mobile'; @@ -88,7 +87,10 @@ export default function Playground() { {/* 2. Middle: Code Editor */}
-

JSON Schema

+
+ +

JSON Schema

+