sunpeak 0.1.25 → 0.2.2

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 (44) hide show
  1. package/README.md +9 -76
  2. package/bin/sunpeak.js +87 -0
  3. package/dist/index.cjs +827 -1361
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +98 -589
  6. package/dist/index.d.ts +98 -589
  7. package/dist/index.js +795 -1337
  8. package/dist/index.js.map +1 -1
  9. package/dist/styles/chatgpt/index.css +146 -0
  10. package/dist/styles/globals.css +220 -0
  11. package/package.json +34 -36
  12. package/template/.prettierignore +4 -0
  13. package/template/.prettierrc +9 -0
  14. package/template/README.md +47 -0
  15. package/template/assets/favicon.ico +0 -0
  16. package/template/components.json +21 -0
  17. package/template/dev/main.tsx +65 -0
  18. package/template/dev/styles.css +5 -0
  19. package/template/eslint.config.cjs +49 -0
  20. package/template/index.html +13 -0
  21. package/template/package.json +56 -0
  22. package/template/src/App.tsx +45 -0
  23. package/template/src/components/index.ts +2 -0
  24. package/template/src/components/shadcn/button.tsx +60 -0
  25. package/template/src/components/shadcn/card.tsx +76 -0
  26. package/template/src/components/shadcn/carousel.tsx +260 -0
  27. package/template/src/components/shadcn/index.ts +5 -0
  28. package/template/src/components/shadcn/label.tsx +24 -0
  29. package/template/src/components/shadcn/select.tsx +157 -0
  30. package/template/src/components/sunpeak-card.test.tsx +76 -0
  31. package/template/src/components/sunpeak-card.tsx +140 -0
  32. package/template/src/components/sunpeak-carousel.test.tsx +42 -0
  33. package/template/src/components/sunpeak-carousel.tsx +126 -0
  34. package/template/src/index.ts +3 -0
  35. package/template/src/lib/index.ts +1 -0
  36. package/template/src/lib/utils.ts +6 -0
  37. package/template/src/styles/chatgpt.css +146 -0
  38. package/template/src/styles/globals.css +220 -0
  39. package/template/src/test/setup.ts +37 -0
  40. package/template/tsconfig.json +32 -0
  41. package/template/tsconfig.node.json +11 -0
  42. package/template/tsup.config.ts +23 -0
  43. package/template/vite.config.ts +35 -0
  44. package/template/vitest.config.ts +15 -0
@@ -0,0 +1,220 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ /* Tailwind v4 theme configuration */
7
+ @theme {
8
+ /* ===================================
9
+ * COLORS - Mapped to shadcn and --sp- variables
10
+ * ===================================
11
+ */
12
+ --color-*: initial;
13
+
14
+ /* Shadcn colors */
15
+ --color-background: var(--background);
16
+ --color-foreground: var(--foreground);
17
+ --color-border: var(--border);
18
+ --color-card: var(--card);
19
+ --color-card-foreground: var(--card-foreground);
20
+ --color-popover: var(--popover);
21
+ --color-popover-foreground: var(--popover-foreground);
22
+ --color-primary: var(--primary);
23
+ --color-primary-foreground: var(--primary-foreground);
24
+ --color-secondary: var(--secondary);
25
+ --color-secondary-foreground: var(--secondary-foreground);
26
+ --color-muted: var(--muted);
27
+ --color-muted-foreground: var(--muted-foreground);
28
+ --color-accent: var(--accent);
29
+ --color-accent-foreground: var(--accent-foreground);
30
+ --color-destructive: var(--destructive);
31
+ --color-destructive-foreground: var(--destructive-foreground);
32
+ --color-input: var(--input);
33
+ --color-ring: var(--ring);
34
+
35
+ /* Sunpeak semantic colors */
36
+ --color-success: var(--sp-success);
37
+ --color-warning: var(--sp-warning);
38
+ --color-error: var(--sp-error);
39
+ --color-info: var(--sp-info);
40
+
41
+ /* ===================================
42
+ * TYPOGRAPHY - Mapped to --sp- variables
43
+ * ===================================
44
+ */
45
+
46
+ /* Font families */
47
+ --font-sans: var(--sp-font-family);
48
+
49
+ /* Font sizes */
50
+ --text-xs: var(--sp-font-size-xs);
51
+ --text-sm: var(--sp-font-size-sm);
52
+ --text-base: var(--sp-font-size-base);
53
+ --text-lg: var(--sp-font-size-lg);
54
+ --text-xl: var(--sp-font-size-xl);
55
+
56
+ /* Font weights */
57
+ --font-weight-normal: var(--sp-font-weight-normal);
58
+ --font-weight-medium: var(--sp-font-weight-medium);
59
+ --font-weight-semibold: var(--sp-font-weight-semibold);
60
+ --font-weight-bold: var(--sp-font-weight-bold);
61
+
62
+ /* Line heights */
63
+ --leading-tight: var(--sp-line-height-tight);
64
+ --leading-normal: var(--sp-line-height-normal);
65
+ --leading-relaxed: var(--sp-line-height-relaxed);
66
+
67
+ /* ===================================
68
+ * SPACING - Mapped to --sp- variables
69
+ * ===================================
70
+ */
71
+ --spacing-1: var(--sp-spacing-1);
72
+ --spacing-2: var(--sp-spacing-2);
73
+ --spacing-3: var(--sp-spacing-3);
74
+ --spacing-4: var(--sp-spacing-4);
75
+ --spacing-5: var(--sp-spacing-5);
76
+ --spacing-6: var(--sp-spacing-6);
77
+ --spacing-8: var(--sp-spacing-8);
78
+
79
+ /* ===================================
80
+ * BORDER RADIUS - Mapped to --sp- variables
81
+ * ===================================
82
+ */
83
+ --radius-sm: var(--sp-radius-sm);
84
+ --radius-md: var(--sp-radius-md);
85
+ --radius-lg: var(--sp-radius-lg);
86
+ --radius-xl: var(--sp-radius-xl);
87
+ --radius-2xl: var(--sp-radius-2xl);
88
+ --radius-full: var(--sp-radius-full);
89
+
90
+ /* ===================================
91
+ * SHADOWS - Mapped to --sp- variables
92
+ * ===================================
93
+ */
94
+ --shadow-sm: var(--sp-shadow-sm);
95
+ --shadow-md: var(--sp-shadow-md);
96
+ --shadow-lg: var(--sp-shadow-lg);
97
+ --shadow-xl: var(--sp-shadow-xl);
98
+ }
99
+
100
+ /* ===================================
101
+ * THEME MODE CLASSES
102
+ * ===================================
103
+ * Force light/dark mode on specific elements
104
+ */
105
+
106
+ .light {
107
+ --sp-color-bg-primary: var(--sp-light-color-bg-primary);
108
+ --sp-color-bg-secondary: var(--sp-light-color-bg-secondary);
109
+ --sp-color-bg-tertiary: var(--sp-light-color-bg-tertiary);
110
+ --sp-color-text-primary: var(--sp-light-color-text-primary);
111
+ --sp-color-text-secondary: var(--sp-light-color-text-secondary);
112
+ --sp-color-text-tertiary: var(--sp-light-color-text-tertiary);
113
+ --sp-color-text-inverted: var(--sp-light-color-text-inverted);
114
+ --sp-color-border: var(--sp-light-color-border);
115
+ --sp-success: var(--sp-light-success);
116
+ --sp-warning: var(--sp-light-warning);
117
+ --sp-error: var(--sp-light-error);
118
+ --sp-info: var(--sp-light-info);
119
+ --sp-accent: var(--sp-light-accent);
120
+ --sp-accent-hover: var(--sp-light-accent-hover);
121
+ --sp-accent-active: var(--sp-light-accent-active);
122
+ --sp-accent-foreground: var(--sp-light-accent-foreground);
123
+
124
+ /* shadcn/ui light mode variables */
125
+ --background: var(--sp-light-color-bg-primary);
126
+ --foreground: var(--sp-light-color-text-primary);
127
+ --card: var(--sp-light-color-bg-primary);
128
+ --card-foreground: var(--sp-light-color-text-primary);
129
+ --popover: var(--sp-light-color-bg-primary);
130
+ --popover-foreground: var(--sp-light-color-text-primary);
131
+ --primary: var(--sp-light-accent);
132
+ --primary-foreground: var(--sp-accent-foreground);
133
+ --secondary: var(--sp-light-color-bg-secondary);
134
+ --secondary-foreground: var(--sp-light-color-text-primary);
135
+ --muted: var(--sp-light-color-bg-tertiary);
136
+ --muted-foreground: var(--sp-light-color-text-tertiary);
137
+ --accent: var(--sp-light-accent);
138
+ --accent-foreground: var(--sp-accent-foreground);
139
+ --destructive: var(--sp-light-error);
140
+ --destructive-foreground: var(--sp-accent-foreground);
141
+ --border: var(--sp-light-color-border);
142
+ --input: var(--sp-light-color-bg-secondary);
143
+ --ring: var(--sp-light-accent);
144
+ --sidebar: var(--sp-light-sidebar);
145
+ --sidebar-foreground: var(--sp-light-color-text-primary);
146
+ --sidebar-primary: var(--sp-light-accent);
147
+ --sidebar-primary-foreground: var(--sp-light-color-text-primary);
148
+ --sidebar-accent: var(--sp-light-accent);
149
+ --sidebar-accent-foreground: var(--sp-light-color-text-primary);
150
+ --sidebar-border: var(--sp-light-color-border);
151
+ --sidebar-ring: var(--sp-light-accent);
152
+ }
153
+
154
+ .dark {
155
+ --sp-color-bg-primary: var(--sp-dark-color-bg-primary);
156
+ --sp-color-bg-secondary: var(--sp-dark-color-bg-secondary);
157
+ --sp-color-bg-tertiary: var(--sp-dark-color-bg-tertiary);
158
+ --sp-color-text-primary: var(--sp-dark-color-text-primary);
159
+ --sp-color-text-secondary: var(--sp-dark-color-text-secondary);
160
+ --sp-color-text-tertiary: var(--sp-dark-color-text-tertiary);
161
+ --sp-color-text-inverted: var(--sp-dark-color-text-inverted);
162
+ --sp-color-border: var(--sp-dark-color-border);
163
+ --sp-success: var(--sp-dark-success);
164
+ --sp-warning: var(--sp-dark-warning);
165
+ --sp-error: var(--sp-dark-error);
166
+ --sp-info: var(--sp-dark-info);
167
+ --sp-accent: var(--sp-dark-accent);
168
+ --sp-accent-hover: var(--sp-dark-accent-hover);
169
+ --sp-accent-active: var(--sp-dark-accent-active);
170
+ --sp-accent-foreground: var(--sp-dark-accent-foreground);
171
+
172
+ /* shadcn/ui dark mode variables */
173
+ --background: var(--sp-dark-color-bg-primary);
174
+ --foreground: var(--sp-dark-color-text-primary);
175
+ --card: var(--sp-dark-color-bg-primary);
176
+ --card-foreground: var(--sp-dark-color-text-primary);
177
+ --popover: var(--sp-dark-color-bg-primary);
178
+ --popover-foreground: var(--sp-dark-color-text-primary);
179
+ --primary: var(--sp-dark-accent);
180
+ --primary-foreground: var(--sp-accent-foreground);
181
+ --secondary: var(--sp-dark-color-bg-secondary);
182
+ --secondary-foreground: var(--sp-dark-color-text-primary);
183
+ --muted: var(--sp-dark-color-bg-tertiary);
184
+ --muted-foreground: var(--sp-dark-color-text-tertiary);
185
+ --accent: var(--sp-dark-accent);
186
+ --accent-foreground: var(--sp-accent-foreground);
187
+ --destructive: var(--sp-dark-error);
188
+ --destructive-foreground: var(--sp-accent-foreground);
189
+ --border: var(--sp-dark-color-border);
190
+ --input: var(--sp-dark-color-bg-secondary);
191
+ --ring: var(--sp-dark-accent);
192
+ --sidebar: var(--sp-dark-sidebar);
193
+ --sidebar-foreground: var(--sp-dark-color-text-primary);
194
+ --sidebar-primary: var(--sp-dark-accent);
195
+ --sidebar-primary-foreground: var(--sp-dark-color-text-primary);
196
+ --sidebar-accent: var(--sp-dark-accent);
197
+ --sidebar-accent-foreground: var(--sp-dark-color-text-primary);
198
+ --sidebar-border: var(--sp-dark-color-border);
199
+ --sidebar-ring: var(--sp-dark-accent);
200
+ }
201
+
202
+ @theme inline {
203
+ --color-sidebar: var(--sidebar);
204
+ --color-sidebar-foreground: var(--sidebar-foreground);
205
+ --color-sidebar-primary: var(--sidebar-primary);
206
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
207
+ --color-sidebar-accent: var(--sidebar-accent);
208
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
209
+ --color-sidebar-border: var(--sidebar-border);
210
+ --color-sidebar-ring: var(--sidebar-ring);
211
+ }
212
+
213
+ @layer base {
214
+ * {
215
+ @apply border-border outline-ring/50;
216
+ }
217
+ body {
218
+ @apply bg-background text-foreground;
219
+ }
220
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sunpeak",
3
- "version": "0.1.25",
4
- "description": "React library for cross-platform genAI App UIs.",
3
+ "version": "0.2.2",
4
+ "description": "The ChatGPT Apps UI SDK. Build and test your ChatGPT App UI locally with approved shadcn React components.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -17,10 +17,17 @@
17
17
  "default": "./dist/index.cjs"
18
18
  }
19
19
  },
20
+ "./styles/globals.css": "./dist/styles/globals.css",
21
+ "./styles/chatgpt": "./dist/styles/chatgpt/index.css",
20
22
  "./package.json": "./package.json"
21
23
  },
24
+ "bin": {
25
+ "sunpeak": "./bin/sunpeak.js"
26
+ },
22
27
  "files": [
23
28
  "dist",
29
+ "bin",
30
+ "template",
24
31
  "README.md"
25
32
  ],
26
33
  "sideEffects": false,
@@ -32,43 +39,34 @@
32
39
  "multi-platform",
33
40
  "react",
34
41
  "components",
35
- "material-ui",
42
+ "shadcn-ui",
43
+ "tailwindcss",
36
44
  "sunpeak"
37
45
  ],
38
46
  "author": "Sunpeak AI",
39
47
  "license": "MIT",
40
48
  "peerDependencies": {
41
- "@emotion/react": "^11.0.0",
42
- "@emotion/styled": "^11.0.0",
43
- "@mui/icons-material": "^6.0.0 || ^7.0.0",
44
- "@mui/material": "^6.0.0 || ^7.0.0",
45
49
  "react": "^18.0.0 || ^19.0.0",
46
50
  "react-dom": "^18.0.0 || ^19.0.0"
47
51
  },
48
- "peerDependenciesMeta": {
49
- "@mui/material": {
50
- "optional": true
51
- },
52
- "@mui/icons-material": {
53
- "optional": true
54
- },
55
- "@emotion/react": {
56
- "optional": true
57
- },
58
- "@emotion/styled": {
59
- "optional": true
60
- }
61
- },
62
52
  "dependencies": {
63
- "@emotion/react": "^11.14.0",
64
- "@emotion/styled": "^11.14.1",
65
- "@mui/icons-material": "^7.3.5",
66
- "@mui/material": "^7.3.5"
53
+ "@radix-ui/react-dialog": "^1.1.15",
54
+ "@radix-ui/react-label": "^2.1.8",
55
+ "@radix-ui/react-select": "^2.2.6",
56
+ "@radix-ui/react-separator": "^1.1.8",
57
+ "@radix-ui/react-slot": "^1.2.4",
58
+ "@radix-ui/react-tooltip": "^1.2.8",
59
+ "class-variance-authority": "^0.7.1",
60
+ "clsx": "^2.1.1",
61
+ "lucide-react": "^0.554.0",
62
+ "tailwind-merge": "^3.4.0",
63
+ "tw-animate-css": "^1.4.0"
67
64
  },
68
65
  "devDependencies": {
69
- "@testing-library/jest-dom": "^6.6.3",
70
- "@testing-library/react": "^16.0.1",
71
- "@types/jest": "^30.0.0",
66
+ "@tailwindcss/vite": "^4.1.17",
67
+ "@testing-library/jest-dom": "^6.9.1",
68
+ "@testing-library/react": "^16.3.0",
69
+ "@testing-library/user-event": "^14.6.1",
72
70
  "@types/node": "^24.10.1",
73
71
  "@types/react": "^18.3.12",
74
72
  "@types/react-dom": "^18.3.1",
@@ -79,17 +77,17 @@
79
77
  "eslint-config-prettier": "^10.1.8",
80
78
  "eslint-plugin-react": "^7.37.5",
81
79
  "eslint-plugin-react-hooks": "^7.0.1",
82
- "jest": "^30.2.0",
83
- "jest-environment-jsdom": "^30.2.0",
84
- "jsdom": "^25.0.1",
80
+ "jsdom": "^27.2.0",
85
81
  "prettier": "^3.6.2",
86
82
  "react": "^18.3.1",
87
83
  "react-dom": "^18.3.1",
88
- "ts-jest": "^29.4.5",
84
+ "tailwindcss": "^4.1.17",
89
85
  "ts-node": "^10.9.2",
90
86
  "tsup": "^8.3.5",
87
+ "tsx": "^4.20.6",
91
88
  "typescript": "^5.6.3",
92
- "vite": "^5.4.21"
89
+ "vite": "^5.4.21",
90
+ "vitest": "^4.0.12"
93
91
  },
94
92
  "repository": {
95
93
  "type": "git",
@@ -101,9 +99,9 @@
101
99
  "homepage": "https://sunpeak.ai/",
102
100
  "scripts": {
103
101
  "build": "tsup",
104
- "dev": "vite",
105
- "test": "jest",
102
+ "dev": "pnpm --filter my-sunpeak-app dev",
106
103
  "lint": "eslint . --ext .ts,.tsx --fix",
107
- "typecheck": "tsc --noEmit"
104
+ "typecheck": "tsc --noEmit",
105
+ "test": "vitest run"
108
106
  }
109
107
  }
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ coverage
4
+ *.cjs
@@ -0,0 +1,9 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 100,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "arrowParens": "always"
9
+ }
@@ -0,0 +1,47 @@
1
+ # sunpeak-app
2
+
3
+ A ChatGPT App UI built with [sunpeak](https://github.com/Sunpeak-AI/sunpeak).
4
+
5
+ ## Quickstart
6
+
7
+ Requirements: Node (20+), pnpm (10+)
8
+
9
+ ```bash
10
+ pnpm dev
11
+ ```
12
+
13
+ Edit [src/App.tsx](./src/App.tsx) to build your app UI.
14
+
15
+ ## Development
16
+
17
+ ### Initial Project Structure
18
+
19
+ ```
20
+ src/
21
+ ├── App.tsx # Your main app component
22
+ └── components # Your shadcn/ui React components
23
+
24
+ dist/ # Build output (generated)
25
+ └── chatgpt/ # ChatGPT builds
26
+ ```
27
+
28
+ ## Build & Deploy
29
+
30
+ Build your app for production:
31
+
32
+ ```bash
33
+ pnpm build
34
+ ```
35
+
36
+ This creates optimized builds in the `dist/` directory:
37
+
38
+ - `dist/chatgpt/index.js` - ChatGPT iframe component
39
+ - Host this file somewhere and reference it as a resource in your MCP server.
40
+
41
+ ## Resources
42
+
43
+ - [sunpeak](https://github.com/Sunpeak-AI/sunpeak)
44
+ - [ChatGPT Apps SDK Design Guidelines](https://developers.openai.com/apps-sdk/concepts/design-guidelines)
45
+ - [ChatGPT Apps SDK UI Documentation](https://developers.openai.com/apps-sdk/build/chatgpt-ui)
46
+ - [ChatGPT Apps SDK Examples](https://github.com/openai/openai-apps-sdk-examples)
47
+ - [shadcn/ui](https://ui.shadcn.com/)
Binary file
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib",
16
+ "ui": "@/components/shadcn",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
@@ -0,0 +1,65 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { ChatGPTSimulator } from 'sunpeak';
4
+ import { App } from '@/App';
5
+ import './styles.css';
6
+
7
+ const places = [
8
+ {
9
+ id: '1',
10
+ name: 'Lady Bird Lake',
11
+ rating: 4.5,
12
+ category: 'Waterfront',
13
+ location: 'Austin',
14
+ image: 'https://images.unsplash.com/photo-1520950237264-dfe336995c34?w=400&h=400&fit=crop',
15
+ description: 'Scenic lake perfect for kayaking, paddleboarding, and trails.',
16
+ },
17
+ {
18
+ id: '2',
19
+ name: 'Texas State Capitol',
20
+ rating: 4.8,
21
+ category: 'Historic Site',
22
+ location: 'Austin',
23
+ image: 'https://images.unsplash.com/photo-1664231978322-4d0b45c7027b?w=400&h=400&fit=crop',
24
+ description: 'Stunning capitol building with free tours and beautiful grounds.',
25
+ },
26
+ {
27
+ id: '3',
28
+ name: 'The Paramount Theatre',
29
+ rating: 4.7,
30
+ category: 'Architecture',
31
+ location: 'Austin',
32
+ image: 'https://images.unsplash.com/photo-1583097090970-4d3b940ea1a0?w=400&h=400&fit=crop',
33
+ description: 'Century-old performance and movie theatre in the heart of downtown Austin.',
34
+ },
35
+ {
36
+ id: '4',
37
+ name: 'Zilker Park',
38
+ rating: 4.7,
39
+ category: 'Park',
40
+ location: 'Austin',
41
+ image: 'https://images.unsplash.com/photo-1563828568124-f800803ba13c?w=400&h=400&fit=crop',
42
+ description: 'Popular park with trails, sports fields, and Barton Springs Pool.',
43
+ },
44
+ {
45
+ id: '5',
46
+ name: 'South Congress Avenue',
47
+ rating: 4.6,
48
+ category: 'Landmark',
49
+ location: 'Austin',
50
+ image: 'https://images.unsplash.com/photo-1588993608283-7f0eda4438be?w=400&h=400&fit=crop',
51
+ description: 'Vibrant street with unique shops, restaurants, and live music.',
52
+ },
53
+ ];
54
+
55
+ createRoot(document.getElementById('root')!).render(
56
+ <StrictMode>
57
+ <ChatGPTSimulator
58
+ appName="Splorin"
59
+ appIcon="✈️"
60
+ userMessage="Show me popular places to visit in Austin Texas"
61
+ >
62
+ <App data={places} />
63
+ </ChatGPTSimulator>
64
+ </StrictMode>
65
+ );
@@ -0,0 +1,5 @@
1
+ /* Dev styles - imports globals.css */
2
+ @import "@/styles/globals.css";
3
+
4
+ /* Scan sunpeak source for Tailwind classes (only works in workspace, ignored otherwise) */
5
+ @source "../../src/**/*.{ts,tsx}";
@@ -0,0 +1,49 @@
1
+ // eslint.config.cjs
2
+ const ts = require('@typescript-eslint/eslint-plugin');
3
+ const tsParser = require('@typescript-eslint/parser');
4
+ const react = require('eslint-plugin-react');
5
+ const reactHooks = require('eslint-plugin-react-hooks');
6
+ const prettier = require('eslint-config-prettier');
7
+
8
+ module.exports = [
9
+ {
10
+ ignores: ['**/dist', '**/node_modules', '*.cjs', '**/tmp',],
11
+ },
12
+ {
13
+ files: ['**/*.{ts,tsx,js,jsx}'],
14
+
15
+ languageOptions: {
16
+ parser: tsParser,
17
+ ecmaVersion: 'latest',
18
+ sourceType: 'module',
19
+ parserOptions: {
20
+ ecmaFeatures: { jsx: true },
21
+ },
22
+ },
23
+
24
+ plugins: {
25
+ '@typescript-eslint': ts,
26
+ react,
27
+ 'react-hooks': reactHooks,
28
+ },
29
+
30
+ settings: {
31
+ react: { version: 'detect' },
32
+ },
33
+
34
+ rules: {
35
+ ...ts.configs.recommended.rules,
36
+ ...react.configs.recommended.rules,
37
+ ...react.configs['jsx-runtime'].rules,
38
+ ...reactHooks.configs.recommended.rules,
39
+ ...prettier.rules,
40
+
41
+ 'react/prop-types': 'off',
42
+
43
+ '@typescript-eslint/no-unused-vars': [
44
+ 'error',
45
+ { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
46
+ ],
47
+ },
48
+ },
49
+ ];
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Sunpeak - ChatGPT App SDK</title>
7
+ <link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/dev/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "my-sunpeak-app",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsup",
8
+ "dev": "vite",
9
+ "lint": "eslint . --ext .ts,.tsx --fix",
10
+ "typecheck": "tsc --noEmit",
11
+ "test": "vitest run"
12
+ },
13
+ "dependencies": {
14
+ "@radix-ui/react-dialog": "^1.1.15",
15
+ "@radix-ui/react-label": "^2.1.8",
16
+ "@radix-ui/react-select": "^2.2.6",
17
+ "@radix-ui/react-separator": "^1.1.8",
18
+ "@radix-ui/react-slot": "^1.2.4",
19
+ "@radix-ui/react-tooltip": "^1.2.8",
20
+ "class-variance-authority": "^0.7.1",
21
+ "clsx": "^2.1.1",
22
+ "embla-carousel-react": "^8.6.0",
23
+ "embla-carousel-wheel-gestures": "^8.1.0",
24
+ "lucide-react": "^0.554.0",
25
+ "sunpeak": "workspace:*",
26
+ "tailwind-merge": "^3.4.0",
27
+ "tw-animate-css": "^1.4.0"
28
+ },
29
+ "devDependencies": {
30
+ "@tailwindcss/vite": "^4.1.17",
31
+ "@testing-library/jest-dom": "^6.9.1",
32
+ "@testing-library/react": "^16.3.0",
33
+ "@testing-library/user-event": "^14.6.1",
34
+ "@types/node": "^24.10.1",
35
+ "@types/react": "^18.3.12",
36
+ "@types/react-dom": "^18.3.1",
37
+ "@typescript-eslint/eslint-plugin": "^8.47.0",
38
+ "@typescript-eslint/parser": "^8.47.0",
39
+ "@vitejs/plugin-react": "^4.3.4",
40
+ "eslint": "^9.39.1",
41
+ "eslint-config-prettier": "^10.1.8",
42
+ "eslint-plugin-react": "^7.37.5",
43
+ "eslint-plugin-react-hooks": "^7.0.1",
44
+ "jsdom": "^27.2.0",
45
+ "prettier": "^3.6.2",
46
+ "react": "^18.3.1",
47
+ "react-dom": "^18.3.1",
48
+ "tailwindcss": "^4.1.17",
49
+ "ts-node": "^10.9.2",
50
+ "tsup": "^8.3.5",
51
+ "tsx": "^4.20.6",
52
+ "typescript": "^5.6.3",
53
+ "vite": "^5.4.21",
54
+ "vitest": "^4.0.12"
55
+ }
56
+ }
@@ -0,0 +1,45 @@
1
+ import { SunpeakCarousel, SunpeakCard } from './components';
2
+ import '@/styles/globals.css';
3
+ import '@/styles/chatgpt.css';
4
+
5
+ export interface Place {
6
+ id: string;
7
+ name: string;
8
+ rating: number;
9
+ category: string;
10
+ location: string;
11
+ image: string;
12
+ description: string;
13
+ }
14
+
15
+ export interface AppProps {
16
+ data: Place[];
17
+ }
18
+
19
+ export function App({ data }: AppProps) {
20
+ return (
21
+ <SunpeakCarousel gap={16} showArrows={true} showEdgeGradients={true} cardWidth={280}>
22
+ {data.map((place) => (
23
+ <SunpeakCard
24
+ key={place.id}
25
+ image={place.image}
26
+ imageAlt={place.name}
27
+ header={place.name}
28
+ metadata={`⭐ ${place.rating} • ${place.category} • ${place.location}`}
29
+ button1={{
30
+ isPrimary: true,
31
+ onClick: () => console.log(`Visit ${place.name}`),
32
+ children: 'Visit',
33
+ }}
34
+ button2={{
35
+ isPrimary: false,
36
+ onClick: () => console.log(`Learn more about ${place.name}`),
37
+ children: 'Learn More',
38
+ }}
39
+ >
40
+ {place.description}
41
+ </SunpeakCard>
42
+ ))}
43
+ </SunpeakCarousel>
44
+ );
45
+ }
@@ -0,0 +1,2 @@
1
+ export * from './sunpeak-carousel';
2
+ export * from './sunpeak-card';