Schema Overview

The PrototypeSchema type controls every aspect of a tool — its fields, derived values, collections, results, visualizations, and layout.

Schema Overview

A PrototypeSchema is a plain TypeScript object that fully describes a tool. It is the single source of truth for rendering, computation, and persistence.

Full type signature

interface PrototypeSchema {
  // Identity
  key:     string           // Unique across all tools — also the default Y.js doc key

  // Display metadata
  title?:       string
  description?: string
  icon?:        string      // Iconify icon e.g. 'i-lucide-calculator'

  // Form content (use one or the other)
  fields?:      Record<string, FieldDef>      // flat form
  sections?:    SectionDef[]                  // grouped/tabbed form

  // CRUD lists
  collections?: Record<string, CollectionSchema>

  // Computation
  derived?:     Record<string, DerivedDef>
  connections?: ConnectionsDef[]      // legacy cross-doc watcher (prefer produces/consumes)

  // Output display
  results?: {
    badge?: (ctx: ComputeContext) => { label: string; color: string } | null
    stats?: (ctx: ComputeContext) => StatItem[]
  }

  // Charts and tables
  visualizations?: VizDef[]

  // Dashboard card summaries
  cards?: CardDef[]

  // Cross-tool data flow
  produces?: Record<string, string>   // 'outputKey': 'derived.fieldName'
  consumes?: Record<string, string>   // 'localAlias': 'sourceTool.sourceKey'

  // Action toolbar
  actions?: ProtoAction[]

  // Custom layout (overrides default sequential rendering)
  layout?: DashboardLayout
}

Key groupings

fields vs sections

Use fields for a simple flat form:

fields: {
  revenue:  { type: 'number', label: 'Monthly Revenue (€)',  default: 10000 },
  costs:    { type: 'number', label: 'Monthly Costs (€)',     default: 6000 },
  margin:   { type: 'number', label: 'Target Margin (%)',     default: 30 },
}

Use sections to group inputs into labeled panels:

sections: [
  {
    title: 'Revenue',
    cols: 2,
    fields: {
      recurring:   { type: 'number', label: 'Recurring Revenue (€)' },
      oneTime:     { type: 'number', label: 'One-Time Revenue (€)' },
    }
  },
  {
    title: 'Costs',
    cols: 2,
    fields: {
      fixed:    { type: 'number', label: 'Fixed Costs (€)' },
      variable: { type: 'number', label: 'Variable Costs (€)' },
    }
  }
]

If both fields and sections are present, sections takes precedence for rendering.

results

Renders a badge and stat cards below the form:

results: {
  badge: ({ derived }) =>
    derived.margin >= 0.3
      ? { label: 'Healthy Margin',   color: 'success' }
      : { label: 'Low Margin',       color: 'warning' },

  stats: ({ fields, derived }) => [
    { label: 'Gross Profit',   value: derived.grossProfit,  format: 'money' },
    { label: 'Margin',         value: derived.margin,       format: 'percent' },
    { label: 'Annual Revenue', value: derived.annualRevenue, format: 'money' },
  ],
}

actions

actions: [
  { type: 'copy-text',       label: 'Copy Summary',    icon: 'i-lucide-clipboard', text:},
  { type: 'export-markdown', label: 'Export Report',   icon: 'i-lucide-download',  markdown:},
  { type: 'reset',           label: 'Reset',           icon: 'i-lucide-trash-2',  confirm: true },
]

The ComputeContext type

All callback functions in results.badge, results.stats, derived[x].compute, visualization items, and layout badge receive a ComputeContext:

interface ComputeContext {
  fields:      Record<string, any>   // current unwrapped field values
  derived:     Record<string, any>   // computed derived values (in definition order)
  connections: Record<string, any>   // values from upstream produces
  collections: Record<string, any[]> // CRUD collection items
}

StatItem format

interface StatItem {
  label:   string
  value:   any
  format?: 'money' | 'percent' | 'number' | 'date' | 'text'
  prefix?: string
  suffix?: string
  color?:  string   // Nuxt UI color name
}

Stat format types

formatInput typeExample output
'money'number€ 12,345
'percent'number (0–100)23.5 %
'number'number1,234
'date'ISO stringMar 2026
'text'stringas-is

Use the format property on DerivedDef for the same formatting in derived value display:

{ type: 'money', currency: '', decimals: 0 }
{ type: 'percent', decimals: 1 }
{ type: 'number', decimals: 0, suffix: ' months' }

Need a Landing Page?

Modern landing pages with optional modules (blog, docs, forms, i18n). Let's discuss your project.

Build Your MVP

Full-stack SaaS development. Expert in database design, multi-tenancy, and scalable architecture.

Deployment Help

Dockerize your backend, set up CI/CD pipelines, deploy to Cloudflare or Hetzner. Early-stage setup.

Suggest a SaaS Tool

Missing a calculator or tool? Suggest what you'd like to see on our site.