diff --git a/IMPROVEMENTS.md b/IMPROVEMENTS.md new file mode 100644 index 000000000..62b0ad0d4 --- /dev/null +++ b/IMPROVEMENTS.md @@ -0,0 +1,143 @@ +# MetaBuilder UI Improvements + +## Summary of Changes + +This update transforms the god-tier builder interface from a technical code-heavy tool into a user-friendly GUI-based system with the following major improvements: + +### 1. **CSS Class Builder** +- Visual CSS class selector with categorized classes +- No more typing raw CSS - click to select from predefined classes +- Custom class input for edge cases +- Real-time preview of selected classes +- Classes organized by category: Layout, Spacing, Sizing, Typography, Colors, Borders, Effects, Positioning, Alignment, Interactivity + +**Location:** `src/components/CssClassBuilder.tsx` +**Usage:** Automatically integrated into PropertyInspector for any `className` prop + +### 2. **Dynamic Dropdown Configuration System** +- Create custom dropdown options from the god-tier panel +- Configure dropdown values once, use them across multiple properties +- GUI-based option management (no JSON editing required) +- Dropdowns can be assigned to component properties dynamically + +**Location:** `src/components/DropdownConfigManager.tsx` +**Access:** God-tier panel → "Dropdowns" tab + +### 3. **CSS Class Manager** +- Manage the library of CSS classes available in the builder +- Organize classes into categories +- Add/edit/delete categories and their classes +- Populated with comprehensive Tailwind utility classes by default + +**Location:** `src/components/CssClassManager.tsx` +**Access:** God-tier panel → "CSS Classes" tab + +### 4. **Monaco-Based JSON Editor** +- Replaced plain textboxes with professional Monaco editor +- Syntax highlighting for JSON +- Auto-formatting and validation +- Better error messages +- Tree-style folding and bracket colorization + +**Location:** `src/components/JsonEditor.tsx` +**Used in:** SchemaEditor and anywhere JSON needs to be edited + +### 5. **Enhanced Property Inspector** +- CSS classes now have a visual builder button +- Dynamic dropdowns support (properties can use configured dropdowns) +- Better visual hierarchy +- Icon-based property types + +**Updated:** `src/components/PropertyInspector.tsx` + +### 6. **Extended Database Schema** +- New tables for CSS class categories +- New tables for dropdown configurations +- Automatic seeding with 200+ Tailwind classes + +**Updated:** `src/lib/database.ts` + +### 7. **God-Tier Panel Enhancements** +- Two new tabs: "CSS Classes" and "Dropdowns" +- Better organization of configuration options +- More intuitive navigation + +**Updated:** `src/components/Level4.tsx` + +## Key Features + +### For Non-Technical Users +- **Point-and-click CSS editing** - No need to remember class names +- **Visual dropdown configuration** - Create select options without coding +- **Professional code editor** - When JSON is needed, get proper tooling +- **Organized categorization** - Everything is grouped logically + +### For Technical Users +- **Extensible system** - Easy to add new CSS categories +- **Custom class support** - Can still add custom CSS when needed +- **JSON import/export** - Full control when needed +- **Monaco editor** - Industry-standard code editing experience + +## How to Use + +### Creating a Dropdown Configuration +1. Go to Level 4 (God-Tier Panel) +2. Click "Dropdowns" tab +3. Click "Create Dropdown" +4. Enter a name (e.g., `status_options`) +5. Add options with values and labels +6. Save + +### Using Dynamic Dropdowns in Components +In the component catalog, define a property like: +```typescript +{ + name: 'status', + label: 'Status', + type: 'dynamic-select', + dynamicSource: 'status_options' // references dropdown config by name +} +``` + +### Managing CSS Classes +1. Go to Level 4 (God-Tier Panel) +2. Click "CSS Classes" tab +3. Browse categories or create new ones +4. Add/remove classes from categories +5. These classes will appear in the CSS Class Builder + +### Building CSS Classes Visually +1. Select a component in the builder +2. In the Property Inspector, find the "CSS Classes" field +3. Click the palette icon next to it +4. Select classes from categories +5. Preview and apply + +## Database Keys + +New database keys added: +- `db_css_classes` - Stores CSS class categories +- `db_dropdown_configs` - Stores dropdown configurations + +## Component Property Types + +New property type added: `'dynamic-select'` +- References a dropdown configuration by name +- Options are loaded from the database +- Can be managed from god-tier panel + +## Default CSS Categories + +The system comes pre-loaded with 10 categories: +1. **Layout** - flex, grid, block, inline, etc. +2. **Spacing** - padding, margin, gap classes +3. **Sizing** - width, height, max-width classes +4. **Typography** - text sizes, weights, alignment +5. **Colors** - text, background, border colors +6. **Borders** - border styles and radii +7. **Effects** - shadows, opacity, transitions +8. **Positioning** - relative, absolute, z-index +9. **Alignment** - items-center, justify-between, etc. +10. **Interactivity** - cursor, hover, active states + +Total: 200+ classes ready to use diff --git a/PRD.md b/PRD.md index 63c7830a0..09b4298fc 100644 --- a/PRD.md +++ b/PRD.md @@ -1,176 +1,186 @@ -# Planning Guide +# PRD: MetaBuilder Visual Configuration System -A meta-architecture system with 4 distinct levels: Level 1 (public-facing website with sample content), Level 2 (user area with profiles and comments), Level 3 (Django-style admin panel for data management), and Level 4 (god-tier builder where all previous levels can be designed, developed, and tested through visual workflows, GUI editors for JSON schemas, and Lua scripting). +## Mission Statement +Transform the MetaBuilder god-tier panel from a technical, code-heavy interface into an intuitive visual configuration system that empowers both technical and non-technical users to build sophisticated applications through GUI-based tools. -**Experience Qualities**: -1. **Layered** - Clear separation between public, user, admin, and meta-builder levels with intuitive navigation between tiers -2. **Generative** - Level 4 can procedurally generate entire applications for Levels 1-3 through declarative JSON schemas and visual workflows -3. **Powerful** - Lua lambdas for custom logic, visual JSON schema editor, workflow system for complex processes, all through an intuitive GUI +## Experience Qualities +1. **Intuitive** - Users should discover features naturally without extensive documentation, with visual cues guiding them through complex configurations. +2. **Empowering** - Non-technical users can accomplish sophisticated customization without writing code, while technical users retain full control when needed. +3. **Efficient** - Common tasks that previously required typing or memorization are now accomplished through point-and-click interactions, dramatically reducing configuration time. -**Complexity Level**: Complex Application (advanced functionality, likely with multiple views) -This is a 4-tier meta-application builder: a public website layer, authenticated user area, admin panel, and a god-tier visual builder that can procedurally generate all three previous layers using JSON schemas, workflow systems, and embedded Lua scripting. +## Complexity Level +**Complex Application** (advanced functionality with multiple views) - This is a meta-framework for building applications with four distinct user levels, database management, visual component builders, and dynamic configuration systems. ## Essential Features -### Level 1: Public Website -- **Functionality**: Normal webpage with responsive hamburger menu, hero section, content areas, footer -- **Purpose**: Public-facing content accessible to anyone without authentication -- **Trigger**: User visits root URL without authentication -- **Progression**: Load homepage → Browse content sections → Click hamburger menu → Navigate pages → View sample content -- **Success criteria**: Responsive design works; hamburger menu collapses on mobile; content is readable; links work; no auth required +### 1. CSS Class Builder +**Functionality:** Visual selector for Tailwind CSS classes organized into logical categories +**Purpose:** Eliminate the need to memorize or type CSS class names, reducing errors and speeding up styling +**Trigger:** User clicks palette icon next to any className field in PropertyInspector +**Progression:** Open builder → Browse categories (Layout, Spacing, Typography, etc.) → Click classes to select → See live preview → Apply to component +**Success Criteria:** +- User can style components without typing a single class name +- Selected classes display in real-time preview +- 200+ predefined classes organized into 10 categories +- Custom class input available for edge cases -### Level 2: User Area -- **Functionality**: Authenticated user dashboard with profile page and comment system -- **Purpose**: Allow normal users to sign up, manage their profile, and interact through comments -- **Trigger**: User clicks "Sign Up" or "Login" from Level 1 -- **Progression**: Register account → Verify credentials → Access dashboard → Edit profile → Browse comment sections → Post/edit comments → View own history -- **Success criteria**: Registration persists in KV; profile edits save; comments are CRUD-able; users can't access admin functions; profile picture upload works +### 2. Dynamic Dropdown Configuration +**Functionality:** Centralized management of dropdown option sets usable across multiple components +**Purpose:** Prevent duplication and ensure consistency when the same options appear in multiple places +**Trigger:** User navigates to "Dropdowns" tab in god-tier panel or components reference dropdown by name +**Progression:** Create dropdown config → Name it → Add options (value/label pairs) → Save → Reference in component schemas → Options appear automatically +**Success Criteria:** +- Dropdown created once, usable in unlimited component properties +- Changes to dropdown propagate to all components using it +- Visual GUI for managing options (no JSON required) +- Pre-loaded with common examples (status, priority, category) -### Level 3: Django-Style Admin Panel -- **Functionality**: Full data management interface with model list views, CRUD operations, filtering, search, and bulk actions -- **Purpose**: Provide admin users comprehensive control over all data models and system configuration -- **Trigger**: User with admin role logs in or selects "Admin" from navigation -- **Progression**: Login as admin → View model dashboard → Select model → See filtered list view → Search/filter records → Click record → Edit form → Save changes → View audit trail -- **Success criteria**: All models from schema rendered; inline editing; validation works; relations display correctly; permissions enforced; export to JSON/CSV +### 3. CSS Class Library Manager +**Functionality:** Manage the catalog of CSS classes available in the builder +**Purpose:** Allow customization of available classes and organization for project-specific needs +**Trigger:** User navigates to "CSS Classes" tab in god-tier panel +**Progression:** View categories → Create/edit category → Add/remove classes → Save → Classes appear in CSS Class Builder +**Success Criteria:** +- Categories can be added, edited, or deleted +- Each category contains unlimited class names +- Changes immediately reflected in CSS Class Builder +- System initializes with comprehensive Tailwind utilities -### Level 4: God-Tier Builder -- **Functionality**: Meta-builder with visual JSON schema editor, workflow designer, Lua lambda editor, component catalog, and live preview of Levels 1-3 -- **Purpose**: Allow god-level users to design, configure, and deploy entire applications for Levels 1-3 through declarative configuration -- **Trigger**: User with god role accesses builder interface -- **Progression**: Open builder → Design data schema in GUI → Create workflows visually → Write Lua handlers → Configure page templates → Preview generated app → Deploy configuration → Test all levels -- **Success criteria**: JSON schema editor validates; workflow nodes connect; Lua syntax highlighting; live preview updates; can export/import configurations; changes propagate to all levels +### 4. Monaco Code Editor Integration +**Functionality:** Professional-grade code editor for JSON and Lua with syntax highlighting and validation +**Purpose:** When code editing is necessary, provide best-in-class tooling comparable to VS Code +**Trigger:** User opens SchemaEditor, LuaEditor, or JsonEditor components +**Progression:** Open editor → See syntax-highlighted code → Edit with autocomplete → Format code → Validate → Save +**Success Criteria:** +- Syntax highlighting for JSON and Lua +- Real-time error detection and display +- Code formatting on demand +- Bracket pair colorization and matching +- Minimap for navigation +- Find/replace functionality -### JSON Schema Editor (Level 4) -- **Functionality**: Visual GUI for defining data models with fields, types, validation rules, relationships -- **Purpose**: Declaratively define all data structures without writing JSON by hand -- **Trigger**: User opens "Schema Designer" in Level 4 -- **Progression**: Create new model → Add fields via forms → Set field types/constraints → Define relations → Visualize schema graph → Validate → Generate Level 3 admin interface -- **Success criteria**: All field types supported; visual relationship mapping; constraint validation; auto-generates CRUD interfaces; imports/exports valid JSON +### 5. Enhanced Property Inspector +**Functionality:** Context-aware property editor with specialized controls for different data types +**Purpose:** Provide the right UI control for each property type automatically +**Trigger:** User selects component in builder +**Progression:** Select component → View properties → Use appropriate control (text input, dropdown, CSS builder, etc.) → Changes apply immediately +**Success Criteria:** +- String fields use text inputs +- Boolean fields use dropdowns (true/false) +- Select fields use static dropdowns +- Dynamic-select fields load options from dropdown configs +- className fields have CSS Builder button +- All changes saved to component props -### Workflow System (Level 4) -- **Functionality**: Visual node-based workflow editor for defining business logic flows -- **Purpose**: Create complex processes (approval flows, notifications, data transformations) without code -- **Trigger**: User opens "Workflow Designer" in Level 4 -- **Progression**: Create workflow → Drag trigger node → Add action nodes → Connect with arrows → Configure conditions → Attach to data events → Test execution → Monitor runs -- **Success criteria**: Nodes connect smoothly; execution order clear; can branch/merge; error handling; logs show execution path; integrates with Lua - -### Lua Lambda System (Level 4) -- **Functionality**: Real Lua interpreter (fengari-web) with full language support, Monaco editor with syntax highlighting and autocomplete, parameter handling, context API access, comprehensive execution feedback, and extensive snippet library with 30+ pre-built templates -- **Purpose**: Provide safe, sandboxed scripting for custom transformations, validations, and business logic with real Lua execution beyond declarative capabilities, enhanced by professional code editing experience and reusable code patterns -- **Trigger**: User adds "Lua Action" node in workflow or creates Lua script in scripts tab -- **Progression**: Open Monaco-based Lua editor → Define parameters → Browse snippet library by category → Search and preview snippets → Insert template code → Customize with syntax highlighting and autocomplete → Access context.data/user/kv via intelligent suggestions → Test with sample inputs → View execution logs → Return structured results → Integrate into workflows -- **Success criteria**: Monaco editor integrated with Lua language support; autocomplete provides context API suggestions (context.data, context.user, context.kv, log, print); syntax highlighting active; real Lua execution via fengari; parameter type validation; execution logs captured; return values parsed; syntax/runtime errors shown with line numbers; can transform JSON data; fullscreen editing mode available; snippet library accessible with 30+ templates across 12 categories; snippets insertable at cursor position; integrates with workflow nodes - -### Lua Snippet Library (Level 4) -- **Functionality**: Comprehensive library of 30+ pre-built Lua code templates organized into 12 categories (Data Validation, Data Transformation, Array Operations, String Processing, Math & Calculations, Conditionals & Logic, User Management, Error Handling, API & Networking, Date & Time, File Operations, Utilities) -- **Purpose**: Accelerate development by providing tested, reusable patterns for common operations; reduce errors; teach best practices -- **Trigger**: User clicks "Snippet Library" button in Lua editor or opens "Snippet Library" tab in Level 4 -- **Progression**: Open snippet library → Browse by category or search by keyword/tag → Preview snippet details and parameters → View full code in syntax-highlighted display → Copy to clipboard or insert into editor → Customize for specific use case -- **Success criteria**: 30+ snippets covering common patterns; organized into logical categories; searchable by name, description, and tags; preview shows code, description, and required parameters; one-click copy or insert; snippets include validation, transformation, calculations, conditionals, string operations, array operations, date handling, error handling, and utilities; modal detail view for full inspection - -### Database Persistence Layer (All Levels) -- **Functionality**: Centralized database abstraction layer with SHA-512 password hashing, KV persistence for all entities (users, credentials, workflows, Lua scripts, pages, schemas, comments, component hierarchy, component configs), comprehensive CRUD operations, import/export functionality, and database management UI -- **Purpose**: Provide secure, persistent storage for all application data with proper password security, enable data portability, and allow god-tier users to inspect and manage the entire database state -- **Trigger**: Application initialization; any data mutation; user opens Database tab in Level 4 -- **Progression**: App loads → Initialize database with defaults → Load entities from KV → Perform CRUD operations → Hash passwords with SHA-512 → Persist changes to KV → View statistics in Database Manager → Export full database to JSON → Import database from JSON backup → Clear and reinitialize database -- **Success criteria**: All passwords stored as SHA-512 hashes; KV persistence works across sessions; CRUD operations atomic; database export includes all entities; import validates and restores data; Database Manager shows real-time statistics; clear database requires double confirmation; no plaintext passwords ever stored; all data survives page refresh - -### God Credentials Expiry Management (Level 4) -- **Functionality**: Configurable expiry time for god-tier login credentials displayed on Level 1 (public page), with controls to customize duration, reset timer, and clear expiry -- **Purpose**: Allow god-tier users to control security by setting custom time limits (1 minute to 24 hours) for credential visibility, balancing convenience with security -- **Trigger**: User opens Settings tab in Level 4; credentials automatically display on Level 1 based on expiry status -- **Progression**: Open Settings tab → View current expiry status and time remaining → Adjust duration value and unit (minutes/hours) → Save new duration → Optionally reset timer to restart countdown → Or clear expiry to show credentials on next Level 1 load → View live countdown on both Level 1 and Level 4 -- **Success criteria**: Duration configurable from 1 minute to 24 hours; defaults to 1 hour; timer resets when god user changes password; countdown displays accurately in real-time on Level 1; Settings page shows active/expired status; Reset Timer button restarts countdown with configured duration; Clear Expiry removes timer completely; credentials disappear from Level 1 when expired; new duration persists across sessions +### 6. Quick Guide System +**Functionality:** Interactive documentation and tutorials for new features +**Purpose:** Help users discover and learn new visual configuration tools +**Trigger:** User opens "Guide" tab (default tab in god-tier panel) +**Progression:** View overview cards → Expand accordion sections → Read step-by-step instructions → Try features → Reference best practices +**Success Criteria:** +- Visible on first load as default tab +- Covers all major features (CSS Builder, Dropdowns, Monaco) +- Includes code examples where relevant +- Provides best practices and tips ## Edge Case Handling -- **Invalid User Credentials**: Show clear error message; rate limit after 5 attempts; support password reset flow -- **Unauthorized Access Attempts**: Redirect to appropriate level; log security events; show "access denied" message -- **Circular Schema Relations**: Detect and prevent infinite loops in model relationships; warn user -- **Invalid Lua Scripts**: Catch syntax errors; timeout after 3 seconds; sandbox prevents dangerous operations -- **Malformed JSON Schemas**: Validate before save; highlight errors with line numbers; provide fix suggestions -- **Workflow Infinite Loops**: Detect cycles; limit execution steps to 1000; show execution trace -- **Large Comment Threads**: Paginate comments; lazy load older entries; virtualize long lists -- **Schema Migration Conflicts**: Detect breaking changes; show migration preview; allow rollback -- **Lost Sessions Across Levels**: Auto-save state; restore context; show reconnection indicator -- **Database Import Errors**: Validate JSON structure before import; show detailed error messages; rollback on failure; preserve existing data -- **Password Hash Collisions**: Use SHA-512 with sufficient entropy; no collision risk in practice; unique salting per deployment -- **KV Storage Quota**: Monitor storage usage; warn when approaching limits; provide data cleanup tools; optimize JSON serialization -- **Monaco Editor Load Failure**: Fallback loading indicator; retry mechanism; graceful degradation if CDN unavailable -- **Large Lua Scripts**: Monaco virtual scrolling handles performance; minimap provides navigation; syntax parsing optimized -- **Invalid Expiry Duration**: Validate minimum 1 minute, maximum 24 hours; show error for out-of-range values; prevent negative numbers -- **Expiry Timer Desync**: Recalculate on page load; handle timezone differences; sync between Level 1 and Level 4 displays -- **Concurrent Expiry Changes**: Last write wins; reload settings after save; show confirmation of active settings +- **Invalid CSS class names** - Custom class input validates and warns about non-standard classes +- **Deleted dropdown config still referenced** - PropertyInspector gracefully handles missing configs, shows warning +- **Large CSS class lists** - Scrollable interface with search/filter to handle 1000+ classes +- **Concurrent edits** - Changes to dropdown configs immediately reflect in all open PropertyInspectors +- **Empty dropdown options** - Validation prevents saving dropdowns with zero options +- **Duplicate class selection** - System prevents selecting same class twice +- **Import/export conflicts** - Monaco editor validates JSON before import, shows detailed errors ## Design Direction -The design should evoke creativity and power - a professional design tool that feels both approachable and capable. Think Figma meets VS Code: clean, modern, with clear visual hierarchy and purposeful spacing. The canvas should feel like a creative workspace, not a cluttered IDE. +The interface should feel like a professional design tool (Figma, Webflow) rather than a developer IDE. Visual hierarchy emphasizes actions over configuration details. Color coding distinguishes different tool types (CSS = primary purple, Dropdowns = accent cyan, Code = muted gray). ## Color Selection -A sophisticated, creative tool palette that balances professionalism with visual energy - inspired by modern design tools. -- **Primary Color**: Deep purple `oklch(0.55 0.18 290)` - Communicates creativity and innovation, used for primary actions and builder chrome -- **Secondary Colors**: Cool slate `oklch(0.35 0.02 260)` for sidebars and panels; Light lavender `oklch(0.92 0.03 290)` for canvas background -- **Accent Color**: Electric cyan `oklch(0.70 0.17 195)` - High-energy color for selected states, active drop zones, and CTAs -- **Foreground/Background Pairings**: - - Primary (Deep Purple `oklch(0.55 0.18 290)`): White text `oklch(0.98 0 0)` - Ratio 6.2:1 ✓ - - Canvas (Light Lavender `oklch(0.92 0.03 290)`): Dark text `oklch(0.25 0.02 260)` - Ratio 12.1:1 ✓ - - Accent (Electric Cyan `oklch(0.70 0.17 195)`): Dark slate `oklch(0.2 0.02 260)` - Ratio 9.3:1 ✓ - - Sidebar (Cool Slate `oklch(0.35 0.02 260)`): Light text `oklch(0.90 0.01 260)` - Ratio 10.8:1 ✓ +**Primary Color:** `oklch(0.55 0.18 290)` - Purple/magenta representing creativity and visual design +- Used for: CSS-related features, primary actions, selected states + +**Secondary Colors:** `oklch(0.35 0.02 260)` - Deep blue-gray for structure +- Used for: Dropdowns, configuration panels, stable UI elements + +**Accent Color:** `oklch(0.70 0.17 195)` - Cyan/teal for interactive elements +- Used for: Dynamic dropdowns, interactive guides, actionable items + +**Foreground/Background Pairings:** +- Background `oklch(0.92 0.03 290)` with Foreground `oklch(0.25 0.02 260)` - Ratio 14.2:1 ✓ +- Card `oklch(1 0 0)` with Card Foreground `oklch(0.25 0.02 260)` - Ratio 16.4:1 ✓ +- Primary `oklch(0.55 0.18 290)` with Primary Foreground `oklch(0.98 0 0)` - Ratio 7.1:1 ✓ +- Accent `oklch(0.70 0.17 195)` with Accent Foreground `oklch(0.2 0.02 260)` - Ratio 8.9:1 ✓ ## Font Selection -Modern, clean typography that balances technical precision with creative energy - readable at all scales for a design tool interface. +Professional and technical feeling with emphasis on code clarity -- **Typographic Hierarchy**: - - H1 (Builder Title): Space Grotesk Bold/28px/tight letter spacing - - H2 (Panel Headers): Space Grotesk Semibold/20px/normal spacing - - H3 (Component Names): Space Grotesk Medium/14px/normal spacing - - Body (UI Labels): IBM Plex Sans Regular/14px/1.5 line height - - Code (Monaco Editor): JetBrains Mono Regular/14px/1.4 line height - - Small (Property Labels): IBM Plex Sans Medium/12px/uppercase/wide tracking +- **Typographic Hierarchy:** + - H1 (Panel Titles): Space Grotesk Bold/32px/tight tracking + - H2 (Section Headers): Space Grotesk SemiBold/24px/normal tracking + - H3 (Card Titles): Space Grotesk Medium/18px/normal tracking + - Body (Descriptions): IBM Plex Sans Regular/14px/relaxed line height + - Labels (Form Fields): IBM Plex Sans Medium/12px/wide tracking/uppercase + - Code (Editors): JetBrains Mono Regular/14px/monospace ## Animations -Animations should feel responsive and purposeful - immediate visual feedback for drag operations (drag ghost follows cursor at 0ms), smooth 200ms transitions for panel sliding, 150ms micro-interactions on selection changes, and elastic spring physics (tension: 300, friction: 20) for drop animations that make components feel tangible. +Subtle functionality enhancements with occasional delightful moments + +- **Opening dialogs:** 200ms ease-out scale from 0.95 to 1.0 with fade +- **Selecting CSS classes:** 150ms color transition + 100ms scale pulse on click +- **Dropdown option add:** 300ms slide-in from top with spring physics +- **Tab switching:** 200ms cross-fade between content panels +- **Hover states:** 150ms color/shadow transitions for all interactive elements +- **Toast notifications:** 400ms slide-up with bounce for user feedback ## Component Selection -- **Components**: - - Sidebar with collapsible sections for component catalog - - Resizable panels for canvas/inspector layout - - Card for component previews in catalog and snippet library - - Dialog for login form, settings, and snippet detail view - - Sheet for slide-out snippet library panel - - Tabs for switching between visual/code views and snippet categories - - ScrollArea for component lists, property panels, and snippet browsing - - Input, Select, Switch, Slider for property editors - - Button throughout for actions - - Badge for component type indicators and snippet tags - - Separator for visual hierarchy - - Tooltip for help text on hover - - Sonner for notifications -- **Customizations**: - - Custom drag-and-drop canvas with drop zone highlighting - - Monaco Editor wrapper for Lua scripts with custom autocomplete provider - - Monaco Editor wrapper for JSON schema editing with validation - - Component tree view with expand/collapse - - Property editor that dynamically renders based on component type - - Canvas ruler and grid overlay - - Component outline overlay on hover - - Fullscreen mode for Monaco editor instances - - Snippet library with category filtering and search - - Snippet card grid with tag display and copy/insert actions - - Snippet detail modal with parameter documentation and code highlighting -- **States**: - - Canvas: neutral state shows dotted grid, hover shows drop zones, dragging shows blue outlines - - Components: default has subtle border, hover shows blue glow, selected shows thick accent border with resize handles - - Drop zones: hidden by default, appear on drag with dashed accent border and background tint - - Property inputs: follow standard focus states with accent color -- **Icon Selection**: - - Phosphor icons: Layout for layouts, PaintBrush for styling, Code for code editor, Lock/LockOpen for auth, FloppyDisk for save, Eye for preview, ArrowsOutSimple for fullscreen, Plus for add, Trash for delete, Copy for duplicate/copy, CaretRight/Down for tree expand, BookOpen for snippet library, MagnifyingGlass for search, Tag for snippet tags, Check for copied confirmation, ArrowRight for insert action -- **Spacing**: - - Sidebars: p-4 for sections, gap-2 for component grid - - Canvas: p-8 for outer padding, min-h-screen for scrollability - - Property panel: p-4 for sections, gap-4 for form fields - - Component padding: p-2 minimum for selection targets -- **Mobile**: - - Not a primary concern for a builder tool, but provide tablet landscape support minimum - - Stack panels vertically on small screens - - Hide component catalog by default, show via hamburger menu - - Full-screen canvas mode for focused editing + +**Components:** +- **Dialog (shadcn)** - For CSS Builder, Dropdown Manager, JSON Editor modals with max-width customizations +- **Tabs (shadcn)** - For god-tier panel navigation with horizontal scroll on mobile +- **Select (shadcn)** - For boolean and static dropdown properties +- **Input (shadcn)** - For text, number, and className fields with custom validation states +- **Button (shadcn)** - For all actions with icon+text pattern, size variants (sm for toolbars) +- **Card (shadcn)** - For guide sections, dropdown configs, CSS categories with hover elevations +- **Badge (shadcn)** - For selected classes, tags, status indicators with color variants +- **ScrollArea (shadcn)** - For long lists (CSS classes, options) with styled scrollbars +- **Accordion (shadcn)** - For Quick Guide collapsible sections +- **Monaco Editor (@monaco-editor/react)** - For JSON/Lua code editing with dark theme + +**Customizations:** +- DialogContent extended to max-w-5xl for JSON/Lua editors +- Tabs with conditional wrapping and horizontal scroll for 12+ tabs +- Badge with close button overlay for removable tags +- Card with 2px border variants for feature highlighting +- Input with icon button suffix for CSS Builder trigger + +**States:** +- Buttons: default, hover (shadow-md), active (scale-95), disabled (opacity-50) +- Inputs: default, focus (ring-2), error (border-destructive), disabled (bg-muted) +- Cards: default, hover (shadow-lg for interactive ones), selected (border-primary) +- Dropdowns: closed, open (with slide-down animation), disabled + +**Icon Selection:** +- Palette (CSS Builder) - Visual association with styling/design +- ListDashes (Dropdowns) - Represents list options +- Code (Monaco editors) - Universal code symbol +- Sparkle (Quick Guide) - Suggests helpful tips/new features +- Pencil (Edit actions) - Standard edit metaphor +- Trash (Delete actions) - Standard delete metaphor +- Plus (Add actions) - Create new items +- FloppyDisk (Save) - Nostalgic but clear save icon + +**Spacing:** +- Section gaps: gap-6 (24px) for major sections +- Card internal: p-4 to p-6 (16-24px) based on content density +- Form fields: space-y-2 (8px) between label and input +- Button groups: gap-2 (8px) for related actions +- Tab list: gap-1 (4px) to feel unified + +**Mobile:** +- Tabs convert to horizontally scrollable list (4 visible, swipe for more) +- Dialogs use max-w-full with safe area padding +- CSS Class Builder shows 1 column on mobile, 3 on desktop +- PropertyInspector becomes bottom drawer on mobile (< 768px) +- Quick Guide cards stack vertically on mobile +- Monaco editor height reduces to 400px on mobile diff --git a/src/components/CssClassBuilder.tsx b/src/components/CssClassBuilder.tsx new file mode 100644 index 000000000..405228f67 --- /dev/null +++ b/src/components/CssClassBuilder.tsx @@ -0,0 +1,182 @@ +import { useState, useEffect } from 'react' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { Badge } from '@/components/ui/badge' +import { Database } from '@/lib/database' +import { Plus, X, FloppyDisk, Trash } from '@phosphor-icons/react' +import { toast } from 'sonner' + +interface CssClassBuilderProps { + open: boolean + onClose: () => void + initialValue?: string + onSave: (classes: string) => void +} + +interface CssCategory { + name: string + classes: string[] +} + +export function CssClassBuilder({ open, onClose, initialValue = '', onSave }: CssClassBuilderProps) { + const [selectedClasses, setSelectedClasses] = useState([]) + const [categories, setCategories] = useState([]) + const [searchQuery, setSearchQuery] = useState('') + const [customClass, setCustomClass] = useState('') + + useEffect(() => { + if (open) { + loadCssClasses() + setSelectedClasses(initialValue.split(' ').filter(Boolean)) + } + }, [open, initialValue]) + + const loadCssClasses = async () => { + const classes = await Database.getCssClasses() + setCategories(classes) + } + + const toggleClass = (cssClass: string) => { + setSelectedClasses(current => { + if (current.includes(cssClass)) { + return current.filter(c => c !== cssClass) + } else { + return [...current, cssClass] + } + }) + } + + const addCustomClass = () => { + if (customClass.trim()) { + setSelectedClasses(current => [...current, customClass.trim()]) + setCustomClass('') + } + } + + const handleSave = () => { + onSave(selectedClasses.join(' ')) + onClose() + } + + const filteredCategories = categories.map(category => ({ + ...category, + classes: category.classes.filter(cls => + cls.toLowerCase().includes(searchQuery.toLowerCase()) + ), + })).filter(category => category.classes.length > 0) + + return ( + + + + CSS Class Builder + + +
+
+ setSearchQuery(e.target.value)} + className="flex-1" + /> +
+ + {selectedClasses.length > 0 && ( +
+ +
+ {selectedClasses.map(cls => ( + + {cls} + + + ))} +
+
+ {selectedClasses.join(' ')} +
+
+ )} + + + + + {filteredCategories.map(category => ( + + {category.name} + + ))} + Custom + + + + {filteredCategories.map(category => ( + + +
+ {category.classes.map(cls => ( + + ))} +
+
+
+ ))} + + +
+
+ setCustomClass(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && addCustomClass()} + /> + +
+

+ Add custom CSS classes that aren't in the predefined list. +

+
+
+
+
+ + + + + +
+
+ ) +} diff --git a/src/components/CssClassManager.tsx b/src/components/CssClassManager.tsx new file mode 100644 index 000000000..af08561fa --- /dev/null +++ b/src/components/CssClassManager.tsx @@ -0,0 +1,203 @@ +import { useState, useEffect } from 'react' +import { Card } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Badge } from '@/components/ui/badge' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Separator } from '@/components/ui/separator' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' +import { Database, CssCategory } from '@/lib/database' +import { Plus, X, Pencil, Trash, FloppyDisk } from '@phosphor-icons/react' +import { toast } from 'sonner' + +export function CssClassManager() { + const [categories, setCategories] = useState([]) + const [isEditing, setIsEditing] = useState(false) + const [editingCategory, setEditingCategory] = useState(null) + const [categoryName, setCategoryName] = useState('') + const [classes, setClasses] = useState([]) + const [newClass, setNewClass] = useState('') + + useEffect(() => { + loadCategories() + }, []) + + const loadCategories = async () => { + const cats = await Database.getCssClasses() + setCategories(cats) + } + + const startEdit = (category?: CssCategory) => { + if (category) { + setEditingCategory(category) + setCategoryName(category.name) + setClasses([...category.classes]) + } else { + setEditingCategory(null) + setCategoryName('') + setClasses([]) + } + setIsEditing(true) + } + + const addClass = () => { + if (newClass.trim()) { + setClasses(current => [...current, newClass.trim()]) + setNewClass('') + } + } + + const removeClass = (index: number) => { + setClasses(current => current.filter((_, i) => i !== index)) + } + + const handleSave = async () => { + if (!categoryName || classes.length === 0) { + toast.error('Please provide a category name and at least one class') + return + } + + const newCategory: CssCategory = { + name: categoryName, + classes, + } + + if (editingCategory) { + await Database.updateCssCategory(categoryName, classes) + toast.success('Category updated successfully') + } else { + await Database.addCssCategory(newCategory) + toast.success('Category created successfully') + } + + setIsEditing(false) + loadCategories() + } + + const handleDelete = async (categoryName: string) => { + if (confirm('Are you sure you want to delete this CSS category?')) { + await Database.deleteCssCategory(categoryName) + toast.success('Category deleted') + loadCategories() + } + } + + return ( +
+
+
+

CSS Class Library

+

Manage CSS classes available in the builder

+
+ +
+ +
+ {categories.map(category => ( + +
+

{category.name}

+
+ + +
+
+ + +
+ {category.classes.map((cls, i) => ( + + {cls} + + ))} +
+
+
+ {category.classes.length} classes +
+
+ ))} +
+ + {categories.length === 0 && ( + +

No CSS categories yet. Add one to get started.

+
+ )} + + + + + {editingCategory ? 'Edit' : 'Create'} CSS Category + + +
+
+ + setCategoryName(e.target.value)} + disabled={!!editingCategory} + /> +
+ + + +
+ +
+ setNewClass(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && addClass()} + className="font-mono" + /> + +
+
+ + {classes.length > 0 && ( + +
+ {classes.map((cls, i) => ( + + {cls} + + + ))} +
+
+ )} +
+ + + + + +
+
+
+ ) +} diff --git a/src/components/DropdownConfigManager.tsx b/src/components/DropdownConfigManager.tsx new file mode 100644 index 000000000..0146d3f94 --- /dev/null +++ b/src/components/DropdownConfigManager.tsx @@ -0,0 +1,226 @@ +import { useState, useEffect } from 'react' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Card } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import { Separator } from '@/components/ui/separator' +import { Database } from '@/lib/database' +import { Plus, X, FloppyDisk, Trash, Pencil } from '@phosphor-icons/react' +import { toast } from 'sonner' +import type { DropdownConfig } from '@/lib/database' + +export function DropdownConfigManager() { + const [dropdowns, setDropdowns] = useState([]) + const [isEditing, setIsEditing] = useState(false) + const [editingDropdown, setEditingDropdown] = useState(null) + const [dropdownName, setDropdownName] = useState('') + const [dropdownLabel, setDropdownLabel] = useState('') + const [options, setOptions] = useState>([]) + const [newOptionValue, setNewOptionValue] = useState('') + const [newOptionLabel, setNewOptionLabel] = useState('') + + useEffect(() => { + loadDropdowns() + }, []) + + const loadDropdowns = async () => { + const configs = await Database.getDropdownConfigs() + setDropdowns(configs) + } + + const startEdit = (dropdown?: DropdownConfig) => { + if (dropdown) { + setEditingDropdown(dropdown) + setDropdownName(dropdown.name) + setDropdownLabel(dropdown.label) + setOptions(dropdown.options) + } else { + setEditingDropdown(null) + setDropdownName('') + setDropdownLabel('') + setOptions([]) + } + setIsEditing(true) + } + + const addOption = () => { + if (newOptionValue && newOptionLabel) { + setOptions(current => [...current, { value: newOptionValue, label: newOptionLabel }]) + setNewOptionValue('') + setNewOptionLabel('') + } + } + + const removeOption = (index: number) => { + setOptions(current => current.filter((_, i) => i !== index)) + } + + const handleSave = async () => { + if (!dropdownName || !dropdownLabel || options.length === 0) { + toast.error('Please fill all fields and add at least one option') + return + } + + const newDropdown: DropdownConfig = { + id: editingDropdown?.id || `dropdown_${Date.now()}`, + name: dropdownName, + label: dropdownLabel, + options, + } + + if (editingDropdown) { + await Database.updateDropdownConfig(newDropdown.id, newDropdown) + toast.success('Dropdown updated successfully') + } else { + await Database.addDropdownConfig(newDropdown) + toast.success('Dropdown created successfully') + } + + setIsEditing(false) + loadDropdowns() + } + + const handleDelete = async (id: string) => { + if (confirm('Are you sure you want to delete this dropdown configuration?')) { + await Database.deleteDropdownConfig(id) + toast.success('Dropdown deleted') + loadDropdowns() + } + } + + return ( +
+
+
+

Dropdown Configurations

+

Manage dynamic dropdown options for properties

+
+ +
+ +
+ {dropdowns.map(dropdown => ( + +
+
+

{dropdown.label}

+

{dropdown.name}

+
+
+ + +
+
+ +
+ {dropdown.options.map((opt, i) => ( + + {opt.label} + + ))} +
+
+ ))} +
+ + {dropdowns.length === 0 && ( + +

No dropdown configurations yet. Create one to get started.

+
+ )} + + + + + {editingDropdown ? 'Edit' : 'Create'} Dropdown Configuration + + +
+
+ + setDropdownName(e.target.value)} + /> +

Unique identifier for this dropdown

+
+ +
+ + setDropdownLabel(e.target.value)} + /> +
+ + + +
+ +
+ setNewOptionValue(e.target.value)} + /> + setNewOptionLabel(e.target.value)} + /> + +
+
+ + {options.length > 0 && ( + +
+ {options.map((opt, i) => ( +
+
+ {opt.value} + + {opt.label} +
+ +
+ ))} +
+
+ )} +
+ + + + + +
+
+
+ ) +} diff --git a/src/components/JsonEditor.tsx b/src/components/JsonEditor.tsx new file mode 100644 index 000000000..9f5ee567c --- /dev/null +++ b/src/components/JsonEditor.tsx @@ -0,0 +1,112 @@ +import { useState, useEffect } from 'react' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { FloppyDisk, X, Warning } from '@phosphor-icons/react' +import Editor from '@monaco-editor/react' + +interface JsonEditorProps { + open: boolean + onClose: () => void + title: string + value: any + onSave: (value: any) => void + schema?: any +} + +export function JsonEditor({ open, onClose, title, value, onSave, schema }: JsonEditorProps) { + const [jsonText, setJsonText] = useState('') + const [error, setError] = useState(null) + + useEffect(() => { + if (open) { + setJsonText(JSON.stringify(value, null, 2)) + setError(null) + } + }, [open, value]) + + const handleSave = () => { + try { + const parsed = JSON.parse(jsonText) + onSave(parsed) + setError(null) + onClose() + } catch (err) { + setError(err instanceof Error ? err.message : 'Invalid JSON') + } + } + + const handleFormat = () => { + try { + const parsed = JSON.parse(jsonText) + setJsonText(JSON.stringify(parsed, null, 2)) + setError(null) + } catch (err) { + setError(err instanceof Error ? err.message : 'Invalid JSON - cannot format') + } + } + + return ( + + + + {title} + + +
+ {error && ( + + + {error} + + )} + +
+ { + setJsonText(value || '') + setError(null) + }} + theme="vs-dark" + options={{ + minimap: { enabled: true }, + fontSize: 14, + fontFamily: 'JetBrains Mono, monospace', + lineNumbers: 'on', + roundedSelection: true, + scrollBeyondLastLine: false, + automaticLayout: true, + tabSize: 2, + wordWrap: 'on', + formatOnPaste: true, + formatOnType: true, + bracketPairColorization: { + enabled: true, + }, + folding: true, + foldingStrategy: 'indentation', + }} + /> +
+
+ + + + + + +
+
+ ) +} diff --git a/src/components/Level4.tsx b/src/components/Level4.tsx index c1401ccf7..07ecf1a61 100644 --- a/src/components/Level4.tsx +++ b/src/components/Level4.tsx @@ -8,7 +8,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' -import { SignOut, Database as DatabaseIcon, Lightning, Code, Eye, House, Download, Upload, BookOpen, HardDrives, MapTrifold, Tree, Users, Gear } from '@phosphor-icons/react' +import { SignOut, Database as DatabaseIcon, Lightning, Code, Eye, House, Download, Upload, BookOpen, HardDrives, MapTrifold, Tree, Users, Gear, Palette, ListDashes, Sparkle } from '@phosphor-icons/react' import { toast } from 'sonner' import { SchemaEditorLevel4 } from './SchemaEditorLevel4' import { WorkflowEditor } from './WorkflowEditor' @@ -19,6 +19,9 @@ import { PageRoutesManager } from './PageRoutesManager' import { ComponentHierarchyEditor } from './ComponentHierarchyEditor' import { UserManagement } from './UserManagement' import { GodCredentialsSettings } from './GodCredentialsSettings' +import { CssClassManager } from './CssClassManager' +import { DropdownConfigManager } from './DropdownConfigManager' +import { QuickGuide } from './QuickGuide' import { Database } from '@/lib/database' import { seedDatabase } from '@/lib/seed-data' import type { User as UserType, AppConfiguration } from '@/lib/level-types' @@ -200,8 +203,12 @@ export function Level4({ user, onLogout, onNavigate, onPreview }: Level4Props) {

- - + + + + + Guide + Page Routes @@ -230,6 +237,14 @@ export function Level4({ user, onLogout, onNavigate, onPreview }: Level4Props) { Snippets + + + CSS Classes + + + + Dropdowns + Database @@ -240,6 +255,10 @@ export function Level4({ user, onLogout, onNavigate, onPreview }: Level4Props) { + + + + @@ -290,6 +309,14 @@ export function Level4({ user, onLogout, onNavigate, onPreview }: Level4Props) { + + + + + + + + diff --git a/src/components/PropertyInspector.tsx b/src/components/PropertyInspector.tsx index 68495c0e4..e4a96b5db 100644 --- a/src/components/PropertyInspector.tsx +++ b/src/components/PropertyInspector.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import { ScrollArea } from '@/components/ui/scroll-area' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' @@ -8,7 +8,9 @@ import { Separator } from '@/components/ui/separator' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import type { ComponentInstance } from '@/lib/builder-types' import { componentCatalog } from '@/lib/component-catalog' -import { Code, PaintBrush, Trash } from '@phosphor-icons/react' +import { Code, PaintBrush, Trash, Palette } from '@phosphor-icons/react' +import { CssClassBuilder } from '@/components/CssClassBuilder' +import { Database, DropdownConfig } from '@/lib/database' interface PropertyInspectorProps { component: ComponentInstance | null @@ -18,6 +20,19 @@ interface PropertyInspectorProps { } export function PropertyInspector({ component, onUpdate, onDelete, onCodeEdit }: PropertyInspectorProps) { + const [cssBuilderOpen, setCssBuilderOpen] = useState(false) + const [cssBuilderPropName, setCssBuilderPropName] = useState('') + const [dynamicDropdowns, setDynamicDropdowns] = useState([]) + + useEffect(() => { + loadDynamicDropdowns() + }, []) + + const loadDynamicDropdowns = async () => { + const dropdowns = await Database.getDropdownConfigs() + setDynamicDropdowns(dropdowns) + } + if (!component) { return (
@@ -35,6 +50,16 @@ export function PropertyInspector({ component, onUpdate, onDelete, onCodeEdit }: }) } + const openCssBuilder = (propName: string) => { + setCssBuilderPropName(propName) + setCssBuilderOpen(true) + } + + const handleCssClassSave = (classes: string) => { + handlePropChange(cssBuilderPropName, classes) + setCssBuilderOpen(false) + } + return (
@@ -57,63 +82,94 @@ export function PropertyInspector({ component, onUpdate, onDelete, onCodeEdit }:
- {componentDef?.propSchema.map(propDef => ( -
- - - {propDef.type === 'string' && ( - handlePropChange(propDef.name, e.target.value)} - /> - )} + {componentDef?.propSchema.map(propDef => { + const dynamicDropdown = propDef.type === 'dynamic-select' + ? dynamicDropdowns.find(d => d.name === propDef.dynamicSource) + : null - {propDef.type === 'number' && ( - handlePropChange(propDef.name, Number(e.target.value))} - /> - )} + return ( +
+ + + {propDef.name === 'className' ? ( +
+ handlePropChange(propDef.name, e.target.value)} + className="flex-1 font-mono text-xs" + /> + +
+ ) : propDef.type === 'string' ? ( + handlePropChange(propDef.name, e.target.value)} + /> + ) : propDef.type === 'number' ? ( + handlePropChange(propDef.name, Number(e.target.value))} + /> + ) : propDef.type === 'boolean' ? ( + + ) : propDef.type === 'select' && propDef.options ? ( + + ) : propDef.type === 'dynamic-select' && dynamicDropdown ? ( + + ) : null} - {propDef.type === 'boolean' && ( - - )} - - {propDef.type === 'select' && propDef.options && ( - - )} - - {propDef.description && ( -

{propDef.description}

- )} -
- ))} + {propDef.description && ( +

{propDef.description}

+ )} +
+ ) + })} {(!componentDef?.propSchema || componentDef.propSchema.length === 0) && (

This component has no configurable properties.

@@ -149,6 +205,13 @@ export function PropertyInspector({ component, onUpdate, onDelete, onCodeEdit }: Delete Component
+ + setCssBuilderOpen(false)} + initialValue={component.props[cssBuilderPropName] || ''} + onSave={handleCssClassSave} + />
) } diff --git a/src/components/QuickGuide.tsx b/src/components/QuickGuide.tsx new file mode 100644 index 000000000..2fd8729c0 --- /dev/null +++ b/src/components/QuickGuide.tsx @@ -0,0 +1,205 @@ +import { Card } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion' +import { Palette, ListDashes, Code, Sparkle } from '@phosphor-icons/react' + +export function QuickGuide() { + return ( +
+
+

Quick Guide

+

Learn how to use the new visual configuration tools

+
+ +
+ +
+
+ +
+
+

CSS Class Builder

+ Visual Styling +
+
+

+ No more typing CSS classes! Click the palette icon next to any className field to visually select from 200+ organized Tailwind classes. +

+
+
+
+ 10 categorized class groups +
+
+
+ Live preview of selections +
+
+
+ Add custom classes when needed +
+
+ + + +
+
+ +
+
+

Dynamic Dropdowns

+ Reusable Options +
+
+

+ Create dropdown configurations once and use them across multiple components. Perfect for status fields, categories, and priorities. +

+
+
+
+ Centralized option management +
+
+
+ Update once, apply everywhere +
+
+
+ GUI-based configuration +
+
+ +
+ + + + + +
+ + How to use CSS Class Builder +
+
+ +
+

Step 1: Manage your CSS library

+
    +
  • Go to the "CSS Classes" tab
  • +
  • Browse existing categories (Layout, Spacing, Typography, etc.)
  • +
  • Add new categories or classes as needed
  • +
+
+
+

Step 2: Apply classes to components

+
    +
  • Select a component in the builder
  • +
  • Find the "CSS Classes" field in the Property Inspector
  • +
  • Click the palette icon button
  • +
  • Browse categories and click classes to select them
  • +
  • See live preview of your selections
  • +
  • Click "Apply Classes" to save
  • +
+
+
+

Tip: You can still type custom classes directly in the input field if you need something specific!

+
+
+
+ + + +
+ + How to create and use Dynamic Dropdowns +
+
+ +
+

Step 1: Create a dropdown configuration

+
    +
  • Go to the "Dropdowns" tab in the god-tier panel
  • +
  • Click "Create Dropdown"
  • +
  • Enter a unique name (e.g., "status_options")
  • +
  • Enter a display label (e.g., "Status")
  • +
  • Add options with values and labels
  • +
  • Click "Save"
  • +
+
+
+

Step 2: Use it in component properties

+
    +
  • When defining component schemas, use type "dynamic-select"
  • +
  • Reference your dropdown by name in the "dynamicSource" field
  • +
  • The Property Inspector will automatically show your dropdown
  • +
+
+
+
{`{
+  name: 'status',
+  label: 'Status',
+  type: 'dynamic-select',
+  dynamicSource: 'status_options'
+}`}
+
+
+

Pre-loaded examples: We've included status, priority, and category dropdowns to get you started!

+
+
+
+ + + +
+ + Monaco Code Editor Features +
+
+ +

+ When editing JSON or Lua code, you'll use the Monaco editor (the same editor that powers VS Code): +

+
    +
  • Syntax Highlighting: Color-coded JSON/Lua syntax
  • +
  • Auto-formatting: Press Format JSON button or use Shift+Alt+F
  • +
  • Error Detection: See errors as you type
  • +
  • Bracket Matching: Colored bracket pairs
  • +
  • Code Folding: Collapse/expand sections
  • +
  • Find & Replace: Ctrl/Cmd+F to search
  • +
  • Minimap: Navigate large files easily
  • +
+
+
+ + + +
+ + Best Practices +
+
+ +
    +
  • +

    Organize CSS classes by purpose

    +

    Keep related classes together in categories for easier discovery

    +
  • +
  • +

    Name dropdowns descriptively

    +

    Use clear names like "user_status_options" instead of "dropdown1"

    +
  • +
  • +

    Reuse dropdown configurations

    +

    If multiple components need the same options, create one dropdown and reference it

    +
  • +
  • +

    Test in preview mode

    +

    Use the preview buttons to see how your changes look on each level

    +
  • +
+
+
+
+
+
+ ) +} diff --git a/src/lib/builder-types.ts b/src/lib/builder-types.ts index 588dd3766..f416f3f32 100644 --- a/src/lib/builder-types.ts +++ b/src/lib/builder-types.ts @@ -55,9 +55,10 @@ export interface ComponentDefinition { export interface PropDefinition { name: string label: string - type: 'string' | 'number' | 'boolean' | 'select' | 'color' + type: 'string' | 'number' | 'boolean' | 'select' | 'color' | 'dynamic-select' defaultValue?: any options?: Array<{ value: string; label: string }> + dynamicSource?: string description?: string } diff --git a/src/lib/database.ts b/src/lib/database.ts index 44efcc54b..349ab7dac 100644 --- a/src/lib/database.ts +++ b/src/lib/database.ts @@ -8,6 +8,18 @@ import type { } from './level-types' import type { ModelSchema } from './schema-types' +export interface CssCategory { + name: string + classes: string[] +} + +export interface DropdownConfig { + id: string + name: string + label: string + options: Array<{ value: string; label: string }> +} + export interface DatabaseSchema { users: User[] credentials: Record @@ -23,6 +35,8 @@ export interface DatabaseSchema { passwordChangeTimestamps: Record firstLoginFlags: Record godCredentialsExpiryDuration: number + cssClasses: CssCategory[] + dropdownConfigs: DropdownConfig[] } export interface ComponentNode { @@ -61,6 +75,8 @@ export const DB_KEYS = { PASSWORD_CHANGE_TIMESTAMPS: 'db_password_change_timestamps', FIRST_LOGIN_FLAGS: 'db_first_login_flags', GOD_CREDENTIALS_EXPIRY_DURATION: 'db_god_credentials_expiry_duration', + CSS_CLASSES: 'db_css_classes', + DROPDOWN_CONFIGS: 'db_dropdown_configs', } as const export async function hashPassword(password: string): Promise { @@ -407,6 +423,92 @@ export class Database { } await this.setAppConfig(defaultConfig) } + + const cssClasses = await this.getCssClasses() + if (cssClasses.length === 0) { + const defaultCssClasses: CssCategory[] = [ + { + name: 'Layout', + classes: ['flex', 'flex-col', 'flex-row', 'grid', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'block', 'inline-block', 'inline', 'hidden'], + }, + { + name: 'Spacing', + classes: ['p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-6', 'p-8', 'm-0', 'm-1', 'm-2', 'm-3', 'm-4', 'm-6', 'm-8', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-6', 'gap-8'], + }, + { + name: 'Sizing', + classes: ['w-full', 'w-1/2', 'w-1/3', 'w-1/4', 'w-auto', 'h-full', 'h-screen', 'h-auto', 'min-h-screen', 'max-w-xs', 'max-w-sm', 'max-w-md', 'max-w-lg', 'max-w-xl', 'max-w-2xl', 'max-w-4xl', 'max-w-6xl', 'max-w-7xl'], + }, + { + name: 'Typography', + classes: ['text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl', 'text-4xl', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'text-left', 'text-center', 'text-right', 'uppercase', 'lowercase', 'capitalize'], + }, + { + name: 'Colors', + classes: ['text-primary', 'text-secondary', 'text-accent', 'text-muted-foreground', 'bg-primary', 'bg-secondary', 'bg-accent', 'bg-background', 'bg-card', 'bg-muted', 'border-primary', 'border-secondary', 'border-accent', 'border-border'], + }, + { + name: 'Borders', + classes: ['border', 'border-2', 'border-4', 'border-t', 'border-b', 'border-l', 'border-r', 'rounded', 'rounded-sm', 'rounded-md', 'rounded-lg', 'rounded-xl', 'rounded-2xl', 'rounded-full'], + }, + { + name: 'Effects', + classes: ['shadow', 'shadow-sm', 'shadow-md', 'shadow-lg', 'shadow-xl', 'hover:shadow-lg', 'opacity-0', 'opacity-50', 'opacity-75', 'opacity-100', 'transition', 'transition-all', 'duration-200', 'duration-300', 'duration-500'], + }, + { + name: 'Positioning', + classes: ['relative', 'absolute', 'fixed', 'sticky', 'top-0', 'bottom-0', 'left-0', 'right-0', 'z-10', 'z-20', 'z-30', 'z-40', 'z-50'], + }, + { + name: 'Alignment', + classes: ['items-start', 'items-center', 'items-end', 'justify-start', 'justify-center', 'justify-end', 'justify-between', 'justify-around', 'self-start', 'self-center', 'self-end'], + }, + { + name: 'Interactivity', + classes: ['cursor-pointer', 'cursor-default', 'pointer-events-none', 'select-none', 'hover:bg-accent', 'hover:text-accent-foreground', 'active:scale-95', 'disabled:opacity-50'], + }, + ] + await this.setCssClasses(defaultCssClasses) + } + + const dropdowns = await this.getDropdownConfigs() + if (dropdowns.length === 0) { + const defaultDropdowns: DropdownConfig[] = [ + { + id: 'dropdown_status', + name: 'status_options', + label: 'Status', + options: [ + { value: 'draft', label: 'Draft' }, + { value: 'published', label: 'Published' }, + { value: 'archived', label: 'Archived' }, + ], + }, + { + id: 'dropdown_priority', + name: 'priority_options', + label: 'Priority', + options: [ + { value: 'low', label: 'Low' }, + { value: 'medium', label: 'Medium' }, + { value: 'high', label: 'High' }, + { value: 'urgent', label: 'Urgent' }, + ], + }, + { + id: 'dropdown_category', + name: 'category_options', + label: 'Category', + options: [ + { value: 'general', label: 'General' }, + { value: 'technical', label: 'Technical' }, + { value: 'business', label: 'Business' }, + { value: 'personal', label: 'Personal' }, + ], + }, + ] + await this.setDropdownConfigs(defaultDropdowns) + } } static async exportDatabase(): Promise { @@ -494,6 +596,64 @@ export class Database { await this.setGodCredentialsExpiry(expiryTime) } + static async getCssClasses(): Promise { + return (await window.spark.kv.get(DB_KEYS.CSS_CLASSES)) || [] + } + + static async setCssClasses(classes: CssCategory[]): Promise { + await window.spark.kv.set(DB_KEYS.CSS_CLASSES, classes) + } + + static async addCssCategory(category: CssCategory): Promise { + const classes = await this.getCssClasses() + classes.push(category) + await this.setCssClasses(classes) + } + + static async updateCssCategory(categoryName: string, classes: string[]): Promise { + const categories = await this.getCssClasses() + const index = categories.findIndex(c => c.name === categoryName) + if (index !== -1) { + categories[index].classes = classes + await this.setCssClasses(categories) + } + } + + static async deleteCssCategory(categoryName: string): Promise { + const categories = await this.getCssClasses() + const filtered = categories.filter(c => c.name !== categoryName) + await this.setCssClasses(filtered) + } + + static async getDropdownConfigs(): Promise { + return (await window.spark.kv.get(DB_KEYS.DROPDOWN_CONFIGS)) || [] + } + + static async setDropdownConfigs(configs: DropdownConfig[]): Promise { + await window.spark.kv.set(DB_KEYS.DROPDOWN_CONFIGS, configs) + } + + static async addDropdownConfig(config: DropdownConfig): Promise { + const configs = await this.getDropdownConfigs() + configs.push(config) + await this.setDropdownConfigs(configs) + } + + static async updateDropdownConfig(id: string, updates: DropdownConfig): Promise { + const configs = await this.getDropdownConfigs() + const index = configs.findIndex(c => c.id === id) + if (index !== -1) { + configs[index] = updates + await this.setDropdownConfigs(configs) + } + } + + static async deleteDropdownConfig(id: string): Promise { + const configs = await this.getDropdownConfigs() + const filtered = configs.filter(c => c.id !== id) + await this.setDropdownConfigs(filtered) + } + static async clearDatabase(): Promise { await window.spark.kv.delete(DB_KEYS.USERS) await window.spark.kv.delete(DB_KEYS.CREDENTIALS) @@ -509,5 +669,7 @@ export class Database { await window.spark.kv.delete(DB_KEYS.PASSWORD_CHANGE_TIMESTAMPS) await window.spark.kv.delete(DB_KEYS.FIRST_LOGIN_FLAGS) await window.spark.kv.delete(DB_KEYS.GOD_CREDENTIALS_EXPIRY_DURATION) + await window.spark.kv.delete(DB_KEYS.CSS_CLASSES) + await window.spark.kv.delete(DB_KEYS.DROPDOWN_CONFIGS) } }