@svgrid/mcp 2.3.2 → 2.3.3
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 +3 -3
- package/package.json +4 -3
- 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), pick a starter dataset, point at a REST\r\n 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 database needs the local designer (`npx @svgrid/studio dev`)\r\nbecause database drivers run on your machine, not in a browser tab. On\r\n[svgrid.com/studio](https://svgrid.com/studio) the other three paths work as-is,\r\nand you can rebind to your database later with **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\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) - 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\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",
|
|
@@ -2738,7 +2738,7 @@ export const docs = [
|
|
|
2738
2738
|
"slug": "enterprise/studio/cli",
|
|
2739
2739
|
"path": "docs/enterprise/studio/cli.md",
|
|
2740
2740
|
"title": "The Studio CLI",
|
|
2741
|
-
"markdown": "# The Studio CLI\r\n\r\n`@svgrid/studio` is the command-line generator. One command reads a schema or a\r\nlive database and writes a working CRUD screen into your SvelteKit app.\r\n\r\n\r\n\r\n> **New here?** Start with [Getting started](./getting-started.md) for the full\r\n> step-by-step path (create an app, install, first screen). This page is the CLI\r\n> reference.\r\n\r\n```bash\r\nnpx @svgrid/studio add <name> [--from <schema> | --db <dialect> --url <conn>] [options]\r\n```\r\n\r\n## `init`\r\n\r\nThe guided path: answer a few questions and get a working app. This is also what\r\nruns when you type `npx @svgrid/studio` with no arguments in a terminal.\r\n\r\n```bash\r\nnpx @svgrid/studio init [--db <dialect> --url <conn>] [--dataset <id>] [--out <dir>]\r\n```\r\n\r\nIt asks where the data comes from, which tables to include, which pages each\r\ntable gets, and which theme to use - then writes a runnable SvelteKit app plus a\r\n`studio.config.json` you can reopen in the [designer](./app-designer.md).\r\n\r\nEvery table you pick gets a **list** screen (searchable grid with status pills\r\nand totals), a **manage** screen (grid + editable record panel), and - when other\r\ntables relate to it - a **record page** with a tab per related collection. The\r\napp leads with an overview dashboard over its most-referenced table.\r\n\r\n| Flag | Description |\r\n| --- | --- |\r\n| `--db <dialect>` | Skip the source question and read a live database: `postgres` \\| `supabase` \\| `mysql` \\| `mssql` \\| `sqlite`. The driver is installed for you. |\r\n| `--url <conn>` | Connection string (or file path for SQLite) for `--db`. |\r\n| `--dataset <id>` | Start from sample data: `customers-orders`, `products-categories`, `projects-tasks`, `employees-departments`, `tickets-accounts`. |\r\n| `--title <name>` | App name. |\r\n| `--out <dir>` | Folder to write the app into (default: `.`). |\r\n| `--theme <id>` | Design-system preset (see [Theming](./theming.md)). |\r\n| `--dark` | Start in dark mode. |\r\n| `-y`, `--yes` | Take every default and ask nothing - useful in scripts and CI. |\r\n\r\n```bash\r\n# guided, from your own database\r\nnpx @svgrid/studio init --db postgres --url $DATABASE_URL --out my-app\r\n\r\n# no questions at all: a seeded sample app\r\nnpx @svgrid/studio init --yes --dataset projects-tasks --out demo\r\n```\r\n\r\nThe designer's **New app** button runs the same flow visually, and produces the\r\nsame project - pick whichever suits you.\r\n\r\n## `designer`\r\n\r\nOpen the visual [app designer](./app-designer.md) in your browser, auto-saving\r\nto `studio.config.json` and generating the app to a folder. See\r\n[Launch the designer](./launch.md) for the full guide.\r\n\r\n```bash\r\nnpx @svgrid/studio designer [--config <path>] [--out <dir>] [--port <n>] [--no-open]\r\n```\r\n\r\n## `add`\r\n\r\nGenerates three files for an entity: the schema module, the API route, and the\r\npage.\r\n\r\n| Flag | Description |\r\n| --- | --- |\r\n| `--from <path>` | Introspect a schema file: a Drizzle `schema.ts` or a Prisma `schema.prisma` (auto-detected). |\r\n| `--db <dialect>` | Connect to a live database: `postgres` \\| `supabase` \\| `mysql` \\| `mssql` \\| `sqlite`. |\r\n| `--url <conn>` | Connection string (or file path for SQLite) for `--db`. |\r\n| `--all` | Scaffold a screen for **every** table/model - works with `--from` or `--db`. |\r\n| `--table <name>` | Which table/model to use (defaults to `<name>`). |\r\n| `--sql` | Emit a `createSqlDataSource` with an `execute()` stub instead of a live driver. |\r\n| `--route <seg>` | Route segment (default: `<name>` / table name). |\r\n| `--api <path>` | API route path (default: `/api/<route>`). |\r\n| `-h`, `--help` | Show help. |\r\n\r\n## Examples\r\n\r\n```bash\r\n# from a live database\r\nnpx @svgrid/studio add customers --db postgres --url \"$DATABASE_URL\"\r\n\r\n# every table in the database\r\nnpx @svgrid/studio add --all --db mysql --url \"$DATABASE_URL\"\r\n\r\n# from a Drizzle schema file, a specific table, custom route\r\nnpx @svgrid/studio add orders --from src/lib/db/schema.ts --table orders --route sales/orders\r\n\r\n# every model in a Prisma schema (relations become lookups)\r\nnpx @svgrid/studio add --all --from prisma/schema.prisma\r\n\r\n# SQLite file\r\nnpx @svgrid/studio add todos --db sqlite --url ./data.db\r\n```\r\n\r\n## What it writes\r\n\r\nFor `add customers`:\r\n\r\n```\r\nsrc/lib/customers.schema.ts # EntitySchema + row type\r\nsrc/routes/api/customers/+server.ts # API route (createKitHandlers + a data source)\r\nsrc/routes/customers/+page.svelte # the screen: grid + edit panel\r\n```\r\n\r\nThen `npm run dev` and open `/customers`.\r\n\r\n## A whole app with `--all`\r\n\r\n`--all` scaffolds **every** table/model, plus an app shell that ties them\r\ntogether. It works from a live database (`--db`) **or** a schema file (`--from`\r\na Drizzle `schema.ts` or Prisma `schema.prisma`):\r\n\r\n```\r\nsrc/routes/+layout.svelte # nav sidebar linking every entity screen\r\nsrc/routes/+page.svelte # home page: a card per entity\r\nsrc/routes/<entity>/... # one screen per table/model (as above)\r\n```\r\n\r\nForeign keys are resolved across the whole set - a Drizzle `.references()` or a\r\nPrisma `@relation` becomes a searchable lookup - so each relation points at the\r\nright related screen and every `/api/<entity>` route it needs exists. The same\r\noutput is available programmatically as\r\n[`scaffoldApp(schemas, options)`](./code-generation.md) from `@svgrid/enterprise/studio`,\r\nfed by `introspectDrizzleAll` / `introspectPrismaAll`.\r\n\r\n## The other commands\r\n\r\n`designer` and `add` are the daily drivers; the binary has four more:\r\n\r\n| Command | What it does |\r\n| --- | --- |\r\n| `svgrid-studio dev` | The designer and the **running generated app side by side** - the designer writes real files into the app folder and Vite hot-reloads them. `--app-port <n>` sets the app's port. |\r\n| `svgrid-studio openapi <file\\|url>` | Import an OpenAPI (JSON) spec: paths + schemas become entities with REST sources in `studio.config.json`. See [REST & custom APIs](./rest-api.md). |\r\n| `svgrid-studio eject [--fragment]` | Write the full app (or, with `--fragment`, a drop-in set of files for an existing app) from `studio.config.json` without opening the designer. |\r\n| `svgrid-studio deploy [--target <p>] [--dry-run]` | Build, then deploy through the provider's own CLI. Target: `vercel` \\| `netlify` \\| `cloudflare` \\| `node`, resolved from the flag, the config, or `svelte.config.js`. `--dry-run` prints the commands. |\r\n\r\nUseful `designer` flags beyond the basics: `--template crm|ecommerce|projects|support`\r\nopens a [sample app](./samples.md) directly, and `--ai` enables the built-in\r\ncopilot (reads `ANTHROPIC_API_KEY` from the environment).\r\n\r\n## Safe regeneration\r\n\r\nEvery generated file wraps its body in `svgrid:managed` markers. Re-running\r\n`add` replaces **only** the managed region and preserves everything you wrote\r\noutside it - so you can regenerate after a schema change without losing\r\ncustomizations. See [Code generation](./code-generation.md).\r\n\r\n## Verification\r\n\r\nAfter writing files, run your project's own check to confirm they compile:\r\n\r\n```bash\r\nnpx svelte-check\r\n```\r\n\r\n## Requirements\r\n\r\n`@svgrid/grid` and `@svgrid/enterprise` in your project, plus the driver for your\r\ndatabase (`pg` / `mysql2` / `mssql` / `better-sqlite3`) when using `--db`. Studio\r\nis part of the [Enterprise license](../licensing.md) (soft-gate).\r\n\r\n## See also\r\n\r\n- [Databases](./databases.md) · [Drizzle schema](./drizzle.md)\r\n- [AI generation](./ai-generation.md) - the same engine, driven by an AI agent\r\n- [Visual app designer](./app-designer.md) - the same engine, driven by a UI\r\n"
|
|
2741
|
+
"markdown": "# The Studio CLI\r\n\r\n`@svgrid/studio` is the command-line generator. One command reads a schema or a\r\nlive database and writes a working CRUD screen into your SvelteKit app.\r\n\r\n\r\n\r\n> **New here?** Start with [Getting started](./getting-started.md) for the full\r\n> step-by-step path (create an app, install, first screen). This page is the CLI\r\n> reference.\r\n\r\n```bash\r\nnpx @svgrid/studio add <name> [--from <schema> | --db <dialect> --url <conn>] [options]\r\n```\r\n\r\n## `init`\r\n\r\nThe guided path: answer a few questions and get a working app. This is also what\r\nruns when you type `npx @svgrid/studio` with no arguments in a terminal.\r\n\r\n```bash\r\nnpx @svgrid/studio init [--db <dialect> --url <conn>] [--dataset <id>] [--out <dir>]\r\n```\r\n\r\nIt asks where the data comes from, which tables to include, which pages each\r\ntable gets, and which theme to use - then writes a runnable SvelteKit app plus a\r\n`studio.config.json` you can reopen in the [designer](./app-designer.md).\r\n\r\nEvery table you pick gets a **list** screen (searchable grid with status pills\r\nand totals), a **manage** screen (grid + editable record panel), and - when other\r\ntables relate to it - a **record page** with a tab per related collection. The\r\napp leads with an overview dashboard over its most-referenced table.\r\n\r\n| Flag | Description |\r\n| --- | --- |\r\n| `--db <dialect>` | Skip the source question and read a live database: `postgres` \\| `supabase` \\| `mysql` \\| `mssql` \\| `sqlite`. The driver is installed for you. |\r\n| `--url <conn>` | Connection string (or file path for SQLite) for `--db`. |\r\n| `--supabase-url <url>` | Read a Supabase project over its REST API instead - no driver, no connection string. |\r\n| `--supabase-key <anon>` | The project's anon key, paired with `--supabase-url`. |\r\n| `--dataset <id>` | Start from sample data: `customers-orders`, `products-categories`, `projects-tasks`, `employees-departments`, `tickets-accounts`. |\r\n| `--title <name>` | App name. |\r\n| `--out <dir>` | Folder to write the app into (default: `.`). |\r\n| `--theme <id>` | Design-system preset (see [Theming](./theming.md)). |\r\n| `--dark` | Start in dark mode. |\r\n| `-y`, `--yes` | Take every default and ask nothing - useful in scripts and CI. |\r\n\r\n```bash\r\n# guided, from your own database\r\nnpx @svgrid/studio init --db postgres --url $DATABASE_URL --out my-app\r\n\r\n# from Supabase, over the REST API (no driver to install)\r\nnpx @svgrid/studio init --supabase-url https://xxxx.supabase.co --supabase-key $SUPABASE_ANON_KEY\r\n\r\n# no questions at all: a seeded sample app\r\nnpx @svgrid/studio init --yes --dataset projects-tasks --out demo\r\n```\r\n\r\nThe designer's **New app** button runs the same flow visually, and produces the\r\nsame project - pick whichever suits you.\r\n\r\n## `designer`\r\n\r\nOpen the visual [app designer](./app-designer.md) in your browser, auto-saving\r\nto `studio.config.json` and generating the app to a folder. See\r\n[Launch the designer](./launch.md) for the full guide.\r\n\r\n```bash\r\nnpx @svgrid/studio designer [--config <path>] [--out <dir>] [--port <n>] [--no-open]\r\n```\r\n\r\n## `add`\r\n\r\nGenerates three files for an entity: the schema module, the API route, and the\r\npage.\r\n\r\n| Flag | Description |\r\n| --- | --- |\r\n| `--from <path>` | Introspect a schema file: a Drizzle `schema.ts` or a Prisma `schema.prisma` (auto-detected). |\r\n| `--db <dialect>` | Connect to a live database: `postgres` \\| `supabase` \\| `mysql` \\| `mssql` \\| `sqlite`. |\r\n| `--url <conn>` | Connection string (or file path for SQLite) for `--db`. |\r\n| `--all` | Scaffold a screen for **every** table/model - works with `--from` or `--db`. |\r\n| `--table <name>` | Which table/model to use (defaults to `<name>`). |\r\n| `--sql` | Emit a `createSqlDataSource` with an `execute()` stub instead of a live driver. |\r\n| `--route <seg>` | Route segment (default: `<name>` / table name). |\r\n| `--api <path>` | API route path (default: `/api/<route>`). |\r\n| `-h`, `--help` | Show help. |\r\n\r\n## Examples\r\n\r\n```bash\r\n# from a live database\r\nnpx @svgrid/studio add customers --db postgres --url \"$DATABASE_URL\"\r\n\r\n# every table in the database\r\nnpx @svgrid/studio add --all --db mysql --url \"$DATABASE_URL\"\r\n\r\n# from a Drizzle schema file, a specific table, custom route\r\nnpx @svgrid/studio add orders --from src/lib/db/schema.ts --table orders --route sales/orders\r\n\r\n# every model in a Prisma schema (relations become lookups)\r\nnpx @svgrid/studio add --all --from prisma/schema.prisma\r\n\r\n# SQLite file\r\nnpx @svgrid/studio add todos --db sqlite --url ./data.db\r\n```\r\n\r\n## What it writes\r\n\r\nFor `add customers`:\r\n\r\n```\r\nsrc/lib/customers.schema.ts # EntitySchema + row type\r\nsrc/routes/api/customers/+server.ts # API route (createKitHandlers + a data source)\r\nsrc/routes/customers/+page.svelte # the screen: grid + edit panel\r\n```\r\n\r\nThen `npm run dev` and open `/customers`.\r\n\r\n## A whole app with `--all`\r\n\r\n`--all` scaffolds **every** table/model, plus an app shell that ties them\r\ntogether. It works from a live database (`--db`) **or** a schema file (`--from`\r\na Drizzle `schema.ts` or Prisma `schema.prisma`):\r\n\r\n```\r\nsrc/routes/+layout.svelte # nav sidebar linking every entity screen\r\nsrc/routes/+page.svelte # home page: a card per entity\r\nsrc/routes/<entity>/... # one screen per table/model (as above)\r\n```\r\n\r\nForeign keys are resolved across the whole set - a Drizzle `.references()` or a\r\nPrisma `@relation` becomes a searchable lookup - so each relation points at the\r\nright related screen and every `/api/<entity>` route it needs exists. The same\r\noutput is available programmatically as\r\n[`scaffoldApp(schemas, options)`](./code-generation.md) from `@svgrid/enterprise/studio`,\r\nfed by `introspectDrizzleAll` / `introspectPrismaAll`.\r\n\r\n## The other commands\r\n\r\n`designer` and `add` are the daily drivers; the binary has four more:\r\n\r\n| Command | What it does |\r\n| --- | --- |\r\n| `svgrid-studio dev` | The designer and the **running generated app side by side** - the designer writes real files into the app folder and Vite hot-reloads them. `--app-port <n>` sets the app's port. |\r\n| `svgrid-studio openapi <file\\|url>` | Import an OpenAPI (JSON) spec: paths + schemas become entities with REST sources in `studio.config.json`. See [REST & custom APIs](./rest-api.md). |\r\n| `svgrid-studio eject [--fragment]` | Write the full app (or, with `--fragment`, a drop-in set of files for an existing app) from `studio.config.json` without opening the designer. |\r\n| `svgrid-studio deploy [--target <p>] [--dry-run]` | Build, then deploy through the provider's own CLI. Target: `vercel` \\| `netlify` \\| `cloudflare` \\| `node`, resolved from the flag, the config, or `svelte.config.js`. `--dry-run` prints the commands. |\r\n\r\nUseful `designer` flags beyond the basics: `--template crm|ecommerce|projects|support`\r\nopens a [sample app](./samples.md) directly, and `--ai` enables the built-in\r\ncopilot (reads `ANTHROPIC_API_KEY` from the environment).\r\n\r\n## Safe regeneration\r\n\r\nEvery generated file wraps its body in `svgrid:managed` markers. Re-running\r\n`add` replaces **only** the managed region and preserves everything you wrote\r\noutside it - so you can regenerate after a schema change without losing\r\ncustomizations. See [Code generation](./code-generation.md).\r\n\r\n## Verification\r\n\r\nAfter writing files, run your project's own check to confirm they compile:\r\n\r\n```bash\r\nnpx svelte-check\r\n```\r\n\r\n## Requirements\r\n\r\n`@svgrid/grid` and `@svgrid/enterprise` in your project, plus the driver for your\r\ndatabase (`pg` / `mysql2` / `mssql` / `better-sqlite3`) when using `--db`. Studio\r\nis part of the [Enterprise license](../licensing.md) (soft-gate).\r\n\r\n## See also\r\n\r\n- [Databases](./databases.md) · [Drizzle schema](./drizzle.md)\r\n- [AI generation](./ai-generation.md) - the same engine, driven by an AI agent\r\n- [Visual app designer](./app-designer.md) - the same engine, driven by a UI\r\n"
|
|
2742
2742
|
},
|
|
2743
2743
|
{
|
|
2744
2744
|
"slug": "enterprise/studio/code-behind",
|
|
@@ -2810,7 +2810,7 @@ export const docs = [
|
|
|
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\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- **[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"
|
|
2814
2814
|
},
|
|
2815
2815
|
{
|
|
2816
2816
|
"slug": "enterprise/studio/i18n",
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "commercial",
|
|
6
6
|
"url": "https://svgrid.com/pricing"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.3.
|
|
8
|
+
"version": "2.3.3",
|
|
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.3.
|
|
35
|
+
"@svgrid/enterprise": "^2.3.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^22.10.7",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"build:ts": "tsc -p tsconfig.json",
|
|
65
65
|
"build": "pnpm build:manifests && pnpm build:ts",
|
|
66
66
|
"start": "node dist/index.js",
|
|
67
|
-
"test:types": "tsc -p tsconfig.json --noEmit"
|
|
67
|
+
"test:types": "tsc -p tsconfig.json --noEmit",
|
|
68
|
+
"lint": "eslint ./src"
|
|
68
69
|
}
|
|
69
70
|
}
|
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.3.
|
|
6
|
+
"version": "2.3.3",
|
|
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.3.
|
|
18
|
+
"version": "2.3.3",
|
|
19
19
|
"runtimeHint": "npx",
|
|
20
20
|
"transport": {
|
|
21
21
|
"type": "stdio"
|