Last Updated: February 27, 2026 Current Version: v0.5.x Spec Version: @objectstack/spec v3.0.10 Client Version: @objectstack/client v3.0.10 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 ✅
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), and Right-Side Visual Editor Drawer (P1.11) — 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:
AppShell — No dynamic navigation renderer from spec JSON (last P0 blocker)✅ Complete- 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) ✅
View Config Live Preview Sync — Config panel changes sync in real-time for Grid, but✅ Complete — all 7 phases of P1.8.1 done, 100% coverage across all view typesshowSort/showSearch/showFilters/striped/borderednot yet propagated to Kanban/Calendar/Timeline/Gallery/Map/Gantt (see P1.8.1)- 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).
- Console Advanced Polish — Remaining upgrades for forms, import/export, automation, comments
- PWA Sync — Background sync is simulated only
Upgraded from
@objectstack/spec v3.0.8→v3.0.9on 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.
Upgraded from
@objectstack/spec v3.0.9→v3.0.10on February 25, 2026. UI sub-export adds new Zod schemas andViewFilterRuletype. Dashboard widgets now requireidfield. 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
idfields - Todo active filter converted from
[['status', '!=', 'Done']]to[{ field: 'status', operator: '!=', value: 'Done' }]
Last remaining P0 blocker. Without this, Console cannot render a sidebar from
AppSchemaJSON.
- Implement
AppSchemarenderer consuming spec JSON (name, label, icon, branding) - Build navigation tree renderer (7 nav item types: object, dashboard, page, url, report, action, group)
- Implement
NavigationAreaSchemasupport (business domain partitioning) - Implement mobile navigation modes (drawer/bottom_nav/hamburger)
- Add permission guards (
requiredPermissions,visible) on navigation items
Priority #1. All items below directly affect end-user experience. Target: indistinguishable from Airtable for core CRUD workflows.
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+Skeyboard 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
useConfigDrafthook - 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
useDesignerHistoryhook (command pattern with undo/redo stacks) - Wire undo/redo to DataModelDesigner
- 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
- Form conditional logic with branching
- Multi-page forms with progress indicator
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
ExportFormatenum (json, csv, xlsx, jsonl, parquet) - Integrate spec v3.0.9
CreateExportJobRequest/ExportJobStatusfor async streaming exports - Import template-based field mapping using spec protocol schemas
- 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
- @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/RecordSubscriptiontypes -
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
RecordDetailViewintegration:CommentThread→RecordChatterPanelwithFeedItem[]data model - Documentation for Feed/Chatter plugin in
content/docs/plugins/plugin-detail.mdx(purpose/use cases, JSON schema, props, and Console integration forRecordChatterPanel,RecordActivityTimeline, and related components)
-
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 (
conditionaledge type,isDefaultflag on edges) - Enhanced retry configuration UI (
backoffMultiplier,maxRetryDelayMs,jitter) - Flow version history viewer (
FlowVersionHistory) - Concurrency policy configuration (
ConcurrencyPolicy)
- AppShell
AppSchemarenderer (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-kitviaNavigationRenderer - Console integration: Navigation pin —
useNavPinshook +NavigationRendererenablePinning/onPinToggle - Console integration:
AppSchemaRendererslot system —sidebarHeader,sidebarExtra,sidebarFooterslots for Console customization - Navigation Sync Service —
useNavigationSynchook auto-syncs App navigation tree on Page/Dashboard CRUD (create, delete, rename) with toast + undo - Navigation Sync auto-detection —
NavigationSyncEffectcomponent monitors metadata changes and auto-syncs navigation across ALL apps when pages/dashboards are added or removed - Navigation Sync all-apps convenience API —
sync*AllAppsmethods iterate all apps without requiring explicitappName - ListView Navigation Mode Fix — All 6 navigation modes (page/drawer/modal/split/popover/new_window) now work correctly on row click:
- ✅
pagemode navigates to/record/:recordIddetail page via React Router - ✅
new_windowmode opens correct Console URL in a new browser tab (delegates toonNavigate) - ✅
splitmode renders resizable split panels with main content + detail panel - ✅
popovermode falls back to compact dialog when nopopoverTriggeris provided - ✅
useNavigationOverlayhook delegatesnew_windowtoonNavigatewhen available for app-specific URL control - ✅ plugin-view
handleRowClicksupportssplitandpopoverbranches
- ✅
- 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)
- ✅
showSortadded toObjectViewSchemaand propagated through plugin-view (Grid only) - ✅ Appearance properties (
rowHeight,densityMode) flow throughrenderListViewschema for all view types - ✅
gridSchemain plugin-view includesstriped/borderedfrom active view config (Grid only) - ✅ Plugin
renderContentpassesrowHeight,densityMode,groupBytorenderListViewschema - ✅
useMemodependency arrays expanded to cover full view config - ✅
generateViewSchemapropagatesshowSearch/showSort/showFilters/striped/bordered/colorfromactiveViewfor all view types (hardcodedshowSearch: falseremoved) - ✅ Console
renderListViewpassesshowSort/showSearch/showFilters/striped/bordered/color/filter/sorttofullSchema - ✅
NamedListViewtype declaresshowSearch/showSort/showFilters/striped/bordered/coloras first-class properties - ✅
ListViewSchemaTypeScript interface and Zod schema includeshowSearch/showSort/showFilters/color - ✅ ViewConfigPanel refactored into Page Config (toolbar/shell) and ListView Config (data/appearance) sections
- ✅
NamedListViewtype 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 - ✅
ListViewSchemaZod schema extended with all new properties - ✅ ViewConfigPanel aligned to full
ListViewSchemaspec: 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:
editRecordsInline→inlineEditfield name alignment (i18n keys, data-testid, component label all unified toinlineEdit) - ✅ Semantic fix:
rowHeightvalues 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) - ✅
clickIntoRecordDetailstoggle 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 extensioncomment - ✅ 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)
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):
→ Now propagates fromgenerateViewSchema(plugin-view): HardcodesshowSearch: falsefor non-grid viewsactiveViewConsole→ Now passes all config propertiesrenderListView: Omits toolbar/display flags fromfullSchema→ Added as first-class propertiesNamedListViewtype: Missing toolbar/display propertiesPlugin→ Now propagated (PR #771)renderListViewschema missing toolbar flags:renderContent→renderListViewschema did not includeshowSearch/showFilters/showSortListView toolbar unconditionally rendered: Search/Filter/Sort buttons always visible regardless of schema flags→ Now conditionally rendered based onschema.showSearch/showFilters/showSort(PR #771);userActions.sort/search/filter/rowHeight/addRecordFormnow override toolbar flags (Issue #737)Hide Fields/Group/Color/Density buttons always visible: No schema property to control visibility→ AddedshowHideFields/showGroup/showColor/showDensitywith conditional rendering (Issue #719)Export toggle broken: ViewConfigPanel writes→ Export now checks bothallowExport: booleanbut ListView checksexportOptionsobjectexportOptions && allowExport !== false; Console clearsexportOptionswhenallowExport === false(Issue #719)→ Fixed tohasExportlogic bug:draft.allowExport !== falsewas always true when undefineddraft.allowExport === true || draft.exportOptions != null(Issue #719)- No per-view-type integration tests: Pending — tests verify config reaches
fullSchema, but per-renderer integration tests still needed → Removedkey={refreshKey}on PluginObjectView: Console wrapped PluginObjectView withkey={refreshKey}, which only changed on save/create, preventing live preview of config changeskey={refreshKey}; props changes now flow naturally without remounting (Issue #784)Navigation overlay not consuming→ Navigation now uses priority:activeView.navigation: Detail overlay only readobjectDef.navigation, ignoring view-level navigation configactiveView.navigation > objectDef.navigation > default drawer(Issue #784)
Phase 1 — Grid/Table View (baseline, already complete):
-
gridSchemaincludesstriped/borderedfromactiveView -
showSort/showSearch/showFilterspassed viaObjectViewSchema -
useMemodependency arrays cover all grid config - ListView toolbar buttons conditionally rendered based on
schema.showSearch/showFilters/showSort(PR #771) -
renderListViewschema 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
exportOptionsandallowExport(Issue #719) -
hasExportlogic fixed — no longer always true whenallowExportis undefined (Issue #719) - ViewConfigPanel includes toggles for
showHideFields/showGroup/showColor/showDensity(Issue #719) -
showHideFields/showGroup/showColor/showDensity/allowExportpropagated through ConsolefullSchemaand PluginObjectViewrenderListView(Issue #719) - Full end-to-end data flow: all ViewConfigPanel props (
inlineEdit/wrapHeaders/clickIntoRecordDetails/addRecordViaForm/addDeleteRecordsInline/collapseAllByDefault/fieldTextColor/prefixField/showDescription) propagated through ConsolefullSchema→ PluginObjectViewrenderListView→ ListView (Issue #719) - ListView forwards
striped/bordered/wrapHeadersto childviewComponentSchema(grid getswrapHeaders, all views getstriped/bordered) (Issue #719) - ViewConfigPanel includes
striped/borderedtoggles 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/showFiltersthroughgenerateViewSchemakanban branch - Pass
color/striped/borderedinrenderContent→renderListViewfor kanban - Ensure
groupByconfig changes reflect immediately (currently ✅ viarenderListView) - Add integration test: ViewConfigPanel kanban config change → Kanban renderer receives updated props
Phase 3 — Calendar Live Preview:
- Propagate
showSort/showSearch/showFiltersthroughgenerateViewSchemacalendar branch - Pass
filter/sort/appearance properties to calendar renderer in real-time - Verify
startDateField/endDateFieldconfig changes trigger re-render viauseMemodeps - Add integration test: ViewConfigPanel calendar config change → Calendar renderer receives updated props
Phase 4 — Timeline/Gantt Live Preview:
- Propagate
showSort/showSearch/showFiltersthroughgenerateViewSchematimeline/gantt branches - Pass appearance properties (
color,striped,bordered) throughrenderListViewschema - Ensure
dateField/startDateField/endDateFieldconfig changes trigger re-render - Add integration tests for timeline and gantt config sync
Phase 5 — Gallery & Map Live Preview:
- Propagate
showSort/showSearch/showFiltersthroughgenerateViewSchemagallery/map branches - Pass appearance properties through
renderListViewschema for gallery/map - Ensure gallery
imageField/titleFieldand maplocationField/zoom/centerconfig 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/colortoNamedListViewtype in@object-ui/types - Add
showHideFields/showGroup/showColor/showDensity/allowExporttoNamedListViewandListViewSchematypes and Zod schema (Issue #719) - Add
inlineEdit/wrapHeaders/clickIntoRecordDetails/addRecordViaForm/addDeleteRecordsInline/collapseAllByDefault/fieldTextColor/prefixField/showDescriptiontoNamedListViewandListViewSchematypes and Zod schema (Issue #719) - Update Console
renderListViewto pass all config properties infullSchema - Audit all
useMemo/useEffectdependency arrays inplugin-view/ObjectView.tsxfor missingactiveViewsub-properties — all hooks correctly use wholeactiveViewobject reference; React shallow equality handles sub-property changes - Remove hardcoded
showSearch: falsefromgenerateViewSchema— useactiveView.showSearch ?? schema.showSearchinstead
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
Airtable-style right-side configuration panel for dashboards. Phased rollout from shared infrastructure to full type-safe editing.
Phase 0 — Component Infrastructure:
- Extract
ConfigRow/SectionHeaderfromViewConfigPanelinto@object-ui/componentsas reusable primitives - Implement
useConfigDraftgeneric hook for draft state management (dirty tracking, save/discard) - Define
ConfigPanelSchema/ConfigSection/ConfigFieldtypes for schema-driven panel generation - Implement
ConfigFieldRenderersupporting 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
configPaneli18n keys to all 10 locale files
Phase 1 — Dashboard-Level Config Panel:
- Develop
DashboardConfigPanelsupporting layout (columns/gap/rowHeight), data (refreshInterval), appearance (title/description/theme) - Add Storybook stories for
ConfigPanelRendererandDashboardConfigPanel - 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
WidgetConfigPanelwith 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
FilterBuilderfor dashboard global filters (ConfigFieldRendererfiltertype now renders inline FilterBuilder) - Integrate
SortBuilderfor sort configuration (ConfigFieldRenderersorttype now renders inline SortBuilder) - Add
fieldsprop toConfigFieldtype for filter/sort field definitions - Dropdown filter selector and action button sub-panel visual editing
Phase 4 — Composition & Storybook:
- Build
DashboardWithConfigcomposite component (dashboard + config sidebar) - Support widget selection → WidgetConfigPanel switch with back navigation
- Add Storybook stories for
WidgetConfigPanel,DashboardWithConfig, andDashboardWithConfigClosed - Add Vitest tests (9 tests for DashboardWithConfig)
Phase 5 — Type Definitions & Validation:
- Add
DashboardConfigtypes to@object-ui/types - Add Zod schema validation for
DashboardConfig
Phase 6 — Design Mode Preview Click-to-Select:
- Add
designMode,selectedWidgetId,onWidgetClickprops toDashboardRendererfor 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
selectedWidgetIdandonWidgetSelectprops toDashboardEditorfor external controlled selection - Sync selection between
DashboardRenderer(preview) andDashboardEditor(drawer) via shared state inDashboardView - 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— addedhideOverlayprop to conditionally skip the full-screen backdrop overlay -
DesignDrawer—modal={false}+hideOverlayso preview widgets are clickable while drawer is open -
DashboardEditor— property panel renders above widget grid (stackedflex-collayout) for immediate visibility in narrow drawer -
DashboardEditor— property panel uses full width (removed fixedw-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+DashboardEditorinDashboardViewwith inlineDashboardConfigPanel/WidgetConfigPanel - Right-side panel shows
DashboardConfigPanelwhen no widget selected (dashboard-level properties: columns, gap, refresh, theme) - Right-side panel switches to
WidgetConfigPanelwhen a widget is selected (title, type, data binding, layout, appearance) - Config panels use standard
ConfigPanelRendererwith 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
DashboardRendererwithdesignModeclick-to-select - Widget config flattening/unflattening (layout.w ↔ layoutW, layout.h ↔ layoutH)
- Auto-save on config save via
useAdapter().update() - Live preview updates via
onFieldChangecallback - Config draft stabilization via
configVersioncounter (matching ViewConfigPanel'sstableActiveViewpattern) — preventsuseConfigDraftdraft reset on live field changes - Widget delete via
headerExtradelete button in WidgetConfigPanel header -
WidgetConfigPanel— addedheaderExtraprop 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-noneonSchemaRenderercontent 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 overridepointer-events - Self-contained (metric) widgets: both
pointer-events-noneon SchemaRenderer + overlay insiderelativewrapper - Card-based (chart/table) widgets:
pointer-events-noneonCardContentinner wrapper + overlay insiderelativeCard - No impact on non-design mode — widgets remain fully interactive when not editing
- Updated SchemaRenderer mock to forward
classNameand 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:
DashboardWithConfigdid not passonFieldChangetoWidgetConfigPanel, preventing live preview of widget config changes - Add internal
liveSchemastate toDashboardWithConfigfor real-time widget preview during editing - Add
configVersioncounter to stabilizeselectedWidgetConfigand preventuseConfigDraftdraft reset loops - Fix:
scatterchart type was not handled inDashboardRendererandDashboardGridLayout— switching to scatter caused errors - Add
scatterto chart type conditions in bothDashboardRenderer.getComponentSchema()andDashboardGridLayout.getComponentSchema() - Fix: Data binding fields (
categoryField,valueField,object,aggregate) from config panel did not affect rendering —getComponentSchema()only read fromoptions.xField/options.yField/options.data - Add widget-level field fallbacks:
widget.categoryField || options.xField,widget.valueField || options.yFieldin bothDashboardRendererandDashboardGridLayout - 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.objectwhen 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:
saveSchemainDashboardViewdid not callmetadata.refresh()after PATCH — closing config panel showed stale data from cached metadata - Fix:
previewSchemaonly usededitSchemawhenconfigPanelOpen=true— changed toeditSchema || dashboardso edits remain visible after panel close until metadata refreshes - Add
useEffectto clear staleeditSchemawhen metadata refreshes while config panel is closed (seamless transition) - Clear
editSchemaand config panel state on dashboard navigation (dashboardNamechange) - Fix:
DashboardDesignPage.saveSchemadid not callmetadata.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()inDashboardRendererandDashboardGridLayoutnow merges widget-level fields with data provider aggregate config, with widget-level fields taking precedence - Fix:
objectNamefor table/pivot widgets usedwidgetData.object || widget.object— reversed towidget.object || widgetData.objectso config panel edits to data source are reflected immediately - Fix:
DashboardWithConfigdid not passdesignMode,selectedWidgetId, oronWidgetClicktoDashboardRenderer— 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)
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,toSpecFilterto sharedview-config-utils.ts - Extract
parseCommaSeparated,parseNumberList,VIEW_TYPE_LABELS,ROW_HEIGHT_OPTIONSto shared utils - Add
deriveFieldOptions,toFilterGroup,toSortItemsbridge helpers - Enhance
ConfigPanelRendererwith accessibility props (panelRef,role,ariaLabel,tabIndex) - Enhance
ConfigPanelRendererwith 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
-
ExpandableWidgetcomponent 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
useMemokeyed toactiveView.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
descriptionfield toNamedListViewprotocol interface (spec alignment) - Add
disabledWhenpredicate toConfigFieldtype — grid-only fields (striped/bordered/wrapHeaders/resizable) disabled for non-grid views - Add
expandedSectionsprop toConfigPanelRendererfor external section collapse control (auto-focus/highlight) - Add
helpTextto 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
densityModefield from appearance section (redundant withrowHeightwhich provides finer 5-value granularity) - Remove
prefixFieldfrom data section (not consumed by any runtime renderer) - Remove
collapseAllByDefaultfrom appearance section (not consumed by any runtime renderer) - Remove
fieldTextColorfrom appearance section (not consumed by any runtime renderer) - Remove
clickIntoRecordDetailsfrom userActions section (controlled implicitly via navigation mode, not directly consumed) - Add view-type-aware
visibleWhento toolbar toggles:showGroup(grid/kanban/gallery),showColor(grid/calendar/timeline/gantt),showDensity(grid only),showRecordCount(grid only),allowPrinting(grid only) - Add view-type-aware
visibleWhento data fields:_groupBy(grid/gallery — kanban uses dedicated type-specific option),virtualScroll(grid only) - Add view-type-aware
visibleWhento 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
visibleWhento userActions fields:inlineEdit/addDeleteRecordsInline(grid only),rowActions/bulkActions(grid/kanban) - Correct
searchableFields/filterableFields/quickFilters/showDescriptionto universal (all view types) — data fetch/toolbar features not view-specific - Extend
buildSwitchFieldandbuildFieldMultiSelecthelpers to acceptvisibleWhenparameter - 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
pageConfigsection 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
onConfigViewprop 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
- Add
min-w-0/overflow-hiddento 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-0on 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-0to prevent overflow - Fix ListView container
min-w-0 overflow-hiddento 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
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/EditorModetypes -
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
useUndoRedohook (Ctrl+Z / Ctrl+Y keyboard shortcuts) - JSON Schema export/import (Download/Upload toolbar buttons with
onExport/onImportcallbacks) - 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
useUndoRedohook (Ctrl+Z / Ctrl+Y keyboard shortcuts) - JSON Schema export/import (Download/Upload toolbar buttons with
onExport/onImportcallbacks) - 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:
-
appDesignersection with 133 keys added to all 10 locales (en, zh, ja, de, fr, es, ar, ru, pt, ko) -
useDesignerTranslationsafe 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
-
onSaveDraftcallback for partial progress save -
useConfirmDialoghook 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
useUndoRedohook (Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y keyboard shortcuts) - JSON Schema export/import (Download/Upload toolbar buttons with
onExport/onImportcallbacks) - 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
BrandingConfigtype (AppSchema.branding protocol) - 29 unit tests (rendering, editing, light/dark preview, read-only, undo/redo, export/import, keyboard shortcuts, preview content)
Console Integration:
-
CreateAppPage— rendersAppCreationWizardwithuseMetadata()objects,onComplete/onCancel/onSaveDraftcallbacks -
EditAppPage— reuses wizard withinitialDraftfrom 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 — includesicon,label,brandingfields -
EditAppPagemerges 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
-
createAppi18n key added to all 10 locales - 13 console integration tests (routes, wizard callbacks, draft persistence, saveItem, CommandPalette)
-
PageDesignPage— integratesPageCanvasEditorat/design/page/:pageNameroute with auto-save, JSON export/import -
DashboardDesignPage— integratesDashboardEditorat/design/dashboard/:dashboardNameroute with auto-save, JSON export/import - "Edit" button on
PageViewandDashboardView— opens right-sideDesignDrawerwith real-time preview (no page navigation) -
DesignDrawercomponent — 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
PageCanvasEditorandDashboardEditor(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)
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_permissionobject - Search/filter permissions
- Admin-only create/delete controls
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-approute accessible without any app context - Sidebar fallback navigation with system menu items when no apps are configured
- System pages (
SystemHubPage,AppManagementPage) handle missingappNamegracefully - Login/Register/Forgot password pages remain always accessible regardless of app state
Routes:
-
/system/→ SystemHubPage -
/system/apps→ AppManagementPage -
/system/permissions→ PermissionManagementPage
Tests:
- 11 new tests (SystemHubPage, AppManagementPage, PermissionManagementPage)
- Total: 20 system page tests passing
Status: Complete —
FlowDesignercomponent 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
onSavecallback - Zoom in/out/reset controls
- Edge creation by clicking source → target connection ports
- Node deletion via delete button or
Deletekey
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
conditionexpression field -
isDefaultflag 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,
isDefaulttoggle
Spec v3.0.9 Protocol Features:
-
FlowVersionEntry[]version history panel with current/previous entries -
FlowConcurrencyPolicybadge display (allow/forbid/replace/queue) -
FlowExecutionStep[]execution monitoring overlay with per-node status icons -
FlowBpmnInteropResultBPMN XML export with warning/error reporting
ComponentRegistry:
- Registered as
'flow-designer'with all inputs documented
- 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)
- 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
- 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
- Wire
CollaborationProviderinto 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
-
@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: Fixobject-chartcrash when data is non-array — addedArray.isArray()guards in ObjectChart, ChartRenderer, and AdvancedChartImpl to prevent Rechartsr.slice is not a functionerror -
@object-ui/plugin-workflow: FlowDesigner — canvas-based flow editor (flow-designercomponent) 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 —FlowBpmnInteropResultwithbpmnXmlexport,nodeCount,edgeCount,warnings -
@object-ui/plugin-workflow: Support v3.0.9 node executor descriptors —FlowNodeExecutorDescriptorwithinputSchema,outputSchema, wait event config, retry policy -
@object-ui/plugin-workflow: Support v3.0.9inputSchema/outputSchemaon flow nodes viaFlowNodeExecutorDescriptor -
@object-ui/plugin-workflow: Support v3.0.9boundaryConfigfor boundary event nodes —FlowBoundaryConfigwithattachedToNodeId,eventType,cancelActivity,timer,errorCode -
@object-ui/plugin-workflow: Support v3.0.9 node types —parallel_gateway,join_gateway,boundary_eventinFlowNodeType -
@object-ui/plugin-workflow: Support v3.0.9 conditional edges —FlowEdge.type = 'conditional'+isDefaultflag -
@object-ui/plugin-workflow: Support v3.0.9FlowVersionHistory—FlowVersionEntry[]with version number, author, changeNote, isCurrent -
@object-ui/plugin-workflow: Support v3.0.9ConcurrencyPolicy—FlowConcurrencyPolicy(allow/forbid/replace/queue) -
@object-ui/plugin-workflow: Support v3.0.9WaitEventType—FlowWaitEventType(condition/manual/webhook/timer/signal) -
@object-ui/plugin-workflow: Support v3.0.9 execution tracking —FlowExecutionStepwith status overlay on nodes -
@object-ui/plugin-ai: Configurable AI endpoint adapter (OpenAI, Anthropic) - Navigation
widthproperty: apply to drawer/modal overlays across all plugins - Navigation
viewproperty: specify target form/view on record click across all plugins - Support App
enginefield ({ objectstack: string }) for version pinning (v3.0.9) - Integrate v3.0.9 package upgrade protocol (
PackageArtifact,ArtifactChecksum,UpgradeContext) -
@object-ui/types: AlignViewTypewith spec ListView type enum — add'gallery'and'gantt'(was missing fromviews.tsandviews.zod.ts) -
@object-ui/types: SyncDetailViewFieldSchemaZod 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: AddshowBorderandheaderColortoDetailViewSectionSchemaZod validator (already in TS interface) -
@object-ui/plugin-view: Addgalleryandganttto ViewSwitcher default labels and icons -
@object-ui/plugin-list: Fixlist-viewandlistregistrationviewTypeenum — remove invalid'list'/'chart', add'gallery'/'timeline'/'gantt'/'map'to match spec -
@object-ui/plugin-detail: Add missingdetail-viewregistration inputs (layout,columns,loading,backUrl,editUrl,deleteConfirmation,header,footer) -
@object-ui/plugin-detail: AddshowBorderandheaderColorinputs todetail-sectionregistration
All items from the ListView Spec Protocol analysis have been implemented.
P0 — Core Protocol:
-
data(ViewDataSchema): ListView consumesschema.data— supportsprovider: value(inline items),provider: object(fetch from objectName), and plain array shorthand. Falls back todataSource.find()when not set. -
groupingrendering: Group button enabled with GroupBy field picker popover. Grouping config wired to ObjectGrid child view, which renders collapsible grouped sections viauseGroupedDatahook. -
rowColorrendering: Color button enabled with color-field picker popover. Row color config wired to ObjectGrid child view, which applies row background colors viauseRowColorhook.
P1 — Structural Alignment:
-
quickFiltersstructure 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. StandalonenormalizeQuickFilter()/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. -
conditionalFormattingexpression 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 withz.union()for both formats.evaluatePlainCondition()convenience function in@object-ui/corefor 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 alongsidedata.namespace. Mixed-format arrays handled transparently. -
exportOptionsschema reconciliation: Accepts both specstring[]format (e.g.,['csv', 'xlsx']) and ObjectUI object format{ formats, maxRecords, includeHeaders, fileNamePrefix }. - Column
pinned:pinnedproperty added to ListViewSchema column type. Bridge passes through to ObjectGrid which supportsfrozenColumns. ObjectGrid reorders columns (left-pinned first, right-pinned last with sticky CSS). Zod schema updated withpinnedfield.useColumnSummaryhook created. - Column
summary:summaryproperty added to ListViewSchema column type. Bridge passes through for aggregation rendering. ObjectGrid renders summary footer with count/sum/avg/min/max aggregations viauseColumnSummaryhook. Zod schema updated withsummaryfield. - Column
link: ObjectGrid renders click-to-navigate buttons on link-type columns withnavigation.handleClick. Primary field auto-linked. - Column
action: ObjectGrid renders action dispatch buttons viaexecuteActionon 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 reusableTabBarcomponent inpackages/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.rowActionsstring array items rendered as dropdown menu items, dispatched viaexecuteAction. -
bulkActions: Bulk action bar rendered in ListView when rows are selected andschema.bulkActionsis configured. FiresonBulkActioncallback with action name and selected rows. -
sharingschema reconciliation: Supports both ObjectUI{ visibility, enabled }and spec{ type: personal/collaborative, lockedBy }models. Share button renders when eitherenabled: trueortypeis set. Zod validator updated withtypeandlockedByfields. Bridge normalizes spec format:type: personal→visibility: private,type: collaborative→visibility: team, auto-setsenabled: true. -
exportOptionsschema reconciliation: Zod validator updated to accept both specstring[]format and ObjectUI object format viaz.union(). ListView normalizes string[] to{ formats }at render time. -
pagination.pageSizeOptionsbackend integration: Page size selector is now a controlled component that dynamically updateseffectivePageSize, triggering data re-fetch.onPageSizeChangecallback fires on selection. Full test coverage for selector rendering, option enumeration, and data reload. -
$expandauto-injection:buildExpandFields()utility in@object-ui/corescans schema fields forlookup/master_detailtypes 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$expandis not supported. Cross-repo: objectql engine expand support required for multi-level nesting.
All items from the UI consistency optimization (Issue #749) have been implemented.
Global Theme & Design Tokens:
- Hardcoded gray colors in
GridField.tsx,ReportRenderer.tsx, andObjectGrid.tsxreplaced with theme tokens (text-muted-foreground,bg-muted,border-border,border-foreground) - Global font-family (
Inter, ui-sans-serif, system-ui) injected inindex.css:root -
--config-panel-widthCSS custom property added for unified config panel sizing (updated to320pxin P2.8) - Border radius standardized to
rounded-lgacross report/grid components -
transition-colors duration-150added to all interactive elements (toolbar buttons, tab bar, sidebar menu buttons) -
LayoutRenderer.tsxouter shellbg-slate-50/50 dark:bg-zinc-950replaced withbg-backgroundtheme token
Sidebar Navigation:
-
SidebarMenuButtonactive state enhanced with 3px left indicator bar viabefore:pseudo-element -
SidebarMenuButtontransition expanded to includecolor, background-colorwithduration-150 -
SidebarGroupLabelvisual separator added (border-t border-border/30 pt-3 mt-2) - Collapsed-mode tooltip support in
SidebarNavviatooltip={item.title}prop -
LayoutRenderer.tsxhand-written sidebar →SidebarNavunification (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-autofor responsive behavior -
transition-colors duration-150added 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 fromfield.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 —
DateCellRendererdefaults 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 —
BooleanCellRendererrenders<Checkbox disabled>for true/false; null/undefined values display as—
ConfigPanelRenderer:
-
<Separator>added between sections for visual clarity - Panel width uses
--config-panel-widthCSS 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-150added to tab buttons
Platform-level grid, toolbar, sidebar, and config panel optimizations for Airtable-level experience (Issue #768).
Platform: DataTable & ObjectGrid Enhancements:
-
rowStylecallback prop added toDataTableSchematype — enables inline CSSProperties per row -
<TableRow>in data-table.tsx appliesrowStylecallback for runtime row styling - ObjectGrid:
conditionalFormattingrules wired torowStyle— evaluates both spec-format (condition/style) and ObjectUI-format (field/operator/value) rules per row usingevaluatePlainConditionfrom@object-ui/core - Row number (#) column: hover shows
<Checkbox>for multi-select (whenselectablemode 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
maxVisibleprop added — overflow badges collapse into "More" dropdown with Popover - Toolbar layout uses flex with
min-w-0overflow 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, andaccessibilitysections set todefaultCollapsed: true— common settings remain expanded, advanced settings folded by default
Platform: Config Panel Width:
-
--config-panel-widthCSS variable increased from280pxto320pxfor wider config panel
CRM Example: Product Grid Column Configs:
- All columns upgraded from
string[]toListColumn[]with explicitfield,label,width,type,align -
IS ACTIVE:type: 'boolean'for<Checkbox disabled>rendering - Price:
type: 'currency',align: 'right'forformatCurrencyformatting - 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
Platform-level sidebar, navigation, and grid/table UX improvements (Issue #XX).
Sidebar Navigation:
- Pin icons show-on-hover:
SidebarMenuActioninNavigationRenderernow usesshowOnHover={!item.pinned}— unpinned items show pin icon only on hover; pinned items always show unpin icon. Applied to bothactionand leaf navigation item types. - Search placeholder contrast: Search icon in AppSidebar improved from
opacity-50→opacity-70for 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:
SidebarRailenhanced with pointer-event drag-to-resize (min 200px, max 480px). Width persisted tolocalStorage. Click toggles sidebar, double-click resets to default.useSidebar()hook now exposessidebarWidthandsetSidebarWidth.
Grid/Table Field Inference:
- Percent field auto-inference:
inferColumnType()in ObjectGrid now detects fields with names containingprobability,percent,percentage,completion,progress,rateand assignsPercentCellRendererwith 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) andDateTimeCellRenderer(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 appliesinferColumnType()+getCellRenderer()toaccessorKey-format columns that don't already have acellrenderer. Previously,accessorKeycolumns 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/fieldsconverts snake_case/kebab-case values to Title Case (e.g.,in_progress→In Progress). Used as fallback inSelectCellRendererwhen no explicitoption.labelexists. - PercentCellRenderer progress-field normalization:
PercentCellRenderernow 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.,75→75%), while other fields likeprobabilitystill treat0.75→75%.
Header & Breadcrumb i18n:
- AppHeader breadcrumb labels (
Dashboards,Pages,Reports,System) now uset()translation viauseObjectTranslation. -
console.breadcrumbi18n keys added to all 11 locales (en, zh, ja, ko, de, fr, es, pt, ru, ar). -
header-barrenderer:resolveCrumbLabel()handles both string andI18nLabelobjects for breadcrumb labels. -
breadcrumbrenderer:resolveItemLabel()handles both string andI18nLabelobjects 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
- Background sync queue → real server sync (replace simulation)
- Conflict resolution on reconnection wired into Console flow
- Optimistic updates with TransactionManager state application
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 Junction —
order_itemsobject with line items (quantity, price, discount, item_type) + 12 seed records - P1: Opportunity ↔ Contact Junction —
opportunity_contactsobject with role-based relationships + 7 seed records - P1: Contact ↔ Event Attendees —
participantsfield populated on all event seed data - P2: Dashboard Dynamic Data — "Revenue by Account" widget using
provider: 'object'aggregation. DashboardRenderer now delegatesprovider: '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 handlesresults.dataandresults.valueresponse formats in addition toresults.records, (2) DashboardRenderer auto-adapts seriesdataKeyfromaggregate.fieldwhen aggregate config is present, (3) CRM dashboardyFieldaligned to aggregate fieldamount(wastotal). CentralizedextractRecords()utility in@object-ui/coreand unified data extraction across all 6 data components (ObjectChart, ObjectMap, ObjectCalendar, ObjectGantt, ObjectTimeline, ObjectKanban). Added 16 new tests. - P2: App Branding —
logo,favicon,backgroundColorfields on CRM app - P3: Pages — Settings page (utility) and Getting Started page (onboarding)
- P2: Spec Compliance Audit — Fixed
variant: 'danger'→'destructive'(4 actions),columns: string→number(33 form sections), addedtype: 'dashboard'to dashboard - P2: Dashboard Widget Spec Alignment — Added
id,title,object,categoryField,valueField,aggregateto 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'+descriptionto todo/kitchen-sink dashboards, refactored msw-todo to useObjectSchema.create+Field.*with snake_case field names, added explicit views to kitchen-sink and msw-todo, added missingsuccessMessageon 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 dynamicprovider: '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 tableclose_datefield 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
...optionsspread was leaking provider config objects asdatain data-table and pivot schemas — fixed by destructuringdataout before spread, (2) DataTableRenderer and PivotTable now guard withArray.isArray()for graceful degradation when non-array data arrives, (3) ObjectChart now shows visible loading/warning messages instead of silently rendering blank whendataSourceis 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 contextdataSource(service adapter) instead ofundefinedwhen 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. FixeduseDataScopeto returnundefinedwhen no path is provided. Also improved ObjectChart fault tolerance: usesuseContextdirectly instead ofuseSchemaContext(no throw without provider), validatesdataSource.findis 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
?__debugURL parameter (amis devtools-style).@object-ui/core: exportedDebugFlags,DebugCollector(perf/expr/event data collection, tree-shakeable),parseDebugFlags(), enhancedisDebugEnabled()(URL → globalThis → env resolution, SSR-safe).@object-ui/react:useDebugModehook with URL detection, Ctrl+Shift+D shortcut, manual toggle;SchemaRendererContextextended withdebugFlags;SchemaRendererinjectsdata-debug-type/data-debug-idattrs + reports render perf toDebugCollectorwhen debug enabled.@object-ui/components: floatingDebugPanelwith 7 built-in tabs (Schema, Data, Perf, Expr, Events, Registry, Flags), plugin-extensible viaextraTabs. ConsoleMetadataInspectorauto-opens when?__debugdetected. 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 toDataSourceinterface (AggregateParams,AggregateResulttypes) enabling server-side grouping/aggregation via analytics API (e.g.GET /api/v1/analytics/{resource}?category=…&metric=…&agg=…).ObjectChartnow prefersdataSource.aggregate()when available, falling back todataSource.find()+ client-side aggregation for backward compatibility. Implementedaggregate()inValueDataSource(in-memory),ApiDataSource(HTTP), andObjectStackAdapter(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
I18nLabelobjects. Fix: (1) Updated CRM app/dashboard/navigation metadata to useI18nLabelobjects ({ key, defaultValue }) per spec. (2) UpdatedNavigationItemandNavigationAreatypes to support I18nLabel. (3) AddedresolveLabel()helper in NavigationRenderer. (4) UpdatedresolveI18nLabel()to acceptt()function for translation. (5) AddedloadLanguagecallback in I18nProvider for API-based translation loading. (6) Added/api/v1/i18n/:langendpoint to mock server. Console contains zero CRM-specific code.
- Plugin marketplace website with search, ratings, and install count
- Plugin publishing CLI (
os ui publish) with automated validation - 25+ official plugins
- Official website (www.objectui.org) with interactive playground
- Discord community, monthly webinars, technical blog, YouTube tutorials
- Project hosting, online editor, Database as a Service
- One-click deployment, performance monitoring
- Billing system (Free / Pro / Enterprise)
- CRM, ERP, HRM, E-commerce, Project Management accelerators
- AI-powered schema generation from natural language
| 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 |
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.
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.
Root Cause: While drawer/modal modes worked in the Console ObjectView, the remaining 4 navigation modes had gaps:
- Console's
onNavigatecallback relied on implicit fallthrough forviewaction (page mode) — not explicit. PluginObjectView'sformLayoutonly mappeddrawer/modalmodes;split/popoverfell through to the default layout (drawer), rendering the wrong overlay type.PluginObjectViewlackedNavigationOverlayintegration forsplit(resizable side-by-side panels) andpopover(compact dialog preview).
Fix:
- Console
onNavigatenow explicitly checks foraction === 'view'(page mode) alongside the existing'new_window'check. PluginObjectViewformLayoutnow includessplitandpopoverbranches.PluginObjectViewimports and rendersNavigationOverlayfrom@object-ui/componentsfor bothsplitmode (withmainContentwrapping the grid) andpopovermode (Dialog fallback when nopopoverTrigger).- 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.
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.
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.
Root Cause: Multiple renderer defects caused incorrect field value display across views:
-
LookupCellRenderer— Destructured onlyvalue, ignoring thefieldprop. When the API returned a raw primitive ID (e.g.customer: 2), the renderer fell through toString(value)and showed"2"instead of the related record's name. No attempt was made to resolve IDs viafield.options. -
UserCellRenderer— Did not guard against primitive values (number/string user IDs). Accessing.name/.usernameon a number returnedundefined, silently falling through to"User"as the generic label. -
getCellRendererstandardMap —lookupandmaster_detailwere mapped toSelectCellRendererinstead ofLookupCellRendererin the fallback map. Although the fieldRegistry pre-registration shadowed this bug, it was semantically incorrect. -
status,user,ownertypes — Not pre-registered infieldRegistry. All went through thestandardMappath, making their association with renderers implicit and invisible.
Fix:
LookupCellRenderer: now accepts thefieldprop and resolves primitive IDs againstfield.options(matching byString(opt.value) === String(val)for type-safe comparison). Arrays of primitive IDs are resolved via the same logic. Null/empty-string guard updated from!valuetovalue == null || value === ''to handle0correctly.UserCellRenderer: primitive values (typeof !== 'object') return a plain<span>with the string representation. Array items that are not objects are also handled gracefully.getCellRendererstandardMap:lookupandmaster_detailnow correctly referenceLookupCellRenderer.fieldRegistrynow explicitly registersstatus→SelectCellRenderer,user→UserCellRenderer, andowner→UserCellRendereralongside the existinglookup/master_detail/selectregistrations.
Tests: Added 36 new tests in cell-renderers.test.tsx:
getCellRendererregistry assertions forlookup,master_detail,status,user,ownertypesTextCellRenderer: null, undefined, empty string, numeric zero (0 renders "0" not "-"), boolean falseLookupCellRenderer: 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 optionsUserCellRenderer: null, primitive number ID, primitive string ID, object with name, object with username, array of user objects
TextCellRenderer— Usedvalue || '-'which incorrectly rendered'-'for numeric0(falsy zero). Updated to(value != null && value !== '') ? String(value) : '-'for consistent null-only suppression.
All 313 @object-ui/fields tests pass.
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.
| 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 |
- CONTRIBUTING.md — Contribution guidelines
- QUICK_REFERENCE.md — Developer quick reference
- Plugin Development Guide
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