@svgrid/mcp 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data.js +7 -7
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/data.js
CHANGED
|
@@ -2714,7 +2714,7 @@ export const docs = [
|
|
|
2714
2714
|
"slug": "enterprise/studio/app-designer",
|
|
2715
2715
|
"path": "docs/enterprise/studio/app-designer.md",
|
|
2716
2716
|
"title": "Visual app designer",
|
|
2717
|
-
"markdown": "# Visual app designer\r\n\r\n`SvStudioDesigner` is the grid-centric visual **data-app** designer - compose a\r\nmulti-entity app by arranging data-bound blocks on a canvas, then generate a\r\nrunnable SvelteKit project. It's the app-level companion to the single-entity\r\n[schema designer](./designer.md): where that authors one `EntitySchema`, this\r\ncomposes **screens** across **many entities**.\r\n\r\nGrid-centric by design: the blocks are schema-driven and data-bound (a grid, a\r\nchart, a pivot, a dashboard, a KPI, master-detail, a faceted filter panel, a\r\nrecord panel, a lookup) - not arbitrary layout components. Point it at a\r\ndatabase, get a CRUD app - kept to data views.\r\n\r\n> **Just want to open it?** `npx @svgrid/studio designer` launches this designer\r\n> in your browser, auto-saves your work to `studio.config.json`, and writes the\r\n> generated app to a folder - no host app needed. See\r\n> [Launch the designer](./launch.md).\r\n\r\n## Starting a new app\r\n\r\n**New app** in the top bar walks you from nothing to a working CRUD app: pick\r\nwhere the data comes from, choose the tables, choose the pages, open the result.\r\n\r\n1. **Start** - sample data, your own data, or a blank set of tables you name.\r\n2. **Data** - connect a database (the table picker shows row counts and lets you\r\n preview rows before importing), read a **Supabase** project, pick a starter\r\n dataset, point at a REST endpoint, or paste an OpenAPI document.\r\n3. **Screens** - tick which pages each table gets (list, form, record page,\r\n dashboard) and how rows are edited: a popup form, in the grid, or on the\r\n record page.\r\n4. **Done** - name it and open it. It arrives as one undo step, so Ctrl+Z puts\r\n the previous design back.\r\n\r\nConnecting to a live SQL database needs the local designer\r\n(`npx @svgrid/studio dev`) because database drivers run on your machine, not in a\r\nbrowser tab. **Supabase is the exception**: it serves its own REST API, so the\r\nwizard reads your tables with just the project URL and the anon key - it is the\r\none real database that works from\r\n[svgrid.com/studio](https://svgrid.com/studio) with nothing installed. Row-level\r\nsecurity still applies, so the app sees exactly what the browser may see. The\r\nother paths work there too, and you can rebind to any database later with\r\n**Use my data**.\r\n\r\nThe terminal equivalent is [`svgrid-studio init`](./cli.md#init) - same\r\nquestions, same generator, same app.\r\n\r\n## How the screen is laid out\r\n\r\nHere is the real designer with a small Sales App open:\r\n\r\n\r\n\r\nYou do not need to understand the internals to use it. The same layout, labelled:\r\n\r\n\r\n\r\n- **Screens** (far left) - the pages of your app. Click one to edit it; **+ Add\r\n screen** makes a new one.\r\n- **Blocks** - the pieces you drop onto a screen: a **grid** (a table of records),\r\n a **chart**, a **pivot**, a **KPI** number, a **dashboard**, a **filter panel**,\r\n and a **record panel**. Click or drag one onto the preview.\r\n- **Live preview** (middle) - your screen with **real data**, updating as you\r\n change things. What you see is what the app will look like.\r\n- **Properties** (right) - tune the selected block, or - with nothing selected -\r\n edit the entity's **fields** and pick its **data source**.\r\n- **Generate app** (top right) - when it looks right, one click writes the whole,\r\n runnable app.\r\n\r\nThe rest of this page is the detailed reference for each area, aimed at developers\r\nembedding or scripting the designer. If you just want to build an app, everything\r\nabove is done by pointing and clicking - see\r\n[Launch the designer](./launch.md).\r\n\r\n## What it edits: the project model\r\n\r\nThe designer reads and writes a `StudioProject` - the declarative model behind\r\nthe whole app:\r\n\r\n```ts\r\nimport { createProject } from '@svgrid/enterprise'\r\n\r\n// One default screen (grid + edit form) per entity, in-memory.\r\nlet project = $state(createProject([customerSchema, orderSchema], { title: 'Sales App' }))\r\n```\r\n\r\n```svelte {nocheck}\r\n<script lang=\"ts\">\r\n import { SvStudioDesigner } from '@svgrid/enterprise'\r\n</script>\r\n\r\n<SvStudioDesigner {project} onChange={(p) => (project = p)} />\r\n```\r\n\r\nThe designer is a single IDE-style frame: a **title bar** (app name + accent +\r\nundo/redo + Import/Load/Save/Generate), a **screen tab strip** (switch, close, or\r\nadd a screen), the three work panels, and a **status bar** (validity, entity /\r\nscreen / block counts, current selection, data source).\r\n\r\n- **Rail** (left) - switch and add screens; each screen is bound to an entity.\r\n- **Screen tabs** - the open screens as a document strip; click to switch, the\r\n **x** to remove one, the **+** to add one (from the current template).\r\n- **Palette** - the block kinds. **Drag** one onto the canvas to add it (or click).\r\n- **Canvas** - the screen's blocks in a responsive **12-column** grid, previewed\r\n live with the real components. **Drag a block** to reorder it; **drag its right\r\n edge** to set its width (1-12 columns), use the ⅓ / ½ / ⅔ / full quick buttons\r\n in its header, or the **Layout > Width** slider. **Drag a block's bottom edge**\r\n to make its region taller or shorter - grid, chart, pivot, and master-detail\r\n blocks are height-resizable, and the chosen height flows through to the generated app\r\n (also set it precisely under **Layout > Height** in the inspector).\r\n- **Inspector** (right) - edit the selected block: a grid's **editing mode** +\r\n behavior + **column config** (see below), a chart's group-by / measure / reduce\r\n / type. With no block selected, edit the **page** (title / route /\r\n **nav** settings), the entity's **data source** (see below), and the **entity's\r\n fields** - add, rename, retype, flag (PK / required / read-only), pick a\r\n **relation's target entity + label field**, and **drag to reorder**. The rail\r\n sets the **default source kind** for new entities, the **app layout**, and adds\r\n screens from a **template** (CRUD, dashboard, master-detail, empty).\r\n- **Top bar** - rename the app, set an **accent color** (themes the whole app),\r\n add a **New entity** from scratch, **Import CSV** (drop in a spreadsheet - see\r\n below), **Connect DB** (the launcher's live-database wizard - see\r\n [Launch the designer](./launch.md)), **Import schema** (paste a Drizzle / Prisma\r\n schema to add its entities), **Save / Load** the design as `studio.config.json`,\r\n and **Generate app**. With no entities yet, the canvas shows an **onboarding**\r\n screen offering the same ways to start.\r\n- **✨ Copilot** (when the host wires it) - describe a change in plain English\r\n (\"add an orders screen with a revenue chart\", \"make mrr required\") and the AI\r\n edits your project. It's a host hook: `<SvStudioDesigner onCopilot={...} />`\r\n receives `{ prompt, project }` and returns the edited `StudioProject` - your AI\r\n keys stay server-side. The result is validated before it applies, and it's one\r\n **Ctrl/Cmd+Z** away.\r\n\r\nThe three panels are **resizable** (drag the dividers). Every edit is **undoable**\r\n(Ctrl/Cmd+Z, Ctrl+Shift+Z / Ctrl+Y to redo); **Delete** removes the selected\r\nblock, **Ctrl/Cmd+D** (or the **⧉** header button) **duplicates** it, and\r\n**Escape** deselects. **Preview app** opens the whole app full-screen with a\r\n**Desktop / Tablet / Mobile** device-width toggle to check responsiveness. **Generate app** opens the output in a\r\n**file-tree viewer** modal (scrollable, Copy per file) and a **Download .zip** of\r\nthe **complete runnable SvelteKit + Vite project** - unzip, `npm install`,\r\n`npm run dev`. The zip includes `package.json` (with the right driver deps),\r\n`vite.config.ts`, `svelte.config.js`, `tsconfig.json`, the app shell, and every\r\ngenerated screen.\r\n\r\n## The grid (and how it edits)\r\n\r\nThe grid is the core block, so **editing is a grid property**, not a separate\r\nblock. Select a grid and set its **Editing mode**:\r\n\r\n- **Popup form** - double-click a row to edit it in a modal / drawer / inline\r\n panel (pick the **Form style**); a **+ New** button adds rows. This is the\r\n default.\r\n- **Inline** - edit cells right in the grid (Excel-style); each change saves via\r\n the data source.\r\n- **Read-only** - no editing.\r\n\r\nThe grid's property editor also covers **Behavior** (Sortable, Filtering + search,\r\nRow selection, Cell range selection, Striped rows, Totals footer row, Density),\r\n**Paging** (Paginate on / off, Page size, **Pager position** - bottom / top / both,\r\nand the **Page size options** for the selector), and per-**Column** settings -\r\nexpand a column to set its **header**, **width**, **alignment**, **pin** (left /\r\nright), plus show / hide + reorder. There is no standalone \"Edit form\" block - the\r\ngrid owns editing end to end.\r\n\r\n### Export toolbar\r\n\r\n**Export toolbar** adds a button bar above the grid. Six options, in two groups:\r\n\r\n| Button | Runs through | Adds to the generated app |\r\n| ------ | ------------ | ------------------------- |\r\n| Export CSV / Export JSON / Copy | the free grid API | nothing |\r\n| Export Excel (.xlsx) | `@svgrid/enterprise` | `jszip` |\r\n| Export PDF | `@svgrid/enterprise` | `pdfmake` |\r\n| Print | `@svgrid/enterprise` | nothing |\r\n\r\nThe Excel export is real OOXML - typed number and date cells, styled headers, a\r\nfrozen header row - not a renamed CSV. PDF is paginated with a repeating header,\r\nand Print opens the browser's print dialog on a paginated layout.\r\n\r\nAll six export what the user currently sees: the visible columns, in their\r\ncurrent order, over the filtered and sorted rows. The optional dependencies are\r\ndeclared only for the buttons you switch on, so a CSV-only app installs neither.\r\nThe canvas preview runs the same code the generated app does, so you can try a\r\nreal export before generating.\r\n\r\n## The analytical + companion blocks\r\n\r\nBeyond the grid, every block is still bound to the `EntitySchema` - these are data\r\nviews, not generic widgets:\r\n\r\n| Block | What it renders | Inspector |\r\n| --- | --- | --- |\r\n| **Chart** | A chart (`SvSchemaChart`) - bar, pie, line, area, radar, funnel, waterfall, or treemap. | Group-by dimension, measure, reduce, type. |\r\n| **Pivot** | A full pivot table (`SvPivotDesigner`) the end user can re-pivot live. | Row + column dimensions (checkboxes), a measure, and its aggregate. |\r\n| **Dashboard** | A schema-driven KPI + chart board (`SvSchemaDashboard`). | - |\r\n| **KPI** | A single reduced metric tile. | Label, measure, reduce. |\r\n| **Gauge** | A radial gauge (`SvGauge`) of one reduced measure within a range - utilization, progress, scores. | Label, measure, reduce, min / max, unit. |\r\n| **Tree** | A hierarchical tree (`SvTree`) built from the entity's own rows via a self-referential parent. | A label field + a parent field (a row's link to its parent row). |\r\n| **Tabs** | A tabbed container (`SvTabs`) that **groups display blocks** into tabs - e.g. an Overview tab of KPIs + a Details tab with a chart. | Add / rename / remove tabs; per tab, add child blocks (charts, KPIs, gauges, pivots, trees). |\r\n| **Accordion** | A collapsible-sections container (like Tabs, stacked vertically). | Add / rename / remove sections; child blocks per section. |\r\n| **Master / detail** | A row that expands into a nested grid of related records. | Child entity + foreign key. |\r\n| **Board** | A kanban board of the entity's rows, one lane per value of a group-by field, with drag between lanes. | Group-by, card title / subtitle / badge fields, open-screen drill. |\r\n| **Calendar** | A month event-calendar: each row with a date lands on its day, labelled and optionally color-coded. | Date field, title field, color field, open-screen drill. |\r\n| **Detail** | A full record \"detail page\": header, metric row, field sections, and related-record tabs - the 360 view a row action or drill-through opens. | Title / subtitle / status / metric fields, sections, related child entities. |\r\n| **Form** | A standalone create / edit form for the entity. | Presentation (drawer / modal / inline). |\r\n| **Filter panel** | A faceted sidebar that **filters the screen's grid** - enum / boolean facets pick a value, text facets search. | Title + which fields become facets. |\r\n| **Record panel** | Shows the row **selected in the grid** - a read-only field list, or an inline edit form. | Editable on / off, and (read-only) which fields to show. |\r\n| **Lookup** | Marks a relation field as a searchable picker in the edit form. | The relation field. |\r\n| **UI component** | A component from the SvGrid UI kit dropped from the toolbox - entity-agnostic, works on freestanding pages too. Grouped as Actions, Inputs, Display, Feedback, Layout, and Navigation, and covering headings and prose (heading, text, link, quote, code, keyboard key, list) as well as controls, pickers, and date/time inputs. | The component's own props (extracted from the component's own types, with its JSDoc as the tooltip), plus data bindings. |\r\n\r\nThe **filter** and **record** panels wire to the grid on the same screen: the\r\nfilter panel calls the grid controller's `setFilter`, and clicking a grid row\r\npublishes it to the record panel. So a common layout is a **filter panel + grid +\r\nrecord panel** three-up - list, narrow, inspect - all generated for you.\r\n\r\nThe grid, chart, pivot, and master-detail blocks are **height-resizable** (drag\r\nthe block's bottom edge, or set **Layout > Height**), and the chosen height flows\r\ninto the generated app. The filter and record panels size to their content.\r\n\r\n### Conditional formatting\r\n\r\nA grid's inspector has a **Conditional formatting** section: add no-code rules\r\nthat style a cell by its value - pick a field, a comparison (`=`, `<`, `>`,\r\n`contains`, `is empty`, ...), a value, and a **text color / fill / bold**. Rules\r\nrender **live in the canvas** and compile to the grid's built-in\r\n`conditionalFormats` rule engine in the generated app (e.g. negative `mrr` red,\r\n`status = overdue` filled). It's the same engine you'd use by hand - the designer\r\njust authors the rules.\r\n\r\n### Navigation & row actions\r\n\r\nThe grid's **Navigation & actions** section wires flow between screens:\r\n**drill-through** (row click opens another screen, filtered to the clicked value)\r\nand **row action buttons** (Edit / Delete / Open). A chart can drill too. See\r\n[Navigation & row actions](./navigation.md) for the full picture.\r\n\r\n## Data sources (per entity)\r\n\r\nEach entity binds to **its own backend** - the designer is not limited to one\r\ndata source per app. In the inspector (no block selected), the **Data source**\r\nsection binds the screen's entity to:\r\n\r\n- **In-memory** - seeded sample rows, runs with no backend (the default).\r\n- **Local database (no setup)** - a real, persistent Postgres ([PGlite](https://pglite.dev))\r\n running in the browser and saved to IndexedDB, so rows survive reloads with zero\r\n backend. Same SQL as production - swap to a hosted **SQL** source later without\r\n touching the schema. See [Local database](./local-database.md).\r\n The builder is a draggable, resizable, maximizable panel, and you can open it for\r\n any entity straight from the **Data model** dialog's **Configure** button.\r\n\r\n- **REST API** - a request builder: **method**, **base URL**, **path** (path\r\n params auto-derive from `{tokens}`), and **Query / Path / Header** tabs. **Send**\r\n runs it live and shows a **response table** plus the real rows in the canvas\r\n grid; **Import fields** rewrites the entity's schema to match the response keys.\r\n An **API format** picker (Manual / Offset + Limit / DummyJSON / json-server)\r\n wires a wire-format adapter so the grid does **real server-side paging and sort**;\r\n Manual keeps the rows-path / total-path mapping for a single fetched page.\r\n- **Supabase** - **List tables** reads your project's tables so you pick one, and\r\n **Import schema** pulls its real columns, primary key, foreign keys, and enums\r\n into the entity; **Preview** shows live rows (works in the online designer, since\r\n Supabase is HTTP). The project **URL + anon key are a shared connection** you set\r\n once for every Supabase entity. A **Live updates (Realtime)** toggle emits a live\r\n subscription. **Generate app** emits `createClient` in `connections.ts` reading\r\n `PUBLIC_SUPABASE_URL` / `PUBLIC_SUPABASE_ANON_KEY` from `.env` (the key is never\r\n inlined) and adds `@supabase/supabase-js`; access is protected by your RLS\r\n policies.\r\n- **SQL** - paste a connection string (the dialect is auto-detected) or fill the\r\n guided form, set the **Schema** (Postgres search path). In the **local** designer\r\n (`npx @svgrid/studio designer`) **Preview data** runs a real `SELECT` and shows\r\n your actual rows on the canvas, and a missing `pg` / `mysql2` / ... driver is a\r\n **one-click install**. **Generate app** emits a connected\r\n `src/routes/api/<table>/+server.ts` (the dialect's driver, reading\r\n `DATABASE_URL`) and points the grid at it; the driver dep is added for you. In\r\n the **online** designer, bind the entity and generate - the app connects for real\r\n at runtime. To scaffold from an existing DB via CLI: `npx @svgrid/studio add\r\n <table> --db <dialect> --url …`.\r\n\r\n**Generate app** then emits the matching adapter per entity in `src/lib/data.ts`\r\n(`createRestDataSource` / `createSqlDataSource` / `createSupabaseDataSource` /\r\n`createInMemoryDataSource`). SQL and Supabase entities read their connection from a\r\ngenerated `src/lib/connections.ts` (or the `+server.ts` route) - the SQL driver and\r\nthe Supabase client are wired for you; the only manual step is setting the\r\nconnection in `.env` (the bundle ships a `.env.example`). See\r\n[Databases](./databases.md) and [REST API](./rest-api.md).\r\n\r\n## Import a CSV / spreadsheet\r\n\r\nThe fastest way to start from **your own data**: **Import CSV** in the top bar\r\ntakes a `.csv` file and turns it into a running screen. The designer parses the\r\nfile (quoted fields, embedded newlines, and CRLF included), **infers a type per\r\ncolumn** from its values (number, boolean, date, or text - thousands separators\r\nand `yes/no/true/false` are understood), ensures a **primary key** (it reuses an\r\n`id` column or synthesizes one), and adds an entity with a full CRUD screen,\r\n**seeded with the real rows**. Unsafe headers (`First Name`, `E-mail`) become safe\r\nfield keys and the note tells you what was renamed.\r\n\r\nImported rows ship **in-memory** by default (no dependencies), so the app runs\r\nimmediately. Switch that entity's **Data source** to **Local database** to make\r\nthe same imported rows **persist** across reloads - the seed carries over. This is\r\nall client-side: `csvToEntity(name, text)` is a pure function exported from\r\n`@svgrid/enterprise`, so the same import works in the CLI and your own tools.\r\n\r\n> **Large files:** the imported rows are stored as the entity's seed, so they are\r\n> embedded in `studio.config.json` when you **Save** the design. That is fine for\r\n> reference data and samples; for a large dataset, import a representative sample\r\n> and point the entity at a **database** (Local database / SQL) for the full data.\r\n\r\n## Pages and layout\r\n\r\n- **Pages** - each screen is a route. In the inspector's **Page** section, toggle\r\n **Show in navigation**, set a **nav label** and **nav order**, or start a page\r\n from the **Empty** template. Hidden pages stay routable but drop out of the nav.\r\n- **Render mode** - eligible screens (a single plain grid, or read-only block\r\n screens, on a memory / SQL source) can switch from the default client page to\r\n **SSR**: an idiomatic `+page.server.ts` with `load` + form actions. The rules\r\n are in [Code generation](./code-generation.md#render-mode-spa-or-ssr-per-screen).\r\n- **App layout** - the rail's **App layout** section themes the generated shell:\r\n **Sidebar** or **Top navigation**, a **brand** name, a **company logo**\r\n (uploaded - stored inline and shown in the nav in place of the brand text), a\r\n **footer**, and (for the sidebar) the **nav position** (left / right). This\r\n drives the generated `src/routes/+layout.svelte`. The generated shell is\r\n **responsive**: on phones the sidebar collapses to a hamburger drawer, the\r\n top-nav links scroll, and each screen's block grid stacks to one column.\r\n\r\n## Save, regenerate, round-trip\r\n\r\nThe `StudioProject` is the persisted design. **Save config** exports a\r\n`studio.config.json`; regenerate the app from it any time:\r\n\r\n```bash\r\nnpm create @svgrid/studio@latest my-app -- --project ./studio.config.json\r\n```\r\n\r\nOr programmatically:\r\n\r\n```ts\r\nimport { serializeProject, parseProject, emitStudioProject } from '@svgrid/enterprise'\r\n\r\nconst json = serializeProject(project) // save the design\r\nconst project2 = parseProject(json) // reopen it\r\nconst files = emitStudioProject(project2) // -> the app's source files\r\n```\r\n\r\n**The exported app carries its own design.** The downloaded zip includes a\r\n`studio.config.json` at its root. To keep editing the app visually after you've\r\nworked on it locally, open the designer and **Load** that file - entities,\r\nscreens, blocks, theme, RBAC, i18n, and now the logo all come back exactly as\r\ngenerated. Because the designer regenerates the files under `src/`, keep any\r\nhand-written code in **new** files/modules you import, so a re-generate never\r\noverwrites it (or use the CLI's `svgrid:managed` markers - see\r\n[Code generation](./code-generation.md)).\r\n\r\n## Generate the app\r\n\r\n**Generate app** emits `src/lib/schemas.ts`, `src/lib/data.ts` (the right adapter\r\nper entity, plus `src/lib/connections.ts` when any entity is SQL / Supabase-bound),\r\nand one `src/routes/<route>/+page.svelte` **per screen** that composes that\r\nscreen's blocks with their config - a grid (with your visible columns, in order) +\r\nedit modal, plus any charts / pivots / dashboard / KPI tiles, and filter / record\r\npanels wired to the grid, all bound to the data - and the\r\nnav layout (sidebar or top-nav) + home. The pages are self-contained (they use\r\n`@svgrid/grid` + `@svgrid/enterprise` directly), so the output runs as a standard\r\nSvelteKit app.\r\n\r\n```ts\r\nimport { emitStudioProject } from '@svgrid/enterprise'\r\nconst files = emitStudioProject(project) // [{ path, contents, description }, ...]\r\n```\r\n\r\n## See also\r\n\r\n- [Code behind (Code view)](./code-behind.md) - write TypeScript against a typed `ctx` (grid API, events, `ctx.grid.sortable = true`, lifecycle)\r\n- [Sample apps + bind your data](./samples.md) - start from a ready-made app, then point it at your database\r\n- [Launch the designer](./launch.md) - `npx @svgrid/studio designer` (auto-save + generate to a folder)\r\n- [Schema designer](./designer.md) - author a single entity\r\n- [Dashboards](./dashboards.md) · [Databases](./databases.md) - the blocks + data sources\r\n- [CLI](./cli.md) / [Drizzle](./drizzle.md) / [Prisma](./prisma.md) - import a schema to design from\r\n"
|
|
2717
|
+
"markdown": "# Visual app designer\r\n\r\n`SvStudioDesigner` is the grid-centric visual **data-app** designer - compose a\r\nmulti-entity app by arranging data-bound blocks on a canvas, then generate a\r\nrunnable SvelteKit project. It's the app-level companion to the single-entity\r\n[schema designer](./designer.md): where that authors one `EntitySchema`, this\r\ncomposes **screens** across **many entities**.\r\n\r\nGrid-centric by design: the blocks are schema-driven and data-bound (a grid, a\r\nchart, a pivot, a dashboard, a KPI, master-detail, a faceted filter panel, a\r\nrecord panel, a lookup) - not arbitrary layout components. Point it at a\r\ndatabase, get a CRUD app - kept to data views.\r\n\r\n> **Just want to open it?** `npx @svgrid/studio designer` launches this designer\r\n> in your browser, auto-saves your work to `studio.config.json`, and writes the\r\n> generated app to a folder - no host app needed. See\r\n> [Launch the designer](./launch.md).\r\n\r\n## Starting a new app\r\n\r\n**New app** in the top bar walks you from nothing to a working CRUD app: pick\r\nwhere the data comes from, choose the tables, choose the pages, open the result.\r\n\r\n1. **Start** - sample data, your own data, or a blank set of tables you name.\r\n2. **Data** - connect a database (the table picker shows row counts and lets you\r\n preview rows before importing), read a **Supabase** project, pick a starter\r\n dataset, point at a REST endpoint, or paste an OpenAPI document.\r\n3. **Screens** - tick which pages each table gets (list, form, record page,\r\n dashboard) and how rows are edited: a popup form, in the grid, or on the\r\n record page.\r\n4. **Done** - name it and open it. It arrives as one undo step, so Ctrl+Z puts\r\n the previous design back.\r\n\r\nConnecting to a live SQL database needs the local designer\r\n(`npx @svgrid/studio dev`) because database drivers run on your machine, not in a\r\nbrowser tab. **Supabase is the exception**: it serves its own REST API, so the\r\nwizard reads your tables with just the project URL and the anon key - it is the\r\none real database that works from\r\n[svgrid.com/studio](https://svgrid.com/studio) with nothing installed. Row-level\r\nsecurity still applies, so the app sees exactly what the browser may see. The\r\nother paths work there too, and you can rebind to any database later with\r\n**Use my data**.\r\n\r\nThe terminal equivalent is [`svgrid-studio init`](./cli.md#init) - the same\r\ngenerator, the same app. The questions differ slightly: the CLI also asks for a\r\ntheme and light/dark, while the wizard offers a dashboard page per table (pick\r\nyour theme in the designer afterwards).\r\n\r\n## How the screen is laid out\r\n\r\nHere is the real designer with a small Sales App open:\r\n\r\n\r\n\r\nYou do not need to understand the internals to use it. The same layout, labelled:\r\n\r\n\r\n\r\n- **Screens** (far left) - the pages of your app. Click one to edit it; **+ Add\r\n screen** makes a new one.\r\n- **Blocks** - the pieces you drop onto a screen: a **grid** (a table of records),\r\n a **chart**, a **pivot**, a **KPI** number, a **dashboard**, a **filter panel**,\r\n and a **record panel**. Click or drag one onto the preview.\r\n- **Live preview** (middle) - your screen with **real data**, updating as you\r\n change things. What you see is what the app will look like.\r\n- **Properties** (right) - tune the selected block, or - with nothing selected -\r\n edit the entity's **fields** and pick its **data source**.\r\n- **Generate app** (top right) - when it looks right, one click writes the whole,\r\n runnable app.\r\n\r\nThe rest of this page is the detailed reference for each area, aimed at developers\r\nembedding or scripting the designer. If you just want to build an app, everything\r\nabove is done by pointing and clicking - see\r\n[Launch the designer](./launch.md).\r\n\r\n## What it edits: the project model\r\n\r\nThe designer reads and writes a `StudioProject` - the declarative model behind\r\nthe whole app:\r\n\r\n```ts\r\nimport { createProject } from '@svgrid/enterprise'\r\n\r\n// One default screen (grid + edit form) per entity, in-memory.\r\nlet project = $state(createProject([customerSchema, orderSchema], { title: 'Sales App' }))\r\n```\r\n\r\n```svelte {nocheck}\r\n<script lang=\"ts\">\r\n import { SvStudioDesigner } from '@svgrid/enterprise'\r\n</script>\r\n\r\n<SvStudioDesigner {project} onChange={(p) => (project = p)} />\r\n```\r\n\r\nThe designer is a single IDE-style frame: a **title bar** (app name + accent +\r\nundo/redo + Import/Load/Save/Generate), a **screen tab strip** (switch, close, or\r\nadd a screen), the three work panels, and a **status bar** (validity, entity /\r\nscreen / block counts, current selection, data source).\r\n\r\n- **Rail** (left) - switch and add screens; each screen is bound to an entity.\r\n- **Screen tabs** - the open screens as a document strip; click to switch, the\r\n **x** to remove one, the **+** to add one (from the current template).\r\n- **Palette** - the block kinds. **Drag** one onto the canvas to add it (or click).\r\n- **Canvas** - the screen's blocks in a responsive **12-column** grid, previewed\r\n live with the real components. **Drag a block** to reorder it; **drag its right\r\n edge** to set its width (1-12 columns), use the ⅓ / ½ / ⅔ / full quick buttons\r\n in its header, or the **Layout > Width** slider. **Drag a block's bottom edge**\r\n to make its region taller or shorter - grid, chart, pivot, and master-detail\r\n blocks are height-resizable, and the chosen height flows through to the generated app\r\n (also set it precisely under **Layout > Height** in the inspector).\r\n- **Inspector** (right) - edit the selected block: a grid's **editing mode** +\r\n behavior + **column config** (see below), a chart's group-by / measure / reduce\r\n / type. With no block selected, edit the **page** (title / route /\r\n **nav** settings), the entity's **data source** (see below), and the **entity's\r\n fields** - add, rename, retype, flag (PK / required / read-only), pick a\r\n **relation's target entity + label field**, and **drag to reorder**. The rail\r\n sets the **default source kind** for new entities, the **app layout**, and adds\r\n screens from a **template** (CRUD, dashboard, master-detail, empty).\r\n- **Top bar** - rename the app, set an **accent color** (themes the whole app),\r\n add a **New entity** from scratch, **Import CSV** (drop in a spreadsheet - see\r\n below), **Connect DB** (the launcher's live-database wizard - see\r\n [Launch the designer](./launch.md)), **Import schema** (paste a Drizzle / Prisma\r\n schema to add its entities), **Save / Load** the design as `studio.config.json`,\r\n and **Generate app**. With no entities yet, the canvas shows an **onboarding**\r\n screen offering the same ways to start.\r\n- **✨ Copilot** (when the host wires it) - describe a change in plain English\r\n (\"add an orders screen with a revenue chart\", \"make mrr required\") and the AI\r\n edits your project. It's a host hook: `<SvStudioDesigner onCopilot={...} />`\r\n receives `{ prompt, project }` and returns the edited `StudioProject` - your AI\r\n keys stay server-side. The result is validated before it applies, and it's one\r\n **Ctrl/Cmd+Z** away.\r\n\r\nThe three panels are **resizable** (drag the dividers). Every edit is **undoable**\r\n(Ctrl/Cmd+Z, Ctrl+Shift+Z / Ctrl+Y to redo); **Delete** removes the selected\r\nblock, **Ctrl/Cmd+D** (or the **⧉** header button) **duplicates** it, and\r\n**Escape** deselects. **Preview app** opens the whole app full-screen with a\r\n**Desktop / Tablet / Mobile** device-width toggle to check responsiveness. **Generate app** opens the output in a\r\n**file-tree viewer** modal (scrollable, Copy per file) and a **Download .zip** of\r\nthe **complete runnable SvelteKit + Vite project** - unzip, `npm install`,\r\n`npm run dev`. The zip includes `package.json` (with the right driver deps),\r\n`vite.config.ts`, `svelte.config.js`, `tsconfig.json`, the app shell, and every\r\ngenerated screen.\r\n\r\n## The grid (and how it edits)\r\n\r\nThe grid is the core block, so **editing is a grid property**, not a separate\r\nblock. Select a grid and set its **Editing mode**:\r\n\r\n- **Popup form** - double-click a row to edit it in a modal / drawer / inline\r\n panel (pick the **Form style**); a **+ New** button adds rows. This is the\r\n default.\r\n- **Inline** - edit cells right in the grid (Excel-style); each change saves via\r\n the data source.\r\n- **Read-only** - no editing.\r\n\r\nThe grid's property editor also covers **Behavior** (Sortable, Filtering + search,\r\nRow selection, Cell range selection, Striped rows, Totals footer row, Density),\r\n**Paging** (Paginate on / off, Page size, **Pager position** - bottom / top / both,\r\nand the **Page size options** for the selector), and per-**Column** settings -\r\nexpand a column to set its **header**, **width**, **alignment**, **pin** (left /\r\nright), plus show / hide + reorder. There is no standalone \"Edit form\" block - the\r\ngrid owns editing end to end.\r\n\r\n### Export toolbar\r\n\r\n**Export toolbar** adds a button bar above the grid. Six options, in two groups:\r\n\r\n| Button | Runs through | Adds to the generated app |\r\n| ------ | ------------ | ------------------------- |\r\n| Export CSV / Export JSON / Copy | the free grid API | nothing |\r\n| Export Excel (.xlsx) | `@svgrid/enterprise` | `jszip` |\r\n| Export PDF | `@svgrid/enterprise` | `pdfmake` |\r\n| Print | `@svgrid/enterprise` | nothing |\r\n\r\nThe Excel export is real OOXML - typed number and date cells, styled headers, a\r\nfrozen header row - not a renamed CSV. PDF is paginated with a repeating header,\r\nand Print opens the browser's print dialog on a paginated layout.\r\n\r\nAll six export what the user currently sees: the visible columns, in their\r\ncurrent order, over the filtered and sorted rows. The optional dependencies are\r\ndeclared only for the buttons you switch on, so a CSV-only app installs neither.\r\nThe canvas preview runs the same code the generated app does, so you can try a\r\nreal export before generating.\r\n\r\n## The analytical + companion blocks\r\n\r\nBeyond the grid, every block is still bound to the `EntitySchema` - these are data\r\nviews, not generic widgets:\r\n\r\n| Block | What it renders | Inspector |\r\n| --- | --- | --- |\r\n| **Chart** | A chart (`SvSchemaChart`) - bar, pie, line, area, radar, funnel, waterfall, or treemap. | Group-by dimension, measure, reduce, type. |\r\n| **Pivot** | A full pivot table (`SvPivotDesigner`) the end user can re-pivot live. | Row + column dimensions (checkboxes), a measure, and its aggregate. |\r\n| **Dashboard** | A schema-driven KPI + chart board (`SvSchemaDashboard`). | - |\r\n| **KPI** | A single reduced metric tile. | Label, measure, reduce. |\r\n| **Gauge** | A radial gauge (`SvGauge`) of one reduced measure within a range - utilization, progress, scores. | Label, measure, reduce, min / max, unit. |\r\n| **Tree** | A hierarchical tree (`SvTree`) built from the entity's own rows via a self-referential parent. | A label field + a parent field (a row's link to its parent row). |\r\n| **Tabs** | A tabbed container (`SvTabs`) that **groups display blocks** into tabs - e.g. an Overview tab of KPIs + a Details tab with a chart. | Add / rename / remove tabs; per tab, add child blocks (charts, KPIs, gauges, pivots, trees). |\r\n| **Accordion** | A collapsible-sections container (like Tabs, stacked vertically). | Add / rename / remove sections; child blocks per section. |\r\n| **Master / detail** | A row that expands into a nested grid of related records. | Child entity + foreign key. |\r\n| **Board** | A kanban board of the entity's rows, one lane per value of a group-by field, with drag between lanes. | Group-by, card title / subtitle / badge fields, open-screen drill. |\r\n| **Calendar** | A month event-calendar: each row with a date lands on its day, labelled and optionally color-coded. | Date field, title field, color field, open-screen drill. |\r\n| **Detail** | A full record \"detail page\": header, metric row, field sections, and related-record tabs - the 360 view a row action or drill-through opens. | Title / subtitle / status / metric fields, sections, related child entities. |\r\n| **Form** | A standalone create / edit form for the entity. | Presentation (drawer / modal / inline). |\r\n| **Filter panel** | A faceted sidebar that **filters the screen's grid** - enum / boolean facets pick a value, text facets search. | Title + which fields become facets. |\r\n| **Record panel** | Shows the row **selected in the grid** - a read-only field list, or an inline edit form. | Editable on / off, and (read-only) which fields to show. |\r\n| **Lookup** | Marks a relation field as a searchable picker in the edit form. | The relation field. |\r\n| **UI component** | A component from the SvGrid UI kit dropped from the toolbox - entity-agnostic, works on freestanding pages too. Grouped as Actions, Inputs, Display, Feedback, Layout, and Navigation, and covering headings and prose (heading, text, link, quote, code, keyboard key, list) as well as controls, pickers, and date/time inputs. | The component's own props (extracted from the component's own types, with its JSDoc as the tooltip), plus data bindings. |\r\n\r\nThe **filter** and **record** panels wire to the grid on the same screen: the\r\nfilter panel calls the grid controller's `setFilter`, and clicking a grid row\r\npublishes it to the record panel. So a common layout is a **filter panel + grid +\r\nrecord panel** three-up - list, narrow, inspect - all generated for you.\r\n\r\nThe grid, chart, pivot, and master-detail blocks are **height-resizable** (drag\r\nthe block's bottom edge, or set **Layout > Height**), and the chosen height flows\r\ninto the generated app. The filter and record panels size to their content.\r\n\r\n### Conditional formatting\r\n\r\nA grid's inspector has a **Conditional formatting** section: add no-code rules\r\nthat style a cell by its value - pick a field, a comparison (`=`, `<`, `>`,\r\n`contains`, `is empty`, ...), a value, and a **text color / fill / bold**. Rules\r\nrender **live in the canvas** and compile to the grid's built-in\r\n`conditionalFormats` rule engine in the generated app (e.g. negative `mrr` red,\r\n`status = overdue` filled). It's the same engine you'd use by hand - the designer\r\njust authors the rules.\r\n\r\n### Navigation & row actions\r\n\r\nThe grid's **Navigation & actions** section wires flow between screens:\r\n**drill-through** (row click opens another screen, filtered to the clicked value)\r\nand **row action buttons** (Edit / Delete / Open). A chart can drill too. See\r\n[Navigation & row actions](./navigation.md) for the full picture.\r\n\r\n## Data sources (per entity)\r\n\r\nEach entity binds to **its own backend** - the designer is not limited to one\r\ndata source per app. In the inspector (no block selected), the **Data source**\r\nsection binds the screen's entity to:\r\n\r\n- **In-memory** - seeded sample rows, runs with no backend (the default).\r\n- **Local database (no setup)** - a real, persistent Postgres ([PGlite](https://pglite.dev))\r\n running in the browser and saved to IndexedDB, so rows survive reloads with zero\r\n backend. Same SQL as production - swap to a hosted **SQL** source later without\r\n touching the schema. See [Local database](./local-database.md).\r\n The builder is a draggable, resizable, maximizable panel, and you can open it for\r\n any entity straight from the **Data model** dialog's **Configure** button.\r\n\r\n- **REST API** - a request builder: **method**, **base URL**, **path** (path\r\n params auto-derive from `{tokens}`), and **Query / Path / Header** tabs. **Send**\r\n runs it live and shows a **response table** plus the real rows in the canvas\r\n grid; **Import fields** rewrites the entity's schema to match the response keys.\r\n An **API format** picker (Manual / Offset + Limit / DummyJSON / json-server)\r\n wires a wire-format adapter so the grid does **real server-side paging and sort**;\r\n Manual keeps the rows-path / total-path mapping for a single fetched page.\r\n- **Supabase** - **List tables** reads your project's tables so you pick one, and\r\n **Import schema** pulls its real columns, primary key, foreign keys, and enums\r\n into the entity; **Preview** shows live rows (works in the online designer, since\r\n Supabase is HTTP). The project **URL + anon key are a shared connection** you set\r\n once for every Supabase entity. A **Live updates (Realtime)** toggle emits a live\r\n subscription. **Generate app** emits `createClient` in `connections.ts` reading\r\n `PUBLIC_SUPABASE_URL` / `PUBLIC_SUPABASE_ANON_KEY` from `.env` (the key is never\r\n inlined) and adds `@supabase/supabase-js`; access is protected by your RLS\r\n policies.\r\n- **SQL** - paste a connection string (the dialect is auto-detected) or fill the\r\n guided form, set the **Schema** (Postgres search path). In the **local** designer\r\n (`npx @svgrid/studio designer`) **Preview data** runs a real `SELECT` and shows\r\n your actual rows on the canvas, and a missing `pg` / `mysql2` / ... driver is a\r\n **one-click install**. **Generate app** emits a connected\r\n `src/routes/api/<table>/+server.ts` (the dialect's driver, reading\r\n `DATABASE_URL`) and points the grid at it; the driver dep is added for you. In\r\n the **online** designer, bind the entity and generate - the app connects for real\r\n at runtime. To scaffold from an existing DB via CLI: `npx @svgrid/studio add\r\n <table> --db <dialect> --url …`.\r\n\r\n**Generate app** then emits the matching adapter per entity in `src/lib/data.ts`\r\n(`createRestDataSource` / `createSqlDataSource` / `createSupabaseDataSource` /\r\n`createInMemoryDataSource`). SQL and Supabase entities read their connection from a\r\ngenerated `src/lib/connections.ts` (or the `+server.ts` route) - the SQL driver and\r\nthe Supabase client are wired for you; the only manual step is setting the\r\nconnection in `.env` (the bundle ships a `.env.example`). See\r\n[Databases](./databases.md) and [REST API](./rest-api.md).\r\n\r\n## Import a CSV / spreadsheet\r\n\r\nThe fastest way to start from **your own data**: **Import CSV** in the top bar\r\ntakes a `.csv` file and turns it into a running screen. The designer parses the\r\nfile (quoted fields, embedded newlines, and CRLF included), **infers a type per\r\ncolumn** from its values (number, boolean, date, or text - thousands separators\r\nand `yes/no/true/false` are understood), ensures a **primary key** (it reuses an\r\n`id` column or synthesizes one), and adds an entity with a full CRUD screen,\r\n**seeded with the real rows**. Unsafe headers (`First Name`, `E-mail`) become safe\r\nfield keys and the note tells you what was renamed.\r\n\r\nImported rows ship **in-memory** by default (no dependencies), so the app runs\r\nimmediately. Switch that entity's **Data source** to **Local database** to make\r\nthe same imported rows **persist** across reloads - the seed carries over. This is\r\nall client-side: `csvToEntity(name, text)` is a pure function exported from\r\n`@svgrid/enterprise`, so the same import works in the CLI and your own tools.\r\n\r\n> **Large files:** the imported rows are stored as the entity's seed, so they are\r\n> embedded in `studio.config.json` when you **Save** the design. That is fine for\r\n> reference data and samples; for a large dataset, import a representative sample\r\n> and point the entity at a **database** (Local database / SQL) for the full data.\r\n\r\n## Pages and layout\r\n\r\n- **Pages** - each screen is a route. In the inspector's **Page** section, toggle\r\n **Show in navigation**, set a **nav label** and **nav order**, or start a page\r\n from the **Empty** template. Hidden pages stay routable but drop out of the nav.\r\n- **Render mode** - eligible screens (a single plain grid, or read-only block\r\n screens, on a memory / SQL source) can switch from the default client page to\r\n **SSR**: an idiomatic `+page.server.ts` with `load` + form actions. The rules\r\n are in [Code generation](./code-generation.md#render-mode-spa-or-ssr-per-screen).\r\n- **App layout** - the rail's **App layout** section themes the generated shell:\r\n **Sidebar** or **Top navigation**, a **brand** name, a **company logo**\r\n (uploaded - stored inline and shown in the nav in place of the brand text), a\r\n **footer**, and (for the sidebar) the **nav position** (left / right). This\r\n drives the generated `src/routes/+layout.svelte`. The generated shell is\r\n **responsive**: on phones the sidebar collapses to a hamburger drawer, the\r\n top-nav links scroll, and each screen's block grid stacks to one column.\r\n\r\n## Save, regenerate, round-trip\r\n\r\nThe `StudioProject` is the persisted design. **Save config** exports a\r\n`studio.config.json`; regenerate the app from it any time:\r\n\r\n```bash\r\nnpm create @svgrid/studio@latest my-app -- --project ./studio.config.json\r\n```\r\n\r\nOr programmatically:\r\n\r\n```ts\r\nimport { serializeProject, parseProject, emitStudioProject } from '@svgrid/enterprise'\r\n\r\nconst json = serializeProject(project) // save the design\r\nconst project2 = parseProject(json) // reopen it\r\nconst files = emitStudioProject(project2) // -> the app's source files\r\n```\r\n\r\n**The exported app carries its own design.** The downloaded zip includes a\r\n`studio.config.json` at its root. To keep editing the app visually after you've\r\nworked on it locally, open the designer and **Load** that file - entities,\r\nscreens, blocks, theme, RBAC, i18n, and now the logo all come back exactly as\r\ngenerated. Because the designer regenerates the files under `src/`, keep any\r\nhand-written code in **new** files/modules you import, so a re-generate never\r\noverwrites it (or use the CLI's `svgrid:managed` markers - see\r\n[Code generation](./code-generation.md)).\r\n\r\n## Generate the app\r\n\r\n**Generate app** emits `src/lib/schemas.ts`, `src/lib/data.ts` (the right adapter\r\nper entity, plus `src/lib/connections.ts` when any entity is SQL / Supabase-bound),\r\nand one `src/routes/<route>/+page.svelte` **per screen** that composes that\r\nscreen's blocks with their config - a grid (with your visible columns, in order) +\r\nedit modal, plus any charts / pivots / dashboard / KPI tiles, and filter / record\r\npanels wired to the grid, all bound to the data - and the\r\nnav layout (sidebar or top-nav) + home. The pages are self-contained (they use\r\n`@svgrid/grid` + `@svgrid/enterprise` directly), so the output runs as a standard\r\nSvelteKit app.\r\n\r\n```ts\r\nimport { emitStudioProject } from '@svgrid/enterprise'\r\nconst files = emitStudioProject(project) // [{ path, contents, description }, ...]\r\n```\r\n\r\n## See also\r\n\r\n- [Code behind (Code view)](./code-behind.md) - write TypeScript against a typed `ctx` (grid API, events, `ctx.grid.sortable = true`, lifecycle)\r\n- [Sample apps + bind your data](./samples.md) - start from a ready-made app, then point it at your database\r\n- [Launch the designer](./launch.md) - `npx @svgrid/studio designer` (auto-save + generate to a folder)\r\n- [Schema designer](./designer.md) - author a single entity\r\n- [Dashboards](./dashboards.md) · [Databases](./databases.md) - the blocks + data sources\r\n- [CLI](./cli.md) / [Drizzle](./drizzle.md) / [Prisma](./prisma.md) - import a schema to design from\r\n"
|
|
2718
2718
|
},
|
|
2719
2719
|
{
|
|
2720
2720
|
"slug": "enterprise/studio/audit-log",
|
|
@@ -2750,13 +2750,13 @@ export const docs = [
|
|
|
2750
2750
|
"slug": "enterprise/studio/code-generation",
|
|
2751
2751
|
"path": "docs/enterprise/studio/code-generation.md",
|
|
2752
2752
|
"title": "Code generation",
|
|
2753
|
-
"markdown": "# Code generation\r\n\r\nStudio generates plain SvelteKit code you own - there is no runtime and no\r\nlock-in. The same `scaffold()` core powers the [CLI](./cli.md), the\r\n[AI generator](./ai-generation.md), and the [visual designer](./app-designer.md), so\r\nall three emit identical files.\r\n\r\n\r\n\r\n## What gets generated\r\n\r\nFrom one `EntitySchema`, three files:\r\n\r\n| File | Contents |\r\n| --- | --- |\r\n| `src/lib/<name>.schema.ts` | The `EntitySchema` literal + a typed row type. |\r\n| `src/routes/api/<name>/+server.ts` | The API route - `createKitHandlers` over a data source. |\r\n| `src/routes/<name>/+page.svelte` | The screen - grid + edit panel, wired to the route. |\r\n\r\nThe generated page is a full data screen: server-side sort, filter, and global\r\nsearch; a native pagination footer; a validated create / edit modal;\r\nmulti-select optimistic delete; and loading / error / empty states. Any\r\n`relation` (foreign-key) field also gets a searchable **lookup** wired to the\r\nrelated entity's API route ([Relations](./relations.md)).\r\n\r\n## The three files, up close\r\n\r\n**1. The schema** is the single source of truth - a plain literal you can edit by\r\nhand or regenerate:\r\n\r\n```ts\r\n// src/lib/customers.schema.ts\r\nimport type { EntitySchema } from '@svgrid/enterprise'\r\n\r\nexport type CustomersRow = {\r\n id: string; name: string; email: string; mrr: number; active: boolean\r\n}\r\n\r\nexport const customersSchema: EntitySchema<CustomersRow> = {\r\n name: 'customers',\r\n idField: 'id',\r\n fields: [\r\n { field: 'id', type: 'text', primaryKey: true, readonly: true },\r\n { field: 'name', type: 'text', required: true },\r\n { field: 'email', type: 'text', format: 'email', required: true },\r\n { field: 'mrr', type: 'number', min: 0 },\r\n { field: 'active', type: 'boolean' },\r\n ],\r\n}\r\n```\r\n\r\n**2. The API route** is a single `POST` endpoint. `createKitHandlers` speaks one\r\nJSON wire protocol for read + create + update + delete, so the client needs just\r\nthe one handler:\r\n\r\n```ts\r\n// src/routes/api/customers/+server.ts\r\nimport { createInMemoryDataSource, createKitHandlers } from '@svgrid/enterprise'\r\nimport { customersSchema, type CustomersRow } from '$lib/customers.schema'\r\n\r\nconst source = createInMemoryDataSource<CustomersRow>([], customersSchema)\r\n\r\nexport const { POST } = createKitHandlers({ schema: customersSchema, source })\r\n```\r\n\r\nSwap `createInMemoryDataSource` for a SQL, Supabase, or REST source and the page\r\ndoes not change - that is the [ServerDataSource](./data-binding.md) contract at\r\nwork.\r\n\r\n**3. The screen** wires the grid and the edit panel to that route through the\r\ncontroller - server-side sort / filter / page, the create-edit modal, and\r\noptimistic delete. It is a normal `+page.svelte` you own; see the\r\n[Studio live SQL demo](https://svgrid.com/demos/193-studio-live-sql/) for the\r\nwhole thing running.\r\n\r\n## Managed regions & safe regeneration\r\n\r\nEvery generated file wraps its body in markers:\r\n\r\n```ts\r\n// svgrid:managed:start\r\n// Regenerated by SvGrid Studio. Edits inside these markers are overwritten.\r\n...generated code...\r\n// svgrid:managed:end\r\n```\r\n\r\nRe-running generation replaces **only** the managed region and preserves\r\neverything outside it. So you can:\r\n\r\n- Add imports, helpers, and layout **outside** the markers - they survive\r\n regeneration.\r\n- Re-run `add` after a schema or database change to pull in new columns without\r\n losing your customizations.\r\n\r\nThis idempotent regeneration is what makes the generator safe to keep using,\r\nrather than a one-shot scaffold you fork away from.\r\n\r\n## Choosing the data source\r\n\r\nThe generated `+server.ts` backend depends on how you scaffold:\r\n\r\n| Scaffold | Backend |\r\n| --- | --- |\r\n| `--db <dialect>` | Connected to that driver via `process.env.DATABASE_URL`. |\r\n| `--sql` | `createSqlDataSource` with an `execute()` stub to fill in. |\r\n| `--from` (no `--db`) | In-memory, so the screen runs immediately. |\r\n\r\nSee [Databases](./databases.md) and [Data binding](./data-binding.md).\r\n\r\n## Render mode: SPA or SSR per screen\r\n\r\
|
|
2753
|
+
"markdown": "# Code generation\r\n\r\nStudio generates plain SvelteKit code you own - there is no runtime and no\r\nlock-in. The same `scaffold()` core powers the [CLI](./cli.md), the\r\n[AI generator](./ai-generation.md), and the [visual designer](./app-designer.md), so\r\nall three emit identical files.\r\n\r\n\r\n\r\n## What gets generated\r\n\r\nFrom one `EntitySchema`, three files:\r\n\r\n| File | Contents |\r\n| --- | --- |\r\n| `src/lib/<name>.schema.ts` | The `EntitySchema` literal + a typed row type. |\r\n| `src/routes/api/<name>/+server.ts` | The API route - `createKitHandlers` over a data source. |\r\n| `src/routes/<name>/+page.svelte` | The screen - grid + edit panel, wired to the route. |\r\n\r\nThe generated page is a full data screen: server-side sort, filter, and global\r\nsearch; a native pagination footer; a validated create / edit modal;\r\nmulti-select optimistic delete; and loading / error / empty states. Any\r\n`relation` (foreign-key) field also gets a searchable **lookup** wired to the\r\nrelated entity's API route ([Relations](./relations.md)).\r\n\r\n## The three files, up close\r\n\r\n**1. The schema** is the single source of truth - a plain literal you can edit by\r\nhand or regenerate:\r\n\r\n```ts\r\n// src/lib/customers.schema.ts\r\nimport type { EntitySchema } from '@svgrid/enterprise'\r\n\r\nexport type CustomersRow = {\r\n id: string; name: string; email: string; mrr: number; active: boolean\r\n}\r\n\r\nexport const customersSchema: EntitySchema<CustomersRow> = {\r\n name: 'customers',\r\n idField: 'id',\r\n fields: [\r\n { field: 'id', type: 'text', primaryKey: true, readonly: true },\r\n { field: 'name', type: 'text', required: true },\r\n { field: 'email', type: 'text', format: 'email', required: true },\r\n { field: 'mrr', type: 'number', min: 0 },\r\n { field: 'active', type: 'boolean' },\r\n ],\r\n}\r\n```\r\n\r\n**2. The API route** is a single `POST` endpoint. `createKitHandlers` speaks one\r\nJSON wire protocol for read + create + update + delete, so the client needs just\r\nthe one handler:\r\n\r\n```ts\r\n// src/routes/api/customers/+server.ts\r\nimport { createInMemoryDataSource, createKitHandlers } from '@svgrid/enterprise'\r\nimport { customersSchema, type CustomersRow } from '$lib/customers.schema'\r\n\r\nconst source = createInMemoryDataSource<CustomersRow>([], customersSchema)\r\n\r\nexport const { POST } = createKitHandlers({ schema: customersSchema, source })\r\n```\r\n\r\nSwap `createInMemoryDataSource` for a SQL, Supabase, or REST source and the page\r\ndoes not change - that is the [ServerDataSource](./data-binding.md) contract at\r\nwork.\r\n\r\n**3. The screen** wires the grid and the edit panel to that route through the\r\ncontroller - server-side sort / filter / page, the create-edit modal, and\r\noptimistic delete. It is a normal `+page.svelte` you own; see the\r\n[Studio live SQL demo](https://svgrid.com/demos/193-studio-live-sql/) for the\r\nwhole thing running.\r\n\r\n## Managed regions & safe regeneration\r\n\r\nEvery generated file wraps its body in markers:\r\n\r\n```ts\r\n// svgrid:managed:start\r\n// Regenerated by SvGrid Studio. Edits inside these markers are overwritten.\r\n...generated code...\r\n// svgrid:managed:end\r\n```\r\n\r\nRe-running generation replaces **only** the managed region and preserves\r\neverything outside it. So you can:\r\n\r\n- Add imports, helpers, and layout **outside** the markers - they survive\r\n regeneration.\r\n- Re-run `add` after a schema or database change to pull in new columns without\r\n losing your customizations.\r\n\r\nThis idempotent regeneration is what makes the generator safe to keep using,\r\nrather than a one-shot scaffold you fork away from.\r\n\r\n## Choosing the data source\r\n\r\nThe generated `+server.ts` backend depends on how you scaffold:\r\n\r\n| Scaffold | Backend |\r\n| --- | --- |\r\n| `--db <dialect>` | Connected to that driver via `process.env.DATABASE_URL`. |\r\n| `--sql` | `createSqlDataSource` with an `execute()` stub to fill in. |\r\n| `--from` (no `--db`) | In-memory, so the screen runs immediately. |\r\n\r\nSee [Databases](./databases.md) and [Data binding](./data-binding.md).\r\n\r\n## Render mode: SPA or SSR per screen\r\n\r\nAn **`ssr`** screen emits idiomatic server-rendered SvelteKit: a\r\n`+page.server.ts` with a `load` function and form `actions`, URL-driven sort /\r\nfilter / page state, and progressive enhancement. A **`spa`** screen emits a\r\nclient page where the browser talks to the API route through the data-source\r\ncontroller.\r\n\r\n**New apps built on a database or a REST API get `ssr` for free.** When you\r\ngenerate an app - `svgrid-studio init`, the designer's **New app** wizard, or\r\n`crudAppFromSchemas` - every screen that qualifies starts in `ssr`. You can still\r\nswitch any screen either way in the [app designer](./app-designer.md).\r\n\r\nIn-memory and PGlite apps stay `spa`, on purpose. Those sources are module\r\nsingletons, so a server-rendered screen would read and write the server's copy of\r\nthe rows while the app's remaining client screens read the browser's: add a row\r\non one and the other never sees it. SQL and REST have no such split, because\r\nevery path goes to the same database or the same remote API.\r\n\r\n### How the app is wired\r\n\r\nOnce an app has at least one server-rendered screen, the root `src/routes/+layout.ts`\r\nleaves SvelteKit's own default in place - server rendering on - and each\r\nclient-only screen opts out in its own `+page.ts`:\r\n\r\n```ts\r\n// src/routes/<screen>/+page.ts\r\nexport const ssr = false\r\n```\r\n\r\nSo the nav shell, the home page, and the sign-in pages all render on the server,\r\nand only the screens that fetch in the browser skip it. An app with nothing to\r\nserver-render keeps the single `export const ssr = false` in the root layout, as\r\nbefore.\r\n\r\nNot every screen shape can emit as SSR. The rules:\r\n\r\n- The screen must be entity-bound, without a [code-behind](./code-behind.md)\r\n companion.\r\n- Its data source must be `memory` (runs in-process), `sql` (reuses the connected\r\n `/api` route via `event.fetch`), or `rest` **on an absolute URL** (the server\r\n calls the remote API directly; a relative URL has no origin to resolve against\r\n there, so it stays SPA). `supabase` and `pglite` screens stay SPA - PGlite only\r\n exists in the browser, and a Supabase read carries the signed-in user's token,\r\n which a server-side call with the anon key would silently drop.\r\n- **A grid, optionally with a facet panel**, emits as load + form actions (no\r\n tree data, no scheduler view). The facet panel becomes a plain `GET` form\r\n whose controls are named for the URL params the `load` reads, so filtering\r\n works with JavaScript off and every filtered view has a shareable URL.\r\n- **Read-only block screens** - any mix of chart, pivot, dashboard, KPI, gauge,\r\n tree, detail, and master-detail - emit as a load-only page.\r\n- Anything else (boards, calendars, UI component blocks, containers,\r\n grids with extras) stays SPA.\r\n\r\nThe designer only offers the toggle when the screen qualifies; a screen set to\r\n`ssr` that stops qualifying falls back to the SPA emit.\r\n\r\n## Verification\r\n\r\nThe AI path compiles the generated page (via the Svelte compiler) before handing\r\nit back, and the recommended final step everywhere is your project's own check:\r\n\r\n```bash\r\nnpx svelte-check\r\n```\r\n\r\n## See also\r\n\r\n- [The Studio CLI](./cli.md) · [AI generation](./ai-generation.md) · [Visual app designer](./app-designer.md)\r\n"
|
|
2754
2754
|
},
|
|
2755
2755
|
{
|
|
2756
2756
|
"slug": "enterprise/studio/concepts",
|
|
2757
2757
|
"path": "docs/enterprise/studio/concepts.md",
|
|
2758
2758
|
"title": "Concepts",
|
|
2759
|
-
"markdown": "# Concepts\r\n\r\nThe mental model behind Studio: one `EntitySchema` drives the screens, the data\r\nbinding, and the generated code. This page walks that pipeline once, defines\r\nevery Studio term, and ends with a table for picking which build tool fits how\r\nyou work. Ten minutes here makes every other Studio page shorter.\r\n\r\n\r\n\r\n## The pipeline\r\n\r\nEverything in Studio is one flow, left to right:\r\n\r\n1. **An `EntitySchema` describes your data.** Field names, types, validation,\r\n labels, relations. You get one by introspection (a live database table, a\r\n Drizzle or Prisma schema file, an OpenAPI spec, a CSV, sample JSON) or by\r\n authoring it in the designer. See [The EntitySchema](./schema.md).\r\n2. **Screens arrange blocks over entities.** A screen is one route in your app.\r\n It holds blocks - a grid, a chart, a KPI tile, a board, a calendar - each\r\n bound to an entity. The visual [app designer](./app-designer.md) is where\r\n you compose them; the CLI and AI produce the same structures.\r\n3. **A `ServerDataSource` moves the data.** Read, create, update, delete - one\r\n small contract that every backend implements (SQL databases, Supabase,\r\n REST, in-memory, Postgres-in-the-browser). Sorting, filtering, paging, and\r\n editing work identically no matter where the data lives. See\r\n [Data binding](./data-binding.md).\r\n4. **Codegen writes real SvelteKit files.** The schema, a `+server.ts` API\r\n route, and a `+page.svelte` screen - plain code you own, no runtime, no\r\n proprietary host. Generated sections sit inside `svgrid:managed` region\r\n markers, so re-generating updates them without touching your edits. See\r\n [Code generation](./code-generation.md).\r\n\r\n\r\n\r\nA change flows forward automatically: add a field to the schema and the grid\r\ncolumn, the form input, its validation, and the generated code all pick it up.\r\n\r\n## The project model\r\n\r\nThe designer edits a single JSON document - the **project** - with this shape:\r\n\r\n- **project** - title, theme, default data source, plus optional\r\n [auth](./auth.md), [access control](./access-control.md),\r\n [audit](./audit-log.md), [i18n](./i18n.md), and deploy settings.\r\n - **entities** - one `EntitySchema` per table / collection.\r\n - **screens** - one per route. Each screen has:\r\n - **blocks** - the data-bound building pieces (grid, form, chart,\r\n dashboard, kpi, gauge, tree, tabs, accordion, master-detail, lookup,\r\n pivot, filter, record, board, calendar, detail, component).\r\n - a **layout** - `grid` (12-column flow, the default), `stack`, `split`\r\n (resizable panes), `dock` (dockable / floatable panes, see\r\n [Dock layout](./dock-layout.md)), or `canvas` (free-form placement on a\r\n 12-column cell grid).\r\n - a **render mode** - `
|
|
2759
|
+
"markdown": "# Concepts\r\n\r\nThe mental model behind Studio: one `EntitySchema` drives the screens, the data\r\nbinding, and the generated code. This page walks that pipeline once, defines\r\nevery Studio term, and ends with a table for picking which build tool fits how\r\nyou work. Ten minutes here makes every other Studio page shorter.\r\n\r\n\r\n\r\n## The pipeline\r\n\r\nEverything in Studio is one flow, left to right:\r\n\r\n1. **An `EntitySchema` describes your data.** Field names, types, validation,\r\n labels, relations. You get one by introspection (a live database table, a\r\n Drizzle or Prisma schema file, an OpenAPI spec, a CSV, sample JSON) or by\r\n authoring it in the designer. See [The EntitySchema](./schema.md).\r\n2. **Screens arrange blocks over entities.** A screen is one route in your app.\r\n It holds blocks - a grid, a chart, a KPI tile, a board, a calendar - each\r\n bound to an entity. The visual [app designer](./app-designer.md) is where\r\n you compose them; the CLI and AI produce the same structures.\r\n3. **A `ServerDataSource` moves the data.** Read, create, update, delete - one\r\n small contract that every backend implements (SQL databases, Supabase,\r\n REST, in-memory, Postgres-in-the-browser). Sorting, filtering, paging, and\r\n editing work identically no matter where the data lives. See\r\n [Data binding](./data-binding.md).\r\n4. **Codegen writes real SvelteKit files.** The schema, a `+server.ts` API\r\n route, and a `+page.svelte` screen - plain code you own, no runtime, no\r\n proprietary host. Generated sections sit inside `svgrid:managed` region\r\n markers, so re-generating updates them without touching your edits. See\r\n [Code generation](./code-generation.md).\r\n\r\n\r\n\r\nA change flows forward automatically: add a field to the schema and the grid\r\ncolumn, the form input, its validation, and the generated code all pick it up.\r\n\r\n## The project model\r\n\r\nThe designer edits a single JSON document - the **project** - with this shape:\r\n\r\n- **project** - title, theme, default data source, plus optional\r\n [auth](./auth.md), [access control](./access-control.md),\r\n [audit](./audit-log.md), [i18n](./i18n.md), and deploy settings.\r\n - **entities** - one `EntitySchema` per table / collection.\r\n - **screens** - one per route. Each screen has:\r\n - **blocks** - the data-bound building pieces (grid, form, chart,\r\n dashboard, kpi, gauge, tree, tabs, accordion, master-detail, lookup,\r\n pivot, filter, record, board, calendar, detail, component).\r\n - a **layout** - `grid` (12-column flow, the default), `stack`, `split`\r\n (resizable panes), `dock` (dockable / floatable panes, see\r\n [Dock layout](./dock-layout.md)), or `canvas` (free-form placement on a\r\n 12-column cell grid).\r\n - a **render mode** - `ssr` (emits idiomatic SvelteKit `+page.server.ts`\r\n load + form actions; the default for screens in a database-backed app) or\r\n `spa` (the page fetches through the data source in the browser). See\r\n [Code generation](./code-generation.md).\r\n - optional **code-behind** - a user-owned `handlers.ts` companion for\r\n event handlers, written once and never regenerated.\r\n\r\nWhen you run the [local designer](./launch.md), the project auto-saves to\r\n`studio.config.json` in your working folder as you edit; **Generate app** turns\r\nit into the SvelteKit project. The same file is what the\r\n[MCP tools](./ai-generation.md) read and write, so a coding agent and the\r\ndesigner can work on one project interchangeably.\r\n\r\n## Glossary\r\n\r\n| Term | Meaning |\r\n| --- | --- |\r\n| **EntitySchema** | The model of one entity: fields, types, validation, labels, relations. Everything else derives from it. [Schema](./schema.md) |\r\n| **Screen** | One route / page of the generated app; holds blocks and a layout. [App designer](./app-designer.md) |\r\n| **Block** | A data-bound piece placed on a screen: grid, chart, KPI, board, calendar, and so on. [App designer](./app-designer.md) |\r\n| **Companion block** | A block that works alongside a grid on the same screen and shares its data, like a filter panel or a record panel. [App designer](./app-designer.md) |\r\n| **Project model** | The single JSON document (`studio.config.json`) holding entities, screens, sources, theme, auth. This page, above |\r\n| **ServerDataSource** | The read + create + update + delete contract every backend implements. [Data binding](./data-binding.md) |\r\n| **Managed region** | A `svgrid:managed` marker pair in a generated file; regeneration rewrites only what is inside. [Code generation](./code-generation.md) |\r\n| **Code-behind** | A user-owned `handlers.ts` next to a generated screen for typed event handlers; created once, never overwritten. [Code-behind](./code-behind.md) |\r\n| **Scaffold** | The codegen step: schema in, SvelteKit files out. Shared by the CLI, the designer, and the AI path. [CLI](./cli.md) |\r\n| **Introspection** | Reading an existing source (database table, Drizzle / Prisma schema, OpenAPI spec, CSV) to produce an `EntitySchema`. [Databases](./databases.md) |\r\n| **Soft gate** | Enterprise licensing without a hard stop: unlicensed use shows a watermark and a console notice, nothing breaks. [Licensing](../licensing.md) |\r\n\r\n## Which tool when\r\n\r\nAll three build paths share one scaffold core and produce the same output, so\r\nthis is a workflow choice, not a feature choice - and you can switch anytime.\r\n\r\n| Tool | Pick it if | Page |\r\n| --- | --- | --- |\r\n| **CLI** - `npx @svgrid/studio add ...` | you want one deterministic command per screen, in scripts or CI, no AI involved | [The Studio CLI](./cli.md) |\r\n| **AI via MCP** - `@svgrid/mcp` | you already work in a coding agent (Claude Code, Cursor, ...) and want to describe screens in plain language | [AI generation](./ai-generation.md) |\r\n| **Visual designer** - `npx @svgrid/studio designer` | you want to see the app while composing it, or you are not writing code at all | [Visual app designer](./app-designer.md) |\r\n\r\n## See also\r\n\r\n- [Getting started](./getting-started.md) - build your first screen step by step\r\n- [Data binding](./data-binding.md) - the `ServerDataSource` contract in detail\r\n- [Code generation](./code-generation.md) - the emitted files and safe regeneration\r\n"
|
|
2760
2760
|
},
|
|
2761
2761
|
{
|
|
2762
2762
|
"slug": "enterprise/studio/dashboards",
|
|
@@ -2804,13 +2804,13 @@ export const docs = [
|
|
|
2804
2804
|
"slug": "enterprise/studio/edit-forms",
|
|
2805
2805
|
"path": "docs/enterprise/studio/edit-forms.md",
|
|
2806
2806
|
"title": "Edit forms & validation",
|
|
2807
|
-
"markdown": "# Edit forms & validation\r\n\r\n`SvGridEditPanel` is the create / edit form for a row. It renders itself from an\r\n`EntitySchema`, validates input, and hands you a ready payload to save. It\r\npresents as a right-hand **drawer**, a centered **modal**, or **inline**, and\r\nfollows the grid's light / dark theme.\r\n\r\n\r\n\r\n## Usage\r\n\r\n```svelte\r\n<script lang=\"ts\">\r\n import { SvGridEditPanel } from '@svgrid/enterprise'\r\n let editing = $state<Customer | null | undefined>(undefined) // undefined = closed, null = create\r\n\r\n async function save({ mode, id, values }) {\r\n if (mode === 'create') await controller.createRow(values)\r\n else if (id) await controller.updateRow(id, values)\r\n editing = undefined\r\n }\r\n</script>\r\n\r\n{#if editing !== undefined}\r\n <SvGridEditPanel\r\n {schema}\r\n row={editing}\r\n presentation=\"modal\"\r\n onSubmit={save}\r\n onCancel={() => (editing = undefined)}\r\n />\r\n{/if}\r\n```\r\n\r\n## Props\r\n\r\n| Prop | Type | Description |\r\n| --- | --- | --- |\r\n| `schema` | `EntitySchema<TData>` | Drives the fields, validation, and payload. |\r\n| `row` | `TData \\| null` | Row to edit; `null` to create. |\r\n| `presentation` | `'drawer'` \\| `'modal'` \\| `'inline'` | Default `'drawer'` (right slide-over). |\r\n| `title` | `string` | Heading override. |\r\n| `submitLabel` | `string` | Save-button label override. |\r\n| `onSubmit` | `(payload) => void \\| Promise` | Called with a validated `{ mode, id, values }`. Throw to surface an error. |\r\n| `onCancel` | `() => void` | Called on cancel / close (Esc, backdrop, or the X). |\r\n\r\n## Presentation\r\n\r\n- **`drawer`** (default) - slides in from the right, full height.\r\n- **`modal`** - centered popup with a blurred backdrop.\r\n- **`inline`** - renders in the page flow (used by the designer preview).\r\n\r\nDrawer and modal animate in / out, close on **Esc** or backdrop click, and trap\r\nto a dialog role.\r\n\r\n## Validation\r\n\r\nThe panel validates on submit and blocks the save when anything fails, showing a\r\nmessage under each field. Three layers, in order:\r\n\r\n1. **Required** - non-empty for `required` fields.\r\n2. **Built-in constraints** - number validity + `min` / `max`,\r\n `minLength` / `maxLength`, `format: 'email' | 'url'`, and `pattern` (see\r\n [The EntitySchema](./schema.md#built-in-validation)).\r\n3. **Standard Schema** - any Zod / Valibot / ArkType validator on `field.validate`.\r\n\r\n```ts\r\n{ field: 'email', type: 'text', required: true, format: 'email' }\r\n{ field: 'mrr', type: 'number', min: 0 }\r\n{ field: 'name', type: 'text', minLength: 2, maxLength: 60 }\r\n```\r\n\r\nNo external library is required for the built-in rules - add a Standard Schema\r\nvalidator only when you need custom logic.\r\n\r\n## Controls\r\n\r\nThe form renders each field with a control from the **editor suite**, not a bare\r\nnative input: numbers use `SvNumberInput` (spinners, min/max/step), booleans a\r\n`SvSwitchButton`, colors `SvColorInput`, passwords `SvPasswordInput` (strength\r\nmeter), ratings a `SvSlider`, dates and date-times a `SvDateTimePicker` (masked\r\ninput + calendar dropdown), enums a themed **dropdown** (`SvGridDropdown`), and\r\nJSON a textarea. The default follows the field type; override per field with\r\n`input.editorType`.\r\n\r\nBeyond the grid's cell editors, the form also offers a few **form-only** controls\r\nvia `input.editorType`: `phone` (`SvPhoneInput`), `country` (`SvCountryInput`),\r\n`mask` (`SvMaskedInput`, with an `input.mask` pattern like `'(999) 000-0000'`),\r\nand `slider`. In the [visual designer](./app-designer.md) each field has a\r\n**Control** picker (scoped to what fits its type) plus a **Wide** toggle\r\n(`input.span = 2`), so you pick the editor without touching code.\r\n\r\n```ts\r\n{ field: 'mrr', type: 'number', input: { editorType: 'slider' } }\r\n{ field: 'brand', type: 'text', input: { editorType: 'color' } }\r\n{ field: 'phone', type: 'text', input: { editorType: 'phone' } }\r\n{ field: 'ssn', type: 'text', input: { editorType: 'mask', mask: '999-99-9999' } }\r\n```\r\n\r\nForm-only editors degrade to a safe in-cell editor when the same field shows in a\r\ngrid (`slider` → number, `phone`/`country`/`mask` → text), so columns stay valid.\r\n\r\n**File / image upload.** Give a field an `upload` config and it renders\r\n`SvFileInput` (a picker with an image preview). With no handler it stores an\r\ninline data URL (no backend needed); pass an `uploads` handler that pushes to\r\nstorage and returns the URL:\r\n\r\n```svelte\r\n{ field: 'avatar', type: 'text', upload: { image: true, accept: 'image/*' } }\r\n\r\n<SvGridEditPanel {schema} row={editing}\r\n uploads={{ avatar: async (file) => await putToStorage(file) }} onSubmit={save} />\r\n```\r\n\r\n**Cascading (dependent) fields.** Compute a field's options from the current\r\nvalues with `dependentOptions` - the field clears when it stops being valid\r\n(e.g. City depends on Country):\r\n\r\n```svelte\r\n<SvGridEditPanel {schema} row={editing}\r\n dependentOptions={{ city: (values) => citiesByCountry[values.country] ?? [] }}\r\n onSubmit={save} />\r\n```\r\n\r\nSee the [rich fields demo](https://svgrid.com/demos/198-studio-form-fields/).\r\n\r\nEnum and `relation` fields use a custom dropdown whose panel **portals to\r\n`document.body`** (position: fixed), so it opens *above* a drawer or modal and\r\nnever grows the form (no scrollbar) - unlike a native `<select>` or an in-flow\r\npopup.\r\n\r\n## The modal is a movable window\r\n\r\nWith `presentation=\"modal\"`, the panel is a floating window: **drag** it by the\r\nheader, **resize** it from its edges (the content resizes with it), **maximize /\r\nrestore**, and **pin** it to any edge (left / top / bottom / right) - handy for\r\nkeeping the form docked beside the grid while you work. Pin again to unpin.\r\n\r\nSet **`persistKey`** to remember the window layout (pin / size / maximized) in\r\n`localStorage`, so it reopens where the user left it:\r\n\r\n```svelte\r\n<SvGridEditPanel {schema} row={editing} presentation=\"modal\" persistKey=\"customers\" ... />\r\n```\r\n\r\nOpen the editor on **double-click** (`onRowDoubleClick`), not single-click, so a\r\nclick can still select or interact with a row without popping the form.\r\n\r\n## See also\r\n\r\n- [The EntitySchema](./schema.md) - fields + validation constraints\r\n- [Master-detail](./master-detail.md) · [Data binding](./data-binding.md)\r\n"
|
|
2807
|
+
"markdown": "# Edit forms & validation\r\n\r\n`SvGridEditPanel` is the create / edit form for a row. It renders itself from an\r\n`EntitySchema`, validates input, and hands you a ready payload to save. It\r\npresents as a right-hand **drawer**, a centered **modal**, or **inline**, and\r\nfollows the grid's light / dark theme.\r\n\r\n\r\n\r\n## Usage\r\n\r\n```svelte\r\n<script lang=\"ts\">\r\n import { SvGridEditPanel } from '@svgrid/enterprise'\r\n let editing = $state<Customer | null | undefined>(undefined) // undefined = closed, null = create\r\n\r\n async function save({ mode, id, values }) {\r\n if (mode === 'create') await controller.createRow(values)\r\n else if (id) await controller.updateRow(id, values)\r\n editing = undefined\r\n }\r\n</script>\r\n\r\n{#if editing !== undefined}\r\n <SvGridEditPanel\r\n {schema}\r\n row={editing}\r\n presentation=\"modal\"\r\n onSubmit={save}\r\n onCancel={() => (editing = undefined)}\r\n />\r\n{/if}\r\n```\r\n\r\n## Props\r\n\r\n| Prop | Type | Description |\r\n| --- | --- | --- |\r\n| `schema` | `EntitySchema<TData>` | Drives the fields, validation, and payload. |\r\n| `row` | `TData \\| null` | Row to edit; `null` to create. |\r\n| `presentation` | `'drawer'` \\| `'modal'` \\| `'inline'` | Default `'drawer'` (right slide-over). |\r\n| `title` | `string` | Heading override. |\r\n| `submitLabel` | `string` | Save-button label override. |\r\n| `onSubmit` | `(payload) => void \\| Promise` | Called with a validated `{ mode, id, values }`. Throw to surface an error. |\r\n| `onCancel` | `() => void` | Called on cancel / close (Esc, backdrop, or the X). |\r\n\r\n## Presentation\r\n\r\n- **`drawer`** (default) - slides in from the right, full height.\r\n- **`modal`** - centered popup with a blurred backdrop.\r\n- **`inline`** - renders in the page flow (used by the designer preview).\r\n\r\nDrawer and modal animate in / out, close on **Esc** or backdrop click, and trap\r\nto a dialog role.\r\n\r\n## Validation\r\n\r\n**When it speaks up.** A field is checked when the user leaves it, not while they\r\nare still typing, so a form never scolds you for a value you have not finished\r\nentering. Once a field is showing an error it re-checks on every keystroke, so a\r\ncorrection clears the message straight away instead of making you submit again to\r\nfind out. A failed submit marks every field as visited, focuses the first one that\r\nneeds fixing, and lists them all in a summary at the top of the form that jumps to\r\na field when clicked.\r\n\r\nEach control carries `aria-invalid`, and its message (or its hint, when there is\r\nno error) is wired up with `aria-describedby`, so a screen reader announces the\r\nproblem with the field rather than leaving it to be discovered.\r\n\r\n**Closing a form with unsaved edits asks first.** Cancel, Escape, or a click on\r\nthe backdrop shows *Discard your changes?* in the footer, with **Keep editing**\r\nand **Discard**; a second Escape confirms. An untouched form closes immediately.\r\n\r\nThe save is blocked while anything fails. Three layers, in order:\r\n\r\n1. **Required** - non-empty for `required` fields.\r\n2. **Built-in constraints** - number validity + `min` / `max`,\r\n `minLength` / `maxLength`, `format: 'email' | 'url'`, and `pattern` (see\r\n [The EntitySchema](./schema.md#built-in-validation)).\r\n3. **Standard Schema** - any Zod / Valibot / ArkType validator on `field.validate`.\r\n\r\n```ts\r\n{ field: 'email', type: 'text', required: true, format: 'email' }\r\n{ field: 'mrr', type: 'number', min: 0 }\r\n{ field: 'name', type: 'text', minLength: 2, maxLength: 60 }\r\n```\r\n\r\nNo external library is required for the built-in rules - add a Standard Schema\r\nvalidator only when you need custom logic.\r\n\r\n### No-code rules\r\n\r\n`EntitySchema.validations` states cross-field rules as data, so the same rule\r\nruns in the form and in a generated app's server route:\r\n\r\n```ts\r\nvalidations: [\r\n { field: 'endsAt', op: 'gte', compareTo: 'startsAt', message: 'End must be after start' },\r\n { field: 'code', op: 'minLen', value: 4, message: 'Code needs 4+ characters' },\r\n]\r\n```\r\n\r\nOperators: `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `required`, `minLen`, `maxLen`.\r\nUse `compareTo` to compare against another field instead of a fixed `value`.\r\n\r\n## Laying the form out\r\n\r\n`EntitySchema.form` says how the form is arranged. It lives on the schema, not on\r\nthe component, so a form you have *built* travels with the entity: it round-trips\r\nthrough `studio.config.json`, generates into an app, and draws the same in the\r\nedit panel and in a server-rendered form.\r\n\r\n```ts\r\nconst customers: EntitySchema = {\r\n name: 'customers',\r\n fields: [/* ... */],\r\n form: {\r\n columns: 2,\r\n sections: [\r\n { title: 'Contact', description: 'How we reach them.', fields: ['name', 'email', 'phone'] },\r\n { title: 'Billing', columns: 1, fields: ['plan', 'vatNumber'] },\r\n // A whole section can be conditional, the same way a field is.\r\n { title: 'Cancellation', fields: ['reason', 'notes'],\r\n visibleWhen: { kind: 'cmp', column: 'status', op: 'equals', value: 'cancelled' } },\r\n ],\r\n },\r\n}\r\n```\r\n\r\n- `fields` gives both the grouping and the order.\r\n- A field in no section still renders, in a trailing untitled group. A form never\r\n silently drops one.\r\n- A section whose fields are all hidden disappears with them, heading included.\r\n- `columns` on a section overrides the form's for that group alone; a field with\r\n `input.span = 2` spans the full width.\r\n\r\n`SvGridEditPanel`'s `columns` and `sections` props still win when passed, for a\r\none-off arrangement of an otherwise shared schema.\r\n\r\n**Server-rendered screens follow the same layout.** A screen with\r\n`renderMode: 'ssr'` renders its sections, descriptions and column counts, marks\r\n`span: 2` fields full-width, shows each field's hint, and states the field's own\r\nconstraints (`minlength`, `maxlength`, `min`, `max`, `pattern`, and an `email` /\r\n`url` input type) as native HTML attributes so the browser catches an obvious\r\nmistake before a round-trip. The action re-checks all of it server-side\r\nregardless, so the attributes save a trip but never decide anything.\r\n\r\n## Fields that react to the answers\r\n\r\nA field can appear, lock, or become required based on what the user has already\r\nentered - the form asks for a reason only when it needs one, and never asks\r\ntwice.\r\n\r\nEach condition is a `PredicateExpr`: **data, not a function**, so it survives a\r\nround-trip through `studio.config.json`, generates into an app unchanged, and can\r\nbe edited in a UI.\r\n\r\n```ts\r\n{\r\n field: 'otherReason',\r\n type: 'text',\r\n when: {\r\n // Only asked for - and only demanded - when the reason is \"other\".\r\n visible: { kind: 'cmp', column: 'reason', op: 'equals', value: 'other' },\r\n required: { kind: 'cmp', column: 'reason', op: 'equals', value: 'other' },\r\n },\r\n}\r\n{\r\n field: 'approver',\r\n type: 'text',\r\n // Locked until the order is big enough to need sign-off.\r\n when: { disabled: { kind: 'cmp', column: 'total', op: 'lessThan', value: 1000 } },\r\n}\r\n```\r\n\r\nThree rules make this safe to rely on:\r\n\r\n- A field hidden by `visible` is **skipped by validation** and **left out of the\r\n submitted payload**. It can never block a save the user cannot fix, and a value\r\n they can no longer see is never written back. The generated SSR route applies\r\n the same rule to a posted form, so both paths save the same fields.\r\n- `required` **replaces** the static `required` flag rather than adding to it, so\r\n a rule can make a normally required field optional as well as the reverse.\r\n- A malformed condition falls back to showing and enabling the field. A broken\r\n rule degrades to an ordinary form instead of hiding data.\r\n\r\nConditions are the same expressions the alert rules use, so `parsePredicate` and\r\nthe `SvExpressionEditor` component both work on them:\r\n\r\n```ts\r\nimport { parsePredicate } from '@svgrid/enterprise'\r\nwhen: { visible: parsePredicate('reason = \"other\"') }\r\n```\r\n\r\nA section whose fields are all hidden disappears along with them, heading\r\nincluded.\r\n\r\nThree [sample apps](./samples.md) ship this, so you can open one and watch it\r\nwork: **CRM** asks a lost deal what it lost to, **Support desk** demands a\r\nresolution before a ticket can be resolved or closed (and only then asks for a\r\nCSAT rating), and **Insurance Claims** requires a justification to deny a claim\r\nand freezes the amount and deductible once it has been paid.\r\n\r\n## Controls\r\n\r\nThe form renders each field with a control from the **editor suite**, not a bare\r\nnative input: numbers use `SvNumberInput` (spinners, min/max/step), booleans a\r\n`SvSwitchButton`, colors `SvColorInput`, passwords `SvPasswordInput` (strength\r\nmeter), ratings a `SvSlider`, dates and date-times a `SvDateTimePicker` (masked\r\ninput + calendar dropdown), enums a themed **dropdown** (`SvGridDropdown`), and\r\nJSON a textarea. The default follows the field type; override per field with\r\n`input.editorType`.\r\n\r\nBeyond the grid's cell editors, the form also offers a few **form-only** controls\r\nvia `input.editorType`: `phone` (`SvPhoneInput`), `country` (`SvCountryInput`),\r\n`mask` (`SvMaskedInput`, with an `input.mask` pattern like `'(999) 000-0000'`),\r\nand `slider`. In the [visual designer](./app-designer.md) each field has a\r\n**Control** picker (scoped to what fits its type) plus a **Wide** toggle\r\n(`input.span = 2`), so you pick the editor without touching code.\r\n\r\n```ts\r\n{ field: 'mrr', type: 'number', input: { editorType: 'slider' } }\r\n{ field: 'brand', type: 'text', input: { editorType: 'color' } }\r\n{ field: 'phone', type: 'text', input: { editorType: 'phone' } }\r\n{ field: 'ssn', type: 'text', input: { editorType: 'mask', mask: '999-99-9999' } }\r\n```\r\n\r\nForm-only editors degrade to a safe in-cell editor when the same field shows in a\r\ngrid (`slider` → number, `phone`/`country`/`mask` → text), so columns stay valid.\r\n\r\n**File / image upload.** Give a field an `upload` config and it renders\r\n`SvFileInput` (a picker with an image preview). With no handler it stores an\r\ninline data URL (no backend needed); pass an `uploads` handler that pushes to\r\nstorage and returns the URL:\r\n\r\n```svelte\r\n{ field: 'avatar', type: 'text', upload: { image: true, accept: 'image/*' } }\r\n\r\n<SvGridEditPanel {schema} row={editing}\r\n uploads={{ avatar: async (file) => await putToStorage(file) }} onSubmit={save} />\r\n```\r\n\r\n**Cascading (dependent) fields.** Compute a field's options from the current\r\nvalues with `dependentOptions` - the field clears when it stops being valid\r\n(e.g. City depends on Country):\r\n\r\n```svelte\r\n<SvGridEditPanel {schema} row={editing}\r\n dependentOptions={{ city: (values) => citiesByCountry[values.country] ?? [] }}\r\n onSubmit={save} />\r\n```\r\n\r\nSee the [rich fields demo](https://svgrid.com/demos/198-studio-form-fields/).\r\n\r\nEnum and `relation` fields use a custom dropdown whose panel **portals to\r\n`document.body`** (position: fixed), so it opens *above* a drawer or modal and\r\nnever grows the form (no scrollbar) - unlike a native `<select>` or an in-flow\r\npopup.\r\n\r\n## The modal is a movable window\r\n\r\nWith `presentation=\"modal\"`, the panel is a floating window: **drag** it by the\r\nheader, **resize** it from its edges (the content resizes with it), **maximize /\r\nrestore**, and **pin** it to any edge (left / top / bottom / right) - handy for\r\nkeeping the form docked beside the grid while you work. Pin again to unpin.\r\n\r\nSet **`persistKey`** to remember the window layout (pin / size / maximized) in\r\n`localStorage`, so it reopens where the user left it:\r\n\r\n```svelte\r\n<SvGridEditPanel {schema} row={editing} presentation=\"modal\" persistKey=\"customers\" ... />\r\n```\r\n\r\nOpen the editor on **double-click** (`onRowDoubleClick`), not single-click, so a\r\nclick can still select or interact with a row without popping the form.\r\n\r\n## See also\r\n\r\n- [The EntitySchema](./schema.md) - fields + validation constraints\r\n- [Master-detail](./master-detail.md) · [Data binding](./data-binding.md)\r\n"
|
|
2808
2808
|
},
|
|
2809
2809
|
{
|
|
2810
2810
|
"slug": "enterprise/studio/getting-started",
|
|
2811
2811
|
"path": "docs/enterprise/studio/getting-started.md",
|
|
2812
2812
|
"title": "Getting started",
|
|
2813
|
-
"markdown": "# Getting started\r\n\r\nThis is the gentlest path into SvGrid Studio. By the end you will have a real,\r\nworking **Customers** screen - a grid you can sort, filter, and page, with a\r\ncreate / edit form and delete - running on your machine. No prior experience\r\nwith SvGrid is assumed.\r\n\r\nIf you would rather just click around first, open a live demo - no install\r\nneeded:\r\n\r\n- **[Studio live SQL](https://svgrid.com/demos/193-studio-live-sql/)** - the whole stack in the browser, backed by real Postgres via PGlite\r\n- **[Live SQL](https://svgrid.com/demos/193-studio-live-sql/)** - a real Postgres in the browser (PGlite)\r\n- **[Supabase](https://svgrid.com/demos/194-studio-supabase/)** - connect your own hosted Postgres\r\n\r\n**Choose your tutorial.** This page needs no database and teaches the whole\r\nshape; the one-page tutorials build the same screen against a real backend.\r\nThey all end in the same place, so pick by what you have:\r\n\r\n| You have | Follow | What it adds |\r\n| --- | --- | --- |\r\n| Nothing yet | this page | the full path: install, schema, generate, run, change |\r\n| A Postgres connection string | [Postgres CRUD grid](./postgres-grid.md) | a server route querying your database |\r\n| A Supabase project | [Supabase CRUD grid](./supabase-grid.md) | browser client, keys, Row-Level Security |\r\n| An HTTP / JSON API | [REST CRUD grid](./rest-grid.md) | the REST adapter, no server route at all |\r\n| An afternoon | [Build a CRM](./tutorial-crm.md) | multi-entity: relations, master-detail, a real DB |\r\n\r\n---\r\n\r\n## Guided path - answer a few questions, get the app\r\n\r\nIf you already know where your data lives, let Studio ask:\r\n\r\n```bash\r\nnpx @svgrid/studio init\r\n```\r\n\r\nIt asks four things - where the data comes from (sample data, your database,\r\nin-browser Postgres, or a REST API), which tables you want, which pages each\r\ntable gets, and what it should look like - then writes a runnable SvelteKit app\r\nwith a list, an edit form and a record page per table, plus an overview\r\ndashboard.\r\n\r\nPointing it at a real database is one line, and Studio installs the driver for\r\nyou:\r\n\r\n```bash\r\nnpx @svgrid/studio init --db postgres --url $DATABASE_URL --out my-app\r\n```\r\n\r\nOn Supabase there is no driver to install at all - it is read over the project's\r\nREST API:\r\n\r\n```bash\r\nnpx @svgrid/studio init --supabase-url https://xxxx.supabase.co --supabase-key $SUPABASE_ANON_KEY\r\n```\r\n\r\nPrefer clicking? The visual designer has the same wizard behind its **New app**\r\nbutton - or open [svgrid.com/studio/new](https://svgrid.com/studio/new) to start\r\none in the browser. Both paths run the same generator, so they produce the same\r\napp. See [The Studio CLI](./cli.md#init) for every flag.\r\n\r\n---\r\n\r\n## Fastest path - a downloadable, ready-to-run example\r\n\r\nRather have a working project on your machine than type code into a blank\r\nfile? One command scaffolds a complete SvelteKit app with everything already\r\nwired up:\r\n\r\n```bash\r\nnpm create @svgrid/studio@latest my-app\r\n```\r\n\r\nYou'll be asked to pick a **theme** - one of `@svgrid/grid`'s 19 built-in\r\npresets (shadcn, Tailwind, Material, Excel, Fluent, and more) - and whether to\r\nstart in **light or dark** mode. Scripting this instead? Both are flags:\r\n\r\n```bash\r\nnpm create @svgrid/studio@latest my-app -- --theme material --dark\r\n```\r\n\r\nThen:\r\n\r\n```bash\r\ncd my-app\r\nnpm install\r\nnpm run dev\r\n```\r\n\r\nOpen `http://localhost:5173`. You get:\r\n\r\n- A **nav shell** and a home page (`src/routes/+layout.svelte`).\r\n- Two linked entities - **Customers** and **Orders** - each a full grid +\r\n modal create/edit/delete screen. Orders has a searchable lookup back to\r\n Customers, so you can see how relations work.\r\n- **Seeded in-memory data** - nothing to install or configure, no database.\r\n- The **theme and mode you picked**, applied to the whole app - not just the grid.\r\n\r\nIt is a real project, not a read-only demo - edit it, add fields, connect a\r\ndatabase, deploy it. A few places to start:\r\n\r\n| Want to... | Edit |\r\n| --- | --- |\r\n| Add or change a field | `src/lib/schemas.ts` - the grid and the form update together |\r\n| See how a screen is built | `src/lib/EntityScreen.svelte` - the reusable grid + modal CRUD screen every route uses |\r\n| Connect a real database | `src/lib/data.ts` - swap `createInMemoryDataSource` for `createSqlDataSource` / `createSupabaseDataSource` (see [Databases](./databases.md)) |\r\n| Add another screen from a live table | `npx @svgrid/studio add invoices --db postgres --url \"$DATABASE_URL\"` |\r\n\r\nThe template's own `README.md` covers the same ground once you're in the\r\nproject. Prefer to see each piece built up by hand instead, or add a screen to\r\nan *existing* app rather than a fresh one? Continue below.\r\n\r\n---\r\n\r\n## Three ways to build - pick yours\r\n\r\n\r\n\r\nThis is the visual designer - a screens list on the left, your data previewed\r\nlive in the middle, and simple property panels on the right. You point, click, and\r\npress **Generate**:\r\n\r\n\r\n\r\n- **Visual designer (no code).** You never write code - you point, click, and\r\n preview, then press one button to generate the finished app. Try it\r\n immediately, no install, at **[svgrid.com/studio](https://svgrid.com/studio)** -\r\n or run it locally with **[Launch the designer](./launch.md)**, which\r\n auto-saves to disk and generates straight into a folder. The\r\n [sample apps](./samples.md) let you open a complete, realistic app in one\r\n click and point it at your own data.\r\n- **CLI.** One deterministic command per screen: `npx @svgrid/studio add ...`\r\n introspects your table or schema and writes the files. No AI involved. This\r\n page uses the CLI from Step 3 on - continue below.\r\n- **AI via MCP.** With [`@svgrid/mcp`](./ai-generation.md) configured, ask your\r\n coding agent to build the screen; it introspects, scaffolds, and\r\n compile-verifies through the same core the CLI uses.\r\n\r\nAll three produce the **same generated code** - pick whichever fits how you\r\nwork, and switch freely later.\r\n\r\n---\r\n\r\n## What you need\r\n\r\n- **[Node.js](https://nodejs.org) 18 or newer.** Check with `node -v` in a\r\n terminal. If that errors, install Node first.\r\n- **A terminal** and a code editor (VS Code is fine).\r\n- **A SvelteKit app.** Don't have one? Create one in 30 seconds:\r\n\r\n ```bash\r\n npx sv create my-app # pick \"SvelteKit minimal\", TypeScript: yes\r\n cd my-app\r\n npm install\r\n ```\r\n\r\nYou do **not** need a database to start - the first screen below runs on\r\nin-memory data. You can point it at PostgreSQL, Supabase, MySQL, and others\r\nlater without changing the UI.\r\n\r\n---\r\n\r\n## Step 1 - Install\r\n\r\nInside your app folder:\r\n\r\n```bash\r\nnpm i @svgrid/grid @svgrid/enterprise\r\n```\r\n\r\n- `@svgrid/grid` is the grid itself.\r\n- `@svgrid/enterprise` adds Studio: the schema, the edit form, and the data-source\r\n helpers. It is **soft-gate only** - everything runs unlicensed, it just nudges.\r\n See [licensing](../licensing.md).\r\n\r\n---\r\n\r\n## Step 2 - Describe your data once\r\n\r\nThe generator needs one description of your table. If you have a live database\r\nit can introspect it directly (Step 3 shows that variant). Here we stay\r\ndatabase-free: describe the table in a small Drizzle schema file, which the\r\ngenerator **reads as text** - it never connects to anything.\r\n\r\n```bash\r\nnpm i -D drizzle-orm\r\n```\r\n\r\n(`drizzle-orm` is only there so the schema file type-checks; nothing runs\r\nagainst a database. It is also the natural next step when you do add one.)\r\n\r\nCreate `src/lib/db/schema.ts`:\r\n\r\n```ts\r\nimport { pgTable, text, integer, boolean } from 'drizzle-orm/pg-core'\r\n\r\nexport const customers = pgTable('customers', {\r\n id: text('id').primaryKey(),\r\n name: text('name').notNull(),\r\n email: text('email').notNull(),\r\n tier: text('tier').notNull().default('free'),\r\n mrr: integer('mrr'),\r\n active: boolean('active'),\r\n})\r\n```\r\n\r\nA Prisma `schema.prisma` works the same way. Prefer to write Studio's own\r\nmodel - the `EntitySchema` - by hand instead? That is the\r\n[appendix](#appendix-wire-it-by-hand-no-generator) at the bottom of this page.\r\n\r\n---\r\n\r\n## Step 3 - Generate the screen\r\n\r\nOne command:\r\n\r\n```bash\r\nnpx @svgrid/studio add customers --from src/lib/db/schema.ts\r\n```\r\n\r\nHave a live database instead? Same command, different source - no schema file\r\nneeded:\r\n\r\n```bash\r\nnpx @svgrid/studio add customers --db postgres --url \"$DATABASE_URL\"\r\n```\r\n\r\nEither way it writes **three files**, and the screen is done. A quick tour of\r\nwhat you now own:\r\n\r\n**1. `src/lib/customers.schema.ts` - the model.** The generator turned your\r\ntable into an `EntitySchema` - the single object that drives the grid columns,\r\nthe form fields, and validation:\r\n\r\n```ts\r\nexport type CustomersRow = {\r\n id: string\r\n name: string\r\n email: string\r\n tier: string\r\n mrr: number | null\r\n active: boolean | null\r\n}\r\n\r\nexport const customersSchema: EntitySchema<CustomersRow> = {\r\n name: 'customers',\r\n idField: 'id',\r\n fields: [\r\n { field: 'id', type: 'text', primaryKey: true, readonly: true },\r\n { field: 'name', type: 'text', required: true },\r\n { field: 'email', type: 'text', required: true },\r\n // ...\r\n ],\r\n}\r\n```\r\n\r\nEvery field option (labels, enum choices, min/max, regex, custom validators) is\r\nexplained in [The EntitySchema](./schema.md).\r\n\r\n**2. `src/routes/api/customers/+server.ts` - the API route.** A\r\n`ServerDataSource` (read + create + update + delete) exposed over one SvelteKit\r\nendpoint. With `--from` it starts in-memory so it runs immediately; with `--db`\r\nit is already wired to your database:\r\n\r\n```ts\r\nconst source = createInMemoryDataSource<CustomersRow>([], customersSchema)\r\n\r\nexport const { POST } = createKitHandlers({ schema: customersSchema, source })\r\n```\r\n\r\nSwapping in a real database later means replacing that one `source` line - the\r\npage never changes. See [Databases](./databases.md).\r\n\r\n**3. `src/routes/customers/+page.svelte` - the screen.** The grid with\r\nserver-side sort, filter, global search, a native pagination footer,\r\nmulti-select delete with optimistic updates, and a modal create / edit form -\r\nall reading through the API route:\r\n\r\n```ts\r\nconst source = createKitDataSource<CustomersRow>({ endpoint: '/api/customers' })\r\nconst columns = schemaToColumns(customersSchema)\r\nconst controller = createServerDataSource<CustomersRow>(source, {\r\n pageSize: 25, optimistic: true,\r\n getRowId: (r) => String(r.id),\r\n onChange: (s) => (state = s),\r\n})\r\n```\r\n\r\nEach file wraps its generated body in `// svgrid:managed:start` /\r\n`// svgrid:managed:end` markers. Everything you write **outside** the markers\r\nis yours; re-running `add` only rewrites what is inside. That is what makes\r\nStep 5 safe.\r\n\r\n> One thing the generated screen inherits from your page: the **font**. A bare\r\n> `npx sv create` app sets no CSS at all, so add a\r\n> `body { font-family: system-ui, sans-serif }` rule (or a\r\n> [`--sg-font`](./theming.md) token) once, or the page renders in the\r\n> browser's default serif. Borders, backgrounds, and hover states the grid\r\n> themes itself.\r\n\r\n---\r\n\r\n## Step 4 - Run it\r\n\r\n```bash\r\nnpm run dev\r\n```\r\n\r\nOpen the URL it prints (usually `http://localhost:5173`) and go to\r\n**`/customers`**. With `--from` the grid starts empty (in-memory source, no\r\nseed) - click **New** and add two or three customers, then try the screen:\r\n\r\n- Click a **column header** to sort.\r\n- Type in the **filter row** under a header to filter (it stays focused as you type).\r\n- Click a **row** to edit. The form validates as you type - clear a required\r\n field and watch it complain.\r\n- Select rows with the checkboxes and **Delete** them - the grid updates\r\n instantly and rolls back if the server says no.\r\n- Page through with the **native pager** at the bottom.\r\n\r\n\r\n\r\n---\r\n\r\n## Step 5 - Change something\r\n\r\nBecause the schema drives everything, changes are one edit. Add a column to\r\n`src/lib/db/schema.ts`:\r\n\r\n```ts\r\ncountry: text('country'),\r\n```\r\n\r\nThen re-run the exact same command:\r\n\r\n```bash\r\nnpx @svgrid/studio add customers --from src/lib/db/schema.ts\r\n```\r\n\r\nThe managed regions are regenerated: the grid gets a **Country** column and the\r\nedit form gets a **Country** input. Anything you wrote outside the\r\n`svgrid:managed` markers - extra buttons, styles, handlers - is untouched.\r\nThat round trip (change schema, re-run, keep your code) is the everyday\r\nworkflow; [Code generation](./code-generation.md) explains the rules.\r\n\r\nPrefer not to re-run the generator? Editing the generated\r\n`customers.schema.ts` directly works too - grid and form update together from\r\nthe one schema object.\r\n\r\n---\r\n\r\n## Where to go next\r\n\r\nYou have the whole shape now. The usual next steps:\r\n\r\n- **Understand the model** - [Concepts](./concepts.md) walks the pipeline\r\n (schema, screens, data source, codegen) once and defines every Studio term.\r\n- **Connect a real database** - re-run `add` with `--db` and a connection\r\n string, or swap the one `source` line in the API route; the page does not\r\n change. For Supabase, follow the one-page\r\n **[Supabase CRUD grid tutorial](./supabase-grid.md)**; for SQL, see\r\n [Databases](./databases.md).\r\n- **Design visually** - `npx @svgrid/studio designer` opens the full app builder:\r\n compose screens across entities, bind data, and click *Generate app*. See the\r\n [Visual app designer](./app-designer.md). (To embed a single-entity schema editor\r\n in your own app, see the [Schema designer](./designer.md).)\r\n- **Build a full app** - the [Build a CRM tutorial](./tutorial-crm.md) wires up\r\n companies, contacts, and deals with relations and master-detail.\r\n\r\n---\r\n\r\n## Appendix: wire it by hand (no generator)\r\n\r\nEverything the generator wrote in Step 3 can be built up by hand - useful when\r\nyou want to see exactly how the pieces fit, or to embed a Studio screen in an\r\nunusual spot. Two files replace the three generated ones (no API route: here\r\nthe data source lives in the page itself).\r\n\r\nFirst, the `EntitySchema` - Studio's own model, the object the generator\r\nderived from your Drizzle file. Create `src/lib/customers.ts`:\r\n\r\n```ts\r\nimport type { EntitySchema } from '@svgrid/enterprise'\r\n\r\nexport type Customer = {\r\n id: string\r\n name: string\r\n email: string\r\n tier: 'free' | 'pro' | 'enterprise'\r\n mrr: number\r\n active: boolean\r\n}\r\n\r\nexport const customersSchema: EntitySchema<Customer> = {\r\n name: 'customers',\r\n label: 'Customer',\r\n idField: 'id',\r\n fields: [\r\n { field: 'id', type: 'text', primaryKey: true, readonly: true },\r\n { field: 'name', type: 'text', required: true, minLength: 2 },\r\n { field: 'email', type: 'text', label: 'Email', required: true, format: 'email' },\r\n { field: 'tier', type: 'enum', options: [\r\n { value: 'free', label: 'Free' },\r\n { value: 'pro', label: 'Pro' },\r\n { value: 'enterprise', label: 'Enterprise' },\r\n ] },\r\n { field: 'mrr', type: 'number', label: 'MRR ($)', min: 0 },\r\n { field: 'active', type: 'boolean' },\r\n ],\r\n}\r\n```\r\n\r\nThen the page. `createInMemoryDataSource` provides the `ServerDataSource`\r\ncontract over a plain array, `createServerDataSource` runs sort / filter /\r\npage / CRUD against it, and the grid + edit panel render it. Create\r\n`src/routes/customers/+page.svelte`:\r\n\r\n```svelte\r\n<script lang=\"ts\">\r\n import { SvGrid, createServerDataSource, type ServerState } from '@svgrid/grid'\r\n import { SvGridEditPanel, createInMemoryDataSource, schemaToColumns } from '@svgrid/enterprise'\r\n import { customersSchema, type Customer } from '$lib/customers'\r\n\r\n const seed: Customer[] = [\r\n { id: 'c1', name: 'Ada Lovelace', email: 'ada@analytic.io', tier: 'enterprise', mrr: 1200, active: true },\r\n { id: 'c2', name: 'Alan Turing', email: 'alan@bletchley.uk', tier: 'pro', mrr: 240, active: true },\r\n { id: 'c3', name: 'Grace Hopper', email: 'grace@navy.mil', tier: 'enterprise', mrr: 980, active: true },\r\n ]\r\n\r\n const columns = schemaToColumns(customersSchema)\r\n const source = createInMemoryDataSource(seed, customersSchema)\r\n\r\n let view = $state<ServerState<Customer>>({\r\n rows: [], total: 0, loading: false, saving: false, error: null,\r\n pageIndex: 0, pageSize: 10, pageCount: 1, sortModel: [], filterModel: {},\r\n })\r\n let editing = $state<Customer | null | undefined>(undefined)\r\n let genId = 4\r\n\r\n const controller = createServerDataSource(source, {\r\n pageSize: 10, optimistic: true, getRowId: (r) => r.id,\r\n onChange: (s) => (view = s),\r\n })\r\n controller.refresh()\r\n\r\n async function save({ mode, id, values }) {\r\n if (mode === 'create') { await controller.createRow({ id: `c${genId++}`, ...values }); controller.setPage(view.pageCount - 1) }\r\n else if (id) { await controller.updateRow(id, values) }\r\n editing = undefined\r\n }\r\n</script>\r\n\r\n<style>\r\n :global(body) {\r\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\r\n }\r\n</style>\r\n\r\n<button onclick={() => (editing = null)}>+ New customer</button>\r\n\r\n<SvGrid\r\n data={view.rows} {columns} loading={view.loading}\r\n fitColumns enableRowSummaries={false}\r\n sortable externalSort onSortingChange={(s) => controller.setSort(s)}\r\n filterable filterMode=\"row\" externalFilter\r\n onFiltersChange={(f) => controller.setFilter({\r\n global: f.global || undefined,\r\n columns: Object.fromEntries(f.columns.map((c) => [c.id, { operator: c.operator, value: c.value, valueTo: c.valueTo, selectedValues: c.selectedValues }])),\r\n })}\r\n onRowClick={(e) => (editing = e.row)}\r\n showPagination externalPagination\r\n rowCount={view.total} pageIndex={view.pageIndex} pageSize={view.pageSize}\r\n onPaginationChange={({ pageIndex, pageSize }) => pageSize !== view.pageSize ? controller.setPageSize(pageSize) : controller.setPage(pageIndex)}\r\n/>\r\n\r\n{#if editing !== undefined}\r\n <SvGridEditPanel schema={customersSchema} row={editing} presentation=\"modal\"\r\n onSubmit={save} onCancel={() => (editing = undefined)} />\r\n{/if}\r\n```\r\n\r\nThe `<style>` block is just a plain font reset - a fresh `npx sv create` app ships no CSS\r\nat all, so without it the page falls back to the browser's default serif font. `<SvGrid>`\r\nand `<SvGridEditPanel>` already theme their own borders, backgrounds, and hover states out\r\nof the box (via [`--sg-*` tokens](../../help/tokens.md) with built-in fallbacks) - font is\r\nthe one thing they intentionally inherit from the page rather than force, so it fits\r\nwhatever type your app already uses. If your app already sets a body font (or a\r\n[`--sg-font`](./theming.md) token), skip this block.\r\n\r\n---\r\n\r\n## See also\r\n\r\n- [SvGrid Studio overview](../studio.md)\r\n- [Concepts](./concepts.md) - the mental model + glossary\r\n- [Data binding](./data-binding.md) - the `ServerDataSource` contract in depth\r\n- [Edit forms & validation](./edit-forms.md)\r\n- [Troubleshooting & FAQ](./troubleshooting.md)\r\n"
|
|
2813
|
+
"markdown": "# Getting started\r\n\r\nThis is the gentlest path into SvGrid Studio. By the end you will have a real,\r\nworking **Customers** screen - a grid you can sort, filter, and page, with a\r\ncreate / edit form and delete - running on your machine. No prior experience\r\nwith SvGrid is assumed.\r\n\r\nIf you would rather just click around first, open a live demo - no install\r\nneeded:\r\n\r\n- **[Studio live SQL](https://svgrid.com/demos/193-studio-live-sql/)** - the whole stack in the browser, backed by real Postgres via PGlite\r\n- **[Supabase](https://svgrid.com/demos/194-studio-supabase/)** - connect your own hosted Postgres\r\n- **[The designer itself](https://svgrid.com/studio)** - open a sample app and edit it, nothing to install\r\n\r\n**Choose your tutorial.** This page needs no database and teaches the whole\r\nshape; the one-page tutorials build the same screen against a real backend.\r\nThey all end in the same place, so pick by what you have:\r\n\r\n| You have | Follow | What it adds |\r\n| --- | --- | --- |\r\n| Nothing yet | this page | the full path: install, schema, generate, run, change |\r\n| A Postgres connection string | [Postgres CRUD grid](./postgres-grid.md) | a server route querying your database |\r\n| A Supabase project | [Supabase CRUD grid](./supabase-grid.md) | browser client, keys, Row-Level Security |\r\n| An HTTP / JSON API | [REST CRUD grid](./rest-grid.md) | the REST adapter, no server route at all |\r\n| An afternoon | [Build a CRM](./tutorial-crm.md) | multi-entity: relations, master-detail, a real DB |\r\n\r\n---\r\n\r\n## Guided path - answer a few questions, get the app\r\n\r\nIf you already know where your data lives, let Studio ask:\r\n\r\n```bash\r\nnpx @svgrid/studio init\r\n```\r\n\r\nIt asks four things - where the data comes from (sample data, your database,\r\nin-browser Postgres, or a REST API), which tables you want, which pages each\r\ntable gets, and what it should look like - then writes a runnable SvelteKit app\r\nwith a list, an edit form and a record page per table, plus an overview\r\ndashboard.\r\n\r\nPointing it at a real database is one line, and Studio installs the driver for\r\nyou:\r\n\r\n```bash\r\nnpx @svgrid/studio init --db postgres --url $DATABASE_URL --out my-app\r\n```\r\n\r\nOn Supabase there is no driver to install at all - it is read over the project's\r\nREST API:\r\n\r\n```bash\r\nnpx @svgrid/studio init --supabase-url https://xxxx.supabase.co --supabase-key $SUPABASE_ANON_KEY\r\n```\r\n\r\nPrefer clicking? The visual designer has the same wizard behind its **New app**\r\nbutton - or open [svgrid.com/studio/new](https://svgrid.com/studio/new) to start\r\none in the browser. Both paths run the same generator, so they produce the same\r\napp. See [The Studio CLI](./cli.md#init) for every flag.\r\n\r\n---\r\n\r\n## Fastest path - a downloadable, ready-to-run example\r\n\r\nRather have a working project on your machine than type code into a blank\r\nfile? One command scaffolds a complete SvelteKit app with everything already\r\nwired up:\r\n\r\n```bash\r\nnpm create @svgrid/studio@latest my-app\r\n```\r\n\r\nYou'll be asked to pick a **theme** - one of `@svgrid/grid`'s 19 built-in\r\npresets (shadcn, Tailwind, Material, Excel, Fluent, and more) - and whether to\r\nstart in **light or dark** mode. Scripting this instead? Both are flags:\r\n\r\n```bash\r\nnpm create @svgrid/studio@latest my-app -- --theme material --dark\r\n```\r\n\r\nThen:\r\n\r\n```bash\r\ncd my-app\r\nnpm install\r\nnpm run dev\r\n```\r\n\r\nOpen `http://localhost:5173`. You get:\r\n\r\n- A **nav shell** and a home page (`src/routes/+layout.svelte`).\r\n- Two linked entities - **Customers** and **Orders** - each a full grid +\r\n modal create/edit/delete screen. Orders has a searchable lookup back to\r\n Customers, so you can see how relations work.\r\n- **Seeded in-memory data** - nothing to install or configure, no database.\r\n- The **theme and mode you picked**, applied to the whole app - not just the grid.\r\n\r\nIt is a real project, not a read-only demo - edit it, add fields, connect a\r\ndatabase, deploy it. A few places to start:\r\n\r\n| Want to... | Edit |\r\n| --- | --- |\r\n| Add or change a field | `src/lib/schemas.ts` - the grid and the form update together |\r\n| See how a screen is built | `src/lib/EntityScreen.svelte` - the reusable grid + modal CRUD screen every route uses |\r\n| Connect a real database | `src/lib/data.ts` - swap `createInMemoryDataSource` for `createSqlDataSource` / `createSupabaseDataSource` (see [Databases](./databases.md)) |\r\n| Add another screen from a live table | `npx @svgrid/studio add invoices --db postgres --url \"$DATABASE_URL\"` |\r\n\r\nThe template's own `README.md` covers the same ground once you're in the\r\nproject. Prefer to see each piece built up by hand instead, or add a screen to\r\nan *existing* app rather than a fresh one? Continue below.\r\n\r\n---\r\n\r\n## Three ways to build - pick yours\r\n\r\n\r\n\r\nThis is the visual designer - a screens list on the left, your data previewed\r\nlive in the middle, and simple property panels on the right. You point, click, and\r\npress **Generate**:\r\n\r\n\r\n\r\n- **Visual designer (no code).** You never write code - you point, click, and\r\n preview, then press one button to generate the finished app. Try it\r\n immediately, no install, at **[svgrid.com/studio](https://svgrid.com/studio)** -\r\n or run it locally with **[Launch the designer](./launch.md)**, which\r\n auto-saves to disk and generates straight into a folder. The\r\n [sample apps](./samples.md) let you open a complete, realistic app in one\r\n click and point it at your own data.\r\n- **CLI.** One deterministic command per screen: `npx @svgrid/studio add ...`\r\n introspects your table or schema and writes the files. No AI involved. This\r\n page uses the CLI from Step 3 on - continue below.\r\n- **AI via MCP.** With [`@svgrid/mcp`](./ai-generation.md) configured, ask your\r\n coding agent to build the screen; it introspects, scaffolds, and\r\n compile-verifies through the same core the CLI uses.\r\n\r\nAll three produce the **same generated code** - pick whichever fits how you\r\nwork, and switch freely later.\r\n\r\n---\r\n\r\n## What you need\r\n\r\n- **[Node.js](https://nodejs.org) 18 or newer.** Check with `node -v` in a\r\n terminal. If that errors, install Node first.\r\n- **A terminal** and a code editor (VS Code is fine).\r\n- **A SvelteKit app.** Don't have one? Create one in 30 seconds:\r\n\r\n ```bash\r\n npx sv create my-app # pick \"SvelteKit minimal\", TypeScript: yes\r\n cd my-app\r\n npm install\r\n ```\r\n\r\nYou do **not** need a database to start - the first screen below runs on\r\nin-memory data. You can point it at PostgreSQL, Supabase, MySQL, and others\r\nlater without changing the UI.\r\n\r\n---\r\n\r\n## Step 1 - Install\r\n\r\nInside your app folder:\r\n\r\n```bash\r\nnpm i @svgrid/grid @svgrid/enterprise\r\n```\r\n\r\n- `@svgrid/grid` is the grid itself.\r\n- `@svgrid/enterprise` adds Studio: the schema, the edit form, and the data-source\r\n helpers. It is **soft-gate only** - everything runs unlicensed, it just nudges.\r\n See [licensing](../licensing.md).\r\n\r\n---\r\n\r\n## Step 2 - Describe your data once\r\n\r\nThe generator needs one description of your table. If you have a live database\r\nit can introspect it directly (Step 3 shows that variant). Here we stay\r\ndatabase-free: describe the table in a small Drizzle schema file, which the\r\ngenerator **reads as text** - it never connects to anything.\r\n\r\n```bash\r\nnpm i -D drizzle-orm\r\n```\r\n\r\n(`drizzle-orm` is only there so the schema file type-checks; nothing runs\r\nagainst a database. It is also the natural next step when you do add one.)\r\n\r\nCreate `src/lib/db/schema.ts`:\r\n\r\n```ts\r\nimport { pgTable, text, integer, boolean } from 'drizzle-orm/pg-core'\r\n\r\nexport const customers = pgTable('customers', {\r\n id: text('id').primaryKey(),\r\n name: text('name').notNull(),\r\n email: text('email').notNull(),\r\n tier: text('tier').notNull().default('free'),\r\n mrr: integer('mrr'),\r\n active: boolean('active'),\r\n})\r\n```\r\n\r\nA Prisma `schema.prisma` works the same way. Prefer to write Studio's own\r\nmodel - the `EntitySchema` - by hand instead? That is the\r\n[appendix](#appendix-wire-it-by-hand-no-generator) at the bottom of this page.\r\n\r\n---\r\n\r\n## Step 3 - Generate the screen\r\n\r\nOne command:\r\n\r\n```bash\r\nnpx @svgrid/studio add customers --from src/lib/db/schema.ts\r\n```\r\n\r\nHave a live database instead? Same command, different source - no schema file\r\nneeded:\r\n\r\n```bash\r\nnpx @svgrid/studio add customers --db postgres --url \"$DATABASE_URL\"\r\n```\r\n\r\nEither way it writes **three files**, and the screen is done. A quick tour of\r\nwhat you now own:\r\n\r\n**1. `src/lib/customers.schema.ts` - the model.** The generator turned your\r\ntable into an `EntitySchema` - the single object that drives the grid columns,\r\nthe form fields, and validation:\r\n\r\n```ts\r\nexport type CustomersRow = {\r\n id: string\r\n name: string\r\n email: string\r\n tier: string\r\n mrr: number | null\r\n active: boolean | null\r\n}\r\n\r\nexport const customersSchema: EntitySchema<CustomersRow> = {\r\n name: 'customers',\r\n idField: 'id',\r\n fields: [\r\n { field: 'id', type: 'text', primaryKey: true, readonly: true },\r\n { field: 'name', type: 'text', required: true },\r\n { field: 'email', type: 'text', required: true },\r\n // ...\r\n ],\r\n}\r\n```\r\n\r\nEvery field option (labels, enum choices, min/max, regex, custom validators) is\r\nexplained in [The EntitySchema](./schema.md).\r\n\r\n**2. `src/routes/api/customers/+server.ts` - the API route.** A\r\n`ServerDataSource` (read + create + update + delete) exposed over one SvelteKit\r\nendpoint. With `--from` it starts in-memory so it runs immediately; with `--db`\r\nit is already wired to your database:\r\n\r\n```ts\r\nconst source = createInMemoryDataSource<CustomersRow>([], customersSchema)\r\n\r\nexport const { POST } = createKitHandlers({ schema: customersSchema, source })\r\n```\r\n\r\nSwapping in a real database later means replacing that one `source` line - the\r\npage never changes. See [Databases](./databases.md).\r\n\r\n**3. `src/routes/customers/+page.svelte` - the screen.** The grid with\r\nserver-side sort, filter, global search, a native pagination footer,\r\nmulti-select delete with optimistic updates, and a modal create / edit form -\r\nall reading through the API route:\r\n\r\n```ts\r\nconst source = createKitDataSource<CustomersRow>({ endpoint: '/api/customers' })\r\nconst columns = schemaToColumns(customersSchema)\r\nconst controller = createServerDataSource<CustomersRow>(source, {\r\n pageSize: 25, optimistic: true,\r\n getRowId: (r) => String(r.id),\r\n onChange: (s) => (state = s),\r\n})\r\n```\r\n\r\nEach file wraps its generated body in `// svgrid:managed:start` /\r\n`// svgrid:managed:end` markers. Everything you write **outside** the markers\r\nis yours; re-running `add` only rewrites what is inside. That is what makes\r\nStep 5 safe.\r\n\r\n> One thing the generated screen inherits from your page: the **font**. A bare\r\n> `npx sv create` app sets no CSS at all, so add a\r\n> `body { font-family: system-ui, sans-serif }` rule (or a\r\n> [`--sg-font`](./theming.md) token) once, or the page renders in the\r\n> browser's default serif. Borders, backgrounds, and hover states the grid\r\n> themes itself.\r\n\r\n---\r\n\r\n## Step 4 - Run it\r\n\r\n```bash\r\nnpm run dev\r\n```\r\n\r\nOpen the URL it prints (usually `http://localhost:5173`) and go to\r\n**`/customers`**. With `--from` the grid starts empty (in-memory source, no\r\nseed) - click **New** and add two or three customers, then try the screen:\r\n\r\n- Click a **column header** to sort.\r\n- Type in the **filter row** under a header to filter (it stays focused as you type).\r\n- Click a **row** to edit. The form validates as you type - clear a required\r\n field and watch it complain.\r\n- Select rows with the checkboxes and **Delete** them - the grid updates\r\n instantly and rolls back if the server says no.\r\n- Page through with the **native pager** at the bottom.\r\n\r\n\r\n\r\n---\r\n\r\n## Step 5 - Change something\r\n\r\nBecause the schema drives everything, changes are one edit. Add a column to\r\n`src/lib/db/schema.ts`:\r\n\r\n```ts\r\ncountry: text('country'),\r\n```\r\n\r\nThen re-run the exact same command:\r\n\r\n```bash\r\nnpx @svgrid/studio add customers --from src/lib/db/schema.ts\r\n```\r\n\r\nThe managed regions are regenerated: the grid gets a **Country** column and the\r\nedit form gets a **Country** input. Anything you wrote outside the\r\n`svgrid:managed` markers - extra buttons, styles, handlers - is untouched.\r\nThat round trip (change schema, re-run, keep your code) is the everyday\r\nworkflow; [Code generation](./code-generation.md) explains the rules.\r\n\r\nPrefer not to re-run the generator? Editing the generated\r\n`customers.schema.ts` directly works too - grid and form update together from\r\nthe one schema object.\r\n\r\n---\r\n\r\n## Where to go next\r\n\r\nYou have the whole shape now. The usual next steps:\r\n\r\n- **Understand the model** - [Concepts](./concepts.md) walks the pipeline\r\n (schema, screens, data source, codegen) once and defines every Studio term.\r\n- **Connect a real database** - re-run `add` with `--db` and a connection\r\n string, or swap the one `source` line in the API route; the page does not\r\n change. For Supabase, follow the one-page\r\n **[Supabase CRUD grid tutorial](./supabase-grid.md)**; for SQL, see\r\n [Databases](./databases.md).\r\n- **Design visually** - `npx @svgrid/studio designer` opens the full app builder:\r\n compose screens across entities, bind data, and click *Generate app*. See the\r\n [Visual app designer](./app-designer.md). (To embed a single-entity schema editor\r\n in your own app, see the [Schema designer](./designer.md).)\r\n- **Build a full app** - the [Build a CRM tutorial](./tutorial-crm.md) wires up\r\n companies, contacts, and deals with relations and master-detail.\r\n\r\n---\r\n\r\n## Appendix: wire it by hand (no generator)\r\n\r\nEverything the generator wrote in Step 3 can be built up by hand - useful when\r\nyou want to see exactly how the pieces fit, or to embed a Studio screen in an\r\nunusual spot. Two files replace the three generated ones (no API route: here\r\nthe data source lives in the page itself).\r\n\r\nFirst, the `EntitySchema` - Studio's own model, the object the generator\r\nderived from your Drizzle file. Create `src/lib/customers.ts`:\r\n\r\n```ts\r\nimport type { EntitySchema } from '@svgrid/enterprise'\r\n\r\nexport type Customer = {\r\n id: string\r\n name: string\r\n email: string\r\n tier: 'free' | 'pro' | 'enterprise'\r\n mrr: number\r\n active: boolean\r\n}\r\n\r\nexport const customersSchema: EntitySchema<Customer> = {\r\n name: 'customers',\r\n label: 'Customer',\r\n idField: 'id',\r\n fields: [\r\n { field: 'id', type: 'text', primaryKey: true, readonly: true },\r\n { field: 'name', type: 'text', required: true, minLength: 2 },\r\n { field: 'email', type: 'text', label: 'Email', required: true, format: 'email' },\r\n { field: 'tier', type: 'enum', options: [\r\n { value: 'free', label: 'Free' },\r\n { value: 'pro', label: 'Pro' },\r\n { value: 'enterprise', label: 'Enterprise' },\r\n ] },\r\n { field: 'mrr', type: 'number', label: 'MRR ($)', min: 0 },\r\n { field: 'active', type: 'boolean' },\r\n ],\r\n}\r\n```\r\n\r\nThen the page. `createInMemoryDataSource` provides the `ServerDataSource`\r\ncontract over a plain array, `createServerDataSource` runs sort / filter /\r\npage / CRUD against it, and the grid + edit panel render it. Create\r\n`src/routes/customers/+page.svelte`:\r\n\r\n```svelte\r\n<script lang=\"ts\">\r\n import { SvGrid, createServerDataSource, type ServerState } from '@svgrid/grid'\r\n import { SvGridEditPanel, createInMemoryDataSource, schemaToColumns } from '@svgrid/enterprise'\r\n import { customersSchema, type Customer } from '$lib/customers'\r\n\r\n const seed: Customer[] = [\r\n { id: 'c1', name: 'Ada Lovelace', email: 'ada@analytic.io', tier: 'enterprise', mrr: 1200, active: true },\r\n { id: 'c2', name: 'Alan Turing', email: 'alan@bletchley.uk', tier: 'pro', mrr: 240, active: true },\r\n { id: 'c3', name: 'Grace Hopper', email: 'grace@navy.mil', tier: 'enterprise', mrr: 980, active: true },\r\n ]\r\n\r\n const columns = schemaToColumns(customersSchema)\r\n const source = createInMemoryDataSource(seed, customersSchema)\r\n\r\n let view = $state<ServerState<Customer>>({\r\n rows: [], total: 0, loading: false, saving: false, error: null,\r\n pageIndex: 0, pageSize: 10, pageCount: 1, sortModel: [], filterModel: {},\r\n })\r\n let editing = $state<Customer | null | undefined>(undefined)\r\n let genId = 4\r\n\r\n const controller = createServerDataSource(source, {\r\n pageSize: 10, optimistic: true, getRowId: (r) => r.id,\r\n onChange: (s) => (view = s),\r\n })\r\n controller.refresh()\r\n\r\n async function save({ mode, id, values }) {\r\n if (mode === 'create') { await controller.createRow({ id: `c${genId++}`, ...values }); controller.setPage(view.pageCount - 1) }\r\n else if (id) { await controller.updateRow(id, values) }\r\n editing = undefined\r\n }\r\n</script>\r\n\r\n<style>\r\n :global(body) {\r\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\r\n }\r\n</style>\r\n\r\n<button onclick={() => (editing = null)}>+ New customer</button>\r\n\r\n<SvGrid\r\n data={view.rows} {columns} loading={view.loading}\r\n fitColumns enableRowSummaries={false}\r\n sortable externalSort onSortingChange={(s) => controller.setSort(s)}\r\n filterable filterMode=\"row\" externalFilter\r\n onFiltersChange={(f) => controller.setFilter({\r\n global: f.global || undefined,\r\n columns: Object.fromEntries(f.columns.map((c) => [c.id, { operator: c.operator, value: c.value, valueTo: c.valueTo, selectedValues: c.selectedValues }])),\r\n })}\r\n onRowClick={(e) => (editing = e.row)}\r\n showPagination externalPagination\r\n rowCount={view.total} pageIndex={view.pageIndex} pageSize={view.pageSize}\r\n onPaginationChange={({ pageIndex, pageSize }) => pageSize !== view.pageSize ? controller.setPageSize(pageSize) : controller.setPage(pageIndex)}\r\n/>\r\n\r\n{#if editing !== undefined}\r\n <SvGridEditPanel schema={customersSchema} row={editing} presentation=\"modal\"\r\n onSubmit={save} onCancel={() => (editing = undefined)} />\r\n{/if}\r\n```\r\n\r\nThe `<style>` block is just a plain font reset - a fresh `npx sv create` app ships no CSS\r\nat all, so without it the page falls back to the browser's default serif font. `<SvGrid>`\r\nand `<SvGridEditPanel>` already theme their own borders, backgrounds, and hover states out\r\nof the box (via [`--sg-*` tokens](../../help/tokens.md) with built-in fallbacks) - font is\r\nthe one thing they intentionally inherit from the page rather than force, so it fits\r\nwhatever type your app already uses. If your app already sets a body font (or a\r\n[`--sg-font`](./theming.md) token), skip this block.\r\n\r\n---\r\n\r\n## See also\r\n\r\n- [SvGrid Studio overview](../studio.md)\r\n- [Concepts](./concepts.md) - the mental model + glossary\r\n- [Data binding](./data-binding.md) - the `ServerDataSource` contract in depth\r\n- [Edit forms & validation](./edit-forms.md)\r\n- [Troubleshooting & FAQ](./troubleshooting.md)\r\n"
|
|
2814
2814
|
},
|
|
2815
2815
|
{
|
|
2816
2816
|
"slug": "enterprise/studio/i18n",
|
|
@@ -2828,7 +2828,7 @@ export const docs = [
|
|
|
2828
2828
|
"slug": "enterprise/studio/launch",
|
|
2829
2829
|
"path": "docs/enterprise/studio/launch.md",
|
|
2830
2830
|
"title": "Launch the designer (CLI)",
|
|
2831
|
-
"markdown": "# Launch the designer (CLI)\r\n\r\n> **No install at all?** Open **[svgrid.com/studio](https://svgrid.com/studio)**\r\n> directly - the same designer, running in your browser, nothing to run\r\n> locally. It can't write to your filesystem, so **Generate app** gives you a\r\n> **Download .zip** instead of the CLI's save-to-folder, and **Add data ->\r\n> Get a database** can provision a free Neon / Supabase / Turso database for\r\n> you without leaving the browser. Everything else - screens, entities, the\r\n> visual editor - is identical. Want auto-save-to-disk, or to point it at a\r\n> database already on your machine? Use the CLI below instead.\r\n\r\nOne command opens the visual [app designer](./app-designer.md) in your browser,\r\nbacked by a tiny local server that **auto-saves** your design to disk and\r\n**generates the app** into a folder - no host app to build, no copy-paste JSON.\r\n\r\n> **Three \"designer\" pages, one map.** This page is about *opening* the app\r\n> builder locally. [Visual app designer](./app-designer.md) documents the\r\n> builder itself (`SvStudioDesigner`, multi-screen). [Schema designer](./designer.md)\r\n> is a different, embeddable component (`SvSchemaDesigner`) for editing a single\r\n> entity inside your own app.\r\n\r\n\r\n\r\nYou run **one command** to open the designer; everything after that is\r\npoint-and-click - no coding.\r\n\r\n```bash\r\nnpx @svgrid/studio designer\r\n```\r\n\r\nThat serves the designer at `http://localhost:4321` and opens it. Design your\r\napp - screens, entities, data sources, grid config - and every edit is saved to\r\n`studio.config.json` in the current folder as you work. A refresh (or a rerun)\r\npicks up exactly where you left off.\r\n\r\n## What it does\r\n\r\n- **Loads** `studio.config.json` from the current folder. If there's none yet,\r\n the designer opens with a small starter project so you have something to edit.\r\n- **Auto-saves** every change back to `studio.config.json` (debounced). This is\r\n the persistence the embedded component never had - your work survives a\r\n refresh.\r\n- **Generates the app** to disk: open **Generate app**, then click **Save to\r\n folder**. Every file of the runnable SvelteKit + Vite project is written into\r\n the output folder (the driver deps for any SQL / Supabase entity are added to\r\n its `package.json` for you). **Download .zip** is still there too.\r\n\r\n```bash\r\ncd my-app # design saved here as studio.config.json\r\nnpx @svgrid/studio designer\r\n# ... design, then Generate app -> Save to folder ...\r\nnpm install\r\nnpm run dev\r\n```\r\n\r\n## Start from zero\r\n\r\n**\r\n> directly - the same designer, running in your browser, nothing to run\r\n> locally. It can't write to your filesystem, so **Generate app** gives you a\r\n> **Download .zip** instead of the CLI's save-to-folder, and **Add data ->\r\n> Get a database** can provision a free Neon / Supabase / Turso database for\r\n> you without leaving the browser. Everything else - screens, entities, the\r\n> visual editor - is identical. Want auto-save-to-disk, or to point it at a\r\n> database already on your machine? Use the CLI below instead.\r\n\r\nOne command opens the visual [app designer](./app-designer.md) in your browser,\r\nbacked by a tiny local server that **auto-saves** your design to disk and\r\n**generates the app** into a folder - no host app to build, no copy-paste JSON.\r\n\r\n> **Three \"designer\" pages, one map.** This page is about *opening* the app\r\n> builder locally. [Visual app designer](./app-designer.md) documents the\r\n> builder itself (`SvStudioDesigner`, multi-screen). [Schema designer](./designer.md)\r\n> is a different, embeddable component (`SvSchemaDesigner`) for editing a single\r\n> entity inside your own app.\r\n\r\n\r\n\r\nYou run **one command** to open the designer; everything after that is\r\npoint-and-click - no coding.\r\n\r\n```bash\r\nnpx @svgrid/studio designer\r\n```\r\n\r\nThat serves the designer at `http://localhost:4321` and opens it. Design your\r\napp - screens, entities, data sources, grid config - and every edit is saved to\r\n`studio.config.json` in the current folder as you work. A refresh (or a rerun)\r\npicks up exactly where you left off.\r\n\r\n## What it does\r\n\r\n- **Loads** `studio.config.json` from the current folder. If there's none yet,\r\n the designer opens with a small starter project so you have something to edit,\r\n and offers the **New app** wizard to replace it with your own data.\r\n- **Auto-saves** every change back to `studio.config.json` (debounced). This is\r\n the persistence the embedded component never had - your work survives a\r\n refresh.\r\n- **Generates the app** to disk: open **Generate app**, then click **Save to\r\n folder**. Every file of the runnable SvelteKit + Vite project is written into\r\n the output folder (the driver deps for any SQL / Supabase entity are added to\r\n its `package.json` for you). **Download .zip** is still there too.\r\n\r\n```bash\r\ncd my-app # design saved here as studio.config.json\r\nnpx @svgrid/studio designer\r\n# ... design, then Generate app -> Save to folder ...\r\nnpm install\r\nnpm run dev\r\n```\r\n\r\n## Start from zero\r\n\r\n\r\n\r\nLaunched in an empty folder, the designer opens the starter project and offers\r\nthe **New app** wizard - four steps (where the data lives, which tables, which\r\npages each table gets, what it's called) that end on a working app. It runs the\r\nsame generator as `svgrid-studio init`, so both routes land in the same place.\r\n\r\nPrefer to start from something finished? A ready-made [sample app](./samples.md)\r\n(`--template <id>`, or the **Sample apps** button) is one click. Or build the\r\ndata model yourself:\r\n\r\n- **New entity** - name it and get a screen with an `id` + `name` field; flesh\r\n out the rest (fields, types, PK / required / relations) in the inspector. The\r\n **New entity** button in the toolbar works any time, not just at the start.\r\n- **Connect a database** - see below.\r\n- **Import a schema** - paste a Drizzle `schema.ts` or Prisma `schema.prisma`.\r\n\r\nClear every screen out of a design and these same choices appear as the\r\ndesigner's start screen.\r\n\r\nOpening a [sample app](./samples.md) gives you a complete, themed app in one\r\nclick. Here the **CRM** sample - a dashboard with KPI tiles and a deal-pipeline\r\nchart, ready to explore or point at your own data:\r\n\r\n\r\n\r\n## Connect a live database\r\n\r\n**Connect DB** (toolbar) opens a wizard that reads your database and turns its\r\ntables into entities - no hand-typing a schema:\r\n\r\n\r\n\r\n\r\n1. Pick the **database** (PostgreSQL / MySQL / SQL Server / SQLite / Supabase)\r\n and paste a **connection string**.\r\n2. The designer lists the **tables**; tick the ones you want.\r\n3. **Add** them - each becomes an entity + screen, with columns typed from the\r\n catalog and **foreign keys turned into relations**. Every added entity is\r\n **bound to its SQL table**, so **Generate app** emits a connected\r\n `src/routes/api/<table>/+server.ts` (driver + `DATABASE_URL`) for it.\r\n\r\nEach database needs a driver (`pg` / `mysql2` / `mssql` / `better-sqlite3`), but\r\nyou don't have to install it yourself: Studio checks the folder you launched the\r\ndesigner in, detects its package manager (pnpm / yarn / bun / npm from the\r\nlockfile) and installs the missing one for you. If that fails - no network, a\r\nlocked file - it says which command to run by hand. The connection stays on your\r\nmachine: the local designer server does the read; nothing is sent to a cloud. To\r\nscaffold from a schema file or a live DB straight from the terminal instead, use\r\n[`npx @svgrid/studio add`](./cli.md).\r\n\r\n## Options\r\n\r\n| Flag | Default | Description |\r\n| --- | --- | --- |\r\n| `--config <path>` | `./studio.config.json` | The design file to load + auto-save. |\r\n| `--out <dir>` | `.` | Folder the generated app is written into (**Save to folder**). |\r\n| `--port <n>` | `4321` | Port to serve on. |\r\n| `--no-open` | - | Don't open the browser (print the URL only). |\r\n\r\n```bash\r\n# keep the design and the generated app in separate folders\r\nnpx @svgrid/studio designer --config ./design/app.json --out ./generated-app\r\n```\r\n\r\n## How it fits together\r\n\r\nThe design is a `StudioProject` (the same model the [app designer](./app-designer.md)\r\nedits and the [CLI](./cli.md) can regenerate from). `studio.config.json` is that\r\nmodel on disk, so the designer, `npx @svgrid/studio designer`, and\r\n`npm create @svgrid/studio -- --project ./studio.config.json` all read and write\r\nthe one file - design visually, regenerate from CI, or hand it to a teammate.\r\n\r\n## See also\r\n\r\n- [Visual app designer](./app-designer.md) - the canvas, blocks, and grid property editor\r\n- [The Studio CLI](./cli.md) - `add` a screen from a schema or a live database\r\n- [Getting started](./getting-started.md) - the full first-app walkthrough\r\n"
|
|
2832
2832
|
},
|
|
2833
2833
|
{
|
|
2834
2834
|
"slug": "enterprise/studio/local-database",
|
|
@@ -2894,7 +2894,7 @@ export const docs = [
|
|
|
2894
2894
|
"slug": "enterprise/studio/samples",
|
|
2895
2895
|
"path": "docs/enterprise/studio/samples.md",
|
|
2896
2896
|
"title": "Sample apps + bind your data",
|
|
2897
|
-
"markdown": "# Sample apps + bind your data\r\n\r\nThe fastest way to a working app: open a **ready-made sample**, see it running with\r\nrealistic data, then point it at **your own database**. No block-by-block build,\r\nno placeholder rows.\r\n\r\n\r\n\r\n## Start from a sample\r\n\r\nIn the designer, click **Sample apps** (toolbar or the
|
|
2897
|
+
"markdown": "# Sample apps + bind your data\r\n\r\nThe fastest way to a working app: open a **ready-made sample**, see it running with\r\nrealistic data, then point it at **your own database**. No block-by-block build,\r\nno placeholder rows.\r\n\r\n\r\n\r\n## Start from a sample\r\n\r\nIn the designer, click **Sample apps** (toolbar or the empty-state screen) and pick\r\none of **18** ready-made apps. Each loads instantly as a full multi-entity app - a\r\ndashboard (KPIs + charts), grids, a master/detail, and **rich edit forms** - themed\r\nand **seeded with believable data**, so you see the real thing, not `name 1 / email 1`.\r\n\r\n| Sample | What's inside |\r\n| --- | --- |\r\n| **CRM** | Companies, Contacts, Deals - pipeline dashboard (value by stage) |\r\n| **E-commerce admin** | Products, Customers, Orders - revenue dashboard + order history |\r\n| **Subscriptions** | Customers, Plans, Subscriptions - MRR + status dashboard |\r\n| **Invoicing** | Clients, Invoices - billed / paid / overdue dashboard |\r\n| **Inventory** | Products, Suppliers, Purchase orders - stock + spend |\r\n| **Support desk** | Tickets, Customers, Agents - triage by status / priority / channel |\r\n| **Recruiting** | Jobs, Candidates, Applications - hiring pipeline |\r\n| **HR** | Employees, Departments, Time off - headcount + payroll |\r\n| **Project tracker** | Projects, Tasks, Members - task dashboard per project |\r\n| **Clinic** | Patients, Doctors, Appointments - visits + revenue |\r\n| **School** | Students, Courses, Enrollments - capacity + enrollment |\r\n| **Events** | Events, Attendees, Registrations - ticket sales + attendance |\r\n| **Restaurant** | Menu, Tables, Orders - sales + order status |\r\n| **Real estate** | Properties, Agents, Leads - listings + sales pipeline |\r\n| **Fleet** | Vehicles, Drivers, Trips - utilization + cost |\r\n| **Gym** | Members, Classes, Bookings - capacity + attendance |\r\n| **Library** | Books, Members, Loans - circulation + overdue |\r\n| **Insurance Claims** | Policies, Adjusters, Claims - claims pipeline board + payout dashboard, behind a sign-in |\r\n\r\n\r\n\r\nLoading a sample replaces the current design (undo with Ctrl+Z). From the\r\n[launcher](./launch.md) you can open one directly:\r\n\r\n```bash\r\nnpx @svgrid/studio designer --template crm\r\n```\r\n\r\nEverything is editable - add fields, screens and blocks, retheme, then\r\n**Generate app** like any project.\r\n\r\n## Realistic sample data everywhere\r\n\r\nSample data is **field-name aware**: a `name` field gets a person, `email` an\r\nemail, `price` / `total` / `mrr` a currency amount, `status` cycles its options,\r\n`created` / `due` real dates, `company` a company. This applies to **every**\r\nin-memory entity - the shipped samples *and* the ones you build - in both the\r\nlive preview and the generated app's seed. Sample apps also carry **hand-curated\r\nseed** so their dashboards and charts look their best out of the box.\r\n\r\n## Rich edit forms\r\n\r\nEvery sample models its fields with the **right editor**, not a wall of text\r\nboxes - so an edit form looks and behaves like a real app. Across the gallery you\r\nget:\r\n\r\n- **Phone** and **country** pickers (SvPhoneInput / SvCountryInput), **masked**\r\n inputs for structured codes (SSN, VIN, license plate, ISBN, ZIP, tax id),\r\n- **rating** stars and **sliders** for scores and percentages (deal probability,\r\n usage, fuel level, tip),\r\n- **tag** inputs for multi-value fields (skills, amenities, dietary tags, segments),\r\n **color** pickers, **password** fields, and **date / date-time** pickers,\r\n- real **validation** (required, min/max, patterns) and **computed fields** - e.g.\r\n a deal's weighted value (`value * probability / 100`), an invoice total\r\n (`subtotal + tax`), or a trip's cost-per-mile - that recalculate as you type.\r\n\r\nEach sample also ships a **form screen**: a grid plus an inline edit panel, so you\r\ncan click a row and see the full editor form immediately. It's all declared on the\r\nentity's fields (`type`, `input.editorType`, `format`, `formula`), so it carries\r\nstraight through **Generate app** into the SvelteKit code - and you can change any\r\nfield's editor from the designer's field inspector.\r\n\r\n## Bind your data\r\n\r\n**Use my data** (toolbar) points a sample's screens at your real backend without\r\nrebuilding them:\r\n\r\n1. Connect: pick a database and either fill the **guided form** (host / port /\r\n database / user / password / SSL) or paste a connection string. **Test\r\n connection** confirms it and lists each table's row count before you commit.\r\n2. **Map** each entity to one of your tables - matches are auto-suggested by name;\r\n leave one as \"Keep sample data\" to skip it.\r\n3. **Bind**. Each mapped entity is bound to SQL, so **Generate app** emits a\r\n connected `src/routes/api/<table>/+server.ts` (driver + `DATABASE_URL`) for it,\r\n and the screens now render **your** data.\r\n\r\n**Columns are matched even when they're named differently.** A sample field is\r\nfuzzy-mapped to your column (exact -> synonyms -> substring): a deal's `value`\r\nbinds to your `amount` column, `stage` to `pipeline_stage`, and so on. The mapping\r\nis stored as the field's `dbColumn`, so the SQL adapter reads your real column and\r\naliases it back - **the dashboard's charts and KPIs keep working unchanged**. Your\r\nextra columns are added as new fields. If a chart or master/detail references a\r\nsample field with no matching column, the wizard lists it so you can adjust.\r\n\r\nThe database driver (`pg` / `mysql2` / `mssql` / `better-sqlite3`) must be\r\ninstalled where you launched the designer - if it's missing, the wizard offers a\r\none-click **Install driver** that runs your project's package manager. The\r\nconnection stays on your machine.\r\n\r\n## See also\r\n\r\n- [Launch the designer](./launch.md) - `npx @svgrid/studio designer` (+ `--template`)\r\n- [Visual app designer](./app-designer.md) - the canvas, blocks, and grid editor\r\n- [Databases](./databases.md) - the per-dialect connection details\r\n"
|
|
2898
2898
|
},
|
|
2899
2899
|
{
|
|
2900
2900
|
"slug": "enterprise/studio/scheduler",
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "commercial",
|
|
6
6
|
"url": "https://svgrid.com/pricing"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.
|
|
8
|
+
"version": "2.5.0",
|
|
9
9
|
"description": "Model Context Protocol (MCP) server for SvGrid. Exposes example sources, docs, and API reference to AI assistants.",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"author": "jQWidgets <sales@jqwidgets.com>",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
34
34
|
"zod": "^3.23.8",
|
|
35
|
-
"@svgrid/enterprise": "^2.
|
|
35
|
+
"@svgrid/enterprise": "^2.5.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^22.10.7",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "com.svgrid/svgrid",
|
|
4
4
|
"title": "SvGrid",
|
|
5
5
|
"description": "Version-pinned Svelte 5 data grid APIs, 373 demo sources, and SvelteKit app scaffolding.",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.5.0",
|
|
7
7
|
"websiteUrl": "https://svgrid.com/docs/help/mcp-server/",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/sv-grid/sv-grid",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
17
17
|
"identifier": "@svgrid/mcp",
|
|
18
|
-
"version": "2.
|
|
18
|
+
"version": "2.5.0",
|
|
19
19
|
"runtimeHint": "npx",
|
|
20
20
|
"transport": {
|
|
21
21
|
"type": "stdio"
|