Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c5187d6
feat(ui-variants): visual variant picker with dev-only recorder
IamGalymzhan Jul 20, 2026
497e79e
docs(ui-variants): regenerate API docs for blockId rename
IamGalymzhan Jul 20, 2026
4a1bb74
feat: add devOnly manifest flag to gate plugin registration
IamGalymzhan Jul 20, 2026
8dbe78f
docs: regenerate API docs for devOnly manifest field
IamGalymzhan Jul 20, 2026
2653dd4
refactor(appkit-ui): rename variants dir to ui-variants
IamGalymzhan Jul 20, 2026
83a62a7
feat: ship /ui skill and add `appkit skills install` command
IamGalymzhan Jul 21, 2026
1566d35
refactor: rename /ui skill to /ui-variants
IamGalymzhan Jul 21, 2026
62b96e1
Merge branch 'main' into feat/ui-variants
IamGalymzhan Jul 21, 2026
233d11a
docs(ui-variants): drop conditional-registration guidance from skill
IamGalymzhan Jul 22, 2026
0b0783b
Merge branch 'main' into feat/ui-variants
IamGalymzhan Jul 22, 2026
4606b9a
refactor(ui-variants): rename shipped skill to appkit-ui-variants
IamGalymzhan Jul 22, 2026
915eade
feat(ui-variants): register the dev-only recorder automatically
IamGalymzhan Jul 24, 2026
2c82a60
Merge branch 'main' into feat/ui-variants
IamGalymzhan Jul 24, 2026
3c8d8b7
feat(appkit-ui): render only the first variant in production builds
IamGalymzhan Jul 24, 2026
c21e260
refactor(appkit-ui): drop the prod-fallback guard comment
IamGalymzhan Jul 24, 2026
6252ab1
Merge branch 'main' into feat/ui-variants
IamGalymzhan Jul 24, 2026
4bd858f
refactor(cli): remove appkit skills install command
IamGalymzhan Jul 24, 2026
0cf0f3e
chore(shared): update postinstall message for skills install flow
IamGalymzhan Jul 24, 2026
8b7b165
chore(shared): drop skills install hint from postinstall message
IamGalymzhan Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/dev-playground/client/src/lib/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
SearchIcon,
ServerIcon,
ShieldIcon,
Wand2Icon,
ZapIcon,
} from "lucide-react";

Expand Down Expand Up @@ -162,6 +163,13 @@ export const NAV_GROUPS: ReadonlyArray<NavGroup> = [
"Resilient SSE streams: automatic Last-Event-ID tracking and reconnection.",
icon: RadioIcon,
},
{
to: "/ui-variants",
label: "UI Variants",
description:
"Author UI in variants, pick one live in the browser, and let the agent finalize it into source.",
icon: Wand2Icon,
},
],
},
];
Expand Down
21 changes: 21 additions & 0 deletions apps/dev-playground/client/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import { Route as rootRouteImport } from './routes/__root'
import { Route as VectorSearchRouteRouteImport } from './routes/vector-search.route'
import { Route as UiVariantsRouteRouteImport } from './routes/ui-variants.route'
import { Route as TypeSafetyRouteRouteImport } from './routes/type-safety.route'
import { Route as TelemetryRouteRouteImport } from './routes/telemetry.route'
import { Route as SqlHelpersRouteRouteImport } from './routes/sql-helpers.route'
Expand All @@ -33,6 +34,11 @@ const VectorSearchRouteRoute = VectorSearchRouteRouteImport.update({
path: '/vector-search',
getParentRoute: () => rootRouteImport,
} as any)
const UiVariantsRouteRoute = UiVariantsRouteRouteImport.update({
id: '/ui-variants',
path: '/ui-variants',
getParentRoute: () => rootRouteImport,
} as any)
const TypeSafetyRouteRoute = TypeSafetyRouteRouteImport.update({
id: '/type-safety',
path: '/type-safety',
Expand Down Expand Up @@ -137,6 +143,7 @@ export interface FileRoutesByFullPath {
'/sql-helpers': typeof SqlHelpersRouteRoute
'/telemetry': typeof TelemetryRouteRoute
'/type-safety': typeof TypeSafetyRouteRoute
'/ui-variants': typeof UiVariantsRouteRoute
'/vector-search': typeof VectorSearchRouteRoute
}
export interface FileRoutesByTo {
Expand All @@ -157,6 +164,7 @@ export interface FileRoutesByTo {
'/sql-helpers': typeof SqlHelpersRouteRoute
'/telemetry': typeof TelemetryRouteRoute
'/type-safety': typeof TypeSafetyRouteRoute
'/ui-variants': typeof UiVariantsRouteRoute
'/vector-search': typeof VectorSearchRouteRoute
}
export interface FileRoutesById {
Expand All @@ -178,6 +186,7 @@ export interface FileRoutesById {
'/sql-helpers': typeof SqlHelpersRouteRoute
'/telemetry': typeof TelemetryRouteRoute
'/type-safety': typeof TypeSafetyRouteRoute
'/ui-variants': typeof UiVariantsRouteRoute
'/vector-search': typeof VectorSearchRouteRoute
}
export interface FileRouteTypes {
Expand All @@ -200,6 +209,7 @@ export interface FileRouteTypes {
| '/sql-helpers'
| '/telemetry'
| '/type-safety'
| '/ui-variants'
| '/vector-search'
fileRoutesByTo: FileRoutesByTo
to:
Expand All @@ -220,6 +230,7 @@ export interface FileRouteTypes {
| '/sql-helpers'
| '/telemetry'
| '/type-safety'
| '/ui-variants'
| '/vector-search'
id:
| '__root__'
Expand All @@ -240,6 +251,7 @@ export interface FileRouteTypes {
| '/sql-helpers'
| '/telemetry'
| '/type-safety'
| '/ui-variants'
| '/vector-search'
fileRoutesById: FileRoutesById
}
Expand All @@ -261,6 +273,7 @@ export interface RootRouteChildren {
SqlHelpersRouteRoute: typeof SqlHelpersRouteRoute
TelemetryRouteRoute: typeof TelemetryRouteRoute
TypeSafetyRouteRoute: typeof TypeSafetyRouteRoute
UiVariantsRouteRoute: typeof UiVariantsRouteRoute
VectorSearchRouteRoute: typeof VectorSearchRouteRoute
}

Expand All @@ -273,6 +286,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof VectorSearchRouteRouteImport
parentRoute: typeof rootRouteImport
}
'/ui-variants': {
id: '/ui-variants'
path: '/ui-variants'
fullPath: '/ui-variants'
preLoaderRoute: typeof UiVariantsRouteRouteImport
parentRoute: typeof rootRouteImport
}
'/type-safety': {
id: '/type-safety'
path: '/type-safety'
Expand Down Expand Up @@ -413,6 +433,7 @@ const rootRouteChildren: RootRouteChildren = {
SqlHelpersRouteRoute: SqlHelpersRouteRoute,
TelemetryRouteRoute: TelemetryRouteRoute,
TypeSafetyRouteRoute: TypeSafetyRouteRoute,
UiVariantsRouteRoute: UiVariantsRouteRoute,
VectorSearchRouteRoute: VectorSearchRouteRoute,
}
export const routeTree = rootRouteImport
Expand Down
260 changes: 260 additions & 0 deletions apps/dev-playground/client/src/routes/ui-variants.route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
import {
Badge,
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Separator,
Variant,
Variants,
} from "@databricks/appkit-ui/react";
import { createFileRoute } from "@tanstack/react-router";
import {
ArrowRight,
Boxes,
Gauge,
Github,
Radio,
Rocket,
ShieldCheck,
Sparkles,
} from "lucide-react";

export const Route = createFileRoute("/ui-variants")({
component: UiVariantsRoute,
});

/**
* Demo of the `<Variants>` picker: a page composed of two independent
* `<Variants>` blocks:
* - `about-hero` — the top hero section (3 treatments)
* - `about-info` — the informational section underneath (3 treatments)
*
* Hover either framed block in the browser to reveal the switcher, flip
* between the variants, and Confirm the one you want. The `/ui` skill then
* finalizes each chosen variant into source, dropping the wrappers.
*/
function UiVariantsRoute() {
return (
<div className="min-h-screen bg-background">
<div className="mx-auto max-w-[1000px] px-6 py-12">
<Variants blockId="about-hero">
<Variant label="Centered">
<section className="flex flex-col items-center px-6 py-20 text-center">
<Badge variant="secondary" className="mb-6">
<Sparkles className="size-3.5" /> About AppKit
</Badge>
<h1 className="text-5xl font-bold tracking-tight text-foreground">
Build Databricks apps, not plumbing
</h1>
<p className="mt-6 max-w-xl text-lg text-muted-foreground">
AppKit is a modular TypeScript SDK with a plugin architecture,
first-class streaming, and built-in observability.
</p>
<div className="mt-10 flex gap-3">
<Button size="lg">
Get started <ArrowRight className="size-4" />
</Button>
<Button size="lg" variant="outline">
<Github className="size-4" /> View source
</Button>
</div>
</section>
</Variant>

<Variant label="Split with stats">
<section className="grid grid-cols-1 items-center gap-10 py-20 md:grid-cols-2">
<div>
<Badge variant="secondary" className="mb-4">
<Rocket className="size-3.5" /> About AppKit
</Badge>
<h1 className="text-4xl font-bold tracking-tight text-foreground">
The SDK for Databricks applications
</h1>
<p className="mt-4 text-lg text-muted-foreground">
A plugin-first toolkit that handles the hard parts — auth,
streaming, telemetry — so your team ships features faster.
</p>
<div className="mt-8">
<Button size="lg">
Get started <ArrowRight className="size-4" />
</Button>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
{[
{ value: "20+", label: "Built-in plugins" },
{ value: "100%", label: "TypeScript" },
{ value: "SSE", label: "Streaming-first" },
{ value: "OTEL", label: "Observability" },
].map((stat) => (
<div
key={stat.label}
className="rounded-lg border bg-card p-5 text-center"
>
<div className="text-3xl font-bold text-foreground">
{stat.value}
</div>
<div className="mt-1 text-sm text-muted-foreground">
{stat.label}
</div>
</div>
))}
</div>
</section>
</Variant>

<Variant label="Minimal">
<section className="border-b py-16">
<h1 className="text-4xl font-bold tracking-tight text-foreground">
About AppKit
</h1>
<p className="mt-4 max-w-2xl text-lg text-muted-foreground">
A modular TypeScript SDK for building Databricks applications
with a plugin-based architecture, streaming, and observability
baked in.
</p>
</section>
</Variant>
</Variants>

<div className="h-12" />

<Variants blockId="about-info">
<Variant label="Feature grid">
<section className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{[
{
icon: Boxes,
title: "Plugin-first",
body: "Everything is a plugin — compose exactly the capabilities your app needs.",
},
{
icon: Radio,
title: "Streaming-first",
body: "Built-in SSE with automatic reconnection and event replay.",
},
{
icon: Gauge,
title: "Observable",
body: "OpenTelemetry traces, metrics, and logs wired in from day one.",
},
{
icon: ShieldCheck,
title: "Type-safe",
body: "Heavy TypeScript with runtime validation via Zod.",
},
{
icon: Sparkles,
title: "Great DX",
body: "HMR, hot-reload, source maps, and inspection tools out of the box.",
},
{
icon: Rocket,
title: "Production-ready",
body: "Graceful shutdown, retries, timeouts, and caching interceptors.",
},
].map((feature) => (
<Card key={feature.title}>
<CardHeader>
<feature.icon className="size-6 text-primary" />
<CardTitle className="mt-2">{feature.title}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
{feature.body}
</p>
</CardContent>
</Card>
))}
</section>
</Variant>

<Variant label="Two column prose">
<section className="grid grid-cols-1 gap-10 md:grid-cols-3">
<div className="md:col-span-1">
<h2 className="text-2xl font-semibold text-foreground">
Why AppKit?
</h2>
<p className="mt-2 text-sm text-muted-foreground">
Designed for teams building data and AI apps on Databricks.
</p>
</div>
<div className="space-y-6 md:col-span-2">
<div>
<h3 className="font-medium text-foreground">
Modular by design
</h3>
<p className="mt-1 text-sm text-muted-foreground">
A plugin architecture lets you add analytics, agents,
Lakebase, and more without rewiring your app.
</p>
</div>
<Separator />
<div>
<h3 className="font-medium text-foreground">
Built for streaming
</h3>
<p className="mt-1 text-sm text-muted-foreground">
Server-Sent Events with reconnection and per-stream
cancellation come standard.
</p>
</div>
<Separator />
<div>
<h3 className="font-medium text-foreground">
Observable and safe
</h3>
<p className="mt-1 text-sm text-muted-foreground">
OpenTelemetry and Zod validation give you confidence in
production.
</p>
</div>
</div>
</section>
</Variant>

<Variant label="Stat callout">
<Card>
<CardHeader>
<CardTitle>What AppKit gives you</CardTitle>
<CardDescription>
The essentials for shipping Databricks apps quickly.
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-3">
{[
{
value: "Plugins",
body: "Compose analytics, agents, files, and more.",
},
{
value: "Streaming",
body: "SSE with reconnection built in.",
},
{
value: "Telemetry",
body: "OpenTelemetry traces and metrics.",
},
].map((item) => (
<div key={item.value}>
<div className="text-lg font-semibold text-foreground">
{item.value}
</div>
<p className="mt-1 text-sm text-muted-foreground">
{item.body}
</p>
</div>
))}
</div>
</CardContent>
</Card>
</Variant>
</Variants>
</div>
</div>
);
}
Loading
Loading