sunpeak 0.18.9 → 0.18.13
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.
- package/README.md +12 -0
- package/bin/commands/inspect.mjs +4 -1
- package/bin/commands/start.mjs +2 -1
- package/bin/lib/sandbox-server.mjs +12 -1
- package/dist/chatgpt/index.cjs +1 -1
- package/dist/chatgpt/index.js +1 -1
- package/dist/claude/index.cjs +1 -1
- package/dist/claude/index.js +1 -1
- package/dist/host/chatgpt/index.cjs +1 -1
- package/dist/host/chatgpt/index.js +1 -1
- package/dist/index.cjs +93 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +54 -54
- package/dist/index.js.map +1 -1
- package/dist/inspector/hosts.d.ts +12 -0
- package/dist/inspector/index.cjs +1 -1
- package/dist/inspector/index.js +1 -1
- package/dist/{inspector-CTMccsz9.cjs → inspector-8nPV2A-z.cjs} +93 -51
- package/dist/inspector-8nPV2A-z.cjs.map +1 -0
- package/dist/{inspector-DkS75JCk.js → inspector-Cdo5BK2D.js} +92 -50
- package/dist/inspector-Cdo5BK2D.js.map +1 -0
- package/dist/mcp/index.cjs +69 -62
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +63 -56
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/production-server.d.ts +12 -0
- package/dist/{protocol-DJmRaBzO.js → protocol-BfAACnv0.js} +14 -9
- package/dist/{protocol-DJmRaBzO.js.map → protocol-BfAACnv0.js.map} +1 -1
- package/dist/{protocol-jbxhzcnS.cjs → protocol-C7kTcBr_.cjs} +14 -9
- package/dist/{protocol-jbxhzcnS.cjs.map → protocol-C7kTcBr_.cjs.map} +1 -1
- package/dist/style.css +36 -1
- package/dist/{use-app-BNbz1uzj.js → use-app-CfP9VypY.js} +107 -56
- package/dist/use-app-CfP9VypY.js.map +1 -0
- package/dist/{use-app-Dqh20JPP.cjs → use-app-CzcYw1Kz.cjs} +165 -114
- package/dist/use-app-CzcYw1Kz.cjs.map +1 -0
- package/package.json +3 -3
- package/template/README.md +12 -0
- package/template/dist/albums/albums.html +16 -15
- package/template/dist/albums/albums.json +1 -1
- package/template/dist/carousel/carousel.html +20 -19
- package/template/dist/carousel/carousel.json +1 -1
- package/template/dist/map/map.html +7 -6
- package/template/dist/map/map.json +1 -1
- package/template/dist/review/review.html +6 -5
- package/template/dist/review/review.json +1 -1
- package/template/node_modules/.vite/deps/_metadata.json +3 -3
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js +49 -49
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js +49 -49
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js +89 -89
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/_metadata.json +24 -24
- package/template/node_modules/.vite-mcp/deps/{protocol-CTflwIfG.js → protocol-B_qKkui_.js} +14 -9
- package/template/node_modules/.vite-mcp/deps/protocol-B_qKkui_.js.map +1 -0
- package/template/src/resources/carousel/carousel.test.tsx +16 -2
- package/template/src/resources/carousel/carousel.tsx +42 -14
- package/template/src/resources/carousel/components/carousel.tsx +20 -0
- package/template/src/resources/carousel/components/index.ts +1 -0
- package/template/src/resources/carousel/components/place-detail.tsx +153 -0
- package/template/tests/e2e/carousel.spec.ts +125 -0
- package/template/tests/live/carousel.spec.ts +2 -2
- package/template/tests/simulations/show-carousel.json +54 -5
- package/dist/inspector-CTMccsz9.cjs.map +0 -1
- package/dist/inspector-DkS75JCk.js.map +0 -1
- package/dist/use-app-BNbz1uzj.js.map +0 -1
- package/dist/use-app-Dqh20JPP.cjs.map +0 -1
- package/template/node_modules/.vite-mcp/deps/protocol-CTflwIfG.js.map +0 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Button } from '@/components/button';
|
|
2
|
+
import { Star } from '@/components/icon';
|
|
3
|
+
|
|
4
|
+
export interface PlaceDetailData {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
rating: number;
|
|
8
|
+
category: string;
|
|
9
|
+
location: string;
|
|
10
|
+
image: string;
|
|
11
|
+
description: string;
|
|
12
|
+
address?: string;
|
|
13
|
+
phone?: string;
|
|
14
|
+
hours?: string;
|
|
15
|
+
priceRange?: string;
|
|
16
|
+
tips?: string[];
|
|
17
|
+
highlights?: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface PlaceDetailProps {
|
|
21
|
+
place: PlaceDetailData;
|
|
22
|
+
buttonSize?: 'xs' | 'sm' | 'md' | 'lg';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function RatingStars({ rating }: { rating: number }) {
|
|
26
|
+
const fullStars = Math.floor(rating);
|
|
27
|
+
const hasHalf = rating - fullStars >= 0.5;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<span className="inline-flex items-center gap-0.5">
|
|
31
|
+
{Array.from({ length: 5 }, (_, i) => (
|
|
32
|
+
<Star
|
|
33
|
+
key={i}
|
|
34
|
+
className={`h-4 w-4 ${
|
|
35
|
+
i < fullStars
|
|
36
|
+
? 'text-[var(--color-text-warning)]'
|
|
37
|
+
: i === fullStars && hasHalf
|
|
38
|
+
? 'text-[var(--color-text-warning)] opacity-50'
|
|
39
|
+
: 'text-[var(--color-border-tertiary)]'
|
|
40
|
+
}`}
|
|
41
|
+
/>
|
|
42
|
+
))}
|
|
43
|
+
<span className="ml-1 text-sm font-medium">{rating}</span>
|
|
44
|
+
</span>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function PlaceDetail({ place, buttonSize = 'sm' }: PlaceDetailProps) {
|
|
49
|
+
return (
|
|
50
|
+
<div className="flex flex-col h-full overflow-y-auto">
|
|
51
|
+
{/* Header */}
|
|
52
|
+
<div className="px-4 pt-4 pb-2">
|
|
53
|
+
<h1 className="text-lg font-semibold truncate">{place.name}</h1>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
{/* Hero image */}
|
|
57
|
+
<div className="px-4 flex justify-center">
|
|
58
|
+
<img src={place.image} alt={place.name} className="max-w-full h-auto rounded-xl" />
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Content */}
|
|
62
|
+
<div className="flex-1 px-4 py-4 space-y-4">
|
|
63
|
+
{/* Rating and category */}
|
|
64
|
+
<div className="flex items-center justify-between flex-wrap gap-2">
|
|
65
|
+
<RatingStars rating={place.rating} />
|
|
66
|
+
<div className="flex items-center gap-2 text-sm text-[var(--color-text-secondary)]">
|
|
67
|
+
<span className="px-2 py-0.5 rounded-full bg-[var(--color-background-secondary)]">
|
|
68
|
+
{place.category}
|
|
69
|
+
</span>
|
|
70
|
+
{place.priceRange && (
|
|
71
|
+
<span className="px-2 py-0.5 rounded-full bg-[var(--color-background-secondary)]">
|
|
72
|
+
{place.priceRange}
|
|
73
|
+
</span>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{/* Description */}
|
|
79
|
+
<p className="text-sm leading-relaxed">{place.description}</p>
|
|
80
|
+
|
|
81
|
+
{/* Info grid */}
|
|
82
|
+
<div className="grid grid-cols-1 gap-3">
|
|
83
|
+
{place.address && <InfoRow label="Address" value={place.address} />}
|
|
84
|
+
{place.hours && <InfoRow label="Hours" value={place.hours} />}
|
|
85
|
+
{place.phone && <InfoRow label="Phone" value={place.phone} />}
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
{/* Highlights */}
|
|
89
|
+
{place.highlights && place.highlights.length > 0 && (
|
|
90
|
+
<div>
|
|
91
|
+
<h3 className="text-sm font-medium mb-2">Highlights</h3>
|
|
92
|
+
<div className="flex flex-wrap gap-2">
|
|
93
|
+
{place.highlights.map((h) => (
|
|
94
|
+
<span
|
|
95
|
+
key={h}
|
|
96
|
+
className="text-xs px-2.5 py-1 rounded-full bg-[var(--color-background-info)] text-[var(--color-text-info)]"
|
|
97
|
+
>
|
|
98
|
+
{h}
|
|
99
|
+
</span>
|
|
100
|
+
))}
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{/* Tips */}
|
|
106
|
+
{place.tips && place.tips.length > 0 && (
|
|
107
|
+
<div>
|
|
108
|
+
<h3 className="text-sm font-medium mb-2">Tips</h3>
|
|
109
|
+
<ul className="space-y-2">
|
|
110
|
+
{place.tips.map((tip, i) => (
|
|
111
|
+
<li
|
|
112
|
+
key={i}
|
|
113
|
+
className="text-sm text-[var(--color-text-secondary)] pl-4 border-l-2 border-[var(--color-border-tertiary)]"
|
|
114
|
+
>
|
|
115
|
+
{tip}
|
|
116
|
+
</li>
|
|
117
|
+
))}
|
|
118
|
+
</ul>
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
|
|
122
|
+
{/* Action buttons */}
|
|
123
|
+
<div className="flex gap-2 pt-2">
|
|
124
|
+
<Button
|
|
125
|
+
variant="solid"
|
|
126
|
+
color="primary"
|
|
127
|
+
size={buttonSize}
|
|
128
|
+
onClick={() => console.log(`Visit ${place.name}`)}
|
|
129
|
+
>
|
|
130
|
+
Visit
|
|
131
|
+
</Button>
|
|
132
|
+
<Button
|
|
133
|
+
variant="soft"
|
|
134
|
+
color="secondary"
|
|
135
|
+
size={buttonSize}
|
|
136
|
+
onClick={() => console.log(`Learn more about ${place.name}`)}
|
|
137
|
+
>
|
|
138
|
+
Learn More
|
|
139
|
+
</Button>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function InfoRow({ label, value }: { label: string; value: string }) {
|
|
147
|
+
return (
|
|
148
|
+
<div className="flex gap-3 text-sm">
|
|
149
|
+
<span className="text-[var(--color-text-secondary)] shrink-0 w-16">{label}</span>
|
|
150
|
+
<span>{value}</span>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
@@ -121,6 +121,131 @@ for (const host of hosts) {
|
|
|
121
121
|
});
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
+
test.describe('Fullscreen Mode', () => {
|
|
125
|
+
test('should render correctly in fullscreen displayMode', async ({ page }) => {
|
|
126
|
+
await page.goto(
|
|
127
|
+
createInspectorUrl({
|
|
128
|
+
simulation: 'show-carousel',
|
|
129
|
+
theme: 'light',
|
|
130
|
+
displayMode: 'fullscreen',
|
|
131
|
+
host,
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
await page.waitForLoadState('networkidle');
|
|
136
|
+
const root = page.locator('#root');
|
|
137
|
+
await expect(root).not.toBeEmpty();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('should show detail view with place info in fullscreen', async ({ page }) => {
|
|
141
|
+
await page.goto(
|
|
142
|
+
createInspectorUrl({
|
|
143
|
+
simulation: 'show-carousel',
|
|
144
|
+
theme: 'light',
|
|
145
|
+
displayMode: 'fullscreen',
|
|
146
|
+
host,
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
151
|
+
|
|
152
|
+
// Click the first card to open fullscreen detail
|
|
153
|
+
const card = iframe.locator('.rounded-2xl').first();
|
|
154
|
+
await expect(card).toBeVisible();
|
|
155
|
+
await card.click();
|
|
156
|
+
|
|
157
|
+
// Should show detail view with place name, description, and detail fields
|
|
158
|
+
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
159
|
+
timeout: 5000,
|
|
160
|
+
});
|
|
161
|
+
await expect(iframe.locator('text=Highlights')).toBeVisible();
|
|
162
|
+
await expect(iframe.locator('text=Tips')).toBeVisible();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('should show detail view when Learn More is clicked', async ({ page }) => {
|
|
166
|
+
await page.goto(
|
|
167
|
+
createInspectorUrl({
|
|
168
|
+
simulation: 'show-carousel',
|
|
169
|
+
theme: 'light',
|
|
170
|
+
displayMode: 'fullscreen',
|
|
171
|
+
host,
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
176
|
+
|
|
177
|
+
// Click "Learn More" on the first card
|
|
178
|
+
const learnMore = iframe.locator('button:has-text("Learn More")').first();
|
|
179
|
+
await expect(learnMore).toBeVisible();
|
|
180
|
+
await learnMore.click();
|
|
181
|
+
|
|
182
|
+
// Should show detail view
|
|
183
|
+
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
184
|
+
timeout: 5000,
|
|
185
|
+
});
|
|
186
|
+
await expect(iframe.locator('text=Address')).toBeVisible();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('should not have a back button in detail view', async ({ page }) => {
|
|
190
|
+
await page.goto(
|
|
191
|
+
createInspectorUrl({
|
|
192
|
+
simulation: 'show-carousel',
|
|
193
|
+
theme: 'light',
|
|
194
|
+
displayMode: 'fullscreen',
|
|
195
|
+
host,
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
200
|
+
|
|
201
|
+
// Click the first card to open detail
|
|
202
|
+
const card = iframe.locator('.rounded-2xl').first();
|
|
203
|
+
await expect(card).toBeVisible();
|
|
204
|
+
await card.click();
|
|
205
|
+
|
|
206
|
+
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
207
|
+
timeout: 5000,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Back button should not exist
|
|
211
|
+
const backButton = iframe.locator('button[aria-label="Back to carousel"]');
|
|
212
|
+
await expect(backButton).not.toBeAttached();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('should center the hero image without stretching', async ({ page }) => {
|
|
216
|
+
await page.goto(
|
|
217
|
+
createInspectorUrl({
|
|
218
|
+
simulation: 'show-carousel',
|
|
219
|
+
theme: 'light',
|
|
220
|
+
displayMode: 'fullscreen',
|
|
221
|
+
host,
|
|
222
|
+
})
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const iframe = page.frameLocator('iframe').frameLocator('iframe');
|
|
226
|
+
|
|
227
|
+
// Click the first card to open detail
|
|
228
|
+
const card = iframe.locator('.rounded-2xl').first();
|
|
229
|
+
await expect(card).toBeVisible();
|
|
230
|
+
await card.click();
|
|
231
|
+
|
|
232
|
+
await expect(iframe.locator('h1:has-text("Lady Bird Lake")')).toBeVisible({
|
|
233
|
+
timeout: 5000,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Check the image container is centered
|
|
237
|
+
const imageContainer = iframe.locator('img').first().locator('..');
|
|
238
|
+
const styles = await imageContainer.evaluate((el) => {
|
|
239
|
+
const computed = window.getComputedStyle(el);
|
|
240
|
+
return {
|
|
241
|
+
justifyContent: computed.justifyContent,
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(styles.justifyContent).toBe('center');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
124
249
|
test.describe('Dark Mode', () => {
|
|
125
250
|
test('should render carousel cards with correct styles', async ({ page }) => {
|
|
126
251
|
await page.goto(createInspectorUrl({ simulation: 'show-carousel', theme: 'dark', host }));
|
|
@@ -5,7 +5,7 @@ test('carousel tool renders cards with correct styles', async ({ live }) => {
|
|
|
5
5
|
|
|
6
6
|
// First place from simulation data: "Lady Bird Lake"
|
|
7
7
|
await expect(app.locator('img').first()).toBeVisible({ timeout: 15_000 });
|
|
8
|
-
await expect(app.
|
|
8
|
+
await expect(app.getByRole('heading', { name: 'Lady Bird Lake' })).toBeVisible();
|
|
9
9
|
const buttons = app.locator('button');
|
|
10
10
|
expect(await buttons.count()).toBeGreaterThanOrEqual(1);
|
|
11
11
|
|
|
@@ -43,7 +43,7 @@ test('carousel tool renders cards with correct styles', async ({ live }) => {
|
|
|
43
43
|
|
|
44
44
|
// Switch to dark mode and verify the app re-themes correctly
|
|
45
45
|
await live.setColorScheme('dark', app);
|
|
46
|
-
await expect(app.
|
|
46
|
+
await expect(app.getByRole('heading', { name: 'Lady Bird Lake' })).toBeVisible();
|
|
47
47
|
const darkBorderColor = await app
|
|
48
48
|
.locator('div[class*="rounded"]')
|
|
49
49
|
.first()
|
|
@@ -17,7 +17,17 @@
|
|
|
17
17
|
"category": "Waterfront",
|
|
18
18
|
"location": "Austin",
|
|
19
19
|
"image": "https://cdn.sunpeak.ai/demo/austin1.jpeg",
|
|
20
|
-
"description": "Scenic lake perfect for kayaking, paddleboarding, and trails."
|
|
20
|
+
"description": "Scenic lake perfect for kayaking, paddleboarding, and trails. The hike-and-bike trail that circles the lake is one of the most popular outdoor destinations in Austin, offering 10 miles of paths with views of the downtown skyline.",
|
|
21
|
+
"address": "1820 S Lakeshore Blvd, Austin, TX 78741",
|
|
22
|
+
"phone": "(512) 974-6700",
|
|
23
|
+
"hours": "Open 24 hours",
|
|
24
|
+
"priceRange": "Free",
|
|
25
|
+
"highlights": ["Kayaking", "Paddleboarding", "Hike & Bike Trail", "Dog Friendly"],
|
|
26
|
+
"tips": [
|
|
27
|
+
"Go early in the morning to avoid crowds on the trail.",
|
|
28
|
+
"Rent kayaks from the boathouse on the south shore for the best skyline views.",
|
|
29
|
+
"The Boardwalk section on the south side is the newest and least crowded part."
|
|
30
|
+
]
|
|
21
31
|
},
|
|
22
32
|
{
|
|
23
33
|
"id": "2",
|
|
@@ -26,7 +36,17 @@
|
|
|
26
36
|
"category": "Historic Site",
|
|
27
37
|
"location": "Austin",
|
|
28
38
|
"image": "https://cdn.sunpeak.ai/demo/austin2.jpeg",
|
|
29
|
-
"description": "Stunning capitol building with free tours and beautiful grounds."
|
|
39
|
+
"description": "Stunning capitol building with free tours and beautiful grounds. Built in 1888, the building is taller than the U.S. Capitol and features a stunning rotunda with portraits of every Texas governor.",
|
|
40
|
+
"address": "1100 Congress Ave, Austin, TX 78701",
|
|
41
|
+
"phone": "(512) 463-0063",
|
|
42
|
+
"hours": "Mon-Fri 7am-10pm, Sat-Sun 9am-8pm",
|
|
43
|
+
"priceRange": "Free",
|
|
44
|
+
"highlights": ["Free Guided Tours", "Architecture", "Historical Exhibits", "Gift Shop"],
|
|
45
|
+
"tips": [
|
|
46
|
+
"Free guided tours run every 30-45 minutes and are well worth the time.",
|
|
47
|
+
"Visit the underground extension for the modern legislative offices.",
|
|
48
|
+
"The south grounds have great photo spots with the building in the background."
|
|
49
|
+
]
|
|
30
50
|
},
|
|
31
51
|
{
|
|
32
52
|
"id": "3",
|
|
@@ -35,7 +55,17 @@
|
|
|
35
55
|
"category": "Architecture",
|
|
36
56
|
"location": "Austin",
|
|
37
57
|
"image": "https://cdn.sunpeak.ai/demo/austin3.jpeg",
|
|
38
|
-
"description": "Century-old performance and movie theatre in the heart of downtown Austin."
|
|
58
|
+
"description": "Century-old performance and movie theatre in the heart of downtown Austin. Opened in 1915, this beautifully restored venue hosts classic film screenings, live performances, and comedy shows year-round.",
|
|
59
|
+
"address": "713 Congress Ave, Austin, TX 78701",
|
|
60
|
+
"phone": "(512) 472-5470",
|
|
61
|
+
"hours": "Box office: Mon-Fri 10am-6pm",
|
|
62
|
+
"priceRange": "$$",
|
|
63
|
+
"highlights": ["Classic Films", "Live Music", "Comedy Shows", "Historic Venue"],
|
|
64
|
+
"tips": [
|
|
65
|
+
"Check the Summer Classic Film Series for discounted screenings.",
|
|
66
|
+
"The balcony seats offer the best view of the ornate ceiling.",
|
|
67
|
+
"Arrive early to grab a drink at the bar before the show."
|
|
68
|
+
]
|
|
39
69
|
},
|
|
40
70
|
{
|
|
41
71
|
"id": "4",
|
|
@@ -44,7 +74,17 @@
|
|
|
44
74
|
"category": "Park",
|
|
45
75
|
"location": "Austin",
|
|
46
76
|
"image": "https://cdn.sunpeak.ai/demo/austin4.jpeg",
|
|
47
|
-
"description": "Popular park with trails, sports fields, and Barton Springs Pool."
|
|
77
|
+
"description": "Popular park with trails, sports fields, and Barton Springs Pool. This 350-acre metropolitan park sits along the south bank of Lady Bird Lake and is home to the Austin City Limits music festival.",
|
|
78
|
+
"address": "2100 Barton Springs Rd, Austin, TX 78704",
|
|
79
|
+
"phone": "(512) 974-6700",
|
|
80
|
+
"hours": "5am-10pm daily",
|
|
81
|
+
"priceRange": "Free",
|
|
82
|
+
"highlights": ["Barton Springs Pool", "Botanical Garden", "Disc Golf", "ACL Festival"],
|
|
83
|
+
"tips": [
|
|
84
|
+
"Barton Springs Pool is spring-fed and stays 68°F year-round.",
|
|
85
|
+
"The botanical garden entrance is on the west side of the park.",
|
|
86
|
+
"Parking fills up fast on weekends. Take the bus or bike instead."
|
|
87
|
+
]
|
|
48
88
|
},
|
|
49
89
|
{
|
|
50
90
|
"id": "5",
|
|
@@ -53,7 +93,16 @@
|
|
|
53
93
|
"category": "Landmark",
|
|
54
94
|
"location": "Austin",
|
|
55
95
|
"image": "https://cdn.sunpeak.ai/demo/austin5.jpeg",
|
|
56
|
-
"description": "
|
|
96
|
+
"description": "Lively street with unique shops, restaurants, and live music. Known locally as SoCo, this stretch south of Lady Bird Lake is the heart of Austin's eclectic culture, with vintage stores, food trucks, and murals.",
|
|
97
|
+
"address": "South Congress Ave, Austin, TX 78704",
|
|
98
|
+
"hours": "Shops typically 10am-8pm",
|
|
99
|
+
"priceRange": "$-$$$",
|
|
100
|
+
"highlights": ["Shopping", "Food Trucks", "Street Art", "Live Music"],
|
|
101
|
+
"tips": [
|
|
102
|
+
"First Thursday of every month features extended hours and street vendors.",
|
|
103
|
+
"The 'I love you so much' mural on the side of Jo's Coffee is an iconic photo spot.",
|
|
104
|
+
"Walk south past Oltorf St for less crowded, more local shops."
|
|
105
|
+
]
|
|
57
106
|
}
|
|
58
107
|
]
|
|
59
108
|
}
|