Schema Editor (VS Code Extension)
The Kalp Schema Editor is a VS Code extension (kalp-schema-editor) that provides a graphical, Guidewire PCF-style screen designer for PolicyOne screens. Open it with Cmd+Shift+P → "Kalp: Open Schema Editor".
All screen definitions live in co-located _screen.meta.ts files next to each page component. The extension reads and writes these files directly. Changes are buffered in memory and only written to disk when you explicitly click Save — there is no auto-save.
Concepts
| Concept | Description |
|---|---|
| Screen | A single page (ScreenMeta). Has a layout (wizard, form, detail, list), route, and server linkage. |
| Step | One page of a multi-step wizard. Each step contains sections. |
| Section | A labeled group of fields within a step or form. Supports visibleFor conditional display. |
| Field | A single input. Defined by id, label, type, and optional metadata. |
| Repeater | A field of type repeater — renders as an inline table with configurable sub-columns (vehicles, drivers, locations, etc.). |
Interface Overview
Screen Sidebar (left panel)
Lists all _screen.meta.ts files found in apps/policyone/app. Screens are grouped by the first segment of their route (e.g. all /submissions/... screens appear under Submissions). Each entry shows the screen title and a layout badge (wizard, form, detail, list).
Click a screen to select it and load it into the main panel.
Header buttons:
- ↻ Refresh — re-reads all
_screen.meta.tsfiles from disk, discarding any unsaved in-memory edits - Open File — opens the selected screen's
_screen.meta.tsdirectly in the VS Code editor - Save Changes — writes the current in-memory state back to the
_screen.meta.tsfile on disk. A●dot appears next to "Save Changes" whenever there are unsaved edits.
Preview / Schema Toggle
The header has a two-button toggle:
| Mode | Description |
|---|---|
| Preview | Renders a pixel-accurate simulation of the screen UI. Fields are interactive. Inspector panel is on the right. |
| Schema | Tabbed table view — one tab per section, showing all fields as rows with inline-editable ID and Label cells. Good for bulk editing IDs and labels without clicking through each field. |
Preview Mode
Preview mode renders the screen using the same layout rules as the actual app. Click any element to inspect and edit it.
Layout Rendering
| Layout | What renders |
|---|---|
wizard | Sidebar stepper + main content area + nav buttons (Back / Continue / Submit). Transaction toolbar at the top. |
form | Single-page form with Save / Cancel buttons in the page header. |
detail | Detail view — same as form. |
list | Stats bar, filter bar, and data table — each driven by named sections (stats, filter, table, and any others). |
Working with Fields
- Select a field — click anywhere on the field wrapper. The inspector panel opens on the right and switches to the Field tab.
- Reorder fields — grab the ⠿ drag handle on the left of any field wrapper and drag it up or down within the same section. Drop position is indicated by a blue line.
- Delete a field — click the ✕ button that appears on the right of the field wrapper (visible on hover).
- Add a field — click the + Add Field button at the bottom of any section. A new
textfield is appended and immediately selected in the inspector so you can set its properties.
Working with Sections
- Select a section — click the section title bar. The inspector switches to the Section tab.
- Delete a section — click ✕ on the right of the section title bar.
- Add a section — click + Add Section at the bottom of the page content area. Sections are always added to the current step (wizard) or to the flat section list (form/detail).
Interactive Preview Widgets
radio-cards, pill-toggle, and select fields are fully interactive in preview mode — clicking them updates the live visibleFor simulation without a reload (see Live visibleFor Simulation).
Schema (Table) Mode
Schema mode shows all sections as tabs across the top. Each tab renders a table of that section's fields:
| Column | Description |
|---|---|
| ID | The field's id. Click to edit inline (contenteditable). |
| Label | The display label. Click to edit inline. |
| Type / Visibility | Type badge, plus visibleFor and permissions indicators if set. |
| DB Column | The dbColumn mapping, or — if not set. |
| Required | required badge, req:<context> if requiredFor is set, or —. |
| (actions) | Delete button — removes the field immediately (still requires Save to persist). |
Inline editing: click any ID or Label cell to edit it in place. Changes are committed when focus leaves the cell.
Add Field form at the bottom of each section: enter an ID, label, and type and click Add. The new field is appended to the section and the table re-renders.
Inspector Tabs
The inspector panel (right side, Preview mode only) has three tabs:
Field Tab
Click any field in the preview to open this tab.
| Property | Description |
|---|---|
id | camelCase key matching form state |
label | Display label |
type | Field type (see Field Types below) |
dbColumn | Reference mapping e.g. party.first_name |
required | Boolean — marks field as always required |
requiredFor | Context-specific required e.g. all, new_renewal |
visibleFor | Condition to show this field e.g. partyType:person |
permissions | Role gate — field is hidden from users without this role e.g. carrier_admin |
placeholder | Input placeholder text |
hint | Helper text shown below the field |
defaultValue | Pre-filled value |
dependsOn | Field ID whose change should reload this field's options (cascading selects) |
fetchesOn | Field ID whose change triggers an async data fetch |
columns | Sub-column definitions for repeater type fields |
options | Value/label pairs for select, radio-cards, pill-toggle |
All changes in the Field tab take effect instantly in the preview and are buffered in memory until saved.
Section Tab
Click a section title bar to open this tab.
| Property | Description |
|---|---|
id | Section identifier |
title | Section heading displayed above the fields |
description | Optional subtitle below the heading |
visibleFor | Condition to show/hide the entire section (see Live visibleFor Simulation) |
⚙ Page Tab
Always accessible — shows screen-level metadata. This tab is the default when no field or section is selected.
| Property | Description |
|---|---|
id | Unique screen ID e.g. submissions.new |
title | Page heading |
description | Optional subtitle |
route | Next.js route e.g. /submissions/new |
layout | wizard | form | detail | list |
componentFile | Path to the .tsx component relative to apps/policyone |
serverAction | Form submit handler e.g. lib/actions/submissions.ts → createSubmission() |
dataSource | Query that populates the page e.g. packages/db/src/queries/submissions.ts → fetchSubmissions() |
Changes on the Page tab update the screen sidebar title immediately.
Field Types
| Type | Renders as | Interactive in preview |
|---|---|---|
text | Single-line text input | No |
email | Email input | No |
tel | Phone number input | No |
date | Date picker | No |
number | Numeric input | No |
select | Dropdown | Yes — updates visibleFor state |
radio-cards | Card-style radio buttons | Yes — updates visibleFor state |
pill-toggle | Inline pill buttons | Yes — updates visibleFor state |
checkbox | Boolean checkbox | No |
textarea | Multi-line text area | No |
repeater | Inline table with sub-columns | No (skeleton rows shown) |
Live visibleFor Simulation
The preview evaluates visibleFor conditions in real time — no reload or save needed.
Syntax:
| Format | Example | Meaning |
|---|---|---|
fieldId:value | partyType:person | Show when the named field equals the given value |
| OR conditions (comma-separated) | lob:auto,lob:home | Show when any condition is met |
| Bare role string | carrier_admin | Always shown in preview (roles are not simulated) |
Both section-level and field-level visibleFor are evaluated. When a condition is not met the section or field is hidden instantly. A ◎ condition badge appears next to the section title or field label to indicate it is conditional.
When you click an interactive field (radio-cards, pill-toggle, select), the selection is recorded and all visibleFor conditions across the entire screen are re-evaluated immediately.
Note: Field values reset to defaults when you switch to a different screen.
Wizard Step Management
In wizard screens the left sidebar shows the step list and supports full CRUD:
| Action | How |
|---|---|
| Add step | Click + Add Step at the bottom of the step sidebar |
| Navigate to step | Click the step in the sidebar (or use Back / Continue buttons in the preview) |
| Rename step | Double-click the step label to edit it inline; press Enter to commit or Escape to cancel |
| Reorder steps | Hover a step to reveal ↑ / ↓ arrow buttons |
| Delete step | Hover a step and click ✕ — blocked if only one step remains |
The current step indicator in the transaction toolbar (e.g. "Draft · Step 2 of 4") updates automatically as you add, remove, and navigate steps.
Transaction Toolbar
Wizard screens automatically show a color-coded transaction toolbar above the content area. The transaction type is detected from the screen's id:
| Screen ID contains | Transaction type | Action buttons shown |
|---|---|---|
endorse | Endorsement (indigo) | Save Draft, Calculate Premium, Bind Endorsement, Withdraw |
cancel | Cancellation (red) | Save Draft, Compute Refund, Process Cancellation, Withdraw |
reinstate | Reinstatement (emerald) | Save Draft, Bind Reinstatement, Withdraw |
renew | Renewal (amber) | Save Draft, Quote Renewal, Bind Renewal, Non-Renew |
| (anything else) | New Submission (orange) | Save Draft, Quote, Bind / Issue, Withdraw |
The toolbar also shows the current step count (e.g. "Draft · Step 1 of 3"). All buttons are display-only in the editor preview — they do not trigger any server action.
Repeater Columns
When a field's type is repeater, the Field inspector shows a Columns editor below the standard properties:
| Column property | Description |
|---|---|
id | Column key |
label | Column header label |
type | Cell widget: text, number, date, select, checkbox |
Use + Add to append a column and ✕ to remove one. The preview renders the repeater as a mini inline table with column headers, 2 skeleton data rows, and a disabled "+ Add Row" button.
Save and File Workflow
- Make edits in the editor (preview or schema view).
- The Save Changes ● button in the header gets a
●dot indicating unsaved changes. - Click Save Changes (or your keyboard shortcut) to write the updated
_screen.meta.tsto disk. - A success toast confirms the save. The
●dot clears. - Click Open File to jump to the raw
_screen.meta.tsin the VS Code editor if you need to make changes outside the UI. - Click ↻ Refresh to discard all in-memory edits and re-read from disk.
Changes written to disk take effect immediately in the running Next.js dev server — Next.js hot-reloads the updated
_screen.meta.tsmodule automatically.
ScreenMeta File Format
Every screen has a _screen.meta.ts file co-located with its page component:
import type { ScreenMeta } from '@/lib/screen-meta/types'
export const meta: ScreenMeta = {
id: 'submissions.new',
title: 'New Submission',
route: '/submissions/new',
layout: 'wizard',
componentFile: 'app/(shell)/submissions/new/page.tsx',
serverAction: 'lib/actions/submissions.ts → createSubmission()',
dataSource: 'packages/db/src/queries/submissions.ts → fetchProductCatalog()',
steps: [
{
id: 'step_policy_type',
title: 'Policy Type',
sections: [
{
id: 'section_lob',
title: 'Line of Business',
fields: [
{
id: 'lob',
label: 'What would you like to insure?',
type: 'radio-cards',
options: [
{ value: 'personal_auto', label: 'Personal Auto' },
{ value: 'homeowners', label: 'Homeowners' },
],
},
{
id: 'vehicleCount',
label: 'Number of vehicles',
type: 'number',
visibleFor: 'lob:personal_auto', // hidden unless lob = personal_auto
},
],
},
],
},
{
id: 'step_named_insureds',
title: 'Named Insureds',
sections: [
{
id: 'section_drivers',
title: 'Drivers',
visibleFor: 'lob:personal_auto', // entire section hidden for non-auto
fields: [
{
id: 'drivers',
label: 'Driver list',
type: 'repeater',
columns: [
{ id: 'firstName', label: 'First Name' },
{ id: 'lastName', label: 'Last Name' },
{ id: 'dob', label: 'Date of Birth', type: 'date' },
{ id: 'license', label: 'License #' },
],
},
],
},
],
},
],
}
Building the Extension
pnpm --filter kalp-schema-editor compile
Output is written to packages/schema-editor/out/. After every change to the extension source, run compile then use "Developer: Reload Window" in VS Code to pick up the new build.
Concepts
| Concept | Description |
|---|---|
| Screen | A single page (ScreenMeta). Has a layout (wizard, form, detail, list), route, and server linkage. |
| Step | One page of a multi-step wizard. Each step contains sections. |
| Section | A labeled group of fields within a step or form. Supports visibleFor conditional display. |
| Field | A single input. Defined by id, label, type, and optional metadata. |
| Repeater | A field of type repeater — renders as an inline table with configurable sub-columns (vehicles, drivers, locations, etc.). |
Field Types
| Type | Renders as |
|---|---|
text | Single-line text input |
email | Email input |
tel | Phone number input |
date | Date picker |
number | Numeric input |
select | Dropdown |
radio-cards | Card-style radio buttons (interactive in preview) |
pill-toggle | Inline pill buttons (interactive in preview) |
checkbox | Boolean checkbox |
textarea | Multi-line text area |
repeater | Inline table with configurable sub-columns |
Inspector Tabs
The inspector panel on the right has three tabs:
Field Tab
Edit the selected field's properties:
| Property | Description |
|---|---|
id | camelCase key matching form state |
label | Display label |
type | Field type (see table above) |
dbColumn | Reference mapping e.g. party.first_name |
required | Marks field as required |
requiredFor | Context-specific required e.g. all, new_renewal |
visibleFor | Condition to show field e.g. partyType:person |
permissions | Role gate e.g. carrier_admin |
placeholder | Input placeholder text |
hint | Helper text shown below the field |
defaultValue | Pre-filled value |
dependsOn | Field ID whose change reloads this field's options |
fetchesOn | Field ID whose change triggers an async data load |
columns | Sub-columns for repeater type fields |
options | Value/label pairs for select, radio-cards, pill-toggle |
Section Tab
Edit the selected section's id, title, description, and visibleFor condition.
⚙ Page Tab
Edit screen-level metadata:
| Property | Description |
|---|---|
id | Unique screen ID e.g. submissions.new |
title | Page heading |
description | Optional subtitle |
route | Next.js route e.g. /submissions/new |
layout | wizard | form | detail | list |
componentFile | Path to the .tsx component relative to apps/policyone |
serverAction | Form submit handler e.g. lib/actions/submissions.ts → createSubmission() |
dataSource | Query that populates the page e.g. packages/db/src/queries/... |
Live visibleFor Simulation
The preview evaluates visibleFor conditions in real time — no reload needed.
- Format:
fieldId:value— e.g.partyType:personhides/shows the section or field based on the current radio-card or pill selection in the preview - OR conditions: comma-separated — e.g.
lob:auto,lob:homeshows when either condition is met - Role-based: a bare string with no
:(e.g.carrier_admin) is always shown in preview since roles are not simulated
When you click a radio-cards, pill-toggle, or select widget in the preview, the extension instantly hides or reveals sections and fields whose visibleFor condition is or is not met. Both section-level and field-level visibleFor are supported.
Wizard Step Management
In wizard screens, the step sidebar supports full CRUD:
- Add step — click the "+ Add Step" button at the bottom of the step sidebar
- Rename step — double-click the step label to edit it inline; press
EnterorEscapeto commit/cancel - Reorder steps — hover a step to reveal ↑ / ↓ arrow buttons
- Delete step — hover a step and click ✕ (the last remaining step cannot be deleted)
Transaction Toolbar
Wizard screens automatically show a color-coded transaction toolbar above the content area based on the screen's id:
| Screen ID contains | Transaction type | Accent color |
|---|---|---|
endorse | Endorsement | Indigo |
cancel | Cancellation | Red |
reinstate | Reinstatement | Emerald |
renew | Renewal | Amber |
| (anything else) | New Submission | Orange |
The toolbar shows the transaction type chip, current step position (e.g. "Draft · Step 2 of 4"), and contextual action buttons (Save Draft, Quote, Bind / Issue, etc.). These buttons are display-only in the editor preview.
Repeater Columns
When a field's type is set to repeater, the Field inspector shows a Columns editor below the standard properties. Each column has:
| Property | Description |
|---|---|
id | Column key |
label | Column header label |
type | Cell widget type (text, number, date, select, checkbox) |
The preview renders the repeater as a mini inline table with 2 skeleton rows and a disabled "+ Add Row" button.
ScreenMeta File Format
Every screen has a _screen.meta.ts file co-located with its page component:
import type { ScreenMeta } from '@/lib/screen-meta/types'
export const meta: ScreenMeta = {
id: 'submissions.new',
title: 'New Submission',
route: '/submissions/new',
layout: 'wizard',
componentFile: 'app/(shell)/submissions/new/page.tsx',
serverAction: 'lib/actions/submissions.ts → createSubmission()',
dataSource: 'packages/db/src/queries/submissions.ts → fetchProductCatalog()',
steps: [
{
id: 'step_policy_type',
title: 'Policy Type',
sections: [
{
id: 'section_lob',
title: 'Line of Business',
fields: [
{
id: 'lob',
label: 'What would you like to insure?',
type: 'radio-cards',
options: [
{ value: 'personal_auto', label: 'Personal Auto' },
{ value: 'homeowners', label: 'Homeowners' },
],
},
{
id: 'vehicleCount',
label: 'Number of vehicles',
type: 'number',
visibleFor: 'lob:personal_auto', // hidden unless lob = personal_auto
},
],
},
],
},
],
}
Building the Extension
pnpm --filter kalp-schema-editor compile
Output is written to packages/schema-editor/out/. After every change to the extension source, run compile then use "Developer: Reload Window" in VS Code to pick up the new build.