Skip to content

Latest commit

 

History

History
1760 lines (1414 loc) · 150 KB

File metadata and controls

1760 lines (1414 loc) · 150 KB

ObjectUI Development Roadmap

Last Updated: April 14, 2026 Current Version: v0.5.x Spec Version: @objectstack/spec v3.3.0 Client Version: @objectstack/client v3.3.0 Target UX Benchmark: 🎯 Airtable parity Current Priority: AppShell Navigation · Designer Interaction · View Config Live Preview Sync ✅ · Dashboard Config Panel · Airtable UX Polish · Flow Designer ✅ · App Creation & Editing Flow ✅ · System Settings & App Management ✅ · Right-Side Visual Editor Drawer ✅ · Object Manager & Field Designer ✅ · AI SDUI Chatbot (service-ai + vercel/ai) ✅ · Unified Home Dashboard ✅ · Unified Copilot Skills Architecture ✅


📋 Executive Summary

ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind + Shadcn. It renders JSON metadata from the @objectstack/spec protocol into pixel-perfect, accessible, and interactive enterprise interfaces.

Where We Are: Foundation is solid and shipping — 35 packages, 99+ components, 6,700+ tests, 80 Storybook stories, 43/43 builds passing, ~85% protocol alignment. SpecBridge, Expression Engine, Action Engine, data binding, all view plugins (Grid/Kanban/Calendar/Gantt/Timeline/Map/Gallery), Record components, Report engine, Dashboard BI features, mobile UX, i18n (11 locales), WCAG AA accessibility, Console through Phase 20 (L3), AppShell Navigation Renderer (P0.1), Flow Designer (P2.4), Feed/Chatter UI (P1.5), App Creation & Editing Flow (P1.11), System Settings & App Management (P1.12), Page/Dashboard Editor Console Integration (P1.11), Right-Side Visual Editor Drawer (P1.11), Console Engine Schema Integration (P1.14), and Unified Home Dashboard (P1.7.1) — all ✅ complete. ViewDesigner has been removed — its capabilities (drag-to-reorder, undo/redo) are now provided by the ViewConfigPanel (right-side config panel).

What Remains: The gap to Airtable-level UX is primarily in:

  1. AppShell — No dynamic navigation renderer from spec JSON (last P0 blocker) ✅ Complete
  2. Designer Interaction — DataModelDesigner has undo/redo, field type selectors, inline editing, Ctrl+S save. ViewDesigner has been removed; its capabilities (drag-to-reorder columns via @dnd-kit, undo/redo via useConfigDraft history) are now integrated into ViewConfigPanel (right-side config panel) ✅
  3. View Config Live Preview Sync — Config panel changes sync in real-time for Grid, but showSort/showSearch/showFilters/striped/bordered not yet propagated to Kanban/Calendar/Timeline/Gallery/Map/Gantt (see P1.8.1) ✅ Complete — all 7 phases of P1.8.1 done, 100% coverage across all view types
  4. Dashboard Config Panel — Airtable-style right-side configuration panel for dashboards (data source, layout, widget properties, sub-editors, type definitions). Widget config live preview sync and scatter chart type switch ✅ fixed (P1.10 Phase 10). Dashboard save/refresh metadata sync ✅ fixed (P1.10 Phase 11). Data provider field override for live preview ✅ fixed (P1.10 Phase 12). Table/Pivot widget enhancements and context-aware config panel ✅ (P1.10 Phase 13).
  5. Console Advanced Polish — Remaining upgrades for forms, import/export, automation, comments
  6. PWA Sync — Background sync is simulated only

🔄 Spec v3.0.9 Upgrade Summary

Upgraded from @objectstack/spec v3.0.8v3.0.9 on February 22, 2026. UI sub-export is unchanged; changes are in Automation, Kernel, Data, and API layers.

New Protocol Capabilities (v3.0.9):

Area What's New Impact on ObjectUI
Workflow Nodes parallel_gateway, join_gateway, boundary_event node types ProcessDesigner (P2.1) & Automation builder (P1.6)
BPMN Interop BpmnImportOptions, BpmnExportOptions, BpmnInteropResult, BpmnElementMapping plugin-workflow BPMN import/export (P2.4)
Wait/Timer Executors WaitEventType (condition/manual/webhook/timer/signal), WaitExecutorConfig, WaitTimeoutBehavior Automation builder wait-step UI (P1.6)
Execution Tracking ExecutionLog, ExecutionStepLog, Checkpoint, ExecutionError, ExecutionStatus Automation execution history (P1.6)
Flow Edges conditional edge type, isDefault flag ProcessDesigner conditional routing (P2.1)
Retry Config backoffMultiplier, maxRetryDelayMs, jitter on retry policy Automation retry settings UI (P1.6)
Flow Versioning FlowVersionHistory, ConcurrencyPolicy, ScheduleState Flow version management & scheduling (P1.6)
Data Export ExportFormat, ExportJobStatus, CreateExportJobRequest (export.zod) Import/Export feature (P1.3)
App Engine Optional engine: { objectstack: string } on App config Version pinning support (P2.4)
Package Upgrade PackageArtifact, ArtifactChecksum, UpgradeContext, DependencyStatus Package management (P2.4)
Kernel Enhancements PluginBuildOptions, PluginPublishOptions, PluginValidateOptions, MetadataCategory, NamespaceConflictError Plugin development tooling (P2.4)

UI Sub-Export: No breaking changes — @objectstack/spec/ui types are identical between v3.0.8 and v3.0.9.


🔄 Spec v3.2.0 Upgrade Summary

Upgraded from @objectstack/spec v3.0.10v3.2.0 on March 2, 2026. All @objectstack/* packages upgraded to v3.2.0.

Breaking Changes Applied:

  • Actions with type: 'api' now require a target field (the API endpoint/handler name). Added target to all API actions across examples (CRM, todo, msw-todo, kitchen-sink).

🔄 Spec v3.0.10 Upgrade Summary

Upgraded from @objectstack/spec v3.0.9v3.0.10 on February 25, 2026. UI sub-export adds new Zod schemas and ViewFilterRule type. Dashboard widgets now require id field. View filters must use object format.

New Protocol Capabilities (v3.0.10):

Area What's New Impact on ObjectUI
UI Schemas DensityModeSchema, ThemeModeSchema, WcagContrastLevelSchema Zod schemas Re-exported from @object-ui/types for runtime validation
View Filter Rules ViewFilterRule, ViewFilterRuleSchema — structured filter format { field, operator, value } All view filters migrated from tuple ['field', '=', 'value'] to object format
Dashboard Widgets id field now required on DashboardWidgetSchema All example dashboard widgets updated with explicit id
Filter AST isFilterAST, parseFilterAST, VALID_AST_OPERATORS in data sub-export Filter engine utilities (P2.4)
Multi-Tenant TursoMultiTenantConfig, TenantResolverStrategy, TenantDatabaseLifecycle Cloud multi-tenancy (P2.4)
Contracts IAppLifecycleService, IDeployPipelineService, IProvisioningService, ITenantRouter, ISchemaDiffService Cloud deployment & lifecycle (P2.4)

Breaking Changes Applied:

  • All CRM view filters converted from ['field', '=', 'value'] to [{ field, operator: '=', value }]
  • All dashboard widgets (kitchen-sink, todo, CRM) given explicit id fields
  • Todo active filter converted from [['status', '!=', 'Done']] to [{ field: 'status', operator: '!=', value: 'Done' }]

🎯 P0 — Must Ship (v1.0 Blockers)

P0.1 AppShell & Navigation Renderer

Last remaining P0 blocker. Without this, Console cannot render a sidebar from AppSchema JSON.

  • Implement AppSchema renderer consuming spec JSON (name, label, icon, branding)
  • Build navigation tree renderer (7 nav item types: object, dashboard, page, url, report, action, group)
  • Implement NavigationAreaSchema support (business domain partitioning)
  • Implement mobile navigation modes (drawer/bottom_nav/hamburger)
  • Add permission guards (requiredPermissions, visible) on navigation items

🎯 P1 — UI-First: Airtable UX Parity

Priority #1. All items below directly affect end-user experience. Target: indistinguishable from Airtable for core CRUD workflows.

P1.1 Designer Interaction (DataModelDesigner)

Source: ROADMAP_DESIGNER Phase 2. ViewDesigner has been removed — its capabilities (column reorder, undo/redo) are now provided by ViewConfigPanel.

ViewDesigner: (Removed — replaced by ViewConfigPanel)

  • Column drag-to-reorder via @dnd-kit/core (replace up/down buttons with drag handles)
  • Add Ctrl+S/Cmd+S keyboard shortcut to save
  • Add field type selector dropdown with icons from DESIGNER_FIELD_TYPES
  • Column width validation (min/max/pattern check)
  • Removed: ViewDesigner replaced by ViewConfigPanel (right-side config panel)
  • ViewConfigPanel upgraded: undo/redo integrated into useConfigDraft hook
  • ViewConfigPanel upgraded: drag-and-drop column sorting via @dnd-kit/sortable

DataModelDesigner:

  • Entity drag-to-move on canvas
  • Inline editing for entity labels (click to edit)
  • Field type selector dropdown (replaces hardcoded 'text' type)
  • Confirmation dialogs for destructive actions (delete entity cascades to relationships)

Shared Infrastructure:

  • Implement useDesignerHistory hook (command pattern with undo/redo stacks)
  • Wire undo/redo to DataModelDesigner

P1.2 Console — Forms & Data Collection

  • ModalForm responsive optimization: sections layout auto-upgrades modal size, slider for percent/progress fields, tablet 2-column layout
  • Camera capture for mobile file upload
  • Image cropping/rotation in file fields
  • Cloud storage integration (S3, Azure Blob) for file upload
  • Upload resume on network failure
  • Advanced lookup: dependent lookups (filter based on other fields)
  • Hierarchical lookups (parent-child relationships)
  • Lookup result caching
  • Lookup field dynamic DataSource loading — popup fetches records via DataSource.find() with $search debounce, loading/error/empty states
  • Lookup field context DataSource — reads DataSource from SchemaRendererContext so forms work without explicit prop
  • Lookup field UX polish — arrow key navigation, description field display, quick-create entry, ARIA listbox roles
  • Enterprise Record Picker — RecordPickerDialog component with multi-column table, pagination, search; LookupField two-level interaction (quick-select + "Show All Results" → full picker); lookup_columns / lookup_page_size schema config
  • CRM Enterprise Lookup Metadata — all 14 lookup fields across 8 CRM objects configured with lookup_columns (type-aware cell rendering), lookup_filters (business-level base filters), description_field; uses post-create injection to bypass ObjectSchema.create() Zod stripping; 12 dedicated test cases
  • Form conditional logic with branching
  • Multi-page forms with progress indicator

P1.3 Console — Import/Export Excellence

Spec v3.0.9 introduces a formal Data Export/Import Protocol (export.zod) with streaming export, import validation, field mapping templates, and scheduled export jobs.

  • Excel (XLSX) export with formatting
  • PDF export with custom formatting
  • Export all data (not just visible rows)
  • Custom column selection for export
  • Scheduled exports via automation
  • Export templates with custom formatting
  • Import field mapping UI (map CSV columns to object fields)
  • Import validation preview with error correction
  • Duplicate detection during import
  • Integrate spec v3.0.9 ExportFormat enum (json, csv, xlsx, jsonl, parquet)
  • Integrate spec v3.0.9 CreateExportJobRequest / ExportJobStatus for async streaming exports
  • Import template-based field mapping using spec protocol schemas

P1.4 Console — Undo/Redo & History

  • Cross-session undo stack persistence (survive page refresh)
  • Undo grouping (batch multiple field changes as one undo step)
  • Visual undo history panel (timeline of changes)
  • Undo/redo for bulk operations

P1.5 Console — Comments & Collaboration

  • @mention notification delivery (email/push)
  • Comment search across all records
  • Comment pinning/starring
  • Activity feed filtering (comments only / field changes only)
  • Airtable-style Feed/Chatter UI components (P0/P1/P2):
    • FeedItem/FieldChangeEntry/Mention/Reaction/RecordSubscription types
    • RecordActivityTimeline — unified timeline renderer (filter, pagination, actor display)
    • RecordChatterPanel — sidebar/inline/drawer panel (collapsible)
    • CommentInput — comment input with Ctrl+Enter submit
    • FieldChangeItem — field change history (old→new display values)
    • MentionAutocomplete — @mention autocomplete dropdown
    • SubscriptionToggle — bell notification toggle
    • ReactionPicker — emoji reaction selector
    • ThreadedReplies — collapsible comment reply threading
    • Comprehensive unit tests for all 6 core Feed/Chatter components (96 tests)
    • Console RecordDetailView integration: CommentThreadRecordChatterPanel with FeedItem[] data model
    • Documentation for Feed/Chatter plugin in content/docs/plugins/plugin-detail.mdx (purpose/use cases, JSON schema, props, and Console integration for RecordChatterPanel, RecordActivityTimeline, and related components)

P1.6 Console — Automation

Spec v3.0.9 significantly expanded the automation/workflow protocol. New node types, BPMN interop, execution tracking, and wait/timer executors are now available in the spec.

  • Multi-step automation builder (if-then chains)
  • Scheduled automations (cron-based triggers)
  • Webhook triggers and actions
  • Email notification actions
  • Automation execution history and logs
  • Support new v3.0.9 workflow node types: parallel_gateway, join_gateway, boundary_event
  • BPMN import/export interop (spec provides BpmnImportOptions, BpmnExportOptions, BpmnInteropResult)
  • Wait/timer executor UI (waitEventConfig: condition, manual, webhook, timer, signal events)
  • Execution tracking dashboard (ExecutionLog, ExecutionStepLog, Checkpoint, ExecutionError)
  • Conditional edge support (conditional edge type, isDefault flag on edges)
  • Enhanced retry configuration UI (backoffMultiplier, maxRetryDelayMs, jitter)
  • Flow version history viewer (FlowVersionHistory)
  • Concurrency policy configuration (ConcurrencyPolicy)

P1.7 Console — Navigation Enhancements

  • AppShell AppSchema renderer (spec-driven sidebar from JSON)
  • Area switcher with grouped navigation
  • User-customizable sidebar (drag reorder, pin favorites)
  • Search within sidebar navigation
  • Console integration: Navigation search filtering (filterNavigationItems + SidebarInput)
  • Console integration: Badge indicators on navigation items (badge + badgeVariant)
  • Console integration: Drag reorder upgrade — replace HTML5 DnD with @dnd-kit via NavigationRenderer
  • Console integration: Navigation pin — useNavPins hook + NavigationRenderer enablePinning/onPinToggle
  • Console integration: AppSchemaRenderer slot system — sidebarHeader, sidebarExtra, sidebarFooter slots for Console customization
  • Navigation Sync Service — useNavigationSync hook auto-syncs App navigation tree on Page/Dashboard CRUD (create, delete, rename) with toast + undo
  • Navigation Sync auto-detection — NavigationSyncEffect component monitors metadata changes and auto-syncs navigation across ALL apps when pages/dashboards are added or removed
  • Navigation Sync all-apps convenience API — sync*AllApps methods iterate all apps without requiring explicit appName
  • ListView Navigation Mode Fix — All 6 navigation modes (page/drawer/modal/split/popover/new_window) now work correctly on row click:
    • page mode navigates to /record/:recordId detail page via React Router
    • new_window mode opens correct Console URL in a new browser tab (delegates to onNavigate)
    • split mode renders resizable split panels with main content + detail panel
    • popover mode falls back to compact dialog when no popoverTrigger is provided
    • useNavigationOverlay hook delegates new_window to onNavigate when available for app-specific URL control
    • ✅ plugin-view handleRowClick supports split and popover branches

P1.7.1 Console — Unified Home Dashboard (Workspace) ✅

  • HomePage component — Unified landing page displaying all available applications
  • Route integration/home route added with proper authentication guards
  • App cards grid — Responsive grid layout showing all active apps with icons, descriptions, and branding colors
  • QuickActions section — Quick access cards for creating apps, managing objects, and system settings
  • Recent items — Display recently accessed objects, dashboards, and pages using useRecentItems hook
  • Starred items — Display user-favorited items using useFavorites hook with star/unstar toggle
  • Empty state — Helpful guidance for new users with "Create First App" and "System Settings" CTAs
  • i18n support — All labels support internationalization via useObjectTranslation
  • RootRedirect update — Root path (/) now redirects to /home instead of first app
  • Responsive design — Mobile-friendly grid layouts that adapt to screen size
  • Airtable/Notion UX pattern — Inspired by industry-leading workspace home pages
  • HomeLayout shell — Lightweight layout wrapper with sticky top nav bar, Home branding, and user menu dropdown (Profile, Settings, Sign Out)
  • Home page user menu — Complete user menu dropdown in HomeLayout header with avatar, name, email, Profile/Settings/Sign Out actions
  • Return-to-Home navigation — "Home" entry in AppSidebar app switcher dropdown for navigating back to /home from any application

Impact: Users now have a unified workspace dashboard that provides overview of all applications, quick actions, and recent activity. This eliminates the previous behavior of auto-redirecting to the first app, giving users better control and visibility.

P1.8 Console — View Config Panel (Phase 20)

  • Inline ViewConfigPanel for all view types (Airtable-style right sidebar)
  • Column visibility toggle from config panel
  • Column reorder (move up/down) from config panel with real-time preview
  • Sort/filter/group config from right sidebar
  • Type-specific options in config panel (kanban/calendar/map/gallery/timeline/gantt)
  • Unified create/edit mode (mode="create"|"edit") — single panel entry point
  • Unified data model (UnifiedViewConfig) for view configuration
  • ViewDesigner removed — its capabilities replaced by ViewConfigPanel (right-side config panel)
  • Panel header breadcrumb navigation (Page > List/Kanban/Gallery)
  • Collapsible/expandable sections with chevron toggle
  • Data section: Sort by (summary), Group by, Prefix field, Fields (count visible)
  • Appearance section: Color, Field text color, Row height (icon toggle), Wrap headers, Show field descriptions, Collapse all by default
  • User actions section: Edit records inline (→ inlineEdit), Add/delete records inline, Navigation mode (page/drawer/modal/split/popover/new_window/none)
  • Calendar endDateField support
  • i18n for all 11 locales (en, zh, ja, de, fr, es, ar, ru, pt, ko)
  • Live preview: ViewConfigPanel changes sync in real-time to all list types (Grid/Kanban/Calendar/Timeline/Gallery/Map) (all 7 phases complete — see P1.8.1 gap analysis below)
    • showSort added to ObjectViewSchema and propagated through plugin-view (Grid only)
    • ✅ Appearance properties (rowHeight, densityMode) flow through renderListView schema for all view types
    • gridSchema in plugin-view includes striped/bordered from active view config (Grid only)
    • ✅ Plugin renderContent passes rowHeight, densityMode, groupBy to renderListView schema
    • useMemo dependency arrays expanded to cover full view config
    • generateViewSchema propagates showSearch/showSort/showFilters/striped/bordered/color from activeView for all view types (hardcoded showSearch: false removed)
    • ✅ Console renderListView passes showSort/showSearch/showFilters/striped/bordered/color/filter/sort to fullSchema
    • NamedListView type declares showSearch/showSort/showFilters/striped/bordered/color as first-class properties
    • ListViewSchema TypeScript interface and Zod schema include showSearch/showSort/showFilters/color
    • ✅ ViewConfigPanel refactored into Page Config (toolbar/shell) and ListView Config (data/appearance) sections
    • NamedListView type extended with 24 new properties: navigation, selection, pagination, searchableFields, filterableFields, resizable, densityMode, rowHeight, hiddenFields, exportOptions, rowActions, bulkActions, sharing, addRecord, conditionalFormatting, quickFilters, showRecordCount, allowPrinting, virtualScroll, emptyState, aria
    • ListViewSchema Zod schema extended with all new properties
    • ✅ ViewConfigPanel aligned to full ListViewSchema spec: navigation mode, selection, pagination, export sub-config, searchable/filterable/hidden fields, resizable, density mode, row/bulk actions, sharing, addRecord sub-editor, conditional formatting, quick filters, showRecordCount, allowPrinting, virtualScroll, empty state, ARIA accessibility
    • ✅ Semantic fix: editRecordsInlineinlineEdit field name alignment (i18n keys, data-testid, component label all unified to inlineEdit)
    • ✅ Semantic fix: rowHeight values aligned to full spec — all 5 RowHeight enum values (compact/short/medium/tall/extra_tall) now supported in NamedListView, ObjectGridSchema, ListViewSchema, Zod schema, UI, and ObjectGrid rendering (cell classes, cycle toggle, icon mapping)
    • clickIntoRecordDetails toggle added to UserActions section (NamedListView spec field — previously only implicit via navigation mode)
    • Strict spec-order alignment: All fields within each section reordered to match NamedListView property declaration order:
      • PageConfig: showSort before showFilters; allowExport before navigation (per spec)
      • Data: columns → filter → sort (per spec); prefixField after sort
      • Appearance: striped/bordered first, then color, wrapHeaders, etc. (per spec)
      • UserActions: inlineEdit before clickIntoRecordDetails (per spec)
    • Spec source annotations: Every field annotated with // spec: NamedListView.* or // UI extension comment
    • Protocol suggestions documented: description, _source, _groupBy, _typeOptions identified as UI extensions pending spec addition
    • Comprehensive spec field coverage test: All 44 NamedListView properties verified mapped to UI fields; field ordering validated per spec
    • ✅ i18n keys verified complete for en/zh and all 10 locale files
    • ✅ Console ObjectView fullSchema propagates all 18 new spec properties
    • ✅ PluginObjectView renderListView schema propagates all 18 new spec properties
    • ✅ Per-view-type integration tests added for Grid/Kanban/Calendar/Timeline/Gantt/Gallery/Map config sync (Phase 7 complete)
  • Conditional formatting rules (editor in Appearance section)

P1.8.1 Live Preview — Gap Analysis & Phased Remediation

Ref: Issue #711 — Right-side view config panel changes not syncing in real-time to all list types.

Current Config Property Propagation Matrix:

Property Grid Kanban Calendar Timeline Gallery Map Gantt
showSearch
showSort
showFilters
showHideFields
showGroup
showColor
showDensity
allowExport
rowHeight
densityMode
striped
bordered
groupBy N/A N/A N/A N/A N/A N/A
color
filter/sort
Type-specific options

Root Causes (resolved):

  1. generateViewSchema (plugin-view): Hardcodes showSearch: false for non-grid views → Now propagates from activeView
  2. Console renderListView: Omits toolbar/display flags from fullSchema → Now passes all config properties
  3. NamedListView type: Missing toolbar/display properties → Added as first-class properties
  4. Plugin renderListView schema missing toolbar flags: renderContentrenderListView schema did not include showSearch/showFilters/showSort → Now propagated (PR #771)
  5. ListView toolbar unconditionally rendered: Search/Filter/Sort buttons always visible regardless of schema flags → Now conditionally rendered based on schema.showSearch/showFilters/showSort (PR #771); userActions.sort/search/filter/rowHeight/addRecordForm now override toolbar flags (Issue #737)
  6. Hide Fields/Group/Color/Density buttons always visible: No schema property to control visibility → Added showHideFields/showGroup/showColor/showDensity with conditional rendering (Issue #719)
  7. Export toggle broken: ViewConfigPanel writes allowExport: boolean but ListView checks exportOptions object → Export now checks both exportOptions && allowExport !== false; Console clears exportOptions when allowExport === false (Issue #719)
  8. hasExport logic bug: draft.allowExport !== false was always true when undefined → Fixed to draft.allowExport === true || draft.exportOptions != null (Issue #719)
  9. No per-view-type integration tests: Pending — tests verify config reaches fullSchema, but per-renderer integration tests still needed
  10. key={refreshKey} on PluginObjectView: Console wrapped PluginObjectView with key={refreshKey}, which only changed on save/create, preventing live preview of config changes → Removed key={refreshKey}; props changes now flow naturally without remounting (Issue #784)
  11. Navigation overlay not consuming activeView.navigation: Detail overlay only read objectDef.navigation, ignoring view-level navigation config → Navigation now uses priority: activeView.navigation > objectDef.navigation > default drawer (Issue #784)

Phase 1 — Grid/Table View (baseline, already complete):

  • gridSchema includes striped/bordered from activeView
  • showSort/showSearch/showFilters passed via ObjectViewSchema
  • useMemo dependency arrays cover all grid config
  • ListView toolbar buttons conditionally rendered based on schema.showSearch/showFilters/showSort (PR #771)
  • renderListView schema includes toolbar toggle flags (showSearch/showFilters/showSort) and display props (striped/bordered/color) (PR #771)
  • Hide Fields/Group/Color/Density toolbar buttons conditionally rendered via showHideFields/showGroup/showColor/showDensity (Issue #719)
  • Export button checks both exportOptions and allowExport (Issue #719)
  • hasExport logic fixed — no longer always true when allowExport is undefined (Issue #719)
  • ViewConfigPanel includes toggles for showHideFields/showGroup/showColor/showDensity (Issue #719)
  • showHideFields/showGroup/showColor/showDensity/allowExport propagated through Console fullSchema and PluginObjectView renderListView (Issue #719)
  • Full end-to-end data flow: all ViewConfigPanel props (inlineEdit/wrapHeaders/clickIntoRecordDetails/addRecordViaForm/addDeleteRecordsInline/collapseAllByDefault/fieldTextColor/prefixField/showDescription) propagated through Console fullSchema → PluginObjectView renderListView → ListView (Issue #719)
  • ListView forwards striped/bordered/wrapHeaders to child viewComponentSchema (grid gets wrapHeaders, all views get striped/bordered) (Issue #719)
  • ViewConfigPanel includes striped/bordered toggles in Appearance section (Issue #719)
  • Type definitions complete: NamedListView + ListViewSchema + Zod schema include all 22 view-config properties (Issue #719)

Phase 2 — Kanban Live Preview:

  • Propagate showSort/showSearch/showFilters through generateViewSchema kanban branch
  • Pass color/striped/bordered in renderContentrenderListView for kanban
  • Ensure groupBy config changes reflect immediately (currently ✅ via renderListView)
  • Add integration test: ViewConfigPanel kanban config change → Kanban renderer receives updated props

Phase 3 — Calendar Live Preview:

  • Propagate showSort/showSearch/showFilters through generateViewSchema calendar branch
  • Pass filter/sort/appearance properties to calendar renderer in real-time
  • Verify startDateField/endDateField config changes trigger re-render via useMemo deps
  • Add integration test: ViewConfigPanel calendar config change → Calendar renderer receives updated props

Phase 4 — Timeline/Gantt Live Preview:

  • Propagate showSort/showSearch/showFilters through generateViewSchema timeline/gantt branches
  • Pass appearance properties (color, striped, bordered) through renderListView schema
  • Ensure dateField/startDateField/endDateField config changes trigger re-render
  • Add integration tests for timeline and gantt config sync

Phase 5 — Gallery & Map Live Preview:

  • Propagate showSort/showSearch/showFilters through generateViewSchema gallery/map branches
  • Pass appearance properties through renderListView schema for gallery/map
  • Ensure gallery imageField/titleField and map locationField/zoom/center config changes trigger re-render
  • Add integration tests for gallery and map config sync

Phase 6 — Data Flow & Dependency Refactor:

  • Add showSearch/showSort/showFilters/striped/bordered/color to NamedListView type in @object-ui/types
  • Add showHideFields/showGroup/showColor/showDensity/allowExport to NamedListView and ListViewSchema types and Zod schema (Issue #719)
  • Add inlineEdit/wrapHeaders/clickIntoRecordDetails/addRecordViaForm/addDeleteRecordsInline/collapseAllByDefault/fieldTextColor/prefixField/showDescription to NamedListView and ListViewSchema types and Zod schema (Issue #719)
  • Update Console renderListView to pass all config properties in fullSchema
  • Audit all useMemo/useEffect dependency arrays in plugin-view/ObjectView.tsx for missing activeView sub-properties — all hooks correctly use whole activeView object reference; React shallow equality handles sub-property changes
  • Remove hardcoded showSearch: false from generateViewSchema — use activeView.showSearch ?? schema.showSearch instead

Phase 7 — End-to-End Integration Tests:

  • Per-view-type test: Grid config sync (showSort, showSearch, showFilters, striped, bordered)
  • Per-view-type test: Kanban config sync (groupBy, color, showSearch)
  • Per-view-type test: Calendar config sync (startDateField, endDateField, showFilters)
  • Per-view-type test: Timeline/Gantt config sync (dateField, appearance)
  • Per-view-type test: Gallery config sync (imageField, titleField, appearance)
  • Per-view-type test: Map config sync (locationField, zoom, center, appearance)
  • Cross-view-type test: Switch view type in ViewConfigPanel → verify config properties transfer correctly

P1.10 Console — Dashboard Config Panel

Airtable-style right-side configuration panel for dashboards. Phased rollout from shared infrastructure to full type-safe editing.

Phase 0 — Component Infrastructure:

  • Extract ConfigRow / SectionHeader from ViewConfigPanel into @object-ui/components as reusable primitives
  • Implement useConfigDraft generic hook for draft state management (dirty tracking, save/discard)
  • Define ConfigPanelSchema / ConfigSection / ConfigField types for schema-driven panel generation
  • Implement ConfigFieldRenderer supporting input/switch/select/checkbox/slider/color/icon-group/field-picker/filter/sort/custom
  • Implement ConfigPanelRenderer — schema-driven panel with header, breadcrumb, collapsible sections, sticky footer
  • Add configPanel i18n keys to all 10 locale files

Phase 1 — Dashboard-Level Config Panel:

  • Develop DashboardConfigPanel supporting layout (columns/gap/rowHeight), data (refreshInterval), appearance (title/description/theme)
  • Add Storybook stories for ConfigPanelRenderer and DashboardConfigPanel
  • Add Vitest tests (65 tests: useConfigDraft 10, ConfigFieldRenderer 22, ConfigPanelRenderer 21, DashboardConfigPanel 12)

Phase 2 — Widget-Level Configuration:

  • Support click-to-select widget → sidebar switches to widget property editor (title, type, data binding, layout)
  • Implement WidgetConfigPanel with schema-driven fields: general (title, description, type), data binding (object, categoryField, valueField, aggregate), layout (width, height), appearance (colorVariant, actionUrl)
  • Add Vitest tests (14 tests for WidgetConfigPanel)

Phase 3 — Sub-Editor Integration:

  • Integrate FilterBuilder for dashboard global filters (ConfigFieldRenderer filter type now renders inline FilterBuilder)
  • Integrate SortBuilder for sort configuration (ConfigFieldRenderer sort type now renders inline SortBuilder)
  • Add fields prop to ConfigField type for filter/sort field definitions
  • Dropdown filter selector and action button sub-panel visual editing

Phase 4 — Composition & Storybook:

  • Build DashboardWithConfig composite component (dashboard + config sidebar)
  • Support widget selection → WidgetConfigPanel switch with back navigation
  • Add Storybook stories for WidgetConfigPanel, DashboardWithConfig, and DashboardWithConfigClosed
  • Add Vitest tests (9 tests for DashboardWithConfig)

Phase 5 — Type Definitions & Validation:

  • Add DashboardConfig types to @object-ui/types
  • Add Zod schema validation for DashboardConfig

Phase 6 — Design Mode Preview Click-to-Select:

  • Add designMode, selectedWidgetId, onWidgetClick props to DashboardRenderer for preview-area widget selection
  • Implement click-to-select with primary ring highlight (light/dark theme compatible, a11y focus-visible ring)
  • Click empty space to deselect; Escape key to deselect
  • Keyboard navigation: ArrowRight/ArrowDown to next widget, ArrowLeft/ArrowUp to previous, Enter/Space to select, Tab/Shift+Tab for focus
  • Add selectedWidgetId and onWidgetSelect props to DashboardEditor for external controlled selection
  • Sync selection between DashboardRenderer (preview) and DashboardEditor (drawer) via shared state in DashboardView
  • Property changes in editor panel instantly reflected in preview (live preview path verified end-to-end)
  • Auto-save property changes to backend via DesignDrawer
  • Add Vitest tests (15 DashboardRenderer design mode + 9 DashboardEditor external selection + 8 DashboardView integration = 32 new tests)

Phase 7 — Non-Modal Drawer & Property Panel UX Fix:

  • SheetContent — added hideOverlay prop to conditionally skip the full-screen backdrop overlay
  • DesignDrawermodal={false} + hideOverlay so preview widgets are clickable while drawer is open
  • DashboardEditor — property panel renders above widget grid (stacked flex-col layout) for immediate visibility in narrow drawer
  • DashboardEditor — property panel uses full width (removed fixed w-72) for better readability in drawer context
  • Preview click → editor property panel linkage now works end-to-end (select, switch, deselect)
  • Add 11 new tests (7 DashboardDesignInteraction integration + 4 DashboardEditor.propertyPanelLayout)

Phase 8 — Inline Config Panel Refactor (ListView Parity):

  • Replace DesignDrawer + DashboardEditor in DashboardView with inline DashboardConfigPanel / WidgetConfigPanel
  • Right-side panel shows DashboardConfigPanel when no widget selected (dashboard-level properties: columns, gap, refresh, theme)
  • Right-side panel switches to WidgetConfigPanel when a widget is selected (title, type, data binding, layout, appearance)
  • Config panels use standard ConfigPanelRenderer with save/discard/footer (matches ListView/PageDesigner pattern)
  • Add-widget toolbar moved to main area header (visible only in edit mode)
  • Main area remains WYSIWYG preview via DashboardRenderer with designMode click-to-select
  • Widget config flattening/unflattening (layout.w ↔ layoutW, layout.h ↔ layoutH)
  • Auto-save on config save via useAdapter().update()
  • Live preview updates via onFieldChange callback
  • Config draft stabilization via configVersion counter (matching ViewConfigPanel's stableActiveView pattern) — prevents useConfigDraft draft reset on live field changes
  • Widget delete via headerExtra delete button in WidgetConfigPanel header
  • WidgetConfigPanel — added headerExtra prop for custom header actions
  • Update 21 integration tests (10 DashboardDesignInteraction + 11 DashboardViewSelection) to verify inline config panel pattern, widget deletion, live preview sync

Phase 9 — Design Mode Widget Selection Click-Through Fix:

  • Fix: Widget content (charts, tables via SchemaRenderer) intercepted click events, preventing selection in edit mode
  • Defense layer 1: pointer-events-none on SchemaRenderer content wrappers disables chart/table hover and tooltip interactivity in design mode
  • Defense layer 2: Transparent click-capture overlay (absolute inset-0 z-10) renders on top of widget content in design mode — guarantees click reaches widget handler even if SVG children override pointer-events
  • Self-contained (metric) widgets: both pointer-events-none on SchemaRenderer + overlay inside relative wrapper
  • Card-based (chart/table) widgets: pointer-events-none on CardContent inner wrapper + overlay inside relative Card
  • No impact on non-design mode — widgets remain fully interactive when not editing
  • Updated SchemaRenderer mock to forward className and include interactive child button for more realistic testing
  • Add 9 new Vitest tests: pointer-events-none presence/absence, overlay presence/absence, relative positioning, click-to-select on Card-based widgets

Phase 10 — Widget Config Live Preview Sync & Type Switch Fix:

  • Fix: DashboardWithConfig did not pass onFieldChange to WidgetConfigPanel, preventing live preview of widget config changes
  • Add internal liveSchema state to DashboardWithConfig for real-time widget preview during editing
  • Add configVersion counter to stabilize selectedWidgetConfig and prevent useConfigDraft draft reset loops
  • Fix: scatter chart type was not handled in DashboardRenderer and DashboardGridLayout — switching to scatter caused errors
  • Add scatter to chart type conditions in both DashboardRenderer.getComponentSchema() and DashboardGridLayout.getComponentSchema()
  • Fix: Data binding fields (categoryField, valueField, object, aggregate) from config panel did not affect rendering — getComponentSchema() only read from options.xField/options.yField/options.data
  • Add widget-level field fallbacks: widget.categoryField || options.xField, widget.valueField || options.yField in both DashboardRenderer and DashboardGridLayout
  • Support object-chart construction from widget-level fields when no explicit data provider exists (e.g. newly created widgets via config panel)
  • Support data-table construction from widget.object when no data provider exists (table widgets created via config panel)
  • Add 7 new Vitest tests: scatter chart (2), widget-level field fallbacks (2), object-chart from widget fields, data-table from widget.object, DashboardWithConfig live preview

Phase 11 — Dashboard Save/Refresh Metadata Sync:

  • Fix: saveSchema in DashboardView did not call metadata.refresh() after PATCH — closing config panel showed stale data from cached metadata
  • Fix: previewSchema only used editSchema when configPanelOpen=true — changed to editSchema || dashboard so edits remain visible after panel close until metadata refreshes
  • Add useEffect to clear stale editSchema when metadata refreshes while config panel is closed (seamless transition)
  • Clear editSchema and config panel state on dashboard navigation (dashboardName change)
  • Fix: DashboardDesignPage.saveSchema did not call metadata.refresh() — other pages saw stale dashboard data after save
  • Add 5 new Vitest tests: metadata refresh after widget save (2), metadata refresh after widget delete (2), metadata refresh after DashboardDesignPage save (1)

Phase 12 — Data Provider Field Override for Live Preview:

  • Fix: Widget-level fields (categoryField, valueField, aggregate, object) did not override data provider config (widget.data.aggregate) — editing these fields in the config panel had no effect on the rendered chart when a data provider was present
  • getComponentSchema() in DashboardRenderer and DashboardGridLayout now merges widget-level fields with data provider aggregate config, with widget-level fields taking precedence
  • Fix: objectName for table/pivot widgets used widgetData.object || widget.object — reversed to widget.object || widgetData.object so config panel edits to data source are reflected immediately
  • Fix: DashboardWithConfig did not pass designMode, selectedWidgetId, or onWidgetClick to DashboardRenderer — widgets could not be selected or live-previewed in the plugin-level component
  • Add 10 new Vitest tests: widget-level field overrides for aggregate groupBy/field/function (3), objectName precedence for chart/table (2), simultaneous field overrides (1), DashboardWithConfig design mode and widget selection (2), existing live preview tests (2)

Phase 13 — Table/Pivot Widget Enhancements & Context-Aware Config Panel:

  • Add pivot to DASHBOARD_WIDGET_TYPES constant and WidgetConfigPanel type options dropdown
  • Context-aware WidgetConfigPanel: sections shown/hidden via visibleWhen based on widget type — Pivot shows Rows/Columns/Values/Totals, Chart shows Axis & Series, Table shows Columns config
  • Pivot-specific config: Row field, Column field, Value field, Sort by (Group/Value icon-group), Sort order (↑/↓ icon-group), Show label toggle, Show totals toggle for both rows and columns, Aggregation, Number format
  • Chart-specific config: X-axis label, Y-axis label, Show legend toggle
  • Table-specific config: Searchable toggle, Pagination toggle
  • Breadcrumb adapts to widget type ("Pivot table", "Table", "Chart", "Widget")
  • I18nLabel resolution: WidgetConfigPanel pre-processes title and description config values via resolveLabel() to prevent [object Object] display
  • DashboardRenderer: widget description rendered in card headers with line-clamp-2; I18nLabel resolved via resolveLabel()
  • ObjectPivotTable: new async-aware pivot wrapper (following ObjectChart pattern) — skeleton loading, error state, no-data-source message, empty state delegation to PivotTable
  • ObjectDataTable: new async-aware table wrapper — skeleton loading, error state, empty state, auto-column derivation from fetched data keys
  • DashboardRenderer: pivot widgets with objectName or provider: 'object' routed to object-pivot type (ObjectPivotTable) for async data loading
  • DashboardRenderer: table widgets with objectName or provider: 'object' routed to object-data-table type (ObjectDataTable) for async data loading
  • DashboardRenderer: grid column clamping — widget layout.w clamped to Math.min(w, columns) preventing layout overflow
  • MetricWidget: overflow protection — overflow-hidden on Card, truncate on label/value/description, shrink-0 on icon/trend
  • PivotTable: friendly empty state with grid icon + "No data available" message instead of empty table body
  • PivotTable: improved total/subtotal row styling — bg-muted/40 on tfoot, bg-muted/20 on row-total column, font-bold on grand total
  • Add 29 new Vitest tests: ObjectPivotTable (8), ObjectDataTable (6), context-aware sections (6), I18nLabel resolution (2), pivot type option (1), pivot object binding (1), widget description rendering (2), grid column clamping (1), pivot empty state (2)

P1.11 Console — Schema-Driven View Config Panel Migration

Migrated the Console ViewConfigPanel from imperative implementation (~1655 lines) to Schema-Driven architecture using ConfigPanelRenderer + useConfigDraft + ConfigPanelSchema, reducing to ~170 lines declarative wrapper + schema factory.

Phase 1 — Infrastructure & Utils Extraction:

  • Extract operator mapping (SPEC_TO_BUILDER_OP, BUILDER_TO_SPEC_OP), normalizeFieldType, parseSpecFilter, toSpecFilter to shared view-config-utils.ts
  • Extract parseCommaSeparated, parseNumberList, VIEW_TYPE_LABELS, ROW_HEIGHT_OPTIONS to shared utils
  • Add deriveFieldOptions, toFilterGroup, toSortItems bridge helpers
  • Enhance ConfigPanelRenderer with accessibility props (panelRef, role, ariaLabel, tabIndex)
  • Enhance ConfigPanelRenderer with test ID override props (testId, closeTitle, footerTestId, saveTestId, discardTestId)

Phase 2 — Schema Factory (All Sections):

  • Page Config section: label, description, viewType, toolbar toggles (7 switches), navigation mode/width/openNewTab, selection, addRecord sub-editor, export + sub-config, showRecordCount, allowPrinting
  • Data section: source, sortBy (expandable), groupBy (grid/gallery), columns selector (expandable w/ reorder), filterBy (expandable), pagination, searchable/filterable/hidden fields (expandable), quickFilters (expandable), virtualScroll (grid only), type-specific options (kanban/calendar/map/gallery/timeline/gantt)
  • Appearance section: color (grid/calendar/timeline/gantt), rowHeight (icon group, grid only), wrapHeaders (grid only), showDescription, striped/bordered (grid only), resizable (grid only), conditionalFormatting (expandable, grid/kanban), emptyState (title/message/icon)
  • User Actions section: inlineEdit (grid only), addDeleteRecordsInline (grid only), rowActions/bulkActions (expandable, grid/kanban)
  • Sharing section: sharingEnabled, sharingVisibility (visibleWhen: sharing.enabled)
  • Accessibility section: ariaLabel, ariaDescribedBy, ariaLive
  • ExpandableWidget component for hook-safe expandable sub-sections within custom render functions

Phase 3 — ViewConfigPanel Wrapper:

  • Rewrite ViewConfigPanel as thin wrapper (~170 lines) using useConfigDraft + buildViewConfigSchema + ConfigPanelRenderer
  • Stabilize source reference with useMemo keyed to activeView.id (prevents draft reset on parent re-renders)
  • Create/edit mode support preserved (onCreate/onSave, discard behavior)
  • All spec format bridging preserved (filter/sort conversion)

Phase 4 — Testing & Validation:

  • All 122 existing ViewConfigPanel tests pass (test mock updated for ConfigPanelRenderer + useConfigDraft)
  • All 23 ObjectView integration tests pass (test ID and title props forwarded)
  • 53 new schema-driven tests (utils + schema factory coverage)
  • 14 new ObjectGrid rowHeight tests (all 5 enum values: initialization, cycle, label, toggle visibility)
  • Full affected test suite: 2457+ tests across 81+ files, all pass

Phase 5 — Spec Alignment Completion (Issue #745):

  • ObjectGrid rowHeight: full 5-enum rendering (cellClassName, cycleRowHeight, icon map) — was hardcoded to 3
  • 18 new ViewConfigPanel interaction tests: collapseAllByDefault, showDescription, clickIntoRecordDetails, addDeleteRecordsInline toggles; sharing visibility conditional hide; navigation width/openNewTab conditional rendering; all 5 rowHeight button clicks; boundary tests (empty actions, long labels, special chars); pageSizeOptions input; densityMode/ARIA live enums; addRecord conditional sub-editor; sharing visibility select
  • 8 new schema-driven spec tests: accessibility field ordering, emptyState compound field, switch field defaults, comprehensive visibleWhen predicates (sharing, navigation width, navigation openNewTab)
  • All spec fields verified: Appearance/UserActions/Sharing/Accessibility sections 100% covered with UI controls, defaults, ordering, and conditional visibility
  • Add description field to NamedListView protocol interface (spec alignment)
  • Add disabledWhen predicate to ConfigField type — grid-only fields (striped/bordered/wrapHeaders/resizable) disabled for non-grid views
  • Add expandedSections prop to ConfigPanelRenderer for external section collapse control (auto-focus/highlight)
  • Add helpText to navigation-dependent fields (width/openNewTab) with i18n hints (all 11 locales)
  • 24 new tests: expandedSections override (3), disabledWhen evaluation (2), grid-only disabledWhen predicates (16), helpText validation (2), description spec alignment (1)

Phase 6 — Config Panel Cleanup (Invalid Items Fix):

  • Remove densityMode field from appearance section (redundant with rowHeight which provides finer 5-value granularity)
  • Remove prefixField from data section (not consumed by any runtime renderer)
  • Remove collapseAllByDefault from appearance section (not consumed by any runtime renderer)
  • Remove fieldTextColor from appearance section (not consumed by any runtime renderer)
  • Remove clickIntoRecordDetails from userActions section (controlled implicitly via navigation mode, not directly consumed)
  • Add view-type-aware visibleWhen to toolbar toggles: showGroup (grid/kanban/gallery), showColor (grid/calendar/timeline/gantt), showDensity (grid only), showRecordCount (grid only), allowPrinting (grid only)
  • Add view-type-aware visibleWhen to data fields: _groupBy (grid/gallery — kanban uses dedicated type-specific option), virtualScroll (grid only)
  • Add view-type-aware visibleWhen to appearance fields: striped/bordered/wrapHeaders/resizable/rowHeight (grid only, changed from disabledWhen to visibleWhen), color (grid/calendar/timeline/gantt), conditionalFormatting (grid/kanban)
  • Add view-type-aware visibleWhen to userActions fields: inlineEdit/addDeleteRecordsInline (grid only), rowActions/bulkActions (grid/kanban)
  • Correct searchableFields/filterableFields/quickFilters/showDescription to universal (all view types) — data fetch/toolbar features not view-specific
  • Extend buildSwitchField and buildFieldMultiSelect helpers to accept visibleWhen parameter
  • Define semantic predicates: supportsGrouping, supportsColorField, supportsConditionalFormatting, supportsRowActions, supportsGenericGroupBy
  • 103 schema tests pass (updated field key lists, visibleWhen predicates for all view types, removed field verification)
  • 136 ViewConfigPanel interaction tests pass (removed tests for deleted fields)
  • 10 config-sync integration tests pass

Phase 7 — Section Restructure & Field Selector Upgrade (Airtable UX Parity):

  • Split monolithic pageConfig section into 5 clear sub-sections: General, Toolbar, Navigation, Records, Export & Print
  • General section: label, description, viewType (always expanded, non-collapsible)
  • Toolbar section: showSearch, showSort, showFilters, showHideFields, showGroup, showColor, showDensity (collapsible)
  • Navigation section: navigation mode, width, openNewTab (collapsible)
  • Records section: selection mode, addRecord sub-editor (collapsible)
  • Export & Print section: allowExport + sub-config, showRecordCount, allowPrinting (collapsible, defaultCollapsed)
  • Field selector upgrade: eye/eye-off toggle for visibility (replacing checkboxes), search input for field filtering, Show All/Hide All batch operations, grip handles for visual reorder hints
  • i18n keys added for all 11 locales (en, zh, ja, de, fr, es, ar, ru, pt, ko, + ar)
  • 110 schema tests pass (+7 new section tests)
  • 136 ViewConfigPanel interaction tests pass (updated for eye toggles, section expansion)
  • 31 ObjectView integration tests pass (updated for section expansion)
  • 10 config-sync integration tests pass

Phase 8 — Tab Gear Icon, Panel Animation & UX Polish:

  • Add onConfigView prop to ViewTabBar with Settings2 gear icon on active tab
  • Wire gear icon in ObjectView: click opens ViewConfigPanel for that view's settings
  • Panel slide-in/slide-out animation: CSS transition on max-width + opacity (300ms ease-in-out)
  • Auto-sync panel content on view tab switch (ViewConfigPanel resets draft when activeView.id changes)
  • 5 new ViewTabBar gear icon tests (show on active, hide on inactive, callback, event isolation)
  • 3 new ViewConfigPanel tests (search input, Show All, Hide All)
  • 49 ViewTabBar tests pass, 139 ViewConfigPanel tests pass, 31 ObjectView tests pass

Code Reduction: ~1655 lines imperative → ~170 lines declarative wrapper + ~1100 lines schema factory + ~180 lines shared utils = >50% net reduction in component code with significantly improved maintainability

P1.9 Console — Content Area Layout & Responsiveness

  • Add min-w-0 / overflow-hidden to flex layout chain (SidebarInset → AppShell → ObjectView → PluginObjectView) to prevent content overflow
  • Fix Gantt task list width — responsive sizing (120px mobile, 200px tablet, 300px desktop) instead of hardcoded 300px
  • Fix Kanban board overflow containment (min-w-0 on swimlane and flat containers)
  • Fix Calendar header responsive wrapping and date label sizing
  • Fix Map container overflow containment via cn() merge
  • Fix Timeline container min-w-0 to prevent overflow
  • Fix ListView container min-w-0 overflow-hidden to prevent overflow
  • Mobile/tablet end-to-end testing for all view types
  • Dynamic width calculation for Gantt task list and Kanban columns based on container width

P1.11 App Creation & Editing Flow (Airtable Interface Designer UX)

Full app creation & editing experience aligned with Airtable Interface Designer UX. Components live in @object-ui/plugin-designer, types in @object-ui/types.

Types & Interfaces:

  • AppWizardDraft / AppWizardStep / BrandingConfig / ObjectSelection / EditorMode types
  • isValidAppName() snake_case validator function
  • wizardDraftToAppSchema() draft-to-schema conversion function

App Creation Wizard (4-step):

  • Step 1: Basic Info — name (snake_case validated), title, description, icon, template, layout selector
  • Step 2: Object Selection — card grid with search, select all/none, toggle selection
  • Step 3: Navigation Builder — auto-generates NavigationItem[] from selected objects, add group/URL/separator, reorder up/down, remove
  • Step 4: Branding — logo URL, primary color, favicon, live preview card
  • Step indicator with connected progress dots
  • Step validation (step 1 requires valid snake_case name + title)
  • onComplete callback returns full AppWizardDraft

Navigation Designer:

  • Recursive NavigationItem tree renderer (supports infinite nesting)
  • Quick add buttons for all 7 nav item types (object, dashboard, page, report, group, URL, separator)
  • Inline label editing (double-click to edit, Enter/Escape to commit/discard)
  • Add child to groups (nested navigation)
  • Reorder items (up/down buttons)
  • Deep tree operations (remove/reorder works at any depth)
  • Live preview sidebar showing navigation as rendered
  • Type badges with color coding
  • Icon editing (inline icon name input with pencil button, Enter/Escape commit/discard)
  • Visibility toggle (eye/eye-off icon per node, hidden badge display)
  • Export/Import navigation JSON Schema (toolbar buttons with file I/O, custom callbacks)
  • Mobile responsive layout (flex-col on mobile, sm:flex-row on desktop)

Page Canvas Editor:

  • Component palette (Grid, Kanban, Calendar, Gallery, Dashboard, Form, Layout Grid)
  • Component list with drag handles, selection, reorder
  • Property panel for selected component (label, type, ID)
  • Add/remove/reorder components
  • Syncs PageSchema children on every change
  • Undo/Redo integration via useUndoRedo hook (Ctrl+Z / Ctrl+Y keyboard shortcuts)
  • JSON Schema export/import (Download/Upload toolbar buttons with onExport/onImport callbacks)
  • Preview mode toggle (Eye icon, renders PagePreview panel)
  • Page/Dashboard mode tab switching (role="tablist" with aria-selected)
  • i18n integration via useDesignerTranslation (all labels use translation keys)
  • Keyboard shortcuts (Delete/Backspace to remove selected component)
  • Mobile responsive layout (flex-col on mobile, sm:flex-row on desktop)

Dashboard Editor:

  • 6 widget types (KPI Metric, Bar Chart, Line Chart, Pie Chart, Table, Grid)
  • Widget card grid with selection, drag handles, reorder
  • Widget property panel (title, type, data source, value field, aggregate, color variant)
  • Add/remove/reorder widgets
  • Grid layout based on DashboardSchema columns
  • Undo/Redo integration via useUndoRedo hook (Ctrl+Z / Ctrl+Y keyboard shortcuts)
  • JSON Schema export/import (Download/Upload toolbar buttons with onExport/onImport callbacks)
  • Preview mode toggle (Eye icon, renders DashboardPreview panel)
  • Widget layout size editing (width/height inputs in property panel)
  • i18n integration via useDesignerTranslation (all labels use translation keys)
  • Keyboard shortcuts (Delete/Backspace to remove selected widget)
  • Mobile responsive layout (flex-col on mobile, sm:flex-row on desktop)

Object View Configurator:

  • 7 view type switcher (Grid, Kanban, Calendar, Gallery, Timeline, Map, Gantt)
  • Column visibility toggle with visible count
  • Column reorder (up/down)
  • Toolbar toggles (showSearch, showFilters, showSort)
  • Appearance section (row height, striped, bordered)
  • Collapsible sections

Editor Mode Toggle:

  • Three-way toggle (Edit / Preview / Code)
  • Radio group accessibility (role="radiogroup", aria-checked)
  • Disabled state support

i18n:

  • appDesigner section with 133 keys added to all 10 locales (en, zh, ja, de, fr, es, ar, ru, pt, ko)
  • useDesignerTranslation safe wrapper hook with English fallback (no I18nProvider required)
  • AppCreationWizard fully i18n-integrated (all labels, buttons, step names, validation messages)
  • NavigationDesigner fully i18n-integrated (type badges, quick-add labels, aria-labels, preview, icon editing, visibility, export/import)
  • DashboardEditor fully i18n-integrated (toolbar labels, preview text)
  • PageCanvasEditor fully i18n-integrated (toolbar labels, mode tabs, preview text)
  • BrandingEditor fully i18n-integrated (14 new keys: editor title, export/import, preview, palette, font, light/dark, mobile preview, sample text)

UX Enhancements:

  • Cancel confirmation dialog with unsaved-changes detection
  • onSaveDraft callback for partial progress save
  • useConfirmDialog hook integration for cancel workflow

Testing:

  • 11 type tests (isValidAppName, wizardDraftToAppSchema, type shapes)
  • 41 AppCreationWizard tests (rendering, steps 1-4, navigation, callbacks, cancel confirm, save draft, i18n, read-only)
  • 33 NavigationDesigner tests (rendering, add, remove, groups, badges, i18n, read-only, icon editing, visibility toggle, export/import, responsive)
  • 7 EditorModeToggle tests (render, active mode, onChange, accessibility, disabled)
  • 22 DashboardEditor tests (rendering, add/remove widgets, property panel, read-only, undo/redo, export/import, preview mode, widget layout)
  • 23 PageCanvasEditor tests (rendering, add/remove components, property panel, read-only, mode tabs, undo/redo, export/import, preview mode)
  • 12 ObjectViewConfigurator tests (rendering, view type switch, column visibility, toggles, read-only)
  • 29 BrandingEditor tests (rendering, editing, light/dark preview, read-only, undo/redo, export/import, keyboard shortcuts, preview content)
  • Total: 238 tests across 10 files, all passing

ComponentRegistry:

  • Registered: app-creation-wizard, navigation-designer, dashboard-editor, page-canvas-editor, object-view-configurator, branding-editor

Branding Editor:

  • Logo URL input with live preview (light/dark logo placeholders)
  • Visual color picker with native <input type="color"> and text hex input
  • 16-color preset palette swatches (Blue, Indigo, Violet, Purple, Pink, Red, Orange, Amber, Yellow, Green, Teal, Cyan, Sky, Slate, Dark, Navy)
  • Favicon URL input with preview
  • Font family selector (9 common web fonts + system default)
  • Light/Dark mode preview toggle
  • Real-time preview panel (desktop + mobile)
  • Undo/Redo via useUndoRedo hook (Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y keyboard shortcuts)
  • JSON Schema export/import (Download/Upload toolbar buttons with onExport/onImport callbacks)
  • Read-only mode support (disables all inputs, palette clicks, undo/redo, import)
  • Mobile responsive layout (flex-col on mobile, sm:flex-row on desktop)
  • i18n integration via useDesignerTranslation (14 new translation keys in all 10 locales)
  • Outputs to BrandingConfig type (AppSchema.branding protocol)
  • 29 unit tests (rendering, editing, light/dark preview, read-only, undo/redo, export/import, keyboard shortcuts, preview content)

Console Integration:

  • CreateAppPage — renders AppCreationWizard with useMetadata() objects, onComplete/onCancel/onSaveDraft callbacks
  • EditAppPage — reuses wizard with initialDraft from existing app config
  • Routes: /apps/:appName/create-app, /apps/:appName/edit-app/:editAppName
  • AppSidebar "Add App" button → navigates to /create-app
  • AppSidebar "Edit App" button → navigates to /edit-app/:appName
  • CommandPalette "Create New App" command (⌘+K → Actions group)
  • Empty state CTA "Create Your First App" when no apps configured
  • wizardDraftToAppSchema() conversion on completion — includes icon, label, branding fields
  • EditAppPage merges wizard output with original app config to preserve fields not in wizard (e.g. active)
  • client.meta.saveItem('app', name, schema) — persists app metadata to backend on create/edit
  • MSW PUT handler for /meta/:type/:name — dev/mock mode metadata persistence
  • MSW handler refactored to use MSWPlugin + protocol broker shim — filter/sort/top/pagination now work correctly in dev/mock mode (Issue #858)
  • Draft persistence to localStorage with auto-clear on success
  • createApp i18n key added to all 10 locales
  • 13 console integration tests (routes, wizard callbacks, draft persistence, saveItem, CommandPalette)
  • PageDesignPage — integrates PageCanvasEditor at /design/page/:pageName route with auto-save, JSON export/import
  • DashboardDesignPage — integrates DashboardEditor at /design/dashboard/:dashboardName route with auto-save, JSON export/import
  • "Edit" button on PageView and DashboardView — opens right-side DesignDrawer with real-time preview (no page navigation)
  • DesignDrawer component — reusable right-side Sheet panel hosting editors with auto-save, Ctrl+S shortcut, and live schema sync
  • Ctrl+S/Cmd+S keyboard shortcut to explicitly save in both design pages (with toast confirmation)
  • Storybook stories for PageCanvasEditor and DashboardEditor (Designers/PageCanvasEditor, Designers/DashboardEditor)
  • 12 console design page tests (PageDesignPage + DashboardDesignPage: routes, 404 handling, editor rendering, onChange, Ctrl+S save)
  • 7 DesignDrawer tests (drawer open/close, editor rendering, real-time preview sync, auto-save, Ctrl+S, no navigation)

P1.12 System Settings & App Management Center

Unified system settings hub, app management page, and permission management page.

System Hub Page (/system/):

  • Card-based overview linking to all system administration sections
  • Live statistics for each section (users, orgs, roles, permissions, audit logs, apps)
  • Navigation to Apps, Users, Organizations, Roles, Permissions, Audit Log, Profile

App Management Page (/system/apps):

  • Full app list with search/filter
  • Enable/disable toggle per app
  • Set default app
  • Delete app with confirmation
  • Bulk select with enable/disable operations
  • Navigate to Create App / Edit App pages
  • Navigate to app home

Permission Management Page (/system/permissions):

  • CRUD grid for sys_permission object
  • Search/filter permissions
  • Admin-only create/delete controls

ObjectView-Driven System Pages (P1.12.2):

  • Shared SystemObjectViewPage component using ObjectView from @object-ui/plugin-view
  • User Management (/system/users) driven by sys_user metadata via ObjectView
  • Organization Management (/system/organizations) driven by sys_org metadata via ObjectView
  • Role Management (/system/roles) driven by sys_role metadata via ObjectView
  • Permission Management (/system/permissions) driven by sys_permission metadata via ObjectView
  • Audit Log (/system/audit-log) driven by sys_audit_log metadata via ObjectView (read-only)
  • Admin-only CRUD operations controlled via ObjectView operations config
  • Automatic search, sort, filter, pagination from ObjectView capabilities
  • 22 system page tests passing

Sidebar & Navigation Updates:

  • Settings button → /system/ hub (was /system/profile)
  • App switcher "Manage All Apps" link → /system/apps

Empty State & System Route Accessibility (P1.12.1):

  • "Create App" button always shown in empty state (even when config loading fails)
  • "System Settings" link always shown alongside "Create App" in empty state
  • Top-level /system/* routes accessible without any app context (promoted to main routes)
  • Top-level /create-app route accessible without any app context
  • Sidebar fallback navigation with system menu items when no apps are configured
  • System pages (SystemHubPage, AppManagementPage) handle missing appName gracefully
  • Login/Register/Forgot password pages remain always accessible regardless of app state

Routes:

  • /system/ → SystemHubPage
  • /system/apps → AppManagementPage
  • /system/permissions → PermissionManagementPage
  • /system/metadata/:metadataType → MetadataManagerPage (generic, registry-driven)

Unified Metadata Management (P1.12.3):

  • Metadata type registry (config/metadataTypeRegistry.ts) — centralized config for all metadata types
  • Generic MetadataManagerPage for listing/managing items of any registered type
  • SystemHubPage auto-generates metadata type cards from registry (dashboard, page, report)
  • Dynamic /system/metadata/:metadataType routes in all route contexts
  • Generic MetadataService methods: getItems(), saveMetadataItem(), deleteMetadataItem()
  • Types with custom pages (app, object) link to existing dedicated routes
  • Legacy routes preserved — no breaking changes
  • 40+ new tests (registry, MetadataManagerPage, MetadataService generic, SystemHubPage registry)

Tests:

  • 11 new tests (SystemHubPage, AppManagementPage, PermissionManagementPage)
  • Total: 20 system page tests passing

P1.13 Airtable Grid/List UX Optimization ✅

Status: Complete — Grid/List components now match Airtable UX patterns for date formatting, row interactions, editing, density, headers, filters, and empty states.

Date Field Humanized Format:

  • formatDate, formatDateTime, DateTimeCellRenderer use browser locale (undefined instead of 'en-US')
  • All date columns auto-format to localized human-readable format (e.g., "2024/2/28 12:57am")

Row Hover "Open >" Button:

  • Expand button changed from icon-only <Expand> to text "Open >" with <ChevronRight> icon
  • Consistent across Grid and ListView (shown on row hover)

Single-Click Edit Mode:

  • Added singleClickEdit prop to DataTableSchema and ObjectGridSchema
  • When true, clicking a cell enters edit mode (instead of double-click)

Default Compact Row Height:

  • ObjectGrid default changed from 'medium' to 'compact' (32-36px rows)
  • ListView default density changed from 'comfortable' to 'compact'
  • Row height toggle preserved in toolbar

Single-Click Edit Mode:

  • Added singleClickEdit prop to DataTableSchema and ObjectGridSchema
  • ObjectGrid defaults singleClickEdit to true (click-to-edit by default)
  • InlineEditing component already compatible (click-to-edit native)

Column Header Minimal Style:

  • Headers use text-xs font-normal text-muted-foreground (was text-[11px] font-semibold uppercase tracking-wider)
  • Sort arrows inline with header text

Filter Pill/Chip Styling:

  • Filter badges use rounded-full for Airtable-style pill appearance
  • "More" overflow button matches pill styling

Column Width Auto-Sizing:

  • Auto column width estimation based on header and data content (80-400px range)
  • Samples up to 50 rows for width calculation

Row Selection Checkbox Style:

  • Added selectionStyle prop ('always'|'hover') to DataTableSchema
  • 'hover' mode shows checkboxes only on row hover

Empty Table Ghost Row:

  • Empty tables show 3 ghost placeholder rows with skeleton-like appearance
  • Ghost rows use varying widths for visual variety

P1.14 Console Integration — Engine Schema Capabilities ✅

Status: Complete — Console now consumes HeaderBarSchema, ViewSwitcher allowCreateView/viewActions, SidebarNav enhanced props, and Airtable UX defaults.

ViewSwitcher allowCreateView & viewActions:

  • Added allowCreateView and viewActions to ObjectViewSchema type
  • Engine ObjectView passes allowCreateView/viewActions to ViewSwitcherSchema builder
  • Engine ObjectView accepts and passes onCreateView/onViewAction callbacks to ViewSwitcher
  • Console ObjectView sets allowCreateView: isAdmin and viewActions (settings/share/duplicate/delete)
  • Console wires onCreateView → open ViewConfigPanel in create mode
  • Console wires onViewAction('settings') → open ViewConfigPanel in edit mode

AppHeader HeaderBarSchema Alignment:

  • Console AppHeader breadcrumb items typed as engine BreadcrumbItem type
  • Breadcrumbs with siblings dropdown navigation working
  • Search triggers ⌘K command palette (desktop + mobile)

SidebarNav Enhanced Props:

  • Storybook stories for badges, nested items, NavGroups, search filtering
  • Documentation updated for SidebarNav enhanced API (badges, badgeVariant, children, searchEnabled, NavGroup)

Airtable UX Defaults Propagation:

  • Verified: Console ObjectView does NOT override rowHeight, density, or singleClickEdit
  • Engine defaults (compact rows, singleClickEdit: true, browser locale dates) flow through correctly

P1.15 Convention-based Auto-resolution for Object & Field Label i18n ✅

  • useObjectLabel hook in @object-ui/i18n — convention-based resolver (objectLabel, objectDescription, fieldLabel)
  • Dynamic app namespace discovery (no hardcoded crm. prefix — scans i18next resources for app namespaces)
  • useSafeFieldLabel shared wrapper for plugins without I18nProvider
  • Wired into Console ObjectView (breadcrumb, page title, description, drawer title)
  • Wired into ObjectGrid column headers (ListColumn, string[], auto-generated paths)
  • Wired into ListView toolbar field labels (hide fields, group by, sort/filter builder)
  • Wired into NavigationRenderer via optional resolveObjectLabel + t() props for full i18n
  • Wired into Console AppSidebar to pass resolver and t to NavigationRenderer
  • Wired into all form variants (ObjectForm, ModalForm, WizardForm, DrawerForm, TabbedForm, SplitForm)
  • I18nProvider loads app-specific translations on mount (fixes initial language loading)
  • 10 unit tests for useObjectLabel hook
  • Zero changes to object metadata files or translation files

P1.16 Object Manager & Field Designer ✅

Status: Complete — ObjectManager and FieldDesigner components shipped in @object-ui/plugin-designer. Object Detail View enhanced with Power Apps-style sections.

Enterprise-grade visual designers for managing object definitions and configuring fields. Supports the full metadata platform workflow: define objects, configure fields with advanced properties, and maintain relationships.

Object Manager (ObjectManager):

  • CRUD operations on object definitions (custom and system objects)
  • Visual configuration of object properties (name, label, plural label, description, icon, group, sort order, enabled)
  • Object relationship display and maintenance
  • Inline property editor with collapsible sections
  • Search/filter functionality
  • Grouped object display with badges
  • System object protection (non-deletable, name-locked)
  • Read-only mode support
  • Confirm dialog for destructive actions
  • 18 unit tests

Object Detail View (Power Apps alignment):

  • Dedicated Relationships section with type badges, foreign key info, and empty state
  • Keys section auto-extracting primary keys, unique keys, and external IDs from field metadata
  • Data Experience placeholder section (Forms, Views, Dashboards) — UI structure ready for future implementation
  • Inline data preview placeholder section (Power Apps sample data grid parity)
  • System field non-editable visual hints
  • Enhanced object properties card with separated concern sections

Metadata Manager Grid Mode:

  • listMode: 'grid' | 'table' | 'card' configuration on MetadataTypeConfig
  • Professional table rendering with column headers and action buttons in grid mode
  • Report type configured with grid mode by default
  • Reusable MetadataGrid component extracted for cross-page reuse

MetadataFormDialog Enhancements:

  • number field type — renders HTML number input
  • boolean field type — renders Shadcn Switch toggle with Yes/No label

MetadataDetailPage & Provider Enhancements:

  • Auto-redirect for custom page types (object → /system/objects/:name) — removed; object now uses metadata pipeline
  • getItemsByType(type) method on MetadataProvider for dynamic registry access
  • Object type merged into MetadataManagerPage pipeline — ObjectManagerPage removed, replaced by ObjectManagerListAdapter via listComponent extension point
  • listComponent extension point on MetadataTypeConfig for injecting custom list UIs
  • All entry points (sidebar, QuickActions, hub cards) unified to /system/metadata/object

Technical Debt Cleanup:

  • Unified icon resolver — consolidated 3 duplicated ICON_MAP/resolveIcon into shared getIcon utility
  • Extracted toObjectDefinition/toFieldDefinition to shared utils/metadataConverters.ts

Field Designer (FieldDesigner):

  • CRUD operations on field definitions with 27 supported field types
  • Advanced field properties: uniqueness, default values, options/picklists, read-only, hidden, validation rules, external ID, history tracking, indexed
  • Field grouping, sorting, and layout management
  • System reserved field protection
  • Type-specific property editors (lookup reference, formula expression, select options)
  • Validation rule builder (min, max, minLength, maxLength, pattern, custom)
  • Search and type-based filtering
  • Read-only mode support
  • 22 unit tests

Metadata Persistence (Console Integration):

  • MetadataService class wrapping client.meta.saveItem for object/field CRUD
  • useMetadataService hook for adapter-aware service access
  • Optimistic update pattern with rollback on API failure
  • Object create/update persisted via saveItem('object', name, data)
  • Object delete via soft-delete (enabled: false, _deleted: true)
  • Field changes persisted as part of parent object metadata
  • MetadataProvider refresh() called after successful mutations
  • Saving state indicators with loading spinners
  • Accurate toast messages (create/update/delete action labels, error details)
  • 16 new MetadataService unit tests + 2 ObjectManagerPage API integration tests

Type Definitions (@object-ui/types):

  • ObjectDefinition, ObjectDefinitionRelationship, ObjectManagerSchema
  • DesignerFieldType (27 types), DesignerFieldOption, DesignerValidationRule
  • DesignerFieldDefinition, FieldDesignerSchema

i18n Support:

  • Full translations for all 10 locales (en, zh, ja, ko, de, fr, es, pt, ru, ar)
  • 50 new translation keys per locale (objectManager + fieldDesigner sections)
  • Fallback translations in useDesignerTranslation for standalone usage

🧩 P2 — Polish & Advanced Features

P2.0 Flow Designer ✅

Status: Complete — FlowDesigner component shipped in @object-ui/plugin-workflow.

The FlowDesigner is a canvas-based flow editor that bridges the gap between the approval-focused WorkflowDesigner and the BPMN-heavyweight ProcessDesigner. It targets automation/integration flows with spec v3.0.9 types.

Core Canvas:

  • Drag-to-reposition nodes on SVG canvas
  • Undo/redo with keyboard shortcuts (Ctrl+Z / Ctrl+Y)
  • Ctrl+S save shortcut with onSave callback
  • Zoom in/out/reset controls
  • Edge creation by clicking source → target connection ports
  • Node deletion via delete button or Delete key

Node Types (spec v3.0.9):

  • Standard nodes: start, end, task, user_task, service_task, script_task, approval, condition
  • Gateway nodes: parallel_gateway, join_gateway
  • Event nodes: boundary_event, delay, notification, webhook

Edges (spec v3.0.9):

  • Edge types: default, conditional, timeout
  • Conditional edges with condition expression field
  • isDefault flag for default/fallthrough edge marking
  • Visual differentiation: dashed lines for conditional, dotted for default

Property Panel:

  • Node property editor: label, type, description
  • Node executor configuration: FlowNodeExecutorDescriptor (type, inputSchema, outputSchema, timeoutMs, retry policy)
  • Wait event config for delay nodes: FlowWaitEventType (condition/manual/webhook/timer/signal)
  • Boundary event config: FlowBoundaryConfig (attachedToNodeId, eventType, cancelActivity, timer, errorCode)
  • Edge property editor: label, type, condition expression, isDefault toggle

Spec v3.0.9 Protocol Features:

  • FlowVersionEntry[] version history panel with current/previous entries
  • FlowConcurrencyPolicy badge display (allow/forbid/replace/queue)
  • FlowExecutionStep[] execution monitoring overlay with per-node status icons
  • FlowBpmnInteropResult BPMN XML export with warning/error reporting

ComponentRegistry:

  • Registered as 'flow-designer' with all inputs documented

P2.1 Designer — Remaining Interaction (Post-v1.0)

  • PageDesigner: Component drag-to-reorder and drag-to-position
  • ProcessDesigner: Node drag-to-move
  • ProcessDesigner/FlowDesigner: Support v3.0.9 node types (parallel_gateway, join_gateway, boundary_event) — implemented in FlowDesigner
  • ProcessDesigner/FlowDesigner: Support v3.0.9 conditional edges and default edge marking — implemented in FlowDesigner
  • ReportView: Refactored to left-right split layout (preview + DesignDrawer) — consistent with DashboardView/ListView
  • ReportConfigPanel: Schema-driven config via ConfigPanelRenderer + useConfigDraft (replaces full-page ReportBuilder in edit mode)
  • ReportConfigPanel: Report type selector (Tabular/Summary/Matrix), visual field picker (multi-select columns), undo/redo, dashboard-style consistency
  • ReportView: Live preview linkage fix — config panel changes instantly refresh report preview (title, description, fields, filters, groupBy)
  • ReportViewer: Client-side grouping — groupBy config renders grouped table with group headers and row counts
  • ReportType added to ReportSchema types (tabular/summary/matrix)
  • ReportConfigPanel: Per-column aggregation (sum/avg/count/min/max) in field picker
  • ReportConfigPanel: Visual chart editor — chart type selection + X/Y axis field mapping (no JSON editing)
  • ReportConfigPanel: Conditional formatting UI — rule builder with field/operator/value + background color
  • ReportConfigPanel: Section/block manager — add/remove/reorder header, summary, table, chart, text sections
  • ReportViewer: Conditional formatting applied to table cells (background/text color)
  • ReportView: Chart config from panel auto-generates chart section in preview
  • ReportDesigner: Element drag-to-reposition within sections
  • FlowDesigner: Edge creation UI (click source port → click target port)
  • FlowDesigner: Property editing for node labels/types + executor config + boundary config
  • Confirmation dialogs for ProcessDesigner destructive actions

P2.2 Designer — Advanced Features

  • Full property editors for all designers (PageDesigner, ProcessDesigner, ReportDesigner)
  • i18n integration: all hardcoded strings through resolveI18nLabel
  • Canvas pan/zoom with minimap
  • Auto-layout algorithms (force-directed for DataModel, Dagre for Process)
  • Copy/paste support (Ctrl+C/Ctrl+V) across all designers
  • Multi-select (Ctrl+Click, Shift+Click) and bulk operations
  • Responsive collapsible panels

P2.3 Designer — Collaboration Integration

  • Wire CollaborationProvider into each designer's state management
  • Live cursor positions (PresenceCursors component)
  • Operation-based undo/redo sync
  • Conflict resolution UI (merge dialog)
  • Version history browser with restore

P2.4 Spec Compliance Gaps (Low Priority)

  • @object-ui/core: DataScope module — row-level permission enforcement
  • @object-ui/core: Custom validator registration API
  • @object-ui/core: STDEV, VARIANCE, PERCENTILE, MEDIAN formula functions
  • @object-ui/react: useTheme hook for component-level theme access
  • @object-ui/react: Re-export usePermissions for unified hook API
  • @object-ui/components: ErrorBoundary wrapper per component
  • @object-ui/fields: Inline validation message rendering
  • @object-ui/plugin-charts: Drill-down click handler for chart segments
  • @object-ui/plugin-charts: Fix object-chart crash when data is non-array — added Array.isArray() guards in ObjectChart, ChartRenderer, and AdvancedChartImpl to prevent Recharts r.slice is not a function error
  • @object-ui/plugin-workflow: FlowDesigner — canvas-based flow editor (flow-designer component) with drag-to-reposition nodes, edge creation UI, undo/redo, Ctrl+S save, property panel, and BPMN export
  • @object-ui/plugin-workflow: Support v3.0.9 BPMN interop types — FlowBpmnInteropResult with bpmnXml export, nodeCount, edgeCount, warnings
  • @object-ui/plugin-workflow: Support v3.0.9 node executor descriptors — FlowNodeExecutorDescriptor with inputSchema, outputSchema, wait event config, retry policy
  • @object-ui/plugin-workflow: Support v3.0.9 inputSchema/outputSchema on flow nodes via FlowNodeExecutorDescriptor
  • @object-ui/plugin-workflow: Support v3.0.9 boundaryConfig for boundary event nodes — FlowBoundaryConfig with attachedToNodeId, eventType, cancelActivity, timer, errorCode
  • @object-ui/plugin-workflow: Support v3.0.9 node types — parallel_gateway, join_gateway, boundary_event in FlowNodeType
  • @object-ui/plugin-workflow: Support v3.0.9 conditional edges — FlowEdge.type = 'conditional' + isDefault flag
  • @object-ui/plugin-workflow: Support v3.0.9 FlowVersionHistoryFlowVersionEntry[] with version number, author, changeNote, isCurrent
  • @object-ui/plugin-workflow: Support v3.0.9 ConcurrencyPolicyFlowConcurrencyPolicy (allow/forbid/replace/queue)
  • @object-ui/plugin-workflow: Support v3.0.9 WaitEventTypeFlowWaitEventType (condition/manual/webhook/timer/signal)
  • @object-ui/plugin-workflow: Support v3.0.9 execution tracking — FlowExecutionStep with status overlay on nodes
  • @object-ui/plugin-ai: Configurable AI endpoint adapter (OpenAI, Anthropic)
  • Navigation width property: apply to drawer/modal overlays across all plugins
  • Navigation view property: specify target form/view on record click across all plugins
  • Support App engine field ({ objectstack: string }) for version pinning (v3.0.9)
  • Integrate v3.0.9 package upgrade protocol (PackageArtifact, ArtifactChecksum, UpgradeContext)
  • @object-ui/types: Align ViewType with spec ListView type enum — add 'gallery' and 'gantt' (was missing from views.ts and views.zod.ts)
  • @object-ui/types: Sync DetailViewFieldSchema Zod validator with TS interface — add data-oriented field types (number, currency, percent, boolean, select, lookup, master_detail, email, url, phone, user) and missing properties (options, reference_to, reference_field, currency)
  • @object-ui/types: Add showBorder and headerColor to DetailViewSectionSchema Zod validator (already in TS interface)
  • @object-ui/plugin-view: Add gallery and gantt to ViewSwitcher default labels and icons
  • @object-ui/plugin-list: Fix list-view and list registration viewType enum — remove invalid 'list'/'chart', add 'gallery'/'timeline'/'gantt'/'map' to match spec
  • @object-ui/plugin-detail: Add missing detail-view registration inputs (layout, columns, loading, backUrl, editUrl, deleteConfirmation, header, footer)
  • @object-ui/plugin-detail: Add showBorder and headerColor inputs to detail-section registration

P2.6 ListView Spec Protocol Gaps (Remaining) ✅

All items from the ListView Spec Protocol analysis have been implemented.

P0 — Core Protocol:

  • data (ViewDataSchema): ListView consumes schema.data — supports provider: value (inline items), provider: object (fetch from objectName), and plain array shorthand. Falls back to dataSource.find() when not set.
  • grouping rendering: Group button enabled with GroupBy field picker popover. Grouping config wired to ObjectGrid child view, which renders collapsible grouped sections via useGroupedData hook.
  • rowColor rendering: Color button enabled with color-field picker popover. Row color config wired to ObjectGrid child view, which applies row background colors via useRowColor hook.

P1 — Structural Alignment:

  • quickFilters structure reconciliation: Auto-normalizes spec { field, operator, value } format into ObjectUI { id, label, filters[] } format. Both formats supported simultaneously. Dual-format type union (QuickFilterItem = ObjectUIQuickFilterItem | SpecQuickFilterItem) exported from @object-ui/types. Standalone normalizeQuickFilter() / normalizeQuickFilters() adapter functions in @object-ui/core. Bridge (list-view.ts) normalizes at spec→SchemaNode transform time. Spec shorthand operators (eq, ne, gt, gte, lt, lte) mapped to ObjectStack AST operators. Mixed-format arrays handled transparently.
  • conditionalFormatting expression reconciliation: Supports spec { condition, style } format alongside ObjectUI field/operator/value rules. Dual-format type union (ConditionalFormattingRule = ObjectUIConditionalFormattingRule | SpecConditionalFormattingRule) exported from @object-ui/types. Zod validator updated with z.union() for both formats. evaluatePlainCondition() convenience function in @object-ui/core for safe plain/template expression evaluation with record context. Plain expressions (e.g., status == 'overdue') evaluated directly without ${} wrapper; record fields spread into evaluator context for direct field references alongside data. namespace. Mixed-format arrays handled transparently.
  • exportOptions schema reconciliation: Accepts both spec string[] format (e.g., ['csv', 'xlsx']) and ObjectUI object format { formats, maxRecords, includeHeaders, fileNamePrefix }.
  • Column pinned: pinned property added to ListViewSchema column type. Bridge passes through to ObjectGrid which supports frozenColumns. ObjectGrid reorders columns (left-pinned first, right-pinned last with sticky CSS). Zod schema updated with pinned field. useColumnSummary hook created.
  • Column summary: summary property added to ListViewSchema column type. Bridge passes through for aggregation rendering. ObjectGrid renders summary footer with count/sum/avg/min/max aggregations via useColumnSummary hook. Zod schema updated with summary field.
  • Column link: ObjectGrid renders click-to-navigate buttons on link-type columns with navigation.handleClick. Primary field auto-linked.
  • Column action: ObjectGrid renders action dispatch buttons via executeAction on action-type columns.
  • tabs (ViewTabSchema): TabBar component renders view tabs above the ListView toolbar. Supports icon (Lucide), pinned (always visible), isDefault (auto-selected), visible filtering, order sorting, and active tab state. Tab switch applies filter config. Extracted as reusable TabBar component in packages/plugin-list/src/components/TabBar.tsx. i18n keys added for all 10 locales.

P2 — Advanced Features:

  • rowActions: Row-level dropdown action menu per row in ObjectGrid. schema.rowActions string array items rendered as dropdown menu items, dispatched via executeAction.
  • bulkActions: Bulk action bar rendered in ListView when rows are selected and schema.bulkActions is configured. Fires onBulkAction callback with action name and selected rows.
  • sharing schema reconciliation: Supports both ObjectUI { visibility, enabled } and spec { type: personal/collaborative, lockedBy } models. Share button renders when either enabled: true or type is set. Zod validator updated with type and lockedBy fields. Bridge normalizes spec format: type: personalvisibility: private, type: collaborativevisibility: team, auto-sets enabled: true.
  • exportOptions schema reconciliation: Zod validator updated to accept both spec string[] format and ObjectUI object format via z.union(). ListView normalizes string[] to { formats } at render time.
  • pagination.pageSizeOptions backend integration: Page size selector is now a controlled component that dynamically updates effectivePageSize, triggering data re-fetch. onPageSizeChange callback fires on selection. Full test coverage for selector rendering, option enumeration, and data reload.
  • $expand auto-injection: buildExpandFields() utility in @object-ui/core scans schema fields for lookup/master_detail types and returns field names for $expand. Integrated into all data-fetching plugins (ListView, ObjectGrid, ObjectKanban, ObjectCalendar, ObjectGantt, ObjectMap, ObjectTimeline, ObjectGallery, ObjectView, ObjectAgGrid) so the backend (objectql) returns expanded objects instead of raw foreign-key IDs. Supports column-scoped expansion (ListColumn[] compatible) and graceful fallback when $expand is not supported. Cross-repo: objectql engine expand support required for multi-level nesting.

P2.7 Platform UI Consistency & Interaction Optimization ✅

All items from the UI consistency optimization (Issue #749) have been implemented.

Global Theme & Design Tokens:

  • Hardcoded gray colors in GridField.tsx, ReportRenderer.tsx, and ObjectGrid.tsx replaced with theme tokens (text-muted-foreground, bg-muted, border-border, border-foreground)
  • Global font-family (Inter, ui-sans-serif, system-ui) injected in index.css :root
  • --config-panel-width CSS custom property added for unified config panel sizing (updated to 320px in P2.8)
  • Border radius standardized to rounded-lg across report/grid components
  • transition-colors duration-150 added to all interactive elements (toolbar buttons, tab bar, sidebar menu buttons)
  • LayoutRenderer.tsx outer shell bg-slate-50/50 dark:bg-zinc-950 replaced with bg-background theme token

Sidebar Navigation:

  • SidebarMenuButton active state enhanced with 3px left indicator bar via before: pseudo-element
  • SidebarMenuButton transition expanded to include color, background-color with duration-150
  • SidebarGroupLabel visual separator added (border-t border-border/30 pt-3 mt-2)
  • Collapsed-mode tooltip support in SidebarNav via tooltip={item.title} prop
  • LayoutRenderer.tsx hand-written sidebar → SidebarNav unification (deferred: requires extending SidebarNav to support nested menus, logo, version footer)

ListView Toolbar:

  • Search changed from expandable button to always-visible inline <Input> (w-48)
  • Activated state (bg-primary/10 border border-primary/20) added to Filter/Sort/Group/Color buttons when active
  • Toolbar overflow improved with overflow-x-auto for responsive behavior
  • transition-colors duration-150 added to all toolbar buttons

ObjectGrid Cell Renderers:

  • formatRelativeDate() function added for relative time display ("Today", "2 days ago", "Yesterday")
  • DataTable/VirtualGrid header styling unified: text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70 bg-muted/30
  • Remaining hardcoded gray colors in ObjectGrid loading spinner and status badge fallback replaced with theme tokens
  • Select/status type Badge rendering — getCellRenderer() returns <Badge> with color mapping from field.options; auto-generated options from unique data values when type is inferred; priority semantic colors (Critical→red, High→orange, Medium→yellow, Low→gray); muted default style for unconfigured colors
  • Date type human-readable formatting — DateCellRenderer defaults to relative format ("Today", "Yesterday", "3 days ago"); overdue dates styled with red text; ISO timestamp shown as hover tooltip; formatRelativeDate() threshold tightened to 7 days
  • Boolean type visual rendering — BooleanCellRenderer renders <Checkbox disabled> for true/false; null/undefined values display as

ConfigPanelRenderer:

  • <Separator> added between sections for visual clarity
  • Panel width uses --config-panel-width CSS custom property

View Tab Bar:

  • Tab spacing tightened (gap-0.5, px-3 py-1.5)
  • Active tab indicator changed to bottom border (border-b-2 border-primary font-medium text-foreground)
  • transition-colors duration-150 added to tab buttons

P2.8 Airtable Parity: Product View & Global UI Detail Optimization

Platform-level grid, toolbar, sidebar, and config panel optimizations for Airtable-level experience (Issue #768).

Platform: DataTable & ObjectGrid Enhancements:

  • rowStyle callback prop added to DataTableSchema type — enables inline CSSProperties per row
  • <TableRow> in data-table.tsx applies rowStyle callback for runtime row styling
  • ObjectGrid: conditionalFormatting rules wired to rowStyle — evaluates both spec-format (condition/style) and ObjectUI-format (field/operator/value) rules per row using evaluatePlainCondition from @object-ui/core
  • Row number (#) column: hover shows <Checkbox> for multi-select (when selectable mode is enabled), replacing expand icon

Platform: ListView Toolbar:

  • Visual <div> separators (h-4 w-px bg-border/60) between toolbar button groups: Search | Hide Fields | Filter/Sort/Group | Color/Density | Export
  • Separators conditionally rendered only when adjacent groups are visible
  • Inline search moved to toolbar left end (w-48, Airtable-style)
  • Density button: activated state highlight (bg-primary/10 border border-primary/20) when density is non-default
  • Merged UserFilters row and tool buttons row into single toolbar line — left: field filter badges, right: tool buttons with separator
  • Search changed from inline input to icon button + Popover — saves toolbar space, matches Airtable pattern
  • UserFilters maxVisible prop added — overflow badges collapse into "More" dropdown with Popover
  • UserFilters config panel: _userFilters integrated into buildDataSection in ViewConfigPanel — element type selector (dropdown/tabs/toggle), field picker, live preview sync, i18n (en/zh), NamedListView.userFilters type parity with ListViewSchema.userFilters
  • FilterBuilder: multi-select support for in/notIn operators — checkbox list UI replaces text input for select/lookup/master_detail fields
  • FilterBuilder: lookup/master_detail field types now show dropdown selector instead of manual ID input
  • FilterBuilder: all field types mapped — currency/percent/rating → number operators, datetime/time → date operators with proper input types, status → select operators, user/owner → lookup operators
  • FilterUI: multi-select filter type added — checkbox-based multi-value selection for filter forms
  • FilterCondition value type expanded to support arrays (string | number | boolean)[] for multi-value filters
  • ObjectView filterSchema: lookup/master_detail/user/owner fields auto-map to multi-select type; status fields auto-map to select
  • Console normalizeFieldType: lookup/master_detail/user/owner/status/time types now properly classified for filter/sort config
  • Toolbar layout uses flex with min-w-0 overflow handling for responsive behavior

Platform: ViewTabBar:

  • Tab "•" dot indicator replaced with descriptive badge (F/S/FS) + tooltip showing "Active filters", "Active sort"

Platform: Console Sidebar:

  • Recent items section: default collapsed with chevron toggle (saves sidebar space)

Platform: ViewConfigPanel Advanced Sections:

  • userActions, sharing, and accessibility sections set to defaultCollapsed: true — common settings remain expanded, advanced settings folded by default

Platform: Config Panel Width:

  • --config-panel-width CSS variable increased from 280px to 320px for wider config panel

CRM Example: Product Grid Column Configs:

  • All columns upgraded from string[] to ListColumn[] with explicit field, label, width, type, align
  • IS ACTIVE: type: 'boolean' for <Checkbox disabled> rendering
  • Price: type: 'currency', align: 'right' for formatCurrency formatting
  • Default rowHeight: 'short' for compact density
  • Conditional formatting: stock=0 red, stock<5 yellow warning
  • Column widths: NAME 250, SKU 120, CATEGORY 110, PRICE 120, STOCK 80

Tests:

  • 7 new CRM metadata tests validating column types, widths, rowHeight, conditionalFormatting
  • 136 ViewConfigPanel tests updated for defaultCollapsed sections (expand before access)
  • 411 ListView + ViewTabBar tests passing (255 plugin-list tests including 9 new toolbar/collapse tests)
  • 11 AppSidebar tests passing

P2.9 Platform UI Navigation & Table Optimization ✅

Platform-level sidebar, navigation, and grid/table UX improvements (Issue #XX).

Sidebar Navigation:

  • Pin icons show-on-hover: SidebarMenuAction in NavigationRenderer now uses showOnHover (always true) — all pin/unpin icons only appear on hover for a cleaner sidebar. Applied to both action and leaf navigation item types.
  • Search placeholder contrast: Search icon in AppSidebar improved from opacity-50opacity-70 for better readability.
  • Recent section position: Recent items section moved above Record Favorites in AppSidebar for quicker access to recently visited items.
  • Favorites section: Already hides automatically when no pinned items exist (verified).
  • Resizable sidebar width: SidebarRail enhanced with pointer-event drag-to-resize (min 200px, max 480px). Width persisted to localStorage. Click toggles sidebar, double-click resets to default. useSidebar() hook now exposes sidebarWidth and setSidebarWidth.

Grid/Table Field Inference:

  • Percent field auto-inference: inferColumnType() in ObjectGrid now detects fields with names containing probability, percent, percentage, completion, progress, rate and assigns PercentCellRenderer with progress bar display.
  • ISO datetime fallback: ObjectGrid inferColumnType() now detects ISO 8601 datetime strings (YYYY-MM-DDTHH:MM) in data values as a catch-all for fields whose names don't match date/datetime patterns.
  • Date/datetime human-friendly display: DateCellRenderer (relative format) and DateTimeCellRenderer (split date/time) already registered in field registry for all grid/table views.
  • Currency/status/boolean renderers: Already implemented with proper formatting (currency symbol, Badge colors, checkbox display).
  • accessorKey-format type inference: generateColumns() in ObjectGrid now applies inferColumnType() + getCellRenderer() to accessorKey-format columns that don't already have a cell renderer. Previously, accessorKey columns bypassed the entire inference pipeline, showing raw dates, plain text status/priority, and raw numbers for progress.
  • humanizeLabel() utility: New humanizeLabel() export in @object-ui/fields converts snake_case/kebab-case values to Title Case (e.g., in_progressIn Progress). Used as fallback in SelectCellRenderer when no explicit option.label exists.
  • PercentCellRenderer progress-field normalization: PercentCellRenderer now uses field name to disambiguate 0-1 fraction vs 0-100 whole number — fields matching /progress|completion/ treat values as already in 0-100 range (e.g., 7575%), while other fields like probability still treat 0.7575%.

Header & Breadcrumb i18n:

  • AppHeader breadcrumb labels (Dashboards, Pages, Reports, System) now use t() translation via useObjectTranslation.
  • console.breadcrumb i18n keys added to all 11 locales (en, zh, ja, ko, de, fr, es, pt, ru, ar).
  • header-bar renderer: resolveCrumbLabel() handles both string and I18nLabel objects for breadcrumb labels.
  • breadcrumb renderer: resolveItemLabel() handles both string and I18nLabel objects for item labels.

Tests:

  • 46 NavigationRenderer tests passing (pin/favorites/search/reorder)
  • 86 field cell renderer tests passing (date/datetime/select/boolean/percent/humanizeLabel/progress)
  • 293 ObjectGrid tests passing (inference, rendering, accessibility, accessorKey-format inference)
  • 28 DataTable tests passing
  • 78 Layout tests passing (NavigationRenderer + AppSchemaRenderer)
  • 11 AppSidebar tests passing
  • 32 i18n tests passing

P2.10 ActionBar Rendering & useActionEngine Integration ✅

Location-aware action toolbar component and React hook for ActionEngine. Bridges ActionSchema metadata → visible, clickable action buttons at all defined locations.

  • action:bar component — Composite toolbar renderer that accepts ActionSchema[] + location filter
    • Filters actions by ActionLocation (list_toolbar, list_item, record_header, record_more, record_related, global_nav)
    • Resolves each action's component type (action:button, action:icon, action:menu, action:group) via ComponentRegistry
    • Overflow support: actions beyond maxVisible threshold grouped into "More" dropdown (action:menu)
    • Supports horizontal/vertical direction, gap, variant/size defaults, custom className
    • WCAG: role="toolbar" + aria-label="Actions"
    • Registered in ComponentRegistry with inputs/defaultProps for Designer/Studio integration
  • useActionEngine hook — React wrapper around ActionEngine
    • getActionsForLocation(location) — returns filtered, priority-sorted actions
    • getBulkActions() — returns bulk-enabled actions
    • executeAction(name) — executes by name with optional context override
    • handleShortcut(keys) — keyboard shortcut dispatch
    • Memoized engine instance, stable callbacks
  • Tests: 23 tests (12 ActionBar, 11 useActionEngine) covering registration, rendering, location filtering, overflow, styling, execution, shortcuts

P2.5 PWA & Offline (Real Sync)

  • Background sync queue → real server sync (replace simulation)
  • Conflict resolution on reconnection wired into Console flow
  • Optimistic updates with TransactionManager state application

P2.6 Plugin Modularization & Dynamic Management

Status: Phase 1 complete — Plugin class standard, install/uninstall API, example plugin classes. Phase 1.5 complete — composeStacks unified to @objectstack/spec, plugin isolation, duplicate merge removal.

Plugin architecture refactoring to support true modular development, plugin isolation, and dynamic plugin install/uninstall at runtime.

Phase 1 — Plugin Class Standard & Example Plugins ✅

  • Define AppMetadataPlugin interface in @object-ui/types (name, version, type, description, init/start/stop/getConfig)
  • Define PluginContext interface for lifecycle hook context (logger, kernel)
  • Add install(plugin) / uninstall(pluginName) convenience methods to PluginSystem in @object-ui/core
  • Create CRMPlugin with full lifecycle (init/start/stop/getConfig) — examples/crm/plugin.ts
  • Create TodoPluginexamples/todo/plugin.ts
  • Create KitchenSinkPluginexamples/kitchen-sink/plugin.ts
  • Update package.json exports for all example apps (./plugin entry point)
  • Refactor root objectstack.config.ts to use plugin-based config collection via getConfig()
  • Unit tests for install() / uninstall() (5 new tests, 18 total in PluginSystem)

Phase 1.5 — Plugin Isolation & Config Composition ✅

  • Add explicit objectName to all example plugin actions (CRM: 28 actions, Todo: 6 actions, Kitchen Sink: 3 actions)
  • Rename Kitchen Sink accountks_account to eliminate same-name object conflicts across plugins
  • Create composeStacks() utility in @object-ui/core — declarative stack merging with objectConflict option, automatic views→objects and actions→objects mapping
  • Remove duplicate mergeActionsIntoObjects() from root config and console shared config
  • Remove duplicate mergeViewsIntoObjects() from root config and console shared config (moved into composeStacks)
  • Refactor root objectstack.config.ts and apps/console/objectstack.shared.ts to use composeStacks()
  • Eliminate defineStack() double-pass hack — single composeStacks() call produces final config with runtime properties (listViews, actions) preserved. defineStack() Zod validation stripped these fields, requiring a second composeStacks pass to restore them.
  • Use composed.apps unified flow in console shared config — replaced manual [...crmApps, ...(todoConfig.apps || []), ...] spreading with CRM navigation patch applied to composed output
  • Use composed.reports in console shared config — replaced ...(crmConfig.reports || []) with ...(composed.reports || []) to include reports from all stacks
  • composeStacks unified to @objectstack/spec: Removed @object-ui/core composeStacks implementation and its 15-test suite (coverage now lives upstream in @objectstack/spec). All config composition now uses composeStacks from @objectstack/spec (protocol-level: object dedup, array concatenation, actions→objects mapping, manifest selection, i18n). Runtime-specific mergeViewsIntoObjects adapter extracted to @object-ui/core and applied post-composition at call sites. A deprecated re-export of composeStacks is kept in @object-ui/core for backward compatibility.

Phase 2 — Dynamic Plugin Loading (Planned)

  • Hot-reload / lazy loading of plugins for development
  • Runtime plugin discovery and loading from registry
  • Plugin dependency graph visualization in Console

Phase 3 — Plugin Identity & Isolation (Planned)

  • Eliminate same-name object conflicts across plugins (Kitchen Sink accountks_account)
  • Preserve origin plugin metadata on objects, actions, dashboards for runtime inspection
  • Per-plugin i18n namespace support
  • Per-plugin permissions and data isolation
  • Move mergeViewsIntoObjects from composeStacks to runtime/provider layer

Phase 4 — Cross-Repo Plugin Ecosystem (Planned)

  • Plugin marketplace / registry for third-party plugins
  • Plugin publish/validate tooling (spec v3.0.9 PluginBuildOptions, PluginPublishOptions)
  • Cross-repo plugin loading from npm packages

🔮 P3 — Future Vision (Deferred)

CRM Example Metadata Enhancement ✅

Comprehensive metadata expansion for the CRM reference implementation.

  • P0: Reports — Sales Report and Pipeline Report with full ReportSchema (fields, groupBy, sections, schedule, export)
  • P0: Report Navigation — Native type: 'report' navigation items (no shared config hack)
  • P1: Seed Data — Users — 7 users covering admin, manager, user, viewer roles + inactive user
  • P1: Seed Data — Orders — 7 orders covering all statuses (draft, pending, paid, shipped, delivered, cancelled)
  • P1: Seed Data — Products — 2 inactive products (is_active: false) for filter testing
  • P1: Order ↔ Product Junctionorder_items object with line items (quantity, price, discount, item_type) + 12 seed records
  • P1: Opportunity ↔ Contact Junctionopportunity_contacts object with role-based relationships + 7 seed records
  • P1: Contact ↔ Event Attendeesparticipants field populated on all event seed data
  • P2: Dashboard Dynamic Data — "Revenue by Account" widget using provider: 'object' aggregation. DashboardRenderer now delegates provider: 'object' widgets to ObjectChart (type: 'object-chart') for async data loading + client-side aggregation (sum/count/avg/min/max)
  • P2: Fix Revenue by Account Chart — Fixed 3 bugs preventing "Revenue by Account" chart from displaying data: (1) ObjectChart extractRecords() now handles results.data and results.value response formats in addition to results.records, (2) DashboardRenderer auto-adapts series dataKey from aggregate.field when aggregate config is present, (3) CRM dashboard yField aligned to aggregate field amount (was total). Centralized extractRecords() utility in @object-ui/core and unified data extraction across all 6 data components (ObjectChart, ObjectMap, ObjectCalendar, ObjectGantt, ObjectTimeline, ObjectKanban). Added 16 new tests.
  • P2: App Brandinglogo, favicon, backgroundColor fields on CRM app
  • P3: Pages — Settings page (utility) and Getting Started page (onboarding)
  • P2: Spec Compliance Audit — Fixed variant: 'danger''destructive' (4 actions), columns: stringnumber (33 form sections), added type: 'dashboard' to dashboard
  • P2: Dashboard Widget Spec Alignment — Added id, title, object, categoryField, valueField, aggregate to all dashboard widgets across CRM, Todo, and Kitchen Sink examples (5 new spec-compliance tests)
  • P2: i18n (10 locales) — Full CRM metadata translations for en, zh, ja, ko, de, fr, es, pt, ru, ar — objects, fields, fieldOptions, navigation, actions, views, formSections, dashboard, reports, pages (24 tests)
  • P2: Full Examples Metadata Audit — Systematic spec compliance audit across all 4 examples: added type: 'dashboard' + description to todo/kitchen-sink dashboards, refactored msw-todo to use ObjectSchema.create + Field.* with snake_case field names, added explicit views to kitchen-sink and msw-todo, added missing successMessage on CRM opportunity action, 21 automated compliance tests
  • P2: CRM Dashboard Full provider:'object' Adaptation — Converted all chart and table widgets in CRM dashboard from static provider: 'value' to dynamic provider: 'object' with aggregation configs. 12 widgets total: 4 KPI metrics (static), 7 charts (sum/count/avg/max aggregation across opportunity, product, order objects), 1 table (dynamic fetch). Cross-object coverage (order), diverse aggregate functions (sum, count, avg, max). Fixed table close_date field alignment. Added i18n for 2 new widgets (10 locales). 9 new CRM metadata tests, 6 new DashboardRenderer rendering tests (area/donut/line/cross-object + edge cases). All provider:'object' paths covered.
  • P1: Dashboard provider:'object' Crash & Blank Rendering Fixes — Fixed 3 critical bugs causing all charts to be blank and tables to crash on provider:'object' dashboards: (1) DashboardRenderer ...options spread was leaking provider config objects as data in data-table and pivot schemas — fixed by destructuring data out before spread, (2) DataTableRenderer and PivotTable now guard with Array.isArray() for graceful degradation when non-array data arrives, (3) ObjectChart now shows visible loading/warning messages instead of silently rendering blank when dataSource is missing. Also added provider:'object' support to DashboardGridLayout (charts, tables, pivots). 2 new regression tests.
  • P1: Dashboard Widget Data Blank — useDataScope/dataSource Injection Fix — Fixed root cause of dashboard widgets showing blank data with no server requests: useDataScope(undefined) was returning the full context dataSource (service adapter) instead of undefined when no bind path was given, causing ObjectChart and all data components (ObjectKanban, ObjectGallery, ObjectTimeline, ObjectGrid) to treat the adapter as pre-bound data and skip async fetching. Fixed useDataScope to return undefined when no path is provided. Also improved ObjectChart fault tolerance: uses useContext directly instead of useSchemaContext (no throw without provider), validates dataSource.find is callable before invoking. 14 new tests (7 useDataScope + 7 ObjectChart data fetch/fault tolerance).
  • P1: URL-Driven Debug/Developer Panel — Universal debug mode activated via ?__debug URL parameter (amis devtools-style). @object-ui/core: exported DebugFlags, DebugCollector (perf/expr/event data collection, tree-shakeable), parseDebugFlags(), enhanced isDebugEnabled() (URL → globalThis → env resolution, SSR-safe). @object-ui/react: useDebugMode hook with URL detection, Ctrl+Shift+D shortcut, manual toggle; SchemaRendererContext extended with debugFlags; SchemaRenderer injects data-debug-type/data-debug-id attrs + reports render perf to DebugCollector when debug enabled. @object-ui/components: floating DebugPanel with 7 built-in tabs (Schema, Data, Perf, Expr, Events, Registry, Flags), plugin-extensible via extraTabs. Console MetadataInspector auto-opens when ?__debug detected. Fine-grained sub-flags: ?__debug_schema, ?__debug_perf, ?__debug_data, ?__debug_expr, ?__debug_events, ?__debug_registry. 48 new tests.
  • P1: Chart Widget Server-Side Aggregation — Fixed chart widgets (bar/line/area/pie/donut/scatter) downloading all raw data and aggregating client-side. Added optional aggregate() method to DataSource interface (AggregateParams, AggregateResult types) enabling server-side grouping/aggregation via analytics API (e.g. GET /api/v1/analytics/{resource}?category=…&metric=…&agg=…). ObjectChart now prefers dataSource.aggregate() when available, falling back to dataSource.find() + client-side aggregation for backward compatibility. Implemented aggregate() in ValueDataSource (in-memory), ApiDataSource (HTTP), and ObjectStackAdapter (analytics API with client-side fallback). Only detail widgets (grid/table/list) continue to fetch full data. 9 new tests.
  • P1: Spec-Aligned CRM I18n — Fixed CRM internationalization not taking effect on the console. Root cause: CRM metadata used plain string labels instead of spec-aligned I18nLabel objects. Fix: (1) Updated CRM app/dashboard/navigation metadata to use I18nLabel objects ({ key, defaultValue }) per spec. (2) Updated NavigationItem and NavigationArea types to support I18nLabel. (3) Added resolveLabel() helper in NavigationRenderer. (4) Updated resolveI18nLabel() to accept t() function for translation. (5) Added loadLanguage callback in I18nProvider for API-based translation loading. (6) Added /api/v1/i18n/translations/:lang endpoint to mock server. Console contains zero CRM-specific code.
  • P0: Opportunity List View & ObjectDef Column Enrichment — Fixed ObjectGrid not using objectDef field metadata for type-aware rendering when columns are string[] or ListColumn[] without full options. (1) Schema resolution always fetches full schema from DataSource for field type metadata. (2) String[] column path enriched with objectDef types, options (with colors), currency, precision for proper CurrencyCellRenderer, SelectCellRenderer (colored badges), PercentCellRenderer, DateCellRenderer. (3) ListColumn[] fieldMeta deep-merged with objectDef field properties (select options with colors, currency code, precision). (4) Opportunity view columns upgraded from bare string[] to ListColumn[] with explicit types, alignment, and summary aggregation. 9 new tests.
  • P1: Actions Merge into Object Definitions — Fixed action buttons never showing in Console/Studio because example object definitions lacked actions field. Initially added mergeActionsIntoObjects() helper with longest-prefix name matching. Later refactored: all actions now declare explicit objectName, and merging is handled by composeStacks() from @objectstack/spec. Created todo task actions (6: complete, start, clone, defer, set_reminder, assign) and kitchen-sink showcase actions (3: change_status, assign_owner, archive). All CRM/Todo/Kitchen Sink objects now serve actions in metadata. Fixes #840.
  • P1: Unified Debug/Metadata Entry — Remove Redundant Metadata Button — Removed the visible <MetadataToggle> button from RecordDetailView, DashboardView, PageView, and ReportView headers. End users no longer see a "</> Metadata" button that had no practical purpose. The MetadataInspector panel is now only accessible via ?__debug URL parameter (auto-opens when debug mode is active). ObjectView retains its admin-only Design Tools menu entry for metadata inspection. This unifies the debug entry point and improves end-user UX by removing redundant UI elements.

Ecosystem & Marketplace

  • Plugin marketplace website with search, ratings, and install count
  • Plugin publishing CLI (os ui publish) with automated validation
  • 25+ official plugins

Community Growth

  • Official website (www.objectui.org) with interactive playground
  • Discord community, monthly webinars, technical blog, YouTube tutorials

ObjectUI Cloud (2027)

  • Project hosting, online editor, Database as a Service
  • One-click deployment, performance monitoring
  • Billing system (Free / Pro / Enterprise)

Industry Solutions (2027)

  • CRM, ERP, HRM, E-commerce, Project Management accelerators
  • AI-powered schema generation from natural language

📈 Success Metrics

v1.0 Release Criteria

Metric Current v1.0 Target How Measured
Protocol Alignment ~85% 90%+ (UI-facing) Protocol Consistency Assessment
AppShell Renderer ✅ Complete Sidebar + nav tree from AppSchema JSON Console renders from spec JSON
Designer Interaction Phase 2 (most complete) DataModelDesigner drag/undo; ViewDesigner removed (replaced by ViewConfigPanel) Manual UX testing
Build Status 43/43 pass 43/43 pass pnpm build
Test Count 6,700+ 6,700+ pnpm test summary
Test Coverage 90%+ 90%+ pnpm test:coverage
Storybook Stories 80 91+ (1 per component) Story file count
Console i18n 100% 100% No hardcoded strings

🐛 Bug Fixes

Record Detail "Record Not Found" in External Metadata Environments (March 2026)

Root Cause: Three compounding issues caused "Record not found" when navigating from a list to a record detail page in external metadata environments:

  1. DetailView.tsx's alt-ID fallback only triggered when findOne returned null. If it threw an error (server 500, network failure, etc.), the error propagated to the outer catch handler and the fallback was never tried.
  2. ObjectStackAdapter.findOne with $expand used rawFindWithPopulate with $filter: { _id: id }, which some external servers don't support. On failure it threw, instead of falling back to the simpler data.get() call.
  3. Record IDs in navigation URLs were not URL-encoded, which could cause routing issues with IDs containing special characters.

Fix: Made DetailView catch all errors from the first findOne (converting to null) so the alt-ID fallback always runs. Made ObjectStackAdapter.findOne fall through to direct data.get() when the $expand raw request fails with a non-404 error. Added encodeURIComponent for record IDs in all navigation URL construction points.

Tests: 32 DetailView tests, 12 expand tests, 33 useNavigationOverlay tests, 6 RecordDetailEdit tests — all pass.

Auth Registration and Login Unavailable in MSW/Server Modes (March 2026)

Root Cause: createKernel.ts (MSW mode) and objectstack.config.ts (Server mode) did not load AuthPlugin, so the kernel had no 'auth' service. All /api/v1/auth/* endpoints (sign-up, sign-in, get-session, sign-out) returned 404.

Fix:

  1. Added AuthPlugin from @objectstack/plugin-auth to objectstack.config.ts for server mode (pnpm dev:server).
  2. Created authHandlers.ts with in-memory mock implementations of better-auth endpoints for MSW mode (pnpm dev). Mock handlers are added to customHandlers in both browser.ts and server.ts.
  3. Mock handlers support: sign-up/email, sign-in/email, get-session, sign-out, forget-password (better-auth convention), reset-password, update-user.

Tests: 11 new auth handler tests, all existing MSW (7) and auth (24) tests pass.

Default Navigation Mode (Page) Clicks Have No Effect — Stale Closure (February 2026)

Root Cause: Three compounding issues created a stale closure chain in ObjectView.tsx:

  1. { mode: 'page' } fallback created a new object literal every render, causing useNavigationOverlay's handleClick useCallback to be recreated with potentially stale deps.
  2. The onNavigate callback was an inline arrow function with unstable identity, so React could batch-skip the render where handleClick picks up the fresh closure.
  3. Cascade instability: navOverlay (from useMemo) got a new reference every render because its deps (handleClick, etc.) changed, propagating stale closures through renderListView.

Fix: Memoized detailNavigation with useMemo (stable reference for the { mode: 'page' } fallback) and extracted onNavigate into a useCallback (handleNavOverlayNavigate) with [navigate, viewId] deps. This ensures stable identities for both inputs to useNavigationOverlay, preventing stale closures.

Tests: Added 2 stale closure prevention tests in useNavigationOverlay.test.ts: (1) verify handleClick uses latest onNavigate after re-render with new callback, (2) verify navigation works after config changes from undefined to explicit page mode. All 31 useNavigationOverlay tests and 739 console tests pass.

ListView Grouping Config Not Taking Effect (February 2026)

Root Cause: viewComponentSchema useMemo in ListView.tsx was missing groupingConfig, rowColorConfig, and navigation.handleClick in its dependency array. When users toggled grouping fields via the toolbar popover, the state changed but the memoized schema was not recomputed, so the child grid/kanban/gallery never received the updated grouping config.

Fix: Added groupingConfig, rowColorConfig, and navigation.handleClick to the useMemo dependency array at line 856 of ListView.tsx.

Tests: Added integration test in ListViewGroupingPropagation.test.tsx that verifies toggling a group field via the toolbar immediately updates the rendered schema. All 117 ListView tests pass.

List View Row Click Not Navigating to Record Detail (February 2026)

Root Cause: onRowClick in both PluginObjectView (line 772) and renderListView (line 599) of ObjectView.tsx fell back to onEdit / editHandler, which only opens an edit form. The navOverlay.handleClick from useNavigationOverlay — which handles drawer/modal/page navigation modes — was never connected to these click handlers. Additionally, the useNavigationOverlay hook was missing the onNavigate callback needed for mode: 'page' to update the URL.

Fix: Replaced onEdit/editHandler fallbacks with navOverlay.handleClick in both row click handlers, added onNavigate callback to useNavigationOverlay that sets the recordId URL search parameter, and added navOverlay to the renderListView useCallback dependency array.

Tests: All 32 ObjectView tests and 29 useNavigationOverlay tests pass.

App.tsx handleRowClick Overriding All Navigation Modes (February 2026)

Root Cause: App.tsx defined a handleRowClick that hardcoded setSearchParams({recordId}) (drawer-only behavior) and passed it unconditionally to <ObjectView> via onRowClick={handleRowClick}. This overrode the internal navOverlay.handleClick in Console's ObjectView, preventing page/modal/split/popover/new_window navigation modes from working.

Fix: Removed handleRowClick from App.tsx and the onRowClick prop from both <ObjectView> route elements. Updated Console ObjectView to always use navOverlay.handleClick (3 locations) instead of falling back to the external onRowClick prop. Removed the unused onRowClick prop from the ObjectView function signature.

Tests: All 42 ObjectView tests and 33 useNavigationOverlay tests pass (3 new tests added).

ListView Multi-Navigation Mode Support — split/popover/page/new_window (February 2026)

Root Cause: While drawer/modal modes worked in the Console ObjectView, the remaining 4 navigation modes had gaps:

  1. Console's onNavigate callback relied on implicit fallthrough for view action (page mode) — not explicit.
  2. PluginObjectView's formLayout only mapped drawer/modal modes; split/popover fell through to the default layout (drawer), rendering the wrong overlay type.
  3. PluginObjectView lacked NavigationOverlay integration for split (resizable side-by-side panels) and popover (compact dialog preview).

Fix:

  • Console onNavigate now explicitly checks for action === 'view' (page mode) alongside the existing 'new_window' check.
  • PluginObjectView formLayout now includes split and popover branches.
  • PluginObjectView imports and renders NavigationOverlay from @object-ui/components for both split mode (with mainContent wrapping the grid) and popover mode (Dialog fallback when no popoverTrigger).
  • Split mode close button properly resets form state via handleFormCancel.

Tests: Updated split/popover tests to verify NavigationOverlay rendering (close panel button for split, dialog role for popover). Added split close-and-return test. All 29 PluginObjectView tests and 37 Console ObjectView tests pass.

ListView Grouping Mode Empty Rows (February 2026)

Root Cause: When grouping is enabled in list view, buildGroupTableSchema in ObjectGrid.tsx sets pagination: false but inherits pageSize: 10 from the parent schema. The DataTableRenderer filler row logic (Array.from({ length: Math.max(0, pageSize - paginatedData.length) })) pads each group table with empty rows up to pageSize, creating many blank lines.

Fix: Added pagination && condition to filler row rendering in data-table.tsx. Filler rows only render when pagination is enabled, since their purpose is maintaining consistent page height during paginated views.

Tests: Added 2 tests in data-table-airtable-ux.test.tsx verifying filler rows are skipped when pagination is off and still rendered when pagination is on. All 59 related tests pass.

Metric Widget I18nLabel Render Crash (February 2026)

Root Cause: MetricWidget and MetricCard rendered I18nLabel objects ({key, defaultValue}) directly as React children. When CRM dashboard metadata used I18nLabel objects for trend.label (e.g. { key: 'crm.dashboard.trendLabel', defaultValue: 'vs last month' }), React threw error #31 ("Objects are not valid as a React child").

Fix: Added resolveLabel() helper to MetricWidget.tsx and MetricCard.tsx that converts I18nLabel objects to plain strings before rendering. Updated prop types to accept both string and I18nLabel objects for label, description, and trend.label.

Tests: Added 3 new tests: 1 in DashboardRenderer.widgetData.test.tsx verifying metric widgets with I18nLabel trend labels render correctly, and 2 in MetricCard.test.tsx verifying I18nLabel resolution for title and description. All 159 dashboard tests pass.

Field Type Display Issues — Lookup, User, Select, Status Renderers (February 2026)

Root Cause: Multiple renderer defects caused incorrect field value display across views:

  1. LookupCellRenderer — Destructured only value, ignoring the field prop. When the API returned a raw primitive ID (e.g. customer: 2), the renderer fell through to String(value) and showed "2" instead of the related record's name. No attempt was made to resolve IDs via field.options.

  2. UserCellRenderer — Did not guard against primitive values (number/string user IDs). Accessing .name / .username on a number returned undefined, silently falling through to "User" as the generic label.

  3. getCellRenderer standardMaplookup and master_detail were mapped to SelectCellRenderer instead of LookupCellRenderer in the fallback map. Although the fieldRegistry pre-registration shadowed this bug, it was semantically incorrect.

  4. status, user, owner types — Not pre-registered in fieldRegistry. All went through the standardMap path, making their association with renderers implicit and invisible.

Fix:

  • LookupCellRenderer: now accepts the field prop and resolves primitive IDs against field.options (matching by String(opt.value) === String(val) for type-safe comparison). Arrays of primitive IDs are resolved via the same logic. Null/empty-string guard updated from !value to value == null || value === '' to handle 0 correctly.
  • UserCellRenderer: primitive values (typeof !== 'object') return a plain <span> with the string representation. Array items that are not objects are also handled gracefully.
  • getCellRenderer standardMap: lookup and master_detail now correctly reference LookupCellRenderer.
  • fieldRegistry now explicitly registers statusSelectCellRenderer, userUserCellRenderer, and ownerUserCellRenderer alongside the existing lookup/master_detail/select registrations.

Tests: Added 36 new tests in cell-renderers.test.tsx:

  • getCellRenderer registry assertions for lookup, master_detail, status, user, owner types
  • TextCellRenderer: null, undefined, empty string, numeric zero (0 renders "0" not "-"), boolean false
  • LookupCellRenderer: null, empty-string, primitive ID (number), primitive ID (string), unresolved primitive, object with name/label/_id, array of objects, array of primitive IDs resolved via options
  • UserCellRenderer: null, primitive number ID, primitive string ID, object with name, object with username, array of user objects
  1. TextCellRenderer — Used value || '-' which incorrectly rendered '-' for numeric 0 (falsy zero). Updated to (value != null && value !== '') ? String(value) : '-' for consistent null-only suppression.

All 313 @object-ui/fields tests pass.

ListView Airtable-Style Toolbar Opt-In & Duplicate Record Count (February 2026)

Root Cause (1 — Toolbar defaults): showHideFields, showColor, and showDensity in ListView.tsx used opt-out logic (!== false), making secondary toolbar buttons visible by default. Airtable hides these controls unless explicitly enabled.

Fix: Changed default logic from !== false (opt-out) to === true (opt-in) for showHideFields, showColor, and showDensity in the toolbarFlags computation. Updated @default JSDoc comments in NamedListView and ListViewSchema interfaces from @default true to @default false.

Root Cause (2 — Duplicate record count): Both ListView.tsx (record-count-bar) and ObjectView.tsx (record-count-footer) independently rendered the record count at the bottom, causing duplicate display.

Fix: Removed the record-count-footer from ObjectView.tsx since ListView already renders the authoritative record-count-bar.

Tests: Updated 11 tests across ListView.test.tsx and ObjectView.test.tsx. All 112 ListView tests and 32 ObjectView tests pass.


DetailView/RecordDetailView SDUI Optimization (March 2026)

Type-aware rendering, responsive layout, virtual scrolling, metadata-driven highlights, performance optimization, and activity panel collapse-when-empty.

HeaderHighlight (@object-ui/plugin-detail):

  • Use getCellRenderer for type-aware display (currency → $250,000.00, select → Badge, etc.) instead of raw String(value)
  • Add objectSchema prop for field metadata enrichment (type, options, currency, precision, format)

autoLayout (@object-ui/plugin-detail):

  • inferDetailColumns accepts optional containerWidth for responsive column capping (<640px→1col, <900px→max 2col)
  • applyDetailAutoLayout passes through containerWidth parameter

RecordChatterPanel (@object-ui/plugin-detail):

  • collapseWhenEmpty prop: auto-collapse panel when no feed items exist
  • Pass collapseWhenEmpty through to embedded RecordActivityTimeline

DetailSection (@object-ui/plugin-detail):

  • virtualScroll config (VirtualScrollOptions): progressive batch rendering for sections with many fields
  • Export VirtualScrollOptions type from package index

RecordDetailView (apps/console):

  • Wrap detailSchema construction with useMemo (deps: objectDef, pureRecordId, related, childRelatedData, actionRefreshKey)
  • Remove hardcoded HIGHLIGHT_FIELD_NAMES; read exclusively from objectDef.views.detail.highlightFields (no fallback)
  • Enable collapseWhenEmpty + collapsible: true on RecordChatterPanel

Tests: 125 plugin-detail tests passing (17 new) covering HeaderHighlight type-aware rendering, autoLayout responsive columns, RecordChatterPanel collapseWhenEmpty, DetailSection virtualScroll.


Platform-level DetailView enhancements: auto-grouping from form sections, empty value hiding, smart header with primaryField/summaryFields, responsive breakpoint fix, and activity timeline collapse.

Types (@object-ui/types):

  • DetailViewSection.hideEmpty?: boolean — filter null/undefined/empty string fields; hide empty sections
  • DetailViewSchema.primaryField?: string — record-level title from data field
  • DetailViewSchema.summaryFields?: string[] — render key attributes as Badge in header

DetailSection (@object-ui/plugin-detail):

  • hideEmpty filtering: fields with null/undefined/empty string values are removed; section returns null when all fields hidden
  • Responsive breakpoint fix: sm:grid-cols-2md:grid-cols-2, sm:grid-cols-2 md:grid-cols-3md:grid-cols-2 lg:grid-cols-3 (correct behavior on iPad+sidebar)

DetailView Header (@object-ui/plugin-detail):

  • Header renders data[primaryField] as h1 title (falls back to schema.title)
  • summaryFields rendered as <Badge variant="secondary"> next to title

RecordActivityTimeline (@object-ui/plugin-detail):

  • collapseWhenEmpty prop: suppress "No activity recorded" message when true, showing only comment input

RecordDetailView (apps/console):

  • Read objectDef.views?.form?.sections for section grouping; fallback to flat field list
  • Remove columns: 2 hardcode — let autoLayout infer optimal columns
  • Auto-detect primaryField from object fields (name/title)

Field Renderers (@object-ui/fields):

  • EmailCellRenderer: mailto link + hover copy-to-clipboard button
  • PhoneCellRenderer: tel link with call icon + hover copy-to-clipboard button
  • BooleanCellRenderer: warning Badge for active/enabled/verified fields when false (e.g. "Active — Off")

Tests: 94 plugin-detail tests passing (11 new), 100 field renderer tests passing (12 new) covering hideEmpty filtering, empty section hiding, primaryField/summaryFields rendering, responsive breakpoints, collapseWhenEmpty, autoLayout undefined-columns regression, email copy, phone copy+icon, boolean warning badge.

Storybook: Added PrimaryFieldWithBadges and HideEmptyFields stories.


ListView/DetailView/DetailSection — Lookup Field & Type Display Consistency (March 2026)

Issue #939: Lookup/master_detail fields displayed raw IDs (e.g. o1, p1) instead of expanded names across ListView, DetailView, and DetailSection.

Root Causes (5 independent bugs):

  1. ListView $expand race conditionexpandFields depended on async objectDef which could be null on first fetch, causing data to be requested without $expand and returning raw foreign-key IDs.
  2. DetailView missing $expand and objectSchemafindOne() was called without $expand parameters and without loading objectSchema, so lookup fields could never be expanded.
  3. DetailSection missing objectSchema enrichment — When field.type was not explicitly set, displayValue fell through to String(value), bypassing type-aware CellRenderers even when objectSchema metadata was available.
  4. ObjectStackAdapter find() dropping $expand — The @objectstack/client v3.0.10's data.find() (GET) does not support expand in its QueryOptions interface, so $expand was silently dropped during convertQueryParams().
  5. ObjectStackAdapter findOne() ignoring params — The findOne() method declared its params argument as _params (unused), meaning $expand was never sent to the server.

Fix:

  • ListView (packages/plugin-list/src/ListView.tsx): Added objectDefLoaded state flag. Data fetch effect is gated on objectDefLoaded so the first fetch always includes correct $expand parameters. The objectDef fetch effect sets the flag in finally block to handle both success and error cases.
  • DetailView (packages/plugin-detail/src/DetailView.tsx): Added objectSchema state. Data fetch effect now calls getObjectSchema() first, computes $expand via buildExpandFields(), and passes the params to findOne(). The resolved objectSchema is passed to DetailSection components.
  • DetailSection (packages/plugin-detail/src/DetailSection.tsx): Added optional objectSchema prop. Fields are enriched with missing metadata (options, currency, precision, format, reference_to, reference_field) from objectSchema regardless of whether field.type is explicitly set. When field.type is not set, the type is also resolved from objectSchema. Explicit field.type always takes precedence for the resolved type.
  • ObjectStackAdapter find() (packages/data-objectstack/src/index.ts): When $expand is present, routes through rawFindWithPopulate() which issues a raw GET request with a populate= query parameter derived from the $expand field names. The server's REST plugin routes GET /data/:object to findData() which processes populate (comma-separated string) for lookup expansion. Falls back to client.data.find() when no expand is needed.
  • ObjectStackAdapter findOne() (packages/data-objectstack/src/index.ts): When $expand is present, uses the same rawFindWithPopulate() mechanism with filter={"_id":"..."} and populate= to fetch the single record with expanded lookup fields. Falls back to client.data.get() when no expand is needed.

Tests: 15 new tests added (2 ListView, 3 DetailView, 4 DetailSection, 8 data-objectstack). All 505 plugin tests + 94 data-objectstack tests pass.


DetailSection — Mobile Responsive col-span Fix (March 2026)

Bug: On mobile devices, detail page fields displayed in 3-column layout instead of single column, causing content to squeeze together and severely impacting readability.

Root Cause: When applyAutoSpan set span: 3 on wide fields (textarea, markdown, etc.) in a 3-column layout, the resulting col-span-3 CSS class was applied without responsive prefixes. In CSS Grid, when an item has col-span-3 in a grid-cols-1 layout, the browser creates 2 implicit column tracks — and subsequent auto-placed items flow into those implicit columns, producing a 3-column layout even on mobile.

Fix:

  • DetailSection (packages/plugin-detail/src/DetailSection.tsx): Added getResponsiveSpanClass() helper that generates responsive col-span classes matching the grid breakpoints. For a 3-column layout: no col-span at base (mobile single-column), md:col-span-2 at tablet, lg:col-span-3 at desktop. For a 2-column layout: no col-span at base, md:col-span-2 at tablet+. This ensures col-span never exceeds the visible column count at each breakpoint, preventing implicit grid columns on mobile.

Tests: 11 new tests (8 getResponsiveSpanClass unit tests + 3 DetailSection integration tests verifying no bare col-span-N classes appear). All 52 plugin-detail tests pass.


RecordDetailView — Action Button Full-Chain Integration (March 2026)

Issue #107: All Action buttons on record detail pages (Change Stage, Mark as Won, etc.) clicked with zero response — no dialogs, no API calls, no toast, no data refresh.

Root Causes (6 independent bugs):

  1. Missing ActionProviderRecordDetailView didn't wrap DetailView with ActionProvider, so useAction() fell back to an empty ActionRunner with no handlers.
  2. Action type overwrittenaction:bar component overrode action.type ('api') with the component type ('action:button'), so ActionRunner never matched the registered 'api' handler.
  3. No API handlerapi action targets were logical names (e.g., 'opportunity_change_stage'), not HTTP URLs. The built-in executeAPI() tried fetch('opportunity_change_stage') which failed silently.
  4. No param collectionActionParam[] was passed as params (values) instead of actionParams (definitions to collect), so the param collection dialog was never triggered.
  5. No confirm/toast handlersconfirmText fell back to window.confirm, success/error messages were silently dropped.
  6. No visibility contextuseCondition evaluated visible expressions like "stage !== 'closed_won'" with empty context, always returning true.

Fix:

  • RecordDetailView (apps/console/src/components/RecordDetailView.tsx): Wrapped DetailView with <ActionProvider> providing onConfirm, onToast, onNavigate, onParamCollection handlers and a custom api handler that maps logical action targets to dataSource.update() operations.
  • action-bar (packages/components/src/renderers/action/action-bar.tsx): Preserves original action.type as actionType when overriding with component type. Forwards data prop to child action renderers for visibility context.
  • action-button (packages/components/src/renderers/action/action-button.tsx): Uses actionType for execution. Detects ActionParam[] arrays and passes as actionParams. Passes record data to useCondition for visibility expressions.
  • ActionConfirmDialog (apps/console/src/components/ActionConfirmDialog.tsx): Promise-based confirmation dialog using Shadcn AlertDialog.
  • ActionParamDialog (apps/console/src/components/ActionParamDialog.tsx): Dynamic form dialog for collecting action parameters (select, text, textarea) using Shadcn Dialog.

Tests: 6 new integration tests covering: action button rendering, confirm dialog show/accept/cancel, param collection dialog, toast notification, dataSource.update invocation. All 764 console tests pass.


⚠️ Risk Management

Risk Mitigation
AppShell complexity (7 nav types, areas, mobile) Start with static nav tree, add mobile modes incrementally
Designer DnD integration time Use @dnd-kit/core (already proven in Kanban/Dashboard)
Airtable UX bar is high Focus on Grid + Kanban + Form triad first; defer Gallery/Timeline polish
PWA real sync complexity Keep simulated sync as fallback; real sync behind feature flag
Performance regression Performance budgets in CI, 10K-record benchmarks
View config live preview dependency chain breakage generateViewSchema hardcodes non-grid defaults; per-view-type integration tests required (see P1.8.1)
Config property type gaps (NamedListView missing fields) Add first-class properties to @object-ui/types; use Zod schema to validate at runtime

P2.11 Airtable UX Polish: Table Selection, i18n, Layout, and Density ✅

Status: Complete — Comprehensive UX polish for Airtable-level table experience.

i18n Compliance:

  • All hardcoded UI strings in data-table.tsx replaced with t() calls (12+ strings)
  • All hardcoded UI strings in ListView.tsx replaced with t() calls (6+ strings)
  • New i18n keys added to en.ts and zh.ts locale files (table/list sections)
  • Default fallback translations ensure standalone usage works without I18nProvider

Row Selection Auto-Enable:

  • ObjectGrid auto-enables selectionMode: 'multiple' when batchActions/bulkActions are defined
  • No explicit selectable config needed for bulk action workflows

Column Width Optimization:

  • Selection checkbox and row number columns reduced from w-12 (48px) to w-10 (40px)
  • Frozen column offset calculations updated to match new widths

Border/Cell Contrast:

  • Table row borders increased from border-border/50 to border-border for better scanability

Pagination Density:

  • Filler row colSpan now correctly includes showRowNumbers count

📦 Detail Page & Related List i18n (P1.15)

Goal: Salesforce-style detail page enhancements: i18n for all detail page UI elements, improved empty value display, related list actions, and auto-discovery of related lists.

i18n Integration:

  • Add detail.* translation keys to all 11 locale files (en, zh, ja, de, fr, es, ar, ru, pt, ko)
  • useDetailTranslation safe wrapper hook with English fallback (follows existing useGridTranslation/useListViewTranslation pattern)
  • DetailView fully i18n-integrated (Back, Edit, Share, Delete, Duplicate, Export, View history, Record not found, Related heading, favorites, navigation)
  • DetailSection copy tooltip i18n via useSectionTranslation
  • RelatedList i18n-integrated (record counts, loading, empty state)
  • Add 'detail' to BUILTIN_KEYS in useObjectLabel.ts to prevent namespace collision

Empty Value Display:

  • Replace hardcoded - with styled em-dash () using text-muted-foreground/50 text-xs italic for elegant empty state

Related List Enhancements:

  • Add onNew prop and "New" button to RelatedList header
  • Add onViewAll prop and "View All" button to RelatedList header
  • Record count uses singular/plural i18n keys

Tests:

  • 10 new RelatedList tests (title, record counts, empty state, New/View All buttons)
  • 2 new DetailView i18n fallback tests (Record not found text, Related heading)
  • Updated DetailSection tests for new empty value styling

Completed:

  • Auto-discover related lists from objectSchema reference fields
  • Tab layout (Details/Related/Activity) for detail page
  • Related list row-level Edit/Delete quick actions
  • Related list pagination, sorting, filtering
  • Collapsible section groups
  • Header highlight area with key fields
  • Console RecordDetailView integration: autoTabs, autoDiscoverRelated, highlightFields, sectionGroups wired into detailSchema for end-to-end availability
  • Console reverse-reference discovery: child objects (e.g., order_itemorder) auto-discovered and rendered with filtered data
  • useSafeFieldLabel wired into DetailSection, RelatedList, HeaderHighlight for convention-based field label i18n (#968, #883, #942)
  • objectName threaded through DetailViewSectionGroupDetailSection / HeaderHighlight / RelatedList
  • RelatedList sortable headers fixed to use effectiveColumns (auto-generated from schema) instead of raw columns prop
  • Added missing detail.* i18n keys (activity, editRow, deleteRow, previousPage, nextPage, etc.) to en.ts and zh.ts
  • RelatedList auto-generated columns use getCellRenderer for type-aware cell rendering (date, currency, select/badge, lookup, boolean, etc.)
  • Console RecordDetailView fallback section title uses t('detail.details') instead of hardcoded English

📚 Reference


Roadmap Status: 🎯 Active — AppShell ✅ · Designer Interaction · View Config Live Preview Sync ✅ · Dashboard Config Panel ✅ · Schema-Driven View Config Panel ✅ · Right-Side Visual Editor Drawer ✅ · Feed/Chatter Documentation ✅ · Airtable UX Parity Next Review: March 15, 2026 Contact: hello@objectui.org | https://github.com/objectstack-ai/objectui