sunpeak 0.6.1 → 0.6.5

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.
Files changed (59) hide show
  1. package/bin/sunpeak.js +133 -6
  2. package/dist/chatgpt/conversation.d.ts +2 -1
  3. package/dist/index.cjs +24 -4
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +24 -4
  6. package/dist/index.js.map +1 -1
  7. package/dist/mcp/entry.cjs +2 -2
  8. package/dist/mcp/entry.cjs.map +1 -1
  9. package/dist/mcp/entry.js +2 -2
  10. package/dist/mcp/entry.js.map +1 -1
  11. package/dist/mcp/index.cjs +1 -1
  12. package/dist/mcp/index.js +1 -1
  13. package/dist/{server-DpriZ4jT.cjs → server-CQGbJWbk.cjs} +17 -8
  14. package/dist/{server-DpriZ4jT.cjs.map → server-CQGbJWbk.cjs.map} +1 -1
  15. package/dist/{server-SBlanUcf.js → server-DGCvp1RA.js} +17 -8
  16. package/dist/{server-SBlanUcf.js.map → server-DGCvp1RA.js.map} +1 -1
  17. package/dist/style.css +444 -0
  18. package/package.json +1 -1
  19. package/template/.sunpeak/dev.tsx +1 -1
  20. package/template/dist/chatgpt/albums.js +2 -2
  21. package/template/dist/chatgpt/carousel.js +1 -1
  22. package/template/dist/chatgpt/counter.js +1 -1
  23. package/template/dist/chatgpt/pizzaz.js +3034 -0
  24. package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Avatar.js +97 -0
  25. package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Avatar.js.map +7 -0
  26. package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Button.js +3 -3
  27. package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_SegmentedControl.js +1 -1
  28. package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Select.js +16 -16
  29. package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Textarea.js +3 -3
  30. package/template/node_modules/.vite/deps/_metadata.json +45 -33
  31. package/template/node_modules/.vite/deps/{chunk-DQAZDQU3.js → chunk-LR7NKCX5.js} +8 -8
  32. package/template/node_modules/.vite/deps/mapbox-gl.js +32835 -0
  33. package/template/node_modules/.vite/deps/mapbox-gl.js.map +7 -0
  34. package/template/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  35. package/template/package.json +1 -0
  36. package/template/src/components/album/album-carousel.test.tsx +84 -0
  37. package/template/src/components/album/album-carousel.tsx +168 -0
  38. package/template/src/components/album/albums.test.tsx +2 -2
  39. package/template/src/components/album/albums.tsx +3 -3
  40. package/template/src/components/album/index.ts +1 -0
  41. package/template/src/components/carousel/index.ts +1 -0
  42. package/template/src/components/index.ts +1 -1
  43. package/template/src/components/pizzaz/index.ts +6 -0
  44. package/template/src/components/pizzaz/map-view.tsx +212 -0
  45. package/template/src/components/pizzaz/pizzaz.tsx +145 -0
  46. package/template/src/components/pizzaz/place-card.tsx +55 -0
  47. package/template/src/components/pizzaz/place-carousel.tsx +45 -0
  48. package/template/src/components/pizzaz/place-inspector.tsx +132 -0
  49. package/template/src/components/pizzaz/place-list.tsx +90 -0
  50. package/template/src/resources/carousel-resource.test.tsx +1 -4
  51. package/template/src/resources/carousel-resource.tsx +1 -2
  52. package/template/src/resources/index.ts +1 -0
  53. package/template/src/resources/pizzaz-resource.tsx +32 -0
  54. package/template/src/simulations/index.ts +2 -0
  55. package/template/src/simulations/pizzaz-simulation.ts +177 -0
  56. package/template/src/components/card/index.ts +0 -1
  57. /package/template/node_modules/.vite/deps/{chunk-DQAZDQU3.js.map → chunk-LR7NKCX5.js.map} +0 -0
  58. /package/template/src/components/{card → carousel}/card.test.tsx +0 -0
  59. /package/template/src/components/{card → carousel}/card.tsx +0 -0
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+ import { Star } from '@openai/apps-sdk-ui/components/Icon';
3
+ import { cn } from '../../lib/index';
4
+ import type { Place } from '../../simulations/pizzaz-simulation';
5
+
6
+ export type PlaceCardProps = {
7
+ place: Place;
8
+ isSelected?: boolean;
9
+ onClick?: () => void;
10
+ className?: string;
11
+ };
12
+
13
+ export const PlaceCard = React.forwardRef<HTMLDivElement, PlaceCardProps>(
14
+ ({ place, isSelected, onClick, className }, ref) => {
15
+ return (
16
+ <div
17
+ ref={ref}
18
+ className={cn(
19
+ 'rounded-2xl px-3 select-none hover:bg-black/5 dark:hover:bg-white/5 cursor-pointer',
20
+ isSelected && 'bg-black/5 dark:bg-white/5',
21
+ className
22
+ )}
23
+ >
24
+ <div
25
+ className={cn(
26
+ 'border-b hover:border-transparent',
27
+ isSelected ? 'border-transparent' : 'border-black/5 dark:border-white/5'
28
+ )}
29
+ >
30
+ <button
31
+ className="w-full text-left py-3 transition flex gap-3 items-center"
32
+ onClick={onClick}
33
+ >
34
+ <img
35
+ src={place.thumbnail}
36
+ alt={place.name}
37
+ className="h-16 w-16 rounded-lg object-cover flex-none"
38
+ loading="lazy"
39
+ />
40
+ <div className="min-w-0">
41
+ <div className="font-medium truncate text-primary">{place.name}</div>
42
+ <div className="text-xs text-secondary truncate">{place.description}</div>
43
+ <div className="text-xs mt-1 text-secondary flex items-center gap-1">
44
+ <Star className="h-3 w-3" aria-hidden="true" />
45
+ {place.rating.toFixed(1)}
46
+ {place.price && <span>· {place.price}</span>}
47
+ </div>
48
+ </div>
49
+ </button>
50
+ </div>
51
+ </div>
52
+ );
53
+ }
54
+ );
55
+ PlaceCard.displayName = 'PlaceCard';
@@ -0,0 +1,45 @@
1
+ import * as React from 'react';
2
+ import useEmblaCarousel from 'embla-carousel-react';
3
+ import { cn } from '../../lib/index';
4
+ import { PlaceCard } from './place-card';
5
+ import type { Place } from '../../simulations/pizzaz-simulation';
6
+
7
+ export type PlaceCarouselProps = {
8
+ places: Place[];
9
+ selectedId: string | null;
10
+ onSelect: (place: Place) => void;
11
+ className?: string;
12
+ };
13
+
14
+ export const PlaceCarousel = React.forwardRef<HTMLDivElement, PlaceCarouselProps>(
15
+ ({ places, selectedId, onSelect, className }, ref) => {
16
+ const [emblaRef] = useEmblaCarousel({ dragFree: true, loop: false });
17
+
18
+ return (
19
+ <div
20
+ ref={ref}
21
+ className={cn('absolute inset-x-0 bottom-0 z-20 pointer-events-auto', className)}
22
+ >
23
+ <div className="pt-2">
24
+ <div className="overflow-hidden" ref={emblaRef}>
25
+ <div className="px-3 py-3 flex gap-3">
26
+ {places.map((place) => (
27
+ <div
28
+ key={place.id}
29
+ className="ring ring-black/10 dark:ring-white/10 max-w-[330px] w-full shadow-xl rounded-2xl bg-surface flex-shrink-0"
30
+ >
31
+ <PlaceCard
32
+ place={place}
33
+ isSelected={selectedId === place.id}
34
+ onClick={() => onSelect(place)}
35
+ />
36
+ </div>
37
+ ))}
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ );
43
+ }
44
+ );
45
+ PlaceCarousel.displayName = 'PlaceCarousel';
@@ -0,0 +1,132 @@
1
+ import * as React from 'react';
2
+ import { Button } from '@openai/apps-sdk-ui/components/Button';
3
+ import { Avatar } from '@openai/apps-sdk-ui/components/Avatar';
4
+ import { X, Star } from '@openai/apps-sdk-ui/components/Icon';
5
+ import { cn } from '../../lib/index';
6
+ import type { Place } from '../../simulations/pizzaz-simulation';
7
+
8
+ export type PlaceInspectorProps = {
9
+ place: Place;
10
+ onClose: () => void;
11
+ className?: string;
12
+ };
13
+
14
+ const REVIEWS = [
15
+ {
16
+ user: 'Leo M.',
17
+ avatar: 'https://persistent.oaistatic.com/pizzaz/user1.png',
18
+ text: 'Fantastic crust and balanced toppings. The marinara is spot on!',
19
+ },
20
+ {
21
+ user: 'Priya S.',
22
+ avatar: 'https://persistent.oaistatic.com/pizzaz/user2.png',
23
+ text: 'Cozy vibe and friendly staff. Quick service on a Friday night.',
24
+ },
25
+ {
26
+ user: 'Maya R.',
27
+ avatar: 'https://persistent.oaistatic.com/pizzaz/user3.png',
28
+ text: 'Great for sharing. Will definitely come back with friends.',
29
+ },
30
+ ];
31
+
32
+ export const PlaceInspector = React.forwardRef<HTMLDivElement, PlaceInspectorProps>(
33
+ ({ place, onClose, className }, ref) => {
34
+ if (!place) return null;
35
+
36
+ return (
37
+ <div
38
+ ref={ref}
39
+ className={cn(
40
+ 'pizzaz-inspector absolute z-30 top-0 bottom-4 left-0 right-auto xl:left-auto xl:right-6 md:z-20 w-[340px] xl:w-[360px] xl:top-6 xl:bottom-8 pointer-events-auto',
41
+ 'animate-in fade-in slide-in-from-left-2 xl:slide-in-from-right-2 duration-200',
42
+ className
43
+ )}
44
+ >
45
+ {/* Close button */}
46
+ <Button
47
+ variant="solid"
48
+ color="secondary"
49
+ size="sm"
50
+ className="absolute z-10 top-4 left-4 xl:top-4 xl:left-4 shadow-xl rounded-full"
51
+ onClick={onClose}
52
+ aria-label="Close details"
53
+ >
54
+ <X className="h-[18px] w-[18px]" aria-hidden="true" />
55
+ </Button>
56
+
57
+ <div className="relative h-full overflow-y-auto rounded-none xl:rounded-3xl bg-surface xl:shadow-xl xl:ring ring-black/10 dark:ring-white/10">
58
+ {/* Thumbnail */}
59
+ <div className="relative mt-2 xl:mt-0 px-2 xl:px-0">
60
+ <img
61
+ src={place.thumbnail}
62
+ alt={place.name}
63
+ className="w-full rounded-3xl xl:rounded-none h-80 object-cover xl:rounded-t-2xl"
64
+ loading="lazy"
65
+ />
66
+ </div>
67
+
68
+ <div className="h-[calc(100%-11rem)] sm:h-[calc(100%-14rem)]">
69
+ {/* Place info */}
70
+ <div className="p-4 sm:p-5">
71
+ <div className="text-2xl font-medium truncate text-primary">{place.name}</div>
72
+ <div className="text-sm mt-1 text-secondary flex items-center gap-1">
73
+ <Star className="h-3.5 w-3.5" aria-hidden="true" />
74
+ {place.rating.toFixed(1)}
75
+ {place.price && <span>· {place.price}</span>}
76
+ <span>· {place.city}</span>
77
+ </div>
78
+
79
+ {/* Action buttons */}
80
+ <div className="mt-3 flex flex-row items-center gap-3 font-medium">
81
+ <Button
82
+ variant="solid"
83
+ color="warning"
84
+ size="sm"
85
+ className="rounded-full"
86
+ onClick={() => console.log('Add to favorites:', place.id)}
87
+ >
88
+ Add to favorites
89
+ </Button>
90
+ <Button
91
+ variant="outline"
92
+ color="primary"
93
+ size="sm"
94
+ className="rounded-full"
95
+ style={{ color: '#F46C21' }}
96
+ onClick={() => console.log('Contact:', place.id)}
97
+ >
98
+ Contact
99
+ </Button>
100
+ </div>
101
+
102
+ {/* Description */}
103
+ <div className="text-sm mt-5 text-primary">
104
+ {place.description} Enjoy a slice at one of SF&apos;s favorites. Fresh ingredients,
105
+ great crust, and cozy vibes.
106
+ </div>
107
+ </div>
108
+
109
+ {/* Reviews */}
110
+ <div className="px-4 sm:px-5 pb-4">
111
+ <div className="text-lg font-medium mb-2 text-primary">Reviews</div>
112
+ <ul className="space-y-3 divide-y divide-black/5 dark:divide-white/5">
113
+ {REVIEWS.map((review, idx) => (
114
+ <li key={idx} className="py-3">
115
+ <div className="flex items-start gap-3">
116
+ <Avatar imageUrl={review.avatar} name={review.user} size={32} />
117
+ <div className="min-w-0 gap-1 flex flex-col">
118
+ <div className="text-xs font-medium text-secondary">{review.user}</div>
119
+ <div className="text-sm text-primary">{review.text}</div>
120
+ </div>
121
+ </div>
122
+ </li>
123
+ ))}
124
+ </ul>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ );
130
+ }
131
+ );
132
+ PlaceInspector.displayName = 'PlaceInspector';
@@ -0,0 +1,90 @@
1
+ import * as React from 'react';
2
+ import { Settings } from '@openai/apps-sdk-ui/components/Icon';
3
+ import { cn } from '../../lib/index';
4
+ import { PlaceCard } from './place-card';
5
+ import type { Place } from '../../simulations/pizzaz-simulation';
6
+
7
+ export type PlaceListProps = {
8
+ places: Place[];
9
+ selectedId: string | null;
10
+ onSelect: (place: Place) => void;
11
+ className?: string;
12
+ };
13
+
14
+ export const PlaceList = React.forwardRef<HTMLDivElement, PlaceListProps>(
15
+ ({ places, selectedId, onSelect, className }, ref) => {
16
+ const scrollRef = React.useRef<HTMLDivElement>(null);
17
+ const [showBottomFade, setShowBottomFade] = React.useState(false);
18
+
19
+ const updateBottomFadeVisibility = React.useCallback(() => {
20
+ const el = scrollRef.current;
21
+ if (!el) return;
22
+ const atBottom = Math.ceil(el.scrollTop + el.clientHeight) >= el.scrollHeight;
23
+ setShowBottomFade(!atBottom);
24
+ }, []);
25
+
26
+ React.useEffect(() => {
27
+ updateBottomFadeVisibility();
28
+ const el = scrollRef.current;
29
+ if (!el) return;
30
+
31
+ const onScroll = () => updateBottomFadeVisibility();
32
+ el.addEventListener('scroll', onScroll, { passive: true });
33
+ window.addEventListener('resize', updateBottomFadeVisibility);
34
+
35
+ return () => {
36
+ el.removeEventListener('scroll', onScroll);
37
+ window.removeEventListener('resize', updateBottomFadeVisibility);
38
+ };
39
+ }, [places, updateBottomFadeVisibility]);
40
+
41
+ return (
42
+ <div
43
+ ref={ref}
44
+ className={cn(
45
+ 'absolute inset-y-0 bottom-4 left-0 z-20 w-[340px] max-w-[75%] pointer-events-auto',
46
+ className
47
+ )}
48
+ >
49
+ <div ref={scrollRef} className="relative px-2 h-full overflow-y-auto bg-surface">
50
+ {/* Header */}
51
+ <div className="flex justify-between flex-row items-center px-3 sticky bg-surface top-0 py-4 text-md font-medium">
52
+ <span className="text-primary">{places.length} results</span>
53
+ <Settings className="h-5 w-5 text-secondary" aria-hidden="true" />
54
+ </div>
55
+
56
+ {/* Place list */}
57
+ <div>
58
+ {places.map((place) => (
59
+ <PlaceCard
60
+ key={place.id}
61
+ place={place}
62
+ isSelected={selectedId === place.id}
63
+ onClick={() => onSelect(place)}
64
+ />
65
+ ))}
66
+ </div>
67
+ </div>
68
+
69
+ {/* Bottom fade gradient */}
70
+ {showBottomFade && (
71
+ <div
72
+ className="pointer-events-none absolute inset-x-0 bottom-0 h-9 z-10 transition-opacity duration-200"
73
+ aria-hidden="true"
74
+ >
75
+ <div
76
+ className="w-full h-full bg-gradient-to-t from-black/15 to-transparent dark:from-white/15"
77
+ style={{
78
+ WebkitMaskImage:
79
+ 'linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.25) 25%, rgba(0,0,0,0.25) 75%, rgba(0,0,0,0) 100%)',
80
+ maskImage:
81
+ 'linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.25) 25%, rgba(0,0,0,0.25) 75%, rgba(0,0,0,0) 100%)',
82
+ }}
83
+ />
84
+ </div>
85
+ )}
86
+ </div>
87
+ );
88
+ }
89
+ );
90
+ PlaceList.displayName = 'PlaceList';
@@ -32,13 +32,10 @@ vi.mock('sunpeak', () => ({
32
32
  }));
33
33
 
34
34
  // Mock child components
35
- vi.mock('../components/carousel/carousel', () => ({
35
+ vi.mock('../components/carousel', () => ({
36
36
  Carousel: ({ children }: { children: React.ReactNode }) => (
37
37
  <div data-testid="carousel">{children}</div>
38
38
  ),
39
- }));
40
-
41
- vi.mock('../components/card/card', () => ({
42
39
  Card: ({ header, buttonSize }: { header: React.ReactNode; buttonSize?: string }) => (
43
40
  <div data-testid="card" data-button-size={buttonSize}>
44
41
  {header}
@@ -1,7 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { useWidgetProps, useSafeArea, useMaxHeight, useUserAgent } from 'sunpeak';
3
- import { Carousel } from '../components/carousel/carousel';
4
- import { Card } from '../components/card/card';
3
+ import { Carousel, Card } from '../components/carousel';
5
4
 
6
5
  /**
7
6
  * Production-ready Carousel Resource
@@ -1,3 +1,4 @@
1
1
  export { CounterResource } from './counter-resource';
2
2
  export { AlbumsResource } from './albums-resource';
3
3
  export { CarouselResource } from './carousel-resource';
4
+ export { PizzazResource } from './pizzaz-resource';
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { useSafeArea, useMaxHeight } from 'sunpeak';
3
+ import { Pizzaz } from '../components/pizzaz/pizzaz';
4
+
5
+ /**
6
+ * Production-ready Pizzaz Resource
7
+ *
8
+ * This resource displays a pizza restaurant finder with an interactive map,
9
+ * place listings, and detailed inspector view.
10
+ * Can be dropped into any production environment without changes.
11
+ */
12
+ export const PizzazResource = React.forwardRef<HTMLDivElement>((_props, ref) => {
13
+ const safeArea = useSafeArea();
14
+ const maxHeight = useMaxHeight();
15
+
16
+ return (
17
+ <div
18
+ ref={ref}
19
+ className="h-full"
20
+ style={{
21
+ paddingTop: `${safeArea?.insets.top ?? 0}px`,
22
+ paddingBottom: `${safeArea?.insets.bottom ?? 0}px`,
23
+ paddingLeft: `${safeArea?.insets.left ?? 0}px`,
24
+ paddingRight: `${safeArea?.insets.right ?? 0}px`,
25
+ maxHeight: maxHeight ?? undefined,
26
+ }}
27
+ >
28
+ <Pizzaz />
29
+ </div>
30
+ );
31
+ });
32
+ PizzazResource.displayName = 'PizzazResource';
@@ -8,9 +8,11 @@
8
8
  import { counterSimulation } from './counter-simulation.js';
9
9
  import { albumsSimulation } from './albums-simulation.js';
10
10
  import { carouselSimulation } from './carousel-simulation.js';
11
+ import { pizzazSimulation } from './pizzaz-simulation.js';
11
12
 
12
13
  export const SIMULATIONS = {
13
14
  counter: counterSimulation,
14
15
  albums: albumsSimulation,
15
16
  carousel: carouselSimulation,
17
+ pizzaz: pizzazSimulation,
16
18
  } as const;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Server-safe configuration for the pizzaz simulation.
3
+ * This file contains only metadata and doesn't import React components or CSS.
4
+ */
5
+
6
+ import { defaultWidgetMeta } from './widget-config';
7
+
8
+ export interface Place {
9
+ id: string;
10
+ name: string;
11
+ coords: [number, number];
12
+ description: string;
13
+ city: string;
14
+ rating: number;
15
+ price: string;
16
+ thumbnail: string;
17
+ }
18
+
19
+ export interface PizzazData extends Record<string, unknown> {
20
+ places: Place[];
21
+ }
22
+
23
+ const pizzazData: PizzazData = {
24
+ places: [
25
+ {
26
+ id: 'nova-slice-lab',
27
+ name: 'Nova Slice Lab',
28
+ coords: [-122.4098, 37.8001],
29
+ description: 'Award-winning Neapolitan pies in North Beach.',
30
+ city: 'North Beach',
31
+ rating: 4.8,
32
+ price: '$$$',
33
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-1.png',
34
+ },
35
+ {
36
+ id: 'midnight-marinara',
37
+ name: 'Midnight Marinara',
38
+ coords: [-122.4093, 37.799],
39
+ description: 'Focaccia-style squares, late-night favorite.',
40
+ city: 'North Beach',
41
+ rating: 4.6,
42
+ price: '$',
43
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-2.png',
44
+ },
45
+ {
46
+ id: 'cinder-oven-co',
47
+ name: 'Cinder Oven Co.',
48
+ coords: [-122.4255, 37.7613],
49
+ description: 'Thin-crust classics on 18th Street.',
50
+ city: 'Mission',
51
+ rating: 4.5,
52
+ price: '$$',
53
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-3.png',
54
+ },
55
+ {
56
+ id: 'neon-crust-works',
57
+ name: 'Neon Crust Works',
58
+ coords: [-122.4388, 37.7775],
59
+ description: 'Deep-dish and cornmeal crust favorites.',
60
+ city: 'Alamo Square',
61
+ rating: 4.5,
62
+ price: '$$',
63
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-6.png',
64
+ },
65
+ {
66
+ id: 'luna-pie-collective',
67
+ name: 'Luna Pie Collective',
68
+ coords: [-122.4077, 37.799],
69
+ description: 'Wood-fired pies and burrata in North Beach.',
70
+ city: 'North Beach',
71
+ rating: 4.6,
72
+ price: '$$',
73
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-4.png',
74
+ },
75
+ {
76
+ id: 'bricklight-deep-dish',
77
+ name: 'Bricklight Deep Dish',
78
+ coords: [-122.4097, 37.7992],
79
+ description: 'Chicago-style pies from Tony Gemignani.',
80
+ city: 'North Beach',
81
+ rating: 4.4,
82
+ price: '$$$',
83
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-5.png',
84
+ },
85
+ {
86
+ id: 'garden-ember-pies',
87
+ name: 'Garden Ember Pies',
88
+ coords: [-122.438, 37.7722],
89
+ description: 'Neighborhood spot with seasonal toppings.',
90
+ city: 'Lower Haight',
91
+ rating: 4.4,
92
+ price: '$$',
93
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-1.png',
94
+ },
95
+ {
96
+ id: 'atlas-fire-pizzeria',
97
+ name: 'Atlas Fire Pizzeria',
98
+ coords: [-122.4123, 37.7899],
99
+ description: 'Sourdough, wood-fired pies near Nob Hill.',
100
+ city: 'Nob Hill',
101
+ rating: 4.6,
102
+ price: '$$$',
103
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-2.png',
104
+ },
105
+ {
106
+ id: 'circuit-slice-garage',
107
+ name: 'Circuit Slice Garage',
108
+ coords: [-122.4135, 37.7805],
109
+ description: 'Crispy-edged Detroit-style in SoMa.',
110
+ city: 'SoMa',
111
+ rating: 4.5,
112
+ price: '$$',
113
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-3.png',
114
+ },
115
+ {
116
+ id: 'velvet-mozza-lounge',
117
+ name: 'Velvet Mozza Lounge',
118
+ coords: [-122.4019, 37.7818],
119
+ description: 'Bianca pies and cocktails near Yerba Buena.',
120
+ city: 'Yerba Buena',
121
+ rating: 4.3,
122
+ price: '$$',
123
+ thumbnail: 'https://persistent.oaistatic.com/pizzaz/pizzaz-6.png',
124
+ },
125
+ ],
126
+ };
127
+
128
+ export const pizzazSimulation = {
129
+ userMessage: 'Find pizza near me',
130
+
131
+ // MCP Tool protocol - official Tool type from MCP SDK used in ListTools response
132
+ tool: {
133
+ name: 'find-pizza',
134
+ description: 'Find pizza restaurants nearby',
135
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false } as const,
136
+ title: 'Find Pizza',
137
+ annotations: { readOnlyHint: true },
138
+ _meta: {
139
+ 'openai/outputTemplate': 'ui://PizzazResource',
140
+ 'openai/toolInvocation/invoking': 'Finding pizza places',
141
+ 'openai/toolInvocation/invoked': 'Found pizza places',
142
+ 'openai/widgetAccessible': true,
143
+ 'openai/resultCanProduceWidget': true,
144
+ },
145
+ },
146
+
147
+ // MCP Resource protocol - official Resource type from MCP SDK used in ListResources response
148
+ // resource.name is used as the simulation identifier
149
+ // resource.title is used as the simulation display label
150
+ resource: {
151
+ uri: 'ui://PizzazResource',
152
+ name: 'pizzaz',
153
+ title: 'Pizzaz',
154
+ description: 'Pizza restaurant finder widget',
155
+ mimeType: 'text/html+skybridge',
156
+ _meta: {
157
+ ...defaultWidgetMeta,
158
+ 'openai/widgetCSP': {
159
+ ...defaultWidgetMeta['openai/widgetCSP'],
160
+ connect_domains: [
161
+ ...defaultWidgetMeta['openai/widgetCSP'].connect_domains,
162
+ 'https://api.mapbox.com',
163
+ ],
164
+ resource_domains: [
165
+ ...defaultWidgetMeta['openai/widgetCSP'].resource_domains,
166
+ 'https://api.mapbox.com',
167
+ ],
168
+ },
169
+ },
170
+ },
171
+
172
+ // MCP CallTool protocol - data for CallTool response
173
+ toolCall: {
174
+ structuredContent: pizzazData,
175
+ _meta: {},
176
+ },
177
+ } as const;
@@ -1 +0,0 @@
1
- export * from './card';