Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion docs/content/docs/api-reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Every framework overlay includes a `get_weather` example backed by Open-Meteo. A

**OpenUI examples**

Interactive `openui create` lists starter templates, and option to create an app from listed [OpenUI examples](https://github.com/thesysdev/openui/blob/main/examples). `--example <name>` skips both menus. `--example` cannot be combined with `--template` or `--backend-framework`. Any other folder name in the catalog works the same way.
Interactive `openui create` lists starter templates and featured [OpenUI examples](https://github.com/thesysdev/openui/blob/main/examples). `--example <name>` skips both menus. `--example` cannot be combined with `--template` or `--backend-framework`. Any other folder name in the catalog works the same way.

**Conversation storage**

Expand Down Expand Up @@ -544,3 +544,7 @@ collection.
`createLibrary`, `PromptOptions`, and the `Library` interface that `openui generate` read.
</Card>
</Cards>

The backend framework picker shows examples marked `featured: true`
in `examples/examples.json`, in catalog order. Select “More OpenUI Examples →” for the
complete catalog, or pass any catalog name through `--example`.
8 changes: 8 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ Each example has one primary home. Complete workflows with a companion tutorial

`miscellaneous` is intentionally flat. If several examples develop the same stable integration seam, promote that seam to a top-level category instead of adding nested miscellaneous taxonomies.

## CLI selection

Set `"featured": true` on an entry in `examples.json` to show it in the CLI's
backend framework picker. It shows up to five featured examples in catalog
order beside the backend choices. “More examples…” opens the complete catalog,
including entries with omitted or false flags. Every example is also available
through `openui create --example <name>`.

## Catalog

### Agent frameworks
Expand Down
16 changes: 10 additions & 6 deletions examples/examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"title": "Mastra",
"description": "A Mastra agent connected to OpenUI through AG-UI.",
"path": "agent-frameworks/mastra",
"envKey": "THESYS_API_KEY"
"envKey": "THESYS_API_KEY",
"featured": true
},
{
"title": "Vercel AI SDK",
Expand Down Expand Up @@ -82,13 +83,15 @@
"title": "Material UI",
"description": "A broad Material UI component library for generated interfaces.",
"path": "design-systems/material-ui",
"envKey": "THESYS_API_KEY"
"envKey": "THESYS_API_KEY",
"featured": true
},
{
"title": "shadcn/ui",
"title": "ShadCN",
"description": "A broad shadcn/ui component library for generated interfaces.",
"path": "design-systems/shadcn",
"envKey": "THESYS_API_KEY"
"envKey": "THESYS_API_KEY",
"featured": true
},
{
"title": "Grok Build",
Expand All @@ -97,10 +100,11 @@
"envKey": "XAI_API_KEY"
},
{
"title": "Pi",
"title": "Pi Harness",
"description": "A Pi coding-agent session on OpenUI Cloud.",
"path": "harnesses/pi",
"envKey": "THESYS_API_KEY"
"envKey": "THESYS_API_KEY",
"featured": true
},
{
"title": "Autofix",
Expand Down
6 changes: 5 additions & 1 deletion packages/openui-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Every framework overlay includes `get_weather` as its example app-owned function

#### OpenUI examples

Interactive `openui create` offers to scaffold [OpenUI examples](https://github.com/thesysdev/openui/blob/main/examples). Pass `--example <name>` to skip the menus. `--example` cannot be combined with `--template` or `--backend-framework`.
Interactive `openui create` offers featured [OpenUI examples](https://github.com/thesysdev/openui/blob/main/examples). Pass `--example <name>` to skip the menus. `--example` cannot be combined with `--template` or `--backend-framework`.

```bash
openui create --example shadcn
Expand Down Expand Up @@ -377,3 +377,7 @@ openui create --no-telemetry
## License

[MIT](https://github.com/thesysdev/openui/blob/main/LICENSE)

The backend framework picker shows examples marked `featured: true`
in `examples/examples.json`, in catalog order. Select “More OpenUI Examples →” for the
complete catalog, or pass any catalog name through `--example`.
12 changes: 12 additions & 0 deletions packages/openui-cli/src/commands/create/lib/examples-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export type ExampleProject = {
envFile: ".env";
/** Primary env var to prompt for. Omit when the example needs several keys. */
envKey?: string;
/** Show this example in the curated interactive picker. */
featured?: boolean;
};

function catalogError(message: string): CreateError {
Expand All @@ -27,6 +29,7 @@ function parseCatalogEntry(item: unknown): ExampleProject {
description?: unknown;
path?: unknown;
envKey?: unknown;
featured?: unknown;
};
if (
typeof entry.title !== "string" ||
Expand All @@ -37,6 +40,9 @@ function parseCatalogEntry(item: unknown): ExampleProject {
`${EXAMPLES_CATALOG_PATH} has an example missing title, description, or path.`,
);
}
if (entry.featured !== undefined && typeof entry.featured !== "boolean") {
throw catalogError(`${EXAMPLES_CATALOG_PATH} has an example with an invalid featured flag.`);
}
const relative = entry.path.replace(/^\/+/, "");
const name = relative.split("/").filter(Boolean).at(-1);
if (!name) {
Expand All @@ -53,6 +59,7 @@ function parseCatalogEntry(item: unknown): ExampleProject {
description: entry.description,
path: relative.startsWith("examples/") ? relative : `examples/${relative}`,
envFile: ".env",
featured: entry.featured === true,
envKey: typeof entry.envKey === "string" ? entry.envKey : undefined,
};
}
Expand All @@ -75,6 +82,11 @@ export async function loadExamplesCatalog(
return parseExamplesCatalog(content);
}

/** Keep catalog order so curators control which five examples are shown. */
export function featuredExamples(examples: ExampleProject[]): ExampleProject[] {
return examples.filter((example) => example.featured === true).slice(0, 5);
}

export function findExample(name: string, examples: ExampleProject[]): ExampleProject {
const normalized = name.toLowerCase();
const match = examples.find((entry) => entry.name.toLowerCase() === normalized);
Expand Down
3 changes: 2 additions & 1 deletion packages/openui-cli/src/commands/create/lib/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ async function loadCreateHelpText(): Promise<string> {

sections.push(`OpenUI examples:
Loaded at runtime from examples/examples.json in the OpenUI repo.
Pick "Scaffold from OpenUI Examples" in the interactive prompt, or pass
Pick a featured example beside the backend choices, choose "More examples…"
for the complete list, or pass
--example <name> with any catalog folder name.`);

return `\n${sections.join("\n\n")}\n`;
Expand Down
24 changes: 21 additions & 3 deletions packages/openui-cli/src/commands/create/lib/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { resolveArgs } from "../../../lib/resolve-args";
import type { RetryAttemptInfo } from "../../../lib/retry";
import type { OverlayName, TemplateName } from "./create-types";
import {
featuredExamples,
findExample,
groupedExampleChoices,
loadExamplesCatalog,
Expand Down Expand Up @@ -111,6 +112,7 @@ export async function resolveProjectIdentity(

const OPENUI_EXAMPLES_CHOICE = "openui-examples";
const GO_BACK_CHOICE = "__back__";
const EXAMPLE_CHOICE_PREFIX = "example:";

export async function resolveCreateSelection(params: {
backendFramework?: OverlayName;
Expand All @@ -126,6 +128,7 @@ export async function resolveCreateSelection(params: {
if (backendFramework) return { kind: "overlay", overlay: backendFramework };
if (!interactive) return { kind: "overlay", overlay: "langgraph" };

const featured = featuredExamples(examples);
const { select, Separator } = await import("@inquirer/prompts");
const prompt = async <T extends string>(
message: string,
Expand Down Expand Up @@ -154,12 +157,21 @@ export async function resolveCreateSelection(params: {
name: overlay.name,
description: overlay.description,
}));
if (examples.length > 0) {
if (featured.length > 0) {
starterChoices.push(new Separator());
starterChoices.push(
...featured.map((example) => ({
value: `${EXAMPLE_CHOICE_PREFIX}${example.name}`,
name: example.label,
description: example.description,
})),
);
}
if (examples.length > 0) {
starterChoices.push({
value: OPENUI_EXAMPLES_CHOICE,
name: "Scaffold from OpenUI Examples",
description: "Browse examples from the OpenUI repo",
name: "More OpenUI Examples →",
description: "Browse the complete OpenUI example catalog",
});
}

Expand All @@ -168,6 +180,12 @@ export async function resolveCreateSelection(params: {
starterChoices,
starterChoices.length,
);
if (selected.startsWith(EXAMPLE_CHOICE_PREFIX)) {
return {
kind: "example",
example: findExample(selected.slice(EXAMPLE_CHOICE_PREFIX.length), examples),
};
}
if (selected !== OPENUI_EXAMPLES_CHOICE) {
return { kind: "overlay", overlay: selected as OverlayName };
}
Expand Down
Loading