Skip to content

feat: Phase B (Element Library & Builder) + Phase C (Sharing, Embedding & Permissions)#699

Merged
hotlong merged 2 commits intomainfrom
copilot/implement-element-library-and-builder
Feb 16, 2026
Merged

feat: Phase B (Element Library & Builder) + Phase C (Sharing, Embedding & Permissions)#699
hotlong merged 2 commits intomainfrom
copilot/implement-element-library-and-builder

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 16, 2026

Implements the next two roadmap phases from the Airtable Interface gap analysis.

Phase B: Element Library & Builder (v3.3)

  • Interactive elementselement:button, element:filter, element:form, element:record_picker with prop schemas, registered in PageComponentType and ComponentPropsMap
  • BlankPageLayoutSchema — Grid-based free-form canvas positioning (columns, rowHeight, gap, positioned items[]), wired as optional blankLayout on PageSchema
  • Variable bindingPageVariableSchema gains record_id type and source field; ElementRecordPickerPropsSchema has targetVariable for bidirectional binding
  • Studio Interface BuilderInterfaceBuilderConfigSchema with snap/zoom/palette/panel settings (studio/interface-builder.zod.ts)

Phase C: Sharing, Embedding & Permissions (v4.0)

  • SharingConfigSchema — public link, password, domain allowlist, expiration, anonymous access (ui/sharing.zod.ts)
  • EmbedConfigSchema — iframe origin restrictions, responsive sizing, header/nav visibility
  • InterfaceSchema — optional sharing and embed fields (assignedRoles already existed)
  • FormViewSchema — optional sharing for public form sharing

Usage

// Blank page with record picker → variable binding
defineInterface({
  name: 'order_portal',
  label: 'Order Portal',
  sharing: { enabled: true, password: 'secret', expiresAt: '2027-01-01T00:00:00Z' },
  embed: { enabled: true, allowedOrigins: ['https://partner.com'] },
  pages: [{
    name: 'picker_page',
    label: 'Pick Account',
    type: 'blank',
    variables: [
      { name: 'selected_id', type: 'record_id', source: 'acct_picker' },
    ],
    regions: [{
      name: 'main',
      components: [
        { id: 'acct_picker', type: 'element:record_picker',
          properties: { object: 'account', displayField: 'name', targetVariable: 'selected_id' } },
        { id: 'submit', type: 'element:button',
          properties: { label: 'Continue', variant: 'primary' } },
      ],
    }],
    blankLayout: {
      columns: 12,
      items: [
        { componentId: 'acct_picker', x: 0, y: 0, width: 8, height: 2 },
        { componentId: 'submit', x: 0, y: 2, width: 4, height: 1 },
      ],
    },
  }],
});

57 new tests, all 5,541 passing. No CodeQL alerts.

Original prompt

按 roadmap 完成 Phase B: Element Library & Builder (v3.3)

Interactive elements — element:button, element:filter, element:form, element:record_picker
BlankPageLayoutSchema — Free-form canvas composition with grid-based positioning
Record picker variable binding — PageVariableSchema integration with element:record_picker
Studio Interface Builder — Drag-and-drop element placement UI
Phase C: Sharing, Embedding & Permissions (v4.0)

SharingConfigSchema — Public link, password, domain restriction, expiration (src/ui/sharing.zod.ts)
EmbedConfigSchema — iframe embedding with origin restrictions and display options
Per-interface role assignment — assignedRoles on InterfaceSchema
Public form sharing — sharing property on FormViewSchema


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 16, 2026

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

Project Deployment Actions Updated (UTC)
objectstack-play Ready Ready Preview, Comment Feb 16, 2026 2:37pm
spec Ready Ready Preview, Comment Feb 16, 2026 2:37pm

Request Review

…ing, Embedding & Permissions)

Phase B:
- Add interactive element schemas: element:button, element:filter, element:form, element:record_picker
- Add BlankPageLayoutSchema for free-form canvas with grid-based positioning
- Add record_id type and source binding to PageVariableSchema
- Add Studio Interface Builder config schema
- Register new elements in PageComponentType and ComponentPropsMap

Phase C:
- Add SharingConfigSchema (public link, password, domain restriction, expiration)
- Add EmbedConfigSchema (iframe embedding, origin restrictions, display options)
- Add sharing/embed to InterfaceSchema
- Add sharing to FormViewSchema for public form sharing
- Per-interface role assignment (assignedRoles already existed)

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add element library and studio interface builder features feat: Phase B (Element Library & Builder) + Phase C (Sharing, Embedding & Permissions) Feb 16, 2026
Copilot AI requested a review from hotlong February 16, 2026 11:10
@hotlong hotlong marked this pull request as ready for review February 16, 2026 17:14
Copilot AI review requested due to automatic review settings February 16, 2026 17:14
@hotlong hotlong merged commit 62f0bcf into main Feb 16, 2026
7 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

This pull request implements Phase B (Element Library & Builder) and Phase C (Sharing, Embedding & Permissions) of the Airtable Interface gap analysis roadmap.

Changes:

  • Phase B adds interactive elements (button, filter, form, record_picker), blank page free-form canvas layout with grid positioning, variable binding for record picker, and Studio Interface Builder configuration
  • Phase C introduces sharing and embedding schemas with public link, password protection, domain restrictions, expiration, and iframe embedding controls for interfaces and forms

Reviewed changes

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

Show a summary per file
File Description
packages/spec/src/ui/sharing.zod.ts New sharing & embedding protocol schemas (SharingConfigSchema, EmbedConfigSchema)
packages/spec/src/ui/sharing.test.ts Comprehensive test coverage for sharing configurations (114 lines, 2 test suites)
packages/spec/src/ui/view.zod.ts Added sharing field to FormViewSchema for public form sharing
packages/spec/src/ui/view.test.ts Tests for FormViewSchema sharing integration (44 lines)
packages/spec/src/ui/interface.zod.ts Added sharing and embed fields to InterfaceSchema
packages/spec/src/ui/interface.test.ts Extensive tests for interactive elements, blank layout, variables, and sharing (394 lines, 8 test suites)
packages/spec/src/ui/page.zod.ts Added interactive element types, PageVariableSchema enhancements (record_id type, source field), BlankPageLayoutSchema for free-form canvas
packages/spec/src/ui/component.zod.ts Added prop schemas for 4 interactive elements (button, filter, form, record_picker) and ComponentPropsMap registration
packages/spec/src/ui/index.ts Export sharing.zod module
packages/spec/src/studio/interface-builder.zod.ts New Studio Interface Builder configuration schemas (snap, zoom, palette, panels)
packages/spec/src/studio/interface-builder.test.ts Test coverage for Interface Builder schemas (118 lines, 4 test suites)
packages/spec/src/studio/index.ts Export Interface Builder schemas and types

password: z.string().optional().describe('Password required to access shared link'),
allowedDomains: z.array(z.string()).optional()
.describe('Restrict access to specific email domains (e.g. ["example.com"])'),
expiresAt: z.string().optional()
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The expiresAt field should use .datetime() validation to ensure proper ISO 8601 format validation. The codebase consistently uses z.string().datetime() for ISO 8601 datetime fields across all other schemas (e.g., identity.zod.ts, audit.zod.ts, conversation.zod.ts). This provides runtime validation that the string is a valid ISO 8601 datetime, preventing invalid values like '2027-13-45' from being accepted.

Change:
expiresAt: z.string().optional()

To:
expiresAt: z.string().datetime().optional()

Suggested change
expiresAt: z.string().optional()
expiresAt: z.string().datetime().optional()

Copilot uses AI. Check for mistakes.
*/
export const SharingConfigSchema = z.object({
enabled: z.boolean().default(false).describe('Enable public sharing'),
publicLink: z.string().optional().describe('Generated public share URL'),
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The publicLink field should use .url() validation to ensure it's a valid URL. The codebase consistently uses z.string().url() for URL fields across all other schemas (e.g., webhook.zod.ts line 69, connector/saas.zod.ts line 80, documentation.zod.ts line 51). This provides runtime validation that the string is a properly formatted URL, preventing invalid values like 'not-a-url' from being accepted.

Change:
publicLink: z.string().optional()

To:
publicLink: z.string().url().optional()

Suggested change
publicLink: z.string().optional().describe('Generated public share URL'),
publicLink: z.string().url().optional().describe('Generated public share URL'),

Copilot uses AI. Check for mistakes.
*/
export const EmbedConfigSchema = z.object({
enabled: z.boolean().default(false).describe('Enable iframe embedding'),
allowedOrigins: z.array(z.string()).optional()
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The allowedOrigins array should validate that each origin is a valid URL. For security-sensitive CORS/iframe configurations, the codebase pattern is to use simple string arrays (e.g., object-storage.zod.ts line 246, plugin-security-advanced.zod.ts line 552), but given that these are iframe parent origins used for security decisions, consider adding .url() validation to each array element to prevent configuration errors.

Consider changing:
allowedOrigins: z.array(z.string()).optional()

To:
allowedOrigins: z.array(z.string().url()).optional()

This ensures origins are properly formatted URLs (e.g., 'https://example.com') rather than accepting potentially invalid values.

Suggested change
allowedOrigins: z.array(z.string()).optional()
allowedOrigins: z.array(z.string().url()).optional()

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