Skip to content

Commit f6b95d9

Browse files
authored
Merge pull request #151 from openai/katia/apps-sdk-ui-migration
Added Apps SDK UI to pizzaz example
2 parents d82ddf7 + 3560736 commit f6b95d9

File tree

11 files changed

+205
-110
lines changed

11 files changed

+205
-110
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
44

5-
This repository showcases example UI components to be used with the Apps SDK, as well as example MCP servers that expose a collection of components as tools.
5+
This repository showcases example UI components to be used with the [Apps SDK](https://developers.openai.com/apps-sdk), as well as example MCP servers that expose a collection of components as tools.
66
It is meant to be used as a starting point and source of inspiration to build your own apps for ChatGPT.
77

88
Note: If you are on Chrome and have recently updated to version 142, you will need to disable the [`local-network-access` flag](https://developer.chrome.com/release-notes/142#local_network_access_restrictions) to see the widget UI.
@@ -42,6 +42,12 @@ The MCP servers in this demo highlight how each tool can light up widgets by com
4242
- `authenticated_server_python/` – Python MCP server that demonstrates authenticated tool calls.
4343
- `build-all.mts` – Vite build orchestrator that produces hashed bundles for every widget entrypoint.
4444

45+
### Pizzaz overview
46+
47+
This example contains multiple components showing multiple types of views and interactions: a list view, a carousel view, a map view. It also contains a "pizzaz shop" showing interactive flows and a checkout page.
48+
49+
This example uses the [Apps SDK UI library](https://github.com/openai/apps-sdk-ui) for simple components such as images, buttons, and badges.
50+
4551
### Kitchen sink lite overview
4652

4753
The kitchen sink lite sample shows the full `window.openai` surface working together:

src/index.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
@source "../node_modules/@openai/apps-sdk-ui";
44
@source ".";
55

6-
@tailwind base;
7-
@tailwind components;
8-
@tailwind utilities;
9-
106
@layer utilities {
117
.overflow-auto > *,
128
.overflow-scroll > *,

src/pizzaz-albums/AlbumCard.jsx

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,44 @@
11
import React from "react";
2+
import { Button } from "@openai/apps-sdk-ui/components/Button";
3+
import { Image } from "@openai/apps-sdk-ui/components/Image";
4+
import { Badge } from "@openai/apps-sdk-ui/components/Badge";
25

36
function AlbumCard({ album, onSelect }) {
47
return (
5-
<button
8+
<Button
69
type="button"
7-
className="group relative cursor-pointer flex-shrink-0 w-[272px] bg-white text-left"
10+
variant="ghost"
11+
color="secondary"
12+
pill={false}
13+
className="group relative flex-shrink-0 w-[272px] bg-white text-left p-0 h-auto min-h-0 rounded-none shadow-none gap-0 before:hidden"
814
onClick={() => onSelect?.(album)}
915
>
10-
<div className="aspect-[4/3] w-full overflow-hidden rounded-2xl shadow-lg">
11-
<img
12-
src={album.cover}
13-
alt={album.title}
14-
className="h-full w-full object-cover"
15-
loading="lazy"
16-
/>
17-
</div>
18-
<div className="pt-3 px-1.5">
19-
<div className="text-base font-medium truncate">{album.title}</div>
20-
<div className="text-sm text-black/60 mt-0.5">
21-
{album.photos.length} photos
16+
<div className="flex w-full flex-col gap-2">
17+
<div className="relative aspect-[4/3] w-full overflow-hidden rounded-2xl shadow-lg">
18+
<Image
19+
src={album.cover}
20+
alt={album.title}
21+
className="h-full w-full object-cover"
22+
loading="lazy"
23+
/>
24+
<Badge
25+
variant="soft"
26+
color="secondary"
27+
size="sm"
28+
pill
29+
className="absolute left-3 top-3 bg-white/50 backdrop-blur-sm"
30+
>
31+
Featured
32+
</Badge>
33+
</div>
34+
<div className="px-1.5">
35+
<div className="text-base font-medium truncate">{album.title}</div>
36+
<div className="mt-0.5 text-sm font-normal text-black/60">
37+
{album.photos.length} photos
38+
</div>
2239
</div>
2340
</div>
24-
</button>
41+
</Button>
2542
);
2643
}
2744

src/pizzaz-albums/index.jsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useMaxHeight } from "../use-max-height";
77
import { useOpenAiGlobal } from "../use-openai-global";
88
import FullscreenViewer from "./FullscreenViewer";
99
import AlbumCard from "./AlbumCard";
10+
import { Button } from "@openai/apps-sdk-ui/components/Button";
1011

1112
function AlbumsCarousel({ onSelect }) {
1213
const albums = albumsData?.albums || [];
@@ -79,9 +80,13 @@ function AlbumsCarousel({ onSelect }) {
7980
/>
8081
</div>
8182
{canPrev && (
82-
<button
83+
<Button
8384
aria-label="Previous"
84-
className="absolute left-2 top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center h-8 w-8 rounded-full bg-white text-black shadow-lg ring ring-black/5 hover:bg-white"
85+
className="absolute left-2 top-1/2 -translate-y-1/2 z-10 shadow-lg"
86+
color="secondary"
87+
size="sm"
88+
variant="soft"
89+
uniform
8590
onClick={() => emblaApi && emblaApi.scrollPrev()}
8691
type="button"
8792
>
@@ -90,12 +95,16 @@ function AlbumsCarousel({ onSelect }) {
9095
className="h-4.5 w-4.5"
9196
aria-hidden="true"
9297
/>
93-
</button>
98+
</Button>
9499
)}
95100
{canNext && (
96-
<button
101+
<Button
97102
aria-label="Next"
98-
className="absolute right-2 top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center h-8 w-8 rounded-full bg-white text-black shadow-lg ring ring-black/5 hover:bg-white"
103+
className="absolute right-2 top-1/2 -translate-y-1/2 z-10 shadow-lg"
104+
color="secondary"
105+
size="sm"
106+
variant="soft"
107+
uniform
99108
onClick={() => emblaApi && emblaApi.scrollNext()}
100109
type="button"
101110
>
@@ -104,7 +113,7 @@ function AlbumsCarousel({ onSelect }) {
104113
className="h-4.5 w-4.5"
105114
aria-hidden="true"
106115
/>
107-
</button>
116+
</Button>
108117
)}
109118
</div>
110119
);

src/pizzaz-carousel/PlaceCard.jsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import React from "react";
22
import { Star } from "lucide-react";
3+
import { Button } from "@openai/apps-sdk-ui/components/Button";
4+
import { Image } from "@openai/apps-sdk-ui/components/Image";
35

46
export default function PlaceCard({ place }) {
57
if (!place) return null;
68
return (
79
<div className="min-w-[220px] select-none max-w-[220px] w-[65vw] sm:w-[220px] self-stretch flex flex-col">
810
<div className="w-full">
9-
<img
11+
<Image
1012
src={place.thumbnail}
1113
alt={place.name}
1214
className="w-full aspect-square rounded-2xl object-cover ring ring-black/5 shadow-[0px_2px_6px_rgba(0,0,0,0.06)]"
1315
/>
1416
</div>
1517
<div className="mt-3 flex flex-col flex-1">
16-
<div className="text-base font-medium truncate line-clamp-1">{place.name}</div>
18+
<div className="text-base font-medium truncate line-clamp-1">
19+
{place.name}
20+
</div>
1721
<div className="text-xs mt-1 text-black/60 flex items-center gap-1">
1822
<Star className="h-3 w-3" aria-hidden="true" />
1923
{place.rating?.toFixed ? place.rating.toFixed(1) : place.rating}
@@ -26,12 +30,9 @@ export default function PlaceCard({ place }) {
2630
</div>
2731
) : null}
2832
<div className="mt-5">
29-
<button
30-
type="button"
31-
className="cursor-pointer inline-flex items-center rounded-full bg-[#F46C21] text-white px-4 py-1.5 text-sm font-medium hover:opacity-90 active:opacity-100"
32-
>
33+
<Button color="primary" size="sm" variant="solid">
3334
Learn more
34-
</button>
35+
</Button>
3536
</div>
3637
</div>
3738
</div>

src/pizzaz-carousel/index.jsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import React from "react";
2+
import "../index.css";
23
import { createRoot } from "react-dom/client";
34
import useEmblaCarousel from "embla-carousel-react";
45
import { ArrowLeft, ArrowRight } from "lucide-react";
56
import markers from "../pizzaz/markers.json";
67
import PlaceCard from "./PlaceCard";
8+
import { Button } from "@openai/apps-sdk-ui/components/Button";
79

810
function App() {
911
const places = markers?.places || [];
@@ -77,9 +79,13 @@ function App() {
7779
/>
7880
</div>
7981
{canPrev && (
80-
<button
82+
<Button
8183
aria-label="Previous"
82-
className="absolute left-2 top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center h-8 w-8 rounded-full bg-white text-black shadow-lg ring ring-black/5 hover:bg-white"
84+
className="absolute left-2 top-1/2 -translate-y-1/2 z-10 shadow-lg"
85+
color="secondary"
86+
size="sm"
87+
variant="soft"
88+
uniform
8389
onClick={() => emblaApi && emblaApi.scrollPrev()}
8490
type="button"
8591
>
@@ -88,12 +94,16 @@ function App() {
8894
className="h-4.5 w-4.5"
8995
aria-hidden="true"
9096
/>
91-
</button>
97+
</Button>
9298
)}
9399
{canNext && (
94-
<button
100+
<Button
95101
aria-label="Next"
96-
className="absolute right-2 top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center h-8 w-8 rounded-full bg-white text-black shadow-lg ring ring-black/5 hover:bg-white"
102+
className="absolute right-2 top-1/2 -translate-y-1/2 z-10 shadow-lg"
103+
color="secondary"
104+
size="sm"
105+
variant="soft"
106+
uniform
97107
onClick={() => emblaApi && emblaApi.scrollNext()}
98108
type="button"
99109
>
@@ -102,7 +112,7 @@ function App() {
102112
className="h-4.5 w-4.5"
103113
aria-hidden="true"
104114
/>
105-
</button>
115+
</Button>
106116
)}
107117
</div>
108118
);

src/pizzaz-list/index.jsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from "react";
22
import { createRoot } from "react-dom/client";
33
import markers from "../pizzaz/markers.json";
44
import { PlusCircle, Star } from "lucide-react";
5+
import { Button } from "@openai/apps-sdk-ui/components/Button";
6+
import { Image } from "@openai/apps-sdk-ui/components/Image";
57

68
function App() {
79
const places = markers?.places || [];
@@ -26,12 +28,9 @@ function App() {
2628
</div>
2729
</div>
2830
<div className="flex-auto hidden sm:flex justify-end pr-2">
29-
<button
30-
type="button"
31-
className="cursor-pointer inline-flex items-center rounded-full bg-[#F46C21] text-white px-4 py-1.5 sm:text-md text-sm font-medium hover:opacity-90 active:opacity-100"
32-
>
31+
<Button color="primary" variant="solid" size="md">
3332
Save List
34-
</button>
33+
</Button>
3534
</div>
3635
</div>
3736
<div className="min-w-full text-sm flex flex-col">
@@ -49,7 +48,7 @@ function App() {
4948
>
5049
<div className="py-3 pr-3 min-w-0 w-full sm:w-3/5">
5150
<div className="flex items-center gap-3">
52-
<img
51+
<Image
5352
src={place.thumbnail}
5453
alt={place.name}
5554
className="h-10 w-10 sm:h-11 sm:w-11 rounded-lg object-cover ring ring-black/5"
@@ -84,7 +83,15 @@ function App() {
8483
{place.city || "–"}
8584
</div>
8685
<div className="py-2 whitespace-nowrap flex justify-end">
87-
<PlusCircle strokeWidth={1.5} className="h-5 w-5" />
86+
<Button
87+
aria-label={`Add ${place.name}`}
88+
color="secondary"
89+
variant="ghost"
90+
size="sm"
91+
uniform
92+
>
93+
<PlusCircle strokeWidth={1.5} className="h-5 w-5" />
94+
</Button>
8895
</div>
8996
</div>
9097
</div>
@@ -96,12 +103,9 @@ function App() {
96103
)}
97104
</div>
98105
<div className="sm:hidden px-0 pt-2 pb-2">
99-
<button
100-
type="button"
101-
className="w-full cursor-pointer inline-flex items-center justify-center rounded-full bg-[#F46C21] text-white px-4 py-2 font-medium hover:opacity-90 active:opacity-100"
102-
>
106+
<Button color="primary" variant="solid" size="md" block>
103107
Save List
104-
</button>
108+
</Button>
105109
</div>
106110
</div>
107111
</div>

0 commit comments

Comments
 (0)