diff --git a/llms.txt b/llms.txt
deleted file mode 100644
index b648413..0000000
--- a/llms.txt
+++ /dev/null
@@ -1,1231 +0,0 @@
-# llms.txt — LLM Guide for Building Great Databricks Apps with AppKit
-Project: Databricks AppKit
-
-This document is written *for LLMs* generating code in a brand-new project folder that installs AppKit from npm. It is intentionally prescriptive.
-
-## High-level mission
-
-Build **full-stack TypeScript apps** on Databricks using:
-
-- **Backend**: `@databricks/appkit`
-- **Frontend**: `@databricks/appkit-ui`
-- **Analytics**: SQL files in `config/queries/*.sql` executed via the AppKit analytics plugin
-
-This file is designed to work even when you *do not* have access to the AppKit source repo. Prefer only public package APIs and portable project structures.
-
-## Hard rules (LLM guardrails)
-
-- **Do not invent APIs**. If unsure, stick to the patterns shown in this file and only documented exports from `@databricks/appkit` and `@databricks/appkit-ui`.
-- **`createApp()` is async**. Prefer **top-level `await createApp(...)`**. If you can’t, use `void createApp(...)` and do not ignore promise rejection.
-- **Always memoize query parameters** passed to `useAnalyticsQuery` / charts to avoid refetch loops.
-- **Always handle loading/error/empty states** in UI (use `Skeleton`, error text, empty state).
-- **Always use `sql.*` helpers** for query parameters (do not pass raw strings/numbers unless the query expects none).
-- **Never construct SQL strings dynamically**. Use parameterized queries with `:paramName`.
-- **Never use `require()`**. Use ESM `import/export`.
-
-## TypeScript import rules (when using `verbatimModuleSyntax`)
-
-If your `tsconfig.json` uses `"verbatimModuleSyntax": true`, **always use `import type` for type-only imports** (otherwise builds can fail in strict setups):
-
-```ts
-import type { ReactNode } from "react";
-import { useMemo } from "react";
-```
-
-## Canonical project layout
-
-Recommended structure (client/server split):
-
-```
-my-app/
-├── server/
-│ ├── index.ts # backend entry point (AppKit)
-│ └── .env # optional local dev env vars (do not commit)
-├── client/
-│ ├── index.html
-│ ├── vite.config.ts
-│ └── src/
-│ ├── main.tsx
-│ └── App.tsx
-├── config/
-│ └── queries/
-│ └── my_query.sql
-├── app.yaml
-├── package.json
-└── tsconfig.json
-```
-
-Why this layout:
-
-- The AppKit `server()` plugin automatically serves:
- - **Dev**: Vite dev server (HMR) from `client/`
- - **Prod**: static files from `client/dist` (built by Vite)
-
-## Project scaffolding (start here)
-
-### `package.json`
-
-```json
-{
- "name": "my-app",
- "private": true,
- "version": "0.0.0",
- "type": "module",
- "scripts": {
- "dev": "NODE_ENV=development tsx watch server/index.ts",
- "build": "npm run build:server && npm run build:client",
- "build:server": "tsdown --out-dir build server/index.ts",
- "build:client": "tsc -b && vite build --config client/vite.config.ts",
- "start": "node build/index.mjs"
- },
- "dependencies": {
- "@databricks/appkit": "^0.1.2"
- "@databricks/appkit-ui": "^0.1.2",
- "react": "^19.2.3",
- "react-dom": "^19.2.3"
- },
- "devDependencies": {
- "@types/node": "^20.0.0",
- "@types/react": "^19.0.0",
- "@types/react-dom": "^19.0.0",
- "@vitejs/plugin-react": "^5.1.1",
- "tsdown": "^0.15.7",
- "tsx": "^4.19.0",
- "typescript": "~5.6.0",
- "vite": "^7.2.4"
- }
-}
-```
-
-### `client/index.html`
-
-```html
-
-
-
-
-
- My App
-
-
-
-
-
-
-```
-
-### `client/src/main.tsx`
-
-```tsx
-import { StrictMode } from "react";
-import { createRoot } from "react-dom/client";
-import App from "./App";
-
-createRoot(document.getElementById("root")!).render(
-
-
- ,
-);
-```
-
-### `client/src/App.tsx` (minimal)
-
-```tsx
-export default function App() {
- return (
-
-
My App
-
- );
-}
-```
-
-### `client/vite.config.ts`
-
-```ts
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
-
-export default defineConfig({
- plugins: [react()],
-});
-```
-
-### `tsconfig.json`
-
-```json
-{
- "compilerOptions": {
- "target": "ES2022",
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "moduleResolution": "bundler",
- "jsx": "react-jsx",
- "strict": true,
- "skipLibCheck": true,
- "noEmit": true,
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true
- },
- "include": ["server", "client/src"]
-}
-```
-
-### `server/index.ts`
-
-```ts
-import { createApp, server } from "@databricks/appkit";
-
-await createApp({
- plugins: [server()],
-});
-```
-
-### Running the app
-
-```bash
-# Install dependencies
-npm install
-
-# Development (starts backend + Vite dev server)
-npm run dev
-
-# Production build
-npm run build
-npm start
-```
-
-## Integrating into an existing app
-
-If you already have a React/Vite app and want to add AppKit:
-
-### 1. Install dependencies
-
-```bash
-npm install @databricks/appkit @databricks/appkit-ui react react-dom
-npm install -D tsx tsdown vite @vitejs/plugin-react typescript
-
-# If you don't already have a client/ folder, create one and move your Vite app into it:
-# - move index.html -> client/index.html
-# - move vite.config.ts -> client/vite.config.ts
-# - move src/ -> client/src/
-#
-```
-
-### 2. Create `server/index.ts` (new file)
-
-```ts
-import { createApp, server } from "@databricks/appkit";
-
-await createApp({
- plugins: [server()],
-});
-```
-
-### 3. Update `package.json` scripts
-
-```json
-{
- "scripts": {
- "dev": "NODE_ENV=development tsx watch server/index.ts",
- "build": "npm run build:server && npm run build:client",
- "build:server": "tsdown --out-dir build server/index.ts",
- "build:client": "tsc -b && vite build --config client/vite.config.ts",
- "start": "node build/index.mjs"
- }
-}
-```
-
-### 4. That's it
-
-- AppKit's server plugin will automatically serve your Vite app in dev mode and `client/dist` in production.
-- If your Vite app must stay at the repo root (no `client/` folder), AppKit can still work, but the recommended layout is `client/` + `server/`.
-
-### Adding analytics to an existing app
-
-```ts
-// server/index.ts
-import { createApp, server, analytics } from "@databricks/appkit";
-
-await createApp({
- plugins: [server(), analytics()],
-});
-```
-
-Then create `config/queries/` and add your `.sql` files.
-
-## Environment variables
-
-### Required for Databricks Apps deployment
-
-These are typically **provided by Databricks Apps runtime** (exact set can vary by platform/version):
-
-| Variable | Description |
-|----------|-------------|
-| `DATABRICKS_HOST` | Workspace URL (e.g. `https://xxx.cloud.databricks.com`) |
-| `DATABRICKS_APP_PORT` | Port to bind (default: `8000`) |
-| `DATABRICKS_APP_NAME` | App name in Databricks |
-
-### Required for SQL queries (analytics plugin)
-
-| Variable | Description | How to set |
-|----------|-------------|------------|
-| `DATABRICKS_WAREHOUSE_ID` | SQL warehouse ID | In `app.yaml`: `valueFrom: sql-warehouse` |
-
-### Optional
-
-| Variable | Description | Default |
-|----------|-------------|---------|
-| `DATABRICKS_WORKSPACE_ID` | Workspace ID | Auto-fetched from API |
-| `NODE_ENV` | `"development"` or `"production"` | — |
-| `FLASK_RUN_HOST` | Host to bind | `0.0.0.0` |
-
-### Local development
-
-For local development, you need to authenticate with Databricks. Options:
-
-**Option 1: Databricks CLI Auth (recommended)**
-
-```bash
-# Configure once
-databricks auth login --host [host] --profile [profile-name]
-
-# If you used `DEFAULT` as the profile name then you can just run
-
-`npm run dev`
-
-# To run with a specific profile
-DATABRICKS_CONFIG_PROFILE=my-profile npm run dev
-# If your Databricks SDK expects a different variable name, try:
-# DATABRICKS_PROFILE=my-profile npm run dev
-```
-
-**Option 2: Environment variables**
-
-```bash
-export DATABRICKS_HOST="https://xxx.cloud.databricks.com"
-export DATABRICKS_TOKEN="dapi..."
-export DATABRICKS_WAREHOUSE_ID="abc123..."
-npm run dev
-```
-
-**Option 3: `.env` file (auto-loaded by AppKit)**
-
-```bash
-# .env (add to .gitignore!)
-DATABRICKS_HOST=https://xxx.cloud.databricks.com
-DATABRICKS_TOKEN=dapi...
-DATABRICKS_WAREHOUSE_ID=abc123...
-```
-
-### Telemetry (optional)
-
-| Variable | Description |
-|----------|-------------|
-| `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry collector endpoint |
-| `OTEL_SERVICE_NAME` | Service name for traces |
-
-## Backend: `@databricks/appkit`
-
-### Minimal server (golden template)
-
-The smallest valid AppKit server:
-
-```ts
-// server/index.ts
-import { createApp, server } from "@databricks/appkit";
-
-await createApp({
- plugins: [server()],
-});
-```
-
-### Server plugin (`server()`)
-
-What it does:
-
-- Starts an Express server (default `host=0.0.0.0`, `port=8000`)
-- Mounts plugin routes under `/api//...`
-- Adds `/health` (returns `{ status: "ok" }`)
-- Serves frontend:
- - **Development** (`NODE_ENV=development`): runs a Vite dev server in middleware mode
- - **Production**: auto-detects static frontend directory (checks `dist`, `client/dist`, `build`, `public`, `out`)
-
-Config (real options):
-
-```ts
-import { createApp, server } from "@databricks/appkit";
-
-await createApp({
- plugins: [
- server({
- port: 8000, // default: Number(process.env.DATABRICKS_APP_PORT) || 8000
- host: "0.0.0.0", // default: process.env.FLASK_RUN_HOST || "0.0.0.0"
- autoStart: true, // default: true
- staticPath: "dist", // optional: force a specific static directory
- }),
- ],
-});
-```
-
-Manual server start (when you need to `.extend()` Express):
-
-```ts
-import { createApp, server } from "@databricks/appkit";
-
-const appkit = await createApp({
- plugins: [server({ autoStart: false })],
-});
-
-appkit.server.extend((app) => {
- app.get("/custom", (_req, res) => res.json({ ok: true }));
-});
-
-await appkit.server.start();
-```
-
-### Analytics plugin (`analytics()`)
-
-Add SQL query execution backed by Databricks SQL Warehouses.
-
-```ts
-import { analytics, createApp, server } from "@databricks/appkit";
-
-await createApp({
- plugins: [server(), analytics({})],
-});
-```
-
-Where queries live:
-
-- Put `.sql` files in `config/queries/`.
-- Query key is the filename without `.sql` (e.g. `spend_summary.sql` → `"spend_summary"`).
-
-SQL parameters:
-
-- Use `:paramName` placeholders.
-- Optionally annotate parameter types using SQL comments:
-
-```sql
--- @param startDate DATE
--- @param endDate DATE
--- @param limit NUMERIC
-SELECT ...
-WHERE usage_date BETWEEN :startDate AND :endDate
-LIMIT :limit
-```
-
-Supported `-- @param` types (case-insensitive):
-
-- `STRING`, `NUMERIC`, `BOOLEAN`, `DATE`, `TIMESTAMP`, `BINARY`
-
-Server-injected params (important):
-
-- `:workspaceId` is **injected by the server** and **must not** be annotated.
-- Example:
-
-```sql
-WHERE workspace_id = :workspaceId
-```
-
-HTTP endpoints exposed (mounted under `/api/analytics`):
-
-- `POST /api/analytics/query/:query_key`
-- `POST /api/analytics/users/me/query/:query_key`
-- `GET /api/analytics/arrow-result/:jobId`
-- `GET /api/analytics/users/me/arrow-result/:jobId`
-
-Formats:
-
-- `format: "JSON"` (default) returns JSON rows
-- `format: "ARROW"` returns an Arrow “statement_id” payload over SSE, then the client fetches binary Arrow from `/api/analytics/arrow-result/:jobId`
-
-### Execution context and `asUser(req)`
-
-AppKit manages Databricks authentication via two contexts:
-
-- **ServiceContext** (singleton): Initialized at app startup with service principal credentials
-- **ExecutionContext**: Determined at runtime - either service principal or user context
-
-**Headers used for user context:**
-
-- `x-forwarded-user`: required in production; identifies the user
-- `x-forwarded-access-token`: required for user token passthrough
-
-**Using `asUser(req)` for user-scoped operations:**
-
-The `asUser(req)` pattern allows plugins to execute operations using the requesting user's credentials:
-
-```ts
-// In a custom plugin route handler
-router.post("/users/me/data", async (req, res) => {
- // Execute as the user (uses their Databricks permissions)
- const result = await this.asUser(req).query("SELECT ...");
- res.json(result);
-});
-
-// Service principal execution (default)
-router.post("/system/data", async (req, res) => {
- const result = await this.query("SELECT ...");
- res.json(result);
-});
-```
-
-**Context helper functions (exported from `@databricks/appkit`):**
-
-- `getExecutionContext()`: Returns current context (user or service)
-- `getCurrentUserId()`: Returns user ID in user context, service user ID otherwise
-- `getWorkspaceClient()`: Returns the appropriate WorkspaceClient for current context
-- `getWarehouseId()`: `Promise` (from `DATABRICKS_WAREHOUSE_ID` or auto-selected in dev)
-- `getWorkspaceId()`: `Promise` (from `DATABRICKS_WORKSPACE_ID` or fetched)
-- `isInUserContext()`: Returns `true` if currently executing in user context
-
-**Development mode behavior:**
-
-In local development (`NODE_ENV=development`), if `asUser(req)` is called without a user token, it logs a warning and falls back to the service principal.
-
-### Custom plugins (backend)
-
-If you need custom API routes or background logic, implement an AppKit plugin.
-
-```ts
-import { Plugin, toPlugin } from "@databricks/appkit";
-import type express from "express";
-
-class MyPlugin extends Plugin {
- name = "my-plugin";
- envVars = []; // list required env vars here
-
- injectRoutes(router: express.Router) {
- this.route(router, {
- name: "hello",
- method: "get",
- path: "/hello",
- handler: async (_req, res) => {
- res.json({ ok: true });
- },
- });
- }
-}
-
-export const myPlugin = toPlugin, "my-plugin">(
- MyPlugin,
- "my-plugin",
-);
-```
-
-### Caching (global + plugin-level)
-
-Global:
-
-```ts
-await createApp({
- plugins: [server(), analytics({})],
- cache: {
- enabled: true,
- ttl: 3600, // seconds
- strictPersistence: false,
- },
-});
-```
-
-- Storage auto-selects **Lakebase persistent cache when healthy**, otherwise falls back to in-memory.
-
-Plugin-level:
-
-```ts
-// inside a Plugin subclass:
-const value = await this.cache.getOrExecute(
- ["my-plugin", "data", userId],
- async () => expensiveWork(),
- userKey,
- { ttl: 300 },
-);
-```
-
-## Frontend: `@databricks/appkit-ui`
-
-### Imports
-
-- React-facing APIs: `@databricks/appkit-ui/react`
-- Non-React utilities (sql markers, arrow, SSE): `@databricks/appkit-ui/js`
-
-```tsx
-import { useAnalyticsQuery, Card, Skeleton } from "@databricks/appkit-ui/react";
-import { sql } from "@databricks/appkit-ui/js";
-```
-
-### `useAnalyticsQuery(queryKey, parameters, options?)`
-
-Facts:
-
-- Uses **SSE** under the hood (not `fetch()` polling).
-- By default it hits `POST /api/analytics/query/:queryKey`.
-- Returns `{ data, loading, error }` where `data` is `null` until loaded.
-- `format` is `"JSON"` or `"ARROW"` (uppercase).
-
-When to use it:
-
-- Use `useAnalyticsQuery` **only** when you need a custom UI (cards/KPIs/forms/conditional rendering).
-- If you just need a standard chart or table, prefer the built-in components (`BarChart`, `LineChart`, `DataTable`, etc.) so you don’t re-implement loading/error/empty states.
-
-Limitations (common LLM pitfall):
-
-- There is **no `enabled` option**. Use conditional rendering to mount/unmount the component.
-- There is **no `refetch()`**. Change `parameters` (memoized) or re-mount to re-run the query.
-
-Recommended usage pattern (memoized params + explicit states):
-
-```tsx
-import { useMemo } from "react";
-import { useAnalyticsQuery, Skeleton } from "@databricks/appkit-ui/react";
-import { sql } from "@databricks/appkit-ui/js";
-
-export function Users() {
- const params = useMemo(
- () => ({
- status: sql.string("active"),
- limit: sql.number(50),
- }),
- [],
- );
-
- const { data, loading, error } = useAnalyticsQuery("users_list", params);
-
- if (loading) return ;
- if (error) return Error: {error}
;
- if (!data || data.length === 0) return No results
;
-
- return {JSON.stringify(data[0], null, 2)};
-}
-```
-
-Options:
-
-- `format?: "JSON" | "ARROW"` (default `"JSON"`)
-- `autoStart?: boolean` (default `true`)
-- `maxParametersSize?: number` (default `100 * 1024` bytes)
-
-### `useChartData({ queryKey, parameters, format, transformer })`
-
-- `format` here is **lowercase**: `"json" | "arrow" | "auto"` (default `"auto"`)
-- Auto-selection heuristics:
- - If `parameters._preferArrow === true` → Arrow
- - If `parameters._preferJson === true` → JSON
- - If `parameters.limit` is a number > 500 → Arrow
- - If `parameters.startDate` and `parameters.endDate` exist → Arrow
-
-### Charts (unified query/data API)
-
-All charts support:
-
-- **Query mode**: `queryKey` + `parameters`
-- **Data mode**: `data` (inline JSON, no server)
-
-Available chart components:
-
-- `BarChart`, `LineChart`, `AreaChart`, `PieChart`, `DonutChart`, `HeatmapChart`, `ScatterChart`, `RadarChart`
-
-Avoid double-fetching:
-
-```tsx
-// ❌ Wrong: fetches the same query twice
-// const { data } = useAnalyticsQuery("spend_data", params);
-// return ;
-
-// ✅ Correct: let the chart fetch
-return ;
-```
-
-Query mode (recommended for Databricks-backed analytics):
-
-```tsx
-import { LineChart } from "@databricks/appkit-ui/react";
-import { sql } from "@databricks/appkit-ui/js";
-import { useMemo } from "react";
-
-export function SpendChart() {
- const params = useMemo(
- () => ({
- startDate: sql.date("2024-01-01"),
- endDate: sql.date("2024-12-31"),
- aggregationLevel: sql.string("day"),
- }),
- [],
- );
-
- return (
-
- );
-}
-```
-
-**Chart props reference (important):**
-
-Charts are **self-contained ECharts components**. Configure via props, NOT children:
-
-```tsx
-// ✅ Correct: use props for customization
-
-
-
-```
-
-**❌ CRITICAL: Charts do NOT accept Recharts children**
-
-```tsx
-// ❌ WRONG - AppKit charts are NOT Recharts wrappers
-import { BarChart } from "@databricks/appkit-ui/react";
-import { Bar, XAxis, YAxis, CartesianGrid } from "recharts";
-
-
- // ❌ This will cause TypeScript errors
- // ❌ Not supported
- // ❌ Not supported
-
-
-// ✅ CORRECT - use props instead
-
-```
-
-### SQL helpers (`sql.*`)
-
-Use these to build typed parameters (they return marker objects: `{ __sql_type, value }`):
-
-- `sql.string(value)` → STRING (accepts string|number|boolean)
-- `sql.number(value)` → NUMERIC (accepts number|string)
-- `sql.boolean(value)` → BOOLEAN (accepts boolean|string("true"/"false")|number(1/0))
-- `sql.date(value)` → DATE (accepts Date or `"YYYY-MM-DD"`)
-- `sql.timestamp(value)` → TIMESTAMP (accepts Date, ISO string, or unix time)
-
-Binary parameters (important):
-
-- Databricks SQL Warehouse doesn't support `BINARY` as a parameter type.
-- `sql.binary(value)` returns a **STRING marker containing hex**, so use `UNHEX(:param)` in SQL.
-- `sql.binary` accepts `Uint8Array`, `ArrayBuffer`, or a hex string.
-
-### SQL result types (important)
-
-Databricks SQL JSON results can return some numeric-like fields (especially `DECIMAL`) as strings. If a field behaves like a string at runtime, convert explicitly:
-
-```ts
-const value = Number(row.amount);
-```
-
-If you need more reliable numeric fidelity for large datasets, prefer `format: "ARROW"` and process Arrow on the client.
-
-### `connectSSE` (custom SSE connections)
-
-For custom streaming endpoints (not analytics), use the `connectSSE` utility:
-
-```tsx
-import { connectSSE } from "@databricks/appkit-ui/js";
-import { useEffect, useState } from "react";
-
-function useCustomStream(endpoint: string) {
- const [messages, setMessages] = useState([]);
- const [connected, setConnected] = useState(false);
-
- useEffect(() => {
- const controller = new AbortController();
-
- connectSSE({
- url: endpoint,
- payload: { key: "value" }, // optional: makes it a POST
- onMessage: async ({ data }) => {
- setConnected(true);
- setMessages((prev) => [...prev, data]);
- },
- onError: (error) => {
- console.error("SSE error:", error);
- setConnected(false);
- },
- signal: controller.signal,
- maxRetries: 3, // default: 3
- retryDelay: 2000, // default: 2000ms (exponential backoff)
- timeout: 300000, // default: 5 minutes
- maxBufferSize: 1048576, // default: 1MB
- });
-
- return () => controller.abort();
- }, [endpoint]);
-
- return { messages, connected };
-}
-```
-
-Options:
-
-- `url`: SSE endpoint URL (required)
-- `payload`: Optional request body (if provided, uses POST; otherwise GET)
-- `onMessage({ id, data })`: Called for each SSE message
-- `onError(error)`: Called on connection errors
-- `signal`: AbortSignal to cancel the connection
-- `lastEventId`: Resume from a specific event ID
-- `maxRetries`: Max retry attempts (default: 3)
-- `retryDelay`: Base delay between retries in ms (default: 2000)
-- `timeout`: Connection timeout in ms (default: 300000)
-- `maxBufferSize`: Max buffer size in bytes (default: 1MB)
-
-### `ArrowClient` (advanced Arrow processing)
-
-For low-level Arrow data handling:
-
-```tsx
-import { ArrowClient } from "@databricks/appkit-ui/js";
-
-// Process Arrow buffer
-const table = await ArrowClient.processArrowBuffer(buffer);
-
-// Fetch and process Arrow data in one call
-const table = await ArrowClient.fetchAndProcessArrow(url, headers);
-
-// Extract fields from table
-const fields = ArrowClient.extractArrowFields(table);
-// → [{ name: "date", type: ... }, { name: "value", type: ... }]
-
-// Extract columns as arrays
-const columns = ArrowClient.extractArrowColumns(table);
-// → { date: [...], value: [...] }
-
-// Extract chart data
-const { xData, yDataMap } = ArrowClient.extractChartData(table, "date", ["value", "count"]);
-// → { xData: [...], yDataMap: { value: [...], count: [...] } }
-
-// Auto-detect chart fields from Arrow table
-const detected = ArrowClient.detectFieldsFromArrow(table);
-// → { xField: "date", yFields: ["value"], chartType: "timeseries" }
-```
-
-### DataTable
-
-`DataTable` is a production-ready table integrated with `useAnalyticsQuery`.
-
-Key behaviors:
-
-- `parameters` is required (use `{}` if none)
-- Supports opinionated mode (auto columns) and full-control mode (`children(table)`)
-
-```tsx
-import { DataTable } from "@databricks/appkit-ui/react";
-
-export function UsersTable() {
- return (
-
- );
-}
-```
-
-### UI components (primitives)
-
-AppKit-UI ships shadcn-style primitives. Import from `@databricks/appkit-ui/react`.
-
-Note: Exact exports can vary by AppKit-UI version. Prefer using IDE auto-import/autocomplete to confirm what your installed version exports.
-
-Radix constraint (common bug):
-
-- `SelectItem` cannot have `value=""`. Use a sentinel value like `"all"` or `"none"`.
-
-**Available components:**
-
-`Accordion`, `Alert`, `AlertDialog`, `AspectRatio`, `Avatar`, `Badge`, `Breadcrumb`, `Button`, `ButtonGroup`, `Calendar`, `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`, `Carousel`, `Checkbox`, `Collapsible`, `Command`, `ContextMenu`, `Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogTitle`, `DialogDescription`, `DialogFooter`, `Drawer`, `DropdownMenu`, `Empty`, `Field`, `Form`, `HoverCard`, `Input`, `InputGroup`, `InputOtp`, `Item`, `Kbd`, `Label`, `Menubar`, `NavigationMenu`, `Pagination`, `Popover`, `Progress`, `RadioGroup`, `Resizable`, `ScrollArea`, `Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`, `Slider`, `Sonner`, `Spinner`, `Switch`, `Table`, `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `Textarea`, `Toggle`, `ToggleGroup`, `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`
-
-### Card pattern
-
-```tsx
-import {
- Card,
- CardHeader,
- CardTitle,
- CardDescription,
- CardContent,
- CardFooter,
-} from "@databricks/appkit-ui/react";
-
-function MetricCard({ title, value, description }: Props) {
- return (
-
-
- {description}
- {value}
-
-
- {/* Optional content */}
-
-
- {/* Optional footer */}
-
-
- );
-}
-```
-
-### Select pattern
-
-```tsx
-import {
- Select,
- SelectTrigger,
- SelectValue,
- SelectContent,
- SelectItem,
-} from "@databricks/appkit-ui/react";
-
-function DateRangeSelect({ value, onChange }: Props) {
- return (
-
- );
-}
-```
-
-### Tabs pattern
-
-```tsx
-import { Tabs, TabsList, TabsTrigger, TabsContent } from "@databricks/appkit-ui/react";
-
-function Dashboard() {
- return (
-
-
- Overview
- Analytics
-
-
- Overview content
-
-
- Analytics content
-
-
- );
-}
-```
-
-### Dialog pattern
-
-```tsx
-import {
- Dialog,
- DialogTrigger,
- DialogContent,
- DialogHeader,
- DialogTitle,
- DialogDescription,
- DialogFooter,
- Button,
-} from "@databricks/appkit-ui/react";
-
-function ConfirmDialog() {
- return (
-
- );
-}
-```
-
-### TooltipProvider requirement
-
-If using tooltips anywhere in your app, wrap your root component with `TooltipProvider`:
-
-```tsx
-import { TooltipProvider } from "@databricks/appkit-ui/react";
-
-function App() {
- return (
-
- {/* Your app content */}
-
- );
-}
-```
-
-### Button variants
-
-```tsx
-import { Button } from "@databricks/appkit-ui/react";
-
-
-
-
-
-
-
-```
-
-### Loading skeleton pattern
-
-```tsx
-import { Card, CardHeader, Skeleton } from "@databricks/appkit-ui/react";
-
-function LoadingCard() {
- return (
-
-
-
-
-
-
-
- );
-}
-```
-
-## Stylesheet
-
-In the main css file import the following
-
-```css
-@import "@databricks/appkit-ui/styles.css";
-```
-
-That will provide a default theme for the app using css variables.
-
-### Customizing theme (light/dark mode)
-
-- Full list of variables to customize the theme.
-
-```css
-@import "@databricks/appkit-ui/styles.css";
-
-:root {
- --radius: 0.625rem;
- --background: oklch(1 0 0);
- --foreground: oklch(0.141 0.005 285.823);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.141 0.005 285.823);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.141 0.005 285.823);
- --primary: oklch(0.21 0.006 285.885);
- --primary-foreground: oklch(0.985 0 0);
- --secondary: oklch(0.967 0.001 286.375);
- --secondary-foreground: oklch(0.21 0.006 285.885);
- --muted: oklch(0.967 0.001 286.375);
- --muted-foreground: oklch(0.552 0.016 285.938);
- --accent: oklch(0.967 0.001 286.375);
- --accent-foreground: oklch(0.21 0.006 285.885);
- --destructive: oklch(0.577 0.245 27.325);
- --destructive-foreground: oklch(0.985 0 0);
- --success: oklch(0.603 0.135 166.892);
- --success-foreground: oklch(1 0 0);
- --warning: oklch(0.795 0.157 78.748);
- --warning-foreground: oklch(0.199 0.027 238.732);
- --border: oklch(0.92 0.004 286.32);
- --input: oklch(0.92 0.004 286.32);
- --ring: oklch(0.705 0.015 286.067);
- --chart-1: oklch(0.646 0.222 41.116);
- --chart-2: oklch(0.6 0.118 184.704);
- --chart-3: oklch(0.398 0.07 227.392);
- --chart-4: oklch(0.828 0.189 84.429);
- --chart-5: oklch(0.769 0.188 70.08);
- --sidebar: oklch(0.985 0 0);
- --sidebar-foreground: oklch(0.141 0.005 285.823);
- --sidebar-primary: oklch(0.21 0.006 285.885);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.967 0.001 286.375);
- --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
- --sidebar-border: oklch(0.92 0.004 286.32);
- --sidebar-ring: oklch(0.705 0.015 286.067);
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background: oklch(0.141 0.005 285.823);
- --foreground: oklch(0.985 0 0);
- --card: oklch(0.21 0.006 285.885);
- --card-foreground: oklch(0.985 0 0);
- --popover: oklch(0.21 0.006 285.885);
- --popover-foreground: oklch(0.985 0 0);
- --primary: oklch(0.92 0.004 286.32);
- --primary-foreground: oklch(0.21 0.006 285.885);
- --secondary: oklch(0.274 0.006 286.033);
- --secondary-foreground: oklch(0.985 0 0);
- --muted: oklch(0.274 0.006 286.033);
- --muted-foreground: oklch(0.705 0.015 286.067);
- --accent: oklch(0.274 0.006 286.033);
- --accent-foreground: oklch(0.985 0 0);
- --destructive: oklch(0.704 0.191 22.216);
- --destructive-foreground: oklch(0.985 0 0);
- --success: oklch(0.67 0.12 167);
- --success-foreground: oklch(1 0 0);
- --warning: oklch(0.83 0.165 85);
- --warning-foreground: oklch(0.199 0.027 238.732);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 15%);
- --ring: oklch(0.552 0.016 285.938);
- --chart-1: oklch(0.488 0.243 264.376);
- --chart-2: oklch(0.696 0.17 162.48);
- --chart-3: oklch(0.769 0.188 70.08);
- --chart-4: oklch(0.627 0.265 303.9);
- --chart-5: oklch(0.645 0.246 16.439);
- --sidebar: oklch(0.21 0.006 285.885);
- --sidebar-foreground: oklch(0.985 0 0);
- --sidebar-primary: oklch(0.488 0.243 264.376);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.274 0.006 286.033);
- --sidebar-accent-foreground: oklch(0.985 0 0);
- --sidebar-border: oklch(1 0 0 / 10%);
- --sidebar-ring: oklch(0.552 0.016 285.938);
- }
-}
-
-```
-
-- If any variable is changed, it must be changed for both light and dark mode.
-
-## Type generation (QueryRegistry + IntelliSense)
-
-Goal: generate `client/src/appKitTypes.d.ts` so query keys, params, and result rows are type-safe.
-
-### Vite plugin: `appKitTypesPlugin`
-
-Correct option names:
-
-- `outFile?: string` (default `src/appKitTypes.d.ts`)
-- `watchFolders?: string[]` (default `["../config/queries"]`)
-
-```ts
-// client/vite.config.ts
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
-import { appKitTypesPlugin } from "@databricks/appkit";
-
-export default defineConfig({
- plugins: [
- react(),
- appKitTypesPlugin({
- outFile: "src/appKitTypes.d.ts",
- watchFolders: ["../config/queries"],
- }),
- ],
-});
-```
-
-Important nuance:
-
-- When the frontend is served through AppKit in dev mode, AppKit’s dev server already includes `appKitTypesPlugin()` internally.
-- You still want it in your client build pipeline if you run `vite build` separately.
-
-### CLI: `appkit-generate-types`
-
-```bash
-# Requires DATABRICKS_WAREHOUSE_ID (or pass as 3rd arg)
-npx appkit-generate-types [rootDir] [outFile] [warehouseId]
-
-# Example:
-npx appkit-generate-types . client/src/appKitTypes.d.ts
-
-# Force regeneration (skip cache):
-npx appkit-generate-types --no-cache
-```
-
-## Databricks Apps config: `app.yaml`
-
-Bind a SQL warehouse for Apps runtime:
-
-```yaml
-env:
- - name: DATABRICKS_WAREHOUSE_ID
- valueFrom: sql-warehouse
-```
-
-Full example with command:
-
-```yaml
-command:
- - node
- - build/index.mjs
-env:
- - name: DATABRICKS_WAREHOUSE_ID
- valueFrom: sql-warehouse
-```
-
-## LLM checklist (before you "finalize" code)
-
-- **Project setup**
- - `package.json` has `"type": "module"`
- - `tsx` is in devDependencies for dev server
- - `dev` script uses `NODE_ENV=development tsx watch server/index.ts`
- - `client/index.html` exists with `` and script pointing to `client/src/main.tsx`
-
-- **Backend**
- - `await createApp({ plugins: [...] })` is used (or `void createApp` with intent)
- - `server()` is included (always)
- - If using SQL: `analytics({})` included + `config/queries/*.sql` present
- - Queries use `:param` placeholders, and params are passed from UI using `sql.*`
- - If query needs workspace scoping: uses `:workspaceId`
-
-- **Frontend**
- - `useMemo` wraps parameters objects
- - Loading/error/empty states are explicit
- - Charts use `format="auto"` unless you have a reason to force `"json"`/`"arrow"`
- - Charts use props (`xKey`, `yKey`, `colors`) NOT children (they're ECharts-based, not Recharts)
- - If using tooltips: root is wrapped with ``
-
-- **Never**
- - Don't build SQL strings manually
- - Don't pass untyped raw params for annotated queries
- - Don't ignore `createApp()`'s promise
- - Don't invent UI components not listed in this file
- - Don't pass Recharts children (``, ``, etc.) to AppKit chart components
-