Skip to content

feat(plugin-designer): P1.1 Designer Interaction — ViewDesigner + DataModelDesigner#686

Merged
hotlong merged 3 commits intomainfrom
copilot/complete-roadmap-development-ff8c83fe-477e-491f-8d7a-0e070174fac8
Feb 21, 2026
Merged

feat(plugin-designer): P1.1 Designer Interaction — ViewDesigner + DataModelDesigner#686
hotlong merged 3 commits intomainfrom
copilot/complete-roadmap-development-ff8c83fe-477e-491f-8d7a-0e070174fac8

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 21, 2026

Implements the P1.1 Designer Interaction roadmap items for ViewDesigner and DataModelDesigner, completing 9/10 checklist items (dnd-kit column reorder deferred).

Shared Infrastructure

  • useDesignerHistory hook — command-pattern wrapper over useUndoRedo, wired into both designers

ViewDesigner

  • Ctrl+S / Cmd+S keyboard shortcut triggers onSave
  • DESIGNER_FIELD_TYPES constant (18 types) displayed as read-only selector in column properties panel
  • Column width validation — numeric values clamped to 50–1000px, accepts "auto", rejects non-numeric/non-auto input

DataModelDesigner

  • Inline entity label editing — double-click entity header to edit, Enter commits, Escape cancels, empty strings discarded
  • Field type selector dropdown — 22 data types replace the static <span> display; read-only mode falls back to text
// Entity label: double-click to edit inline
<span onDoubleClick={() => handleStartEntityLabelEdit(entity.id, entity.label)}>
  {entity.label}
</span>

// Field type: dropdown replaces hardcoded 'text'
<select value={field.type} onChange={(e) => handleFieldTypeChange(entityId, fieldIndex, e.target.value)}>
  {DATA_MODEL_FIELD_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
</select>

Tests

33 new tests across 3 files (63 total in plugin-designer): useDesignerHistory (7), DataModelDesigner (19), ViewDesigner new features (7).

ROADMAP.md

Updated P1.1 checklist — remaining item: column drag-to-reorder via @dnd-kit/core.

Original prompt

完成roadmap开发

🎯 P1 — UI-First: Airtable UX Parity

Priority #1. All items below directly affect end-user experience. Target: indistinguishable from Airtable for core CRUD workflows.
P1.1 Designer Interaction (ViewDesigner + DataModelDesigner)

Source: ROADMAP_DESIGNER Phase 2. These two designers are the core user workflow.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 21, 2026 6:36am
objectui-demo Ready Ready Preview, Comment Feb 21, 2026 6:36am
objectui-storybook Ready Ready Preview, Comment Feb 21, 2026 6:36am

Request Review

Copilot AI and others added 2 commits February 21, 2026 06:12
- Add useDesignerHistory hook (command pattern wrapper)
- ViewDesigner: Ctrl+S/Cmd+S save shortcut, field type selector, width validation (min/max)
- DataModelDesigner: inline entity label editing, field type selector dropdown
- Wire useDesignerHistory to both designers
- Add comprehensive tests (63 total, 33 new)
- Update ROADMAP.md with completed P1.1 items

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…lify width validation, improve test assertions

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Complete roadmap development for UI-First project feat(plugin-designer): P1.1 Designer Interaction — ViewDesigner + DataModelDesigner Feb 21, 2026
Copilot AI requested a review from hotlong February 21, 2026 06:18
@hotlong hotlong marked this pull request as ready for review February 21, 2026 06:21
Copilot AI review requested due to automatic review settings February 21, 2026 06:21
@hotlong hotlong merged commit 50649de into main Feb 21, 2026
3 of 5 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the P1.1 “Designer Interaction” roadmap items for ViewDesigner and DataModelDesigner by adding shared undo/redo wiring (useDesignerHistory), new UX interactions (keyboard save, inline label editing, type selectors), plus expanded test coverage and a roadmap update.

Changes:

  • Added useDesignerHistory hook as a designer-specific wrapper around useUndoRedo, and exported it from public entrypoints.
  • Enhanced ViewDesigner with Ctrl/Cmd+S save handling, a read-only field type display, and column width validation rules.
  • Enhanced DataModelDesigner with inline entity label editing and a field type dropdown, plus new/updated tests and ROADMAP.md checklist updates.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/plugin-designer/src/index.tsx Re-export useDesignerHistory from the package entrypoint.
packages/plugin-designer/src/hooks/useDesignerHistory.ts New hook wrapping useUndoRedo with designer-specific naming/types.
packages/plugin-designer/src/hooks/index.ts Adds barrel exports for useDesignerHistory + types.
packages/plugin-designer/src/tests/useDesignerHistory.test.ts New tests validating undo/redo wrapper behavior.
packages/plugin-designer/src/tests/ViewDesigner.test.tsx New tests for Ctrl+S, field type display, and width validation behavior.
packages/plugin-designer/src/tests/DataModelDesigner.test.tsx New test suite covering inline label editing, type dropdown, and undo/redo.
packages/plugin-designer/src/ViewDesigner.tsx Wires useDesignerHistory, adds Ctrl/Cmd+S, adds field type display, and updates width handling.
packages/plugin-designer/src/DataModelDesigner.tsx Wires useDesignerHistory, adds inline entity label editing and a field type dropdown.
ROADMAP.md Marks most P1.1 checklist items complete and updates the “Designer Interaction” status text.

Comment on lines +779 to 792
columns: columns.map((c, i) => (i === idx ? { ...c, width: val === 'auto' ? 'auto' : undefined } : c)),
});
return;
}
if (/^\d+$/.test(val)) {
const num = Number(val);
const clamped = Math.max(COLUMN_WIDTH_MIN, Math.min(COLUMN_WIDTH_MAX, num));
pushState({
columns: columns.map((c, i) => (i === idx ? { ...c, width: clamped } : c)),
});
return;
}
// Reject non-numeric, non-auto values
}}
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Column width is clamped on every keystroke. Because the input is controlled, typing a value that starts below the min (e.g., trying to enter "100" by typing "1") will immediately clamp to 50 and makes it impossible to enter many valid multi-digit widths. Consider keeping a local string state for the width input and only validating/clamping on blur or on save (and/or allow intermediate values < min while editing).

Suggested change
columns: columns.map((c, i) => (i === idx ? { ...c, width: val === 'auto' ? 'auto' : undefined } : c)),
});
return;
}
if (/^\d+$/.test(val)) {
const num = Number(val);
const clamped = Math.max(COLUMN_WIDTH_MIN, Math.min(COLUMN_WIDTH_MAX, num));
pushState({
columns: columns.map((c, i) => (i === idx ? { ...c, width: clamped } : c)),
});
return;
}
// Reject non-numeric, non-auto values
}}
columns: columns.map((c, i) =>
i === idx ? { ...c, width: val === 'auto' ? 'auto' : undefined } : c,
),
});
return;
}
if (/^\d+$/.test(val)) {
const num = Number(val);
// Allow intermediate numeric values without clamping on each keystroke.
pushState({
columns: columns.map((c, i) => (i === idx ? { ...c, width: num } : c)),
});
return;
}
// Reject non-numeric, non-auto values
}}
onBlur={() => {
if (readOnly) return;
const idx = selectedColumnIndex;
const current = columns[idx]?.width;
if (typeof current === 'number') {
const clamped = Math.max(COLUMN_WIDTH_MIN, Math.min(COLUMN_WIDTH_MAX, current));
if (clamped !== current) {
pushState({
columns: columns.map((c, i) => (i === idx ? { ...c, width: clamped } : c)),
});
}
}
}}

Copilot uses AI. Check for mistakes.
Comment on lines +404 to +408
if (ctrl && e.key === 's' && !readOnly) {
e.preventDefault();
handleSave();
return;
}
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ctrl/Cmd+S handling checks e.key === 's' only. With Caps Lock or some layouts, KeyboardEvent.key can be uppercase ('S'), so the shortcut may not fire reliably. Consider normalizing via e.key.toLowerCase() === 's' (or checking code === 'KeyS') before triggering save.

Copilot uses AI. Check for mistakes.
}}
onClick={(e) => e.stopPropagation()}
className="text-xs text-muted-foreground ml-auto bg-transparent border-none focus:ring-1 focus:ring-primary rounded cursor-pointer p-0"
data-testid={`field-type-${entity.id}-${fieldIndex}`}
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field type has no associated label (it’s visually inline), so screen readers won’t have an accessible name for the control. Add an aria-label/aria-labelledby (e.g., include the field name) so the dropdown is discoverable and understandable via assistive tech. data-testid={`field-type-${entity.id}-${fieldIndex}`} aria-label={`Field type for ${field.name}`}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants