sunpeak 0.6.1 → 0.6.4
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/bin/sunpeak.js +129 -6
- package/dist/chatgpt/conversation.d.ts +2 -1
- package/dist/index.cjs +24 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +24 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp/entry.cjs +2 -2
- package/dist/mcp/entry.cjs.map +1 -1
- package/dist/mcp/entry.js +2 -2
- package/dist/mcp/entry.js.map +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/{server-DpriZ4jT.cjs → server-CQGbJWbk.cjs} +17 -8
- package/dist/{server-DpriZ4jT.cjs.map → server-CQGbJWbk.cjs.map} +1 -1
- package/dist/{server-SBlanUcf.js → server-DGCvp1RA.js} +17 -8
- package/dist/{server-SBlanUcf.js.map → server-DGCvp1RA.js.map} +1 -1
- package/dist/style.css +4 -0
- package/package.json +1 -1
- package/template/.sunpeak/dev.tsx +1 -1
- package/template/dist/chatgpt/albums.js +2 -2
- package/template/dist/chatgpt/carousel.js +1 -1
- package/template/dist/chatgpt/counter.js +1 -1
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Button.js +2 -2
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Select.js +9 -9
- package/template/node_modules/.vite/deps/_metadata.json +22 -22
- package/template/node_modules/.vite/deps/{chunk-DQAZDQU3.js → chunk-EVJ3DVH5.js} +5 -5
- package/template/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/template/src/components/album/album-carousel.test.tsx +84 -0
- package/template/src/components/album/album-carousel.tsx +168 -0
- package/template/src/components/album/albums.test.tsx +2 -2
- package/template/src/components/album/albums.tsx +3 -3
- package/template/src/components/album/index.ts +1 -0
- package/template/src/components/carousel/index.ts +1 -0
- package/template/src/components/index.ts +0 -1
- package/template/src/resources/carousel-resource.test.tsx +1 -4
- package/template/src/resources/carousel-resource.tsx +1 -2
- package/template/src/components/card/index.ts +0 -1
- /package/template/node_modules/.vite/deps/{chunk-DQAZDQU3.js.map → chunk-EVJ3DVH5.js.map} +0 -0
- /package/template/src/components/{card → carousel}/card.test.tsx +0 -0
- /package/template/src/components/{card → carousel}/card.tsx +0 -0
package/bin/sunpeak.js
CHANGED
|
@@ -27,7 +27,81 @@ function checkPackageJson() {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
function parseResourcesInput(input) {
|
|
31
|
+
const VALID_RESOURCES = ['albums', 'carousel', 'counter'];
|
|
32
|
+
|
|
33
|
+
// If no input, return all resources
|
|
34
|
+
if (!input || input.trim() === '') {
|
|
35
|
+
return VALID_RESOURCES;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Split by comma or space and trim
|
|
39
|
+
const tokens = input
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.split(/[,\s]+/)
|
|
42
|
+
.map((s) => s.trim())
|
|
43
|
+
.filter((s) => s.length > 0);
|
|
44
|
+
|
|
45
|
+
// Validate tokens
|
|
46
|
+
const invalid = tokens.filter((t) => !VALID_RESOURCES.includes(t));
|
|
47
|
+
if (invalid.length > 0) {
|
|
48
|
+
console.error(`Error: Invalid resource(s): ${invalid.join(', ')}`);
|
|
49
|
+
console.error(`Valid resources are: ${VALID_RESOURCES.join(', ')}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Remove duplicates
|
|
54
|
+
return [...new Set(tokens)];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function updateIndexFiles(targetDir, selectedResources) {
|
|
58
|
+
// Map resource names to their component/export names
|
|
59
|
+
const resourceMap = {
|
|
60
|
+
albums: { component: 'album', resourceClass: 'AlbumsResource', simulation: 'albums' },
|
|
61
|
+
carousel: { component: 'carousel', resourceClass: 'CarouselResource', simulation: 'carousel' },
|
|
62
|
+
counter: { component: null, resourceClass: 'CounterResource', simulation: 'counter' },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Update components/index.ts
|
|
66
|
+
const componentsIndexPath = join(targetDir, 'src', 'components', 'index.ts');
|
|
67
|
+
const componentExports = selectedResources
|
|
68
|
+
.map((r) => resourceMap[r].component)
|
|
69
|
+
.filter((comp) => comp !== null) // Filter out null components
|
|
70
|
+
.filter((v, i, a) => a.indexOf(v) === i) // Remove duplicates
|
|
71
|
+
.map((comp) => `export * from './${comp}';`)
|
|
72
|
+
.join('\n');
|
|
73
|
+
writeFileSync(componentsIndexPath, componentExports + '\n');
|
|
74
|
+
|
|
75
|
+
// Update resources/index.ts
|
|
76
|
+
const resourcesIndexPath = join(targetDir, 'src', 'resources', 'index.ts');
|
|
77
|
+
const resourceExports = selectedResources
|
|
78
|
+
.map((r) => `export { ${resourceMap[r].resourceClass} } from './${r}-resource';`)
|
|
79
|
+
.join('\n');
|
|
80
|
+
writeFileSync(resourcesIndexPath, resourceExports + '\n');
|
|
81
|
+
|
|
82
|
+
// Update simulations/index.ts
|
|
83
|
+
const simulationsIndexPath = join(targetDir, 'src', 'simulations', 'index.ts');
|
|
84
|
+
const simulationImports = selectedResources
|
|
85
|
+
.map((r) => `import { ${r}Simulation } from './${r}-simulation.js';`)
|
|
86
|
+
.join('\n');
|
|
87
|
+
const simulationExports = selectedResources.map((r) => ` ${r}: ${r}Simulation,`).join('\n');
|
|
88
|
+
const simulationsContent = `/**
|
|
89
|
+
* Server-safe simulation configurations
|
|
90
|
+
*
|
|
91
|
+
* This file contains only metadata and can be safely imported in Node.js contexts
|
|
92
|
+
* (like MCP servers) without causing issues with CSS imports or React components.
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
${simulationImports}
|
|
96
|
+
|
|
97
|
+
export const SIMULATIONS = {
|
|
98
|
+
${simulationExports}
|
|
99
|
+
} as const;
|
|
100
|
+
`;
|
|
101
|
+
writeFileSync(simulationsIndexPath, simulationsContent);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function init(projectName, resourcesArg) {
|
|
31
105
|
if (!projectName) {
|
|
32
106
|
projectName = await prompt('☀️ 🏔️ Project name [my-app]: ');
|
|
33
107
|
if (!projectName) {
|
|
@@ -40,6 +114,16 @@ async function init(projectName) {
|
|
|
40
114
|
process.exit(1);
|
|
41
115
|
}
|
|
42
116
|
|
|
117
|
+
// Use resources from args or ask for them
|
|
118
|
+
let resourcesInput;
|
|
119
|
+
if (resourcesArg) {
|
|
120
|
+
resourcesInput = resourcesArg;
|
|
121
|
+
console.log(`☀️ 🏔️ Resources: ${resourcesArg}`);
|
|
122
|
+
} else {
|
|
123
|
+
resourcesInput = await prompt('☀️ 🏔️ Resources (UIs) to include [albums, carousel, counter]: ');
|
|
124
|
+
}
|
|
125
|
+
const selectedResources = parseResourcesInput(resourcesInput);
|
|
126
|
+
|
|
43
127
|
const targetDir = join(process.cwd(), projectName);
|
|
44
128
|
|
|
45
129
|
if (existsSync(targetDir)) {
|
|
@@ -53,11 +137,44 @@ async function init(projectName) {
|
|
|
53
137
|
|
|
54
138
|
mkdirSync(targetDir, { recursive: true });
|
|
55
139
|
|
|
140
|
+
// Map resource names to their component directory names
|
|
141
|
+
const resourceComponentMap = {
|
|
142
|
+
albums: 'album',
|
|
143
|
+
carousel: 'carousel',
|
|
144
|
+
counter: null, // Counter doesn't have a component directory
|
|
145
|
+
};
|
|
146
|
+
|
|
56
147
|
cpSync(templateDir, targetDir, {
|
|
57
148
|
recursive: true,
|
|
58
149
|
filter: (src) => {
|
|
59
150
|
const name = basename(src);
|
|
60
|
-
|
|
151
|
+
|
|
152
|
+
// Skip node_modules and lock file
|
|
153
|
+
if (name === 'node_modules' || name === 'pnpm-lock.yaml') {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Filter resource files based on selection
|
|
158
|
+
const VALID_RESOURCES = ['albums', 'carousel', 'counter'];
|
|
159
|
+
const excludedResources = VALID_RESOURCES.filter((r) => !selectedResources.includes(r));
|
|
160
|
+
|
|
161
|
+
for (const resource of excludedResources) {
|
|
162
|
+
// Skip resource files
|
|
163
|
+
if (name === `${resource}-resource.tsx` || name === `${resource}-resource.test.tsx`) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
// Skip simulation files
|
|
167
|
+
if (name === `${resource}-simulation.ts`) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
// Skip component directories (map resource name to component dir name)
|
|
171
|
+
const componentDirName = resourceComponentMap[resource];
|
|
172
|
+
if (componentDirName && src.includes('/components/') && name === componentDirName) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return true;
|
|
61
178
|
},
|
|
62
179
|
});
|
|
63
180
|
|
|
@@ -88,6 +205,9 @@ async function init(projectName) {
|
|
|
88
205
|
|
|
89
206
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
90
207
|
|
|
208
|
+
// Update index.ts files based on selected resources
|
|
209
|
+
updateIndexFiles(targetDir, selectedResources);
|
|
210
|
+
|
|
91
211
|
console.log(`
|
|
92
212
|
Done! To get started:
|
|
93
213
|
|
|
@@ -119,7 +239,7 @@ const [, , command, ...args] = process.argv;
|
|
|
119
239
|
|
|
120
240
|
switch (command) {
|
|
121
241
|
case 'new':
|
|
122
|
-
await init(args[0]);
|
|
242
|
+
await init(args[0], args[1]);
|
|
123
243
|
break;
|
|
124
244
|
|
|
125
245
|
case 'dev':
|
|
@@ -149,8 +269,11 @@ const [, , command, ...args] = process.argv;
|
|
|
149
269
|
☀️ 🏔️ sunpeak - The MCP App framework
|
|
150
270
|
|
|
151
271
|
Usage:
|
|
152
|
-
npx sunpeak new [name] Create a new project (no install needed)
|
|
153
|
-
pnpm dlx sunpeak new
|
|
272
|
+
npx sunpeak new [name] [resources] Create a new project (no install needed)
|
|
273
|
+
pnpm dlx sunpeak new Alternative with pnpm
|
|
274
|
+
|
|
275
|
+
Resources: albums, carousel, counter (comma/space separated, or "all")
|
|
276
|
+
Example: npx sunpeak new my-app "albums,carousel"
|
|
154
277
|
|
|
155
278
|
Inside your project, use npm scripts:
|
|
156
279
|
pnpm dev Start development server
|
|
@@ -159,7 +282,7 @@ Inside your project, use npm scripts:
|
|
|
159
282
|
pnpm test Run tests
|
|
160
283
|
|
|
161
284
|
Direct CLI commands (when sunpeak is installed):
|
|
162
|
-
sunpeak new [name]
|
|
285
|
+
sunpeak new [name] [resources] Create a new project
|
|
163
286
|
sunpeak dev Start dev server
|
|
164
287
|
sunpeak build Build resources
|
|
165
288
|
sunpeak mcp Start MCP server
|
|
@@ -6,6 +6,7 @@ interface ConversationProps {
|
|
|
6
6
|
appName?: string;
|
|
7
7
|
appIcon?: string;
|
|
8
8
|
userMessage?: string;
|
|
9
|
+
resourceMeta?: Record<string, unknown>;
|
|
9
10
|
}
|
|
10
|
-
export declare function Conversation({ children, screenWidth, appName, appIcon, userMessage, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function Conversation({ children, screenWidth, appName, appIcon, userMessage, resourceMeta, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
|
|
11
12
|
export {};
|
package/dist/index.cjs
CHANGED
|
@@ -7572,9 +7572,10 @@ const SCREEN_WIDTHS = {
|
|
|
7572
7572
|
function Conversation({
|
|
7573
7573
|
children,
|
|
7574
7574
|
screenWidth,
|
|
7575
|
-
appName = "Sunpeak
|
|
7575
|
+
appName = "Sunpeak",
|
|
7576
7576
|
appIcon,
|
|
7577
|
-
userMessage = "What have you got for me today?"
|
|
7577
|
+
userMessage = "What have you got for me today?",
|
|
7578
|
+
resourceMeta
|
|
7578
7579
|
}) {
|
|
7579
7580
|
const displayMode = useDisplayMode() ?? "inline";
|
|
7580
7581
|
const api = useWidgetAPI();
|
|
@@ -7608,7 +7609,25 @@ function Conversation({
|
|
|
7608
7609
|
}
|
|
7609
7610
|
) }),
|
|
7610
7611
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-primary flex items-center justify-center text-base", children: appName }),
|
|
7611
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end"
|
|
7612
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7613
|
+
Button,
|
|
7614
|
+
{
|
|
7615
|
+
variant: "outline",
|
|
7616
|
+
color: "primary",
|
|
7617
|
+
className: "bg-token-bg-primary",
|
|
7618
|
+
onClick: () => {
|
|
7619
|
+
const widgetDomain = resourceMeta == null ? void 0 : resourceMeta["openai/widgetDomain"];
|
|
7620
|
+
if ((api == null ? void 0 : api.openExternal) && widgetDomain) {
|
|
7621
|
+
api.openExternal({ href: widgetDomain });
|
|
7622
|
+
}
|
|
7623
|
+
},
|
|
7624
|
+
children: [
|
|
7625
|
+
appIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "me-2 h-4 w-4 flex items-center justify-center", children: appIcon }),
|
|
7626
|
+
"Open in ",
|
|
7627
|
+
appName
|
|
7628
|
+
]
|
|
7629
|
+
}
|
|
7630
|
+
) })
|
|
7612
7631
|
] }),
|
|
7613
7632
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative overflow-hidden flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full max-w-full overflow-auto", children }) }),
|
|
7614
7633
|
/* @__PURE__ */ jsxRuntime.jsx("footer", { className: "bg-surface", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[48rem] mx-auto px-4 py-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -7917,7 +7936,7 @@ const DEFAULT_DISPLAY_MODE = "inline";
|
|
|
7917
7936
|
function ChatGPTSimulator({
|
|
7918
7937
|
children,
|
|
7919
7938
|
simulations = [],
|
|
7920
|
-
appName = "Sunpeak
|
|
7939
|
+
appName = "Sunpeak",
|
|
7921
7940
|
appIcon
|
|
7922
7941
|
}) {
|
|
7923
7942
|
const [screenWidth, setScreenWidth] = React__namespace.useState("full");
|
|
@@ -8411,6 +8430,7 @@ function ChatGPTSimulator({
|
|
|
8411
8430
|
appName,
|
|
8412
8431
|
appIcon,
|
|
8413
8432
|
userMessage,
|
|
8433
|
+
resourceMeta: selectedSim == null ? void 0 : selectedSim.resource._meta,
|
|
8414
8434
|
children: content
|
|
8415
8435
|
},
|
|
8416
8436
|
selectedKey
|