sunpeak 0.8.5 → 0.8.8
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/commands/build.mjs +3 -3
- package/bin/commands/pull.mjs +2 -2
- package/bin/sunpeak.js +6 -8
- package/dist/mcp/entry.cjs.map +1 -1
- package/dist/mcp/entry.js.map +1 -1
- package/dist/style.css +0 -37
- package/package.json +1 -1
- package/template/.sunpeak/dev.tsx +2 -2
- package/template/README.md +5 -5
- package/template/dist/albums.js +1 -1
- package/template/dist/albums.json +1 -1
- package/template/dist/carousel.js +1 -1
- package/template/dist/carousel.json +1 -1
- package/template/dist/map.js +1 -1
- package/template/dist/map.json +1 -1
- package/template/dist/review.js +49 -0
- package/template/dist/{confirmation.json → review.json} +4 -4
- package/template/node_modules/.vite/deps/_metadata.json +19 -19
- package/template/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/template/src/resources/index.ts +4 -4
- package/template/src/resources/map-resource.test.tsx +95 -0
- package/template/src/resources/{confirmation-resource.json → review-resource.json} +3 -3
- package/template/src/resources/review-resource.test.tsx +538 -0
- package/template/src/resources/{confirmation-resource.tsx → review-resource.tsx} +20 -20
- package/template/src/simulations/{confirmation-diff-simulation.json → review-diff-simulation.json} +4 -4
- package/template/src/simulations/{confirmation-post-simulation.json → review-post-simulation.json} +4 -4
- package/template/src/simulations/{confirmation-purchase-simulation.json → review-purchase-simulation.json} +4 -4
- package/template/dist/confirmation.js +0 -49
- package/template/dist/counter.js +0 -49
- package/template/dist/counter.json +0 -15
- package/template/src/resources/counter-resource.json +0 -12
- package/template/src/resources/counter-resource.test.tsx +0 -116
- package/template/src/resources/counter-resource.tsx +0 -101
- package/template/src/simulations/counter-show-simulation.json +0 -20
package/bin/commands/build.mjs
CHANGED
|
@@ -87,10 +87,10 @@ export async function build(projectRoot = process.cwd()) {
|
|
|
87
87
|
const resourceFiles = readdirSync(resourcesDir)
|
|
88
88
|
.filter(file => file.endsWith('-resource.tsx'))
|
|
89
89
|
.map(file => {
|
|
90
|
-
// Extract kebab-case name: '
|
|
90
|
+
// Extract kebab-case name: 'review-resource.tsx' -> 'review'
|
|
91
91
|
const kebabName = file.replace('-resource.tsx', '');
|
|
92
92
|
|
|
93
|
-
// Convert kebab-case to PascalCase: '
|
|
93
|
+
// Convert kebab-case to PascalCase: 'review' -> 'Review', 'my-widget' -> 'MyWidget'
|
|
94
94
|
const pascalName = kebabName
|
|
95
95
|
.split('-')
|
|
96
96
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
@@ -107,7 +107,7 @@ export async function build(projectRoot = process.cwd()) {
|
|
|
107
107
|
|
|
108
108
|
if (resourceFiles.length === 0) {
|
|
109
109
|
console.error('Error: No resource files found in src/resources/');
|
|
110
|
-
console.error('Resource files should be named like:
|
|
110
|
+
console.error('Resource files should be named like: review-resource.tsx');
|
|
111
111
|
process.exit(1);
|
|
112
112
|
}
|
|
113
113
|
|
package/bin/commands/pull.mjs
CHANGED
|
@@ -111,7 +111,7 @@ Options:
|
|
|
111
111
|
|
|
112
112
|
Examples:
|
|
113
113
|
sunpeak pull -r myorg/my-app -t prod Pull all resources tagged "prod"
|
|
114
|
-
sunpeak pull -r myorg/my-app -t prod -n
|
|
114
|
+
sunpeak pull -r myorg/my-app -t prod -n review Pull only the "review" resource
|
|
115
115
|
sunpeak pull -r myorg/my-app -t v1.0.0 Pull a specific version
|
|
116
116
|
`);
|
|
117
117
|
return;
|
|
@@ -247,7 +247,7 @@ Options:
|
|
|
247
247
|
|
|
248
248
|
Examples:
|
|
249
249
|
sunpeak pull -r myorg/my-app -t prod Pull all resources tagged "prod"
|
|
250
|
-
sunpeak pull -r myorg/my-app -t prod -n
|
|
250
|
+
sunpeak pull -r myorg/my-app -t prod -n review Pull only the "review" resource
|
|
251
251
|
sunpeak pull -r myorg/my-app -t v1.0.0 Pull a specific version
|
|
252
252
|
`);
|
|
253
253
|
process.exit(0);
|
package/bin/sunpeak.js
CHANGED
|
@@ -28,7 +28,7 @@ function checkPackageJson() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function parseResourcesInput(input) {
|
|
31
|
-
const VALID_RESOURCES = ['albums', 'carousel', '
|
|
31
|
+
const VALID_RESOURCES = ['albums', 'carousel', 'map', 'review'];
|
|
32
32
|
|
|
33
33
|
// If no input, return all resources
|
|
34
34
|
if (!input || input.trim() === '') {
|
|
@@ -59,9 +59,8 @@ function updateIndexFiles(targetDir, selectedResources) {
|
|
|
59
59
|
const resourceMap = {
|
|
60
60
|
albums: { component: 'album', resourceClass: 'AlbumsResource' },
|
|
61
61
|
carousel: { component: 'carousel', resourceClass: 'CarouselResource' },
|
|
62
|
-
confirmation: { component: null, resourceClass: 'ConfirmationResource' },
|
|
63
|
-
counter: { component: null, resourceClass: 'CounterResource' },
|
|
64
62
|
map: { component: 'map', resourceClass: 'MapResource' },
|
|
63
|
+
review: { component: null, resourceClass: 'ReviewResource' },
|
|
65
64
|
};
|
|
66
65
|
|
|
67
66
|
// Update components/index.ts
|
|
@@ -139,7 +138,7 @@ async function init(projectName, resourcesArg) {
|
|
|
139
138
|
console.log(`☀️ 🏔️ Resources: ${resourcesArg}`);
|
|
140
139
|
} else {
|
|
141
140
|
resourcesInput = await prompt(
|
|
142
|
-
'☀️ 🏔️ Resources (UIs) to include [albums, carousel,
|
|
141
|
+
'☀️ 🏔️ Resources (UIs) to include [albums, carousel, map, review]: '
|
|
143
142
|
);
|
|
144
143
|
}
|
|
145
144
|
const selectedResources = parseResourcesInput(resourcesInput);
|
|
@@ -161,9 +160,8 @@ async function init(projectName, resourcesArg) {
|
|
|
161
160
|
const resourceComponentMap = {
|
|
162
161
|
albums: 'album',
|
|
163
162
|
carousel: 'carousel',
|
|
164
|
-
confirmation: null, // Confirmation doesn't have a component directory
|
|
165
|
-
counter: null, // Counter doesn't have a component directory
|
|
166
163
|
map: 'map',
|
|
164
|
+
review: null, // Review doesn't have a component directory
|
|
167
165
|
};
|
|
168
166
|
|
|
169
167
|
cpSync(templateDir, targetDir, {
|
|
@@ -177,7 +175,7 @@ async function init(projectName, resourcesArg) {
|
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
// Filter resource files based on selection
|
|
180
|
-
const VALID_RESOURCES = ['albums', 'carousel', '
|
|
178
|
+
const VALID_RESOURCES = ['albums', 'carousel', 'map', 'review'];
|
|
181
179
|
const excludedResources = VALID_RESOURCES.filter((r) => !selectedResources.includes(r));
|
|
182
180
|
|
|
183
181
|
for (const resource of excludedResources) {
|
|
@@ -416,7 +414,7 @@ Usage:
|
|
|
416
414
|
sunpeak upgrade Upgrade sunpeak to latest version
|
|
417
415
|
sunpeak --version Show version number
|
|
418
416
|
|
|
419
|
-
Resources: albums, carousel,
|
|
417
|
+
Resources: albums, carousel, map, review (comma/space separated)
|
|
420
418
|
Example: sunpeak new my-app "albums,carousel"
|
|
421
419
|
|
|
422
420
|
For more information, visit: https://sunpeak.ai/
|
package/dist/mcp/entry.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.cjs","sources":["../../src/mcp/entry.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Internal MCP server entry point\n * This is run by nodemon or directly to start the MCP server\n *\n * Auto-discovers simulations and resources by file naming convention:\n * - simulations/{resource}-{tool}-simulation.json (e.g., albums-show-simulation.json)\n * - resources/{resource}-resource.json\n */\nimport { runMCPServer, type SimulationWithDist } from './index.js';\nimport path from 'path';\nimport { readFileSync, readdirSync } from 'fs';\nimport type { Resource } from '@modelcontextprotocol/sdk/types.js';\n\n// Determine project root (where this is being run from)\nconst projectRoot = process.cwd();\n\n/**\n * Find the best matching resource key for a simulation key.\n * Matches the longest resource name that is a prefix of the simulation key.\n * e.g., 'albums-show' matches 'albums' (not 'album' if both exist)\n */\nfunction findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined {\n // Sort by length descending to find longest match first\n const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);\n for (const resourceKey of sorted) {\n if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + '-')) {\n return resourceKey;\n }\n }\n return undefined;\n}\n\nasync function startServer() {\n // Read package.json for app metadata\n const pkgPath = path.join(projectRoot, 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n\n // Auto-discover resource files first (to build lookup map)\n const resourcesDir = path.join(projectRoot, 'src/resources');\n const resourceFiles = readdirSync(resourcesDir).filter((f) => f.endsWith('-resource.json'));\n\n const resourcesMap = new Map<string, Resource>();\n for (const filename of resourceFiles) {\n // Extract key from filename: '
|
|
1
|
+
{"version":3,"file":"entry.cjs","sources":["../../src/mcp/entry.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Internal MCP server entry point\n * This is run by nodemon or directly to start the MCP server\n *\n * Auto-discovers simulations and resources by file naming convention:\n * - simulations/{resource}-{tool}-simulation.json (e.g., albums-show-simulation.json)\n * - resources/{resource}-resource.json\n */\nimport { runMCPServer, type SimulationWithDist } from './index.js';\nimport path from 'path';\nimport { readFileSync, readdirSync } from 'fs';\nimport type { Resource } from '@modelcontextprotocol/sdk/types.js';\n\n// Determine project root (where this is being run from)\nconst projectRoot = process.cwd();\n\n/**\n * Find the best matching resource key for a simulation key.\n * Matches the longest resource name that is a prefix of the simulation key.\n * e.g., 'albums-show' matches 'albums' (not 'album' if both exist)\n */\nfunction findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined {\n // Sort by length descending to find longest match first\n const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);\n for (const resourceKey of sorted) {\n if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + '-')) {\n return resourceKey;\n }\n }\n return undefined;\n}\n\nasync function startServer() {\n // Read package.json for app metadata\n const pkgPath = path.join(projectRoot, 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n\n // Auto-discover resource files first (to build lookup map)\n const resourcesDir = path.join(projectRoot, 'src/resources');\n const resourceFiles = readdirSync(resourcesDir).filter((f) => f.endsWith('-resource.json'));\n\n const resourcesMap = new Map<string, Resource>();\n for (const filename of resourceFiles) {\n // Extract key from filename: 'review-resource.json' -> 'review'\n const key = filename.replace(/-resource\\.json$/, '');\n const resourcePath = path.join(resourcesDir, filename);\n const resource = JSON.parse(readFileSync(resourcePath, 'utf-8')) as Resource;\n resourcesMap.set(key, resource);\n }\n\n const resourceKeys = Array.from(resourcesMap.keys());\n\n // Auto-discover simulation files\n const simulationsDir = path.join(projectRoot, 'src/simulations');\n const simulationFiles = readdirSync(simulationsDir).filter((f) => f.endsWith('-simulation.json'));\n\n // Build simulations array from discovered files\n const simulations: SimulationWithDist[] = [];\n\n for (const filename of simulationFiles) {\n // Extract simulation key from filename: 'albums-show-simulation.json' -> 'albums-show'\n const simulationKey = filename.replace(/-simulation\\.json$/, '');\n\n // Load simulation data\n const simulationPath = path.join(simulationsDir, filename);\n const simulation = JSON.parse(readFileSync(simulationPath, 'utf-8'));\n\n // Find matching resource by best prefix match\n const resourceKey = findResourceKey(simulationKey, resourceKeys);\n if (!resourceKey) {\n console.warn(\n `No matching resource found for simulation \"${simulationKey}\". ` +\n `Expected a resource file like src/resources/${simulationKey.split('-')[0]}-resource.json`\n );\n continue;\n }\n\n const resource = resourcesMap.get(resourceKey)!;\n\n simulations.push({\n ...simulation,\n distPath: path.join(projectRoot, `dist/${resourceKey}.js`),\n resource,\n });\n }\n\n runMCPServer({\n name: pkg.name || 'Sunpeak',\n version: pkg.version || '0.1.0',\n simulations,\n port: 6766,\n });\n}\n\nstartServer().catch((error) => {\n console.error('Failed to start MCP server:', error);\n process.exit(1);\n});\n"],"names":["readFileSync","readdirSync","runMCPServer"],"mappings":";;;;;AAeA,MAAM,cAAc,QAAQ,IAAA;AAO5B,SAAS,gBAAgB,eAAuB,cAA4C;AAE1F,QAAM,SAAS,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AACnE,aAAW,eAAe,QAAQ;AAChC,QAAI,kBAAkB,eAAe,cAAc,WAAW,cAAc,GAAG,GAAG;AAChF,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,cAAc;AAE3B,QAAM,UAAU,KAAK,KAAK,aAAa,cAAc;AACrD,QAAM,MAAM,KAAK,MAAMA,GAAAA,aAAa,SAAS,OAAO,CAAC;AAGrD,QAAM,eAAe,KAAK,KAAK,aAAa,eAAe;AAC3D,QAAM,gBAAgBC,eAAY,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,gBAAgB,CAAC;AAE1F,QAAM,mCAAmB,IAAA;AACzB,aAAW,YAAY,eAAe;AAEpC,UAAM,MAAM,SAAS,QAAQ,oBAAoB,EAAE;AACnD,UAAM,eAAe,KAAK,KAAK,cAAc,QAAQ;AACrD,UAAM,WAAW,KAAK,MAAMD,GAAAA,aAAa,cAAc,OAAO,CAAC;AAC/D,iBAAa,IAAI,KAAK,QAAQ;AAAA,EAChC;AAEA,QAAM,eAAe,MAAM,KAAK,aAAa,MAAM;AAGnD,QAAM,iBAAiB,KAAK,KAAK,aAAa,iBAAiB;AAC/D,QAAM,kBAAkBC,eAAY,cAAc,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,kBAAkB,CAAC;AAGhG,QAAM,cAAoC,CAAA;AAE1C,aAAW,YAAY,iBAAiB;AAEtC,UAAM,gBAAgB,SAAS,QAAQ,sBAAsB,EAAE;AAG/D,UAAM,iBAAiB,KAAK,KAAK,gBAAgB,QAAQ;AACzD,UAAM,aAAa,KAAK,MAAMD,GAAAA,aAAa,gBAAgB,OAAO,CAAC;AAGnE,UAAM,cAAc,gBAAgB,eAAe,YAAY;AAC/D,QAAI,CAAC,aAAa;AAChB,cAAQ;AAAA,QACN,8CAA8C,aAAa,kDACV,cAAc,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MAAA;AAE9E;AAAA,IACF;AAEA,UAAM,WAAW,aAAa,IAAI,WAAW;AAE7C,gBAAY,KAAK;AAAA,MACf,GAAG;AAAA,MACH,UAAU,KAAK,KAAK,aAAa,QAAQ,WAAW,KAAK;AAAA,MACzD;AAAA,IAAA,CACD;AAAA,EACH;AAEAE,sBAAa;AAAA,IACX,MAAM,IAAI,QAAQ;AAAA,IAClB,SAAS,IAAI,WAAW;AAAA,IACxB;AAAA,IACA,MAAM;AAAA,EAAA,CACP;AACH;AAEA,cAAc,MAAM,CAAC,UAAU;AAC7B,UAAQ,MAAM,+BAA+B,KAAK;AAClD,UAAQ,KAAK,CAAC;AAChB,CAAC;"}
|
package/dist/mcp/entry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sources":["../../src/mcp/entry.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Internal MCP server entry point\n * This is run by nodemon or directly to start the MCP server\n *\n * Auto-discovers simulations and resources by file naming convention:\n * - simulations/{resource}-{tool}-simulation.json (e.g., albums-show-simulation.json)\n * - resources/{resource}-resource.json\n */\nimport { runMCPServer, type SimulationWithDist } from './index.js';\nimport path from 'path';\nimport { readFileSync, readdirSync } from 'fs';\nimport type { Resource } from '@modelcontextprotocol/sdk/types.js';\n\n// Determine project root (where this is being run from)\nconst projectRoot = process.cwd();\n\n/**\n * Find the best matching resource key for a simulation key.\n * Matches the longest resource name that is a prefix of the simulation key.\n * e.g., 'albums-show' matches 'albums' (not 'album' if both exist)\n */\nfunction findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined {\n // Sort by length descending to find longest match first\n const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);\n for (const resourceKey of sorted) {\n if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + '-')) {\n return resourceKey;\n }\n }\n return undefined;\n}\n\nasync function startServer() {\n // Read package.json for app metadata\n const pkgPath = path.join(projectRoot, 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n\n // Auto-discover resource files first (to build lookup map)\n const resourcesDir = path.join(projectRoot, 'src/resources');\n const resourceFiles = readdirSync(resourcesDir).filter((f) => f.endsWith('-resource.json'));\n\n const resourcesMap = new Map<string, Resource>();\n for (const filename of resourceFiles) {\n // Extract key from filename: '
|
|
1
|
+
{"version":3,"file":"entry.js","sources":["../../src/mcp/entry.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Internal MCP server entry point\n * This is run by nodemon or directly to start the MCP server\n *\n * Auto-discovers simulations and resources by file naming convention:\n * - simulations/{resource}-{tool}-simulation.json (e.g., albums-show-simulation.json)\n * - resources/{resource}-resource.json\n */\nimport { runMCPServer, type SimulationWithDist } from './index.js';\nimport path from 'path';\nimport { readFileSync, readdirSync } from 'fs';\nimport type { Resource } from '@modelcontextprotocol/sdk/types.js';\n\n// Determine project root (where this is being run from)\nconst projectRoot = process.cwd();\n\n/**\n * Find the best matching resource key for a simulation key.\n * Matches the longest resource name that is a prefix of the simulation key.\n * e.g., 'albums-show' matches 'albums' (not 'album' if both exist)\n */\nfunction findResourceKey(simulationKey: string, resourceKeys: string[]): string | undefined {\n // Sort by length descending to find longest match first\n const sorted = [...resourceKeys].sort((a, b) => b.length - a.length);\n for (const resourceKey of sorted) {\n if (simulationKey === resourceKey || simulationKey.startsWith(resourceKey + '-')) {\n return resourceKey;\n }\n }\n return undefined;\n}\n\nasync function startServer() {\n // Read package.json for app metadata\n const pkgPath = path.join(projectRoot, 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n\n // Auto-discover resource files first (to build lookup map)\n const resourcesDir = path.join(projectRoot, 'src/resources');\n const resourceFiles = readdirSync(resourcesDir).filter((f) => f.endsWith('-resource.json'));\n\n const resourcesMap = new Map<string, Resource>();\n for (const filename of resourceFiles) {\n // Extract key from filename: 'review-resource.json' -> 'review'\n const key = filename.replace(/-resource\\.json$/, '');\n const resourcePath = path.join(resourcesDir, filename);\n const resource = JSON.parse(readFileSync(resourcePath, 'utf-8')) as Resource;\n resourcesMap.set(key, resource);\n }\n\n const resourceKeys = Array.from(resourcesMap.keys());\n\n // Auto-discover simulation files\n const simulationsDir = path.join(projectRoot, 'src/simulations');\n const simulationFiles = readdirSync(simulationsDir).filter((f) => f.endsWith('-simulation.json'));\n\n // Build simulations array from discovered files\n const simulations: SimulationWithDist[] = [];\n\n for (const filename of simulationFiles) {\n // Extract simulation key from filename: 'albums-show-simulation.json' -> 'albums-show'\n const simulationKey = filename.replace(/-simulation\\.json$/, '');\n\n // Load simulation data\n const simulationPath = path.join(simulationsDir, filename);\n const simulation = JSON.parse(readFileSync(simulationPath, 'utf-8'));\n\n // Find matching resource by best prefix match\n const resourceKey = findResourceKey(simulationKey, resourceKeys);\n if (!resourceKey) {\n console.warn(\n `No matching resource found for simulation \"${simulationKey}\". ` +\n `Expected a resource file like src/resources/${simulationKey.split('-')[0]}-resource.json`\n );\n continue;\n }\n\n const resource = resourcesMap.get(resourceKey)!;\n\n simulations.push({\n ...simulation,\n distPath: path.join(projectRoot, `dist/${resourceKey}.js`),\n resource,\n });\n }\n\n runMCPServer({\n name: pkg.name || 'Sunpeak',\n version: pkg.version || '0.1.0',\n simulations,\n port: 6766,\n });\n}\n\nstartServer().catch((error) => {\n console.error('Failed to start MCP server:', error);\n process.exit(1);\n});\n"],"names":[],"mappings":";;;;AAeA,MAAM,cAAc,QAAQ,IAAA;AAO5B,SAAS,gBAAgB,eAAuB,cAA4C;AAE1F,QAAM,SAAS,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AACnE,aAAW,eAAe,QAAQ;AAChC,QAAI,kBAAkB,eAAe,cAAc,WAAW,cAAc,GAAG,GAAG;AAChF,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,cAAc;AAE3B,QAAM,UAAU,KAAK,KAAK,aAAa,cAAc;AACrD,QAAM,MAAM,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AAGrD,QAAM,eAAe,KAAK,KAAK,aAAa,eAAe;AAC3D,QAAM,gBAAgB,YAAY,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,gBAAgB,CAAC;AAE1F,QAAM,mCAAmB,IAAA;AACzB,aAAW,YAAY,eAAe;AAEpC,UAAM,MAAM,SAAS,QAAQ,oBAAoB,EAAE;AACnD,UAAM,eAAe,KAAK,KAAK,cAAc,QAAQ;AACrD,UAAM,WAAW,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAC/D,iBAAa,IAAI,KAAK,QAAQ;AAAA,EAChC;AAEA,QAAM,eAAe,MAAM,KAAK,aAAa,MAAM;AAGnD,QAAM,iBAAiB,KAAK,KAAK,aAAa,iBAAiB;AAC/D,QAAM,kBAAkB,YAAY,cAAc,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,kBAAkB,CAAC;AAGhG,QAAM,cAAoC,CAAA;AAE1C,aAAW,YAAY,iBAAiB;AAEtC,UAAM,gBAAgB,SAAS,QAAQ,sBAAsB,EAAE;AAG/D,UAAM,iBAAiB,KAAK,KAAK,gBAAgB,QAAQ;AACzD,UAAM,aAAa,KAAK,MAAM,aAAa,gBAAgB,OAAO,CAAC;AAGnE,UAAM,cAAc,gBAAgB,eAAe,YAAY;AAC/D,QAAI,CAAC,aAAa;AAChB,cAAQ;AAAA,QACN,8CAA8C,aAAa,kDACV,cAAc,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MAAA;AAE9E;AAAA,IACF;AAEA,UAAM,WAAW,aAAa,IAAI,WAAW;AAE7C,gBAAY,KAAK;AAAA,MACf,GAAG;AAAA,MACH,UAAU,KAAK,KAAK,aAAa,QAAQ,WAAW,KAAK;AAAA,MACzD;AAAA,IAAA,CACD;AAAA,EACH;AAEA,eAAa;AAAA,IACX,MAAM,IAAI,QAAQ;AAAA,IAClB,SAAS,IAAI,WAAW;AAAA,IACxB;AAAA,IACA,MAAM;AAAA,EAAA,CACP;AACH;AAEA,cAAc,MAAM,CAAC,UAAU;AAC7B,UAAQ,MAAM,+BAA+B,KAAK;AAClD,UAAQ,KAAK,CAAC;AAChB,CAAC;"}
|
package/dist/style.css
CHANGED
|
@@ -80,7 +80,6 @@
|
|
|
80
80
|
--breakpoint-lg: 1024px;
|
|
81
81
|
--breakpoint-xl: 1280px;
|
|
82
82
|
--breakpoint-2xl: 1536px;
|
|
83
|
-
--container-md: 28rem;
|
|
84
83
|
--leading-tight: 1.25;
|
|
85
84
|
--leading-normal: 1.5;
|
|
86
85
|
--blur-sm: 8px;
|
|
@@ -127,14 +126,6 @@
|
|
|
127
126
|
--text-2xl--line-height: var(--font-heading-lg-line-height);
|
|
128
127
|
--text-2xl--font-weight: var(--font-text-lg-weight);
|
|
129
128
|
--text-2xl--letter-spacing: var(--font-heading-lg-tracking);
|
|
130
|
-
--text-3xl: var(--font-heading-xl-size);
|
|
131
|
-
--text-3xl--line-height: var(--font-heading-xl-line-height);
|
|
132
|
-
--text-3xl--font-weight: var(--font-text-lg-weight);
|
|
133
|
-
--text-3xl--letter-spacing: var(--font-heading-xl-tracking);
|
|
134
|
-
--text-6xl: var(--font-heading-4xl-size);
|
|
135
|
-
--text-6xl--line-height: var(--font-heading-4xl-line-height);
|
|
136
|
-
--text-6xl--font-weight: var(--font-text-lg-weight);
|
|
137
|
-
--text-6xl--letter-spacing: var(--font-heading-4xl-tracking);
|
|
138
129
|
--tracking-wide: var(--font-tracking-wide);
|
|
139
130
|
--tracking-normal: var(--font-tracking-normal);
|
|
140
131
|
--tracking-tight: var(--font-tracking-tight);
|
|
@@ -3575,10 +3566,6 @@
|
|
|
3575
3566
|
max-width: 100%;
|
|
3576
3567
|
}
|
|
3577
3568
|
|
|
3578
|
-
.max-w-md {
|
|
3579
|
-
max-width: var(--container-md);
|
|
3580
|
-
}
|
|
3581
|
-
|
|
3582
3569
|
.min-w-0 {
|
|
3583
3570
|
min-width: calc(var(--spacing) * 0);
|
|
3584
3571
|
}
|
|
@@ -3725,12 +3712,6 @@
|
|
|
3725
3712
|
margin-block-end: calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)));
|
|
3726
3713
|
}
|
|
3727
3714
|
|
|
3728
|
-
:where(.space-y-6 > :not(:last-child)) {
|
|
3729
|
-
--tw-space-y-reverse: 0;
|
|
3730
|
-
margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));
|
|
3731
|
-
margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
|
|
3732
|
-
}
|
|
3733
|
-
|
|
3734
3715
|
:where(.divide-y > :not(:last-child)) {
|
|
3735
3716
|
--tw-divide-y-reverse: 0;
|
|
3736
3717
|
border-bottom-style: var(--tw-border-style);
|
|
@@ -3996,10 +3977,6 @@
|
|
|
3996
3977
|
padding: calc(var(--spacing) * 5);
|
|
3997
3978
|
}
|
|
3998
3979
|
|
|
3999
|
-
.p-8 {
|
|
4000
|
-
padding: calc(var(--spacing) * 8);
|
|
4001
|
-
}
|
|
4002
|
-
|
|
4003
3980
|
.p-\[1px\] {
|
|
4004
3981
|
padding: 1px;
|
|
4005
3982
|
}
|
|
@@ -4103,20 +4080,6 @@
|
|
|
4103
4080
|
font-weight: var(--tw-font-weight, var(--text-2xl--font-weight));
|
|
4104
4081
|
}
|
|
4105
4082
|
|
|
4106
|
-
.text-3xl {
|
|
4107
|
-
font-size: var(--text-3xl);
|
|
4108
|
-
line-height: var(--tw-leading, var(--text-3xl--line-height));
|
|
4109
|
-
letter-spacing: var(--tw-tracking, var(--text-3xl--letter-spacing));
|
|
4110
|
-
font-weight: var(--tw-font-weight, var(--text-3xl--font-weight));
|
|
4111
|
-
}
|
|
4112
|
-
|
|
4113
|
-
.text-6xl {
|
|
4114
|
-
font-size: var(--text-6xl);
|
|
4115
|
-
line-height: var(--tw-leading, var(--text-6xl--line-height));
|
|
4116
|
-
letter-spacing: var(--tw-tracking, var(--text-6xl--letter-spacing));
|
|
4117
|
-
font-weight: var(--tw-font-weight, var(--text-6xl--font-weight));
|
|
4118
|
-
}
|
|
4119
|
-
|
|
4120
4083
|
.text-base {
|
|
4121
4084
|
font-size: var(--text-base);
|
|
4122
4085
|
line-height: var(--tw-leading, var(--text-base--line-height));
|
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@ const resourceModules = import.meta.glob('../src/resources/*-resource.json', { e
|
|
|
23
23
|
type ResourceData = { name: string; [key: string]: unknown };
|
|
24
24
|
const resourcesMap = new Map<string, ResourceData>();
|
|
25
25
|
for (const [path, module] of Object.entries(resourceModules)) {
|
|
26
|
-
// Extract key from path: '../src/resources/
|
|
26
|
+
// Extract key from path: '../src/resources/review-resource.json' -> 'review'
|
|
27
27
|
const match = path.match(/\/([^/]+)-resource\.json$/);
|
|
28
28
|
const key = match?.[1];
|
|
29
29
|
if (key) {
|
|
@@ -52,7 +52,7 @@ function findResourceKey(simulationKey: string): string | undefined {
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Convert resource name to component name
|
|
55
|
-
* Example: 'carousel' -> 'CarouselResource', '
|
|
55
|
+
* Example: 'carousel' -> 'CarouselResource', 'review' -> 'ReviewResource'
|
|
56
56
|
*/
|
|
57
57
|
function getResourceComponent(name: string): React.ComponentType {
|
|
58
58
|
const pascalName = name.charAt(0).toUpperCase() + name.slice(1);
|
package/template/README.md
CHANGED
|
@@ -30,8 +30,8 @@ The template includes a minimal test setup with Vitest. You can add additional t
|
|
|
30
30
|
|
|
31
31
|
- `src/resources/` - Resource files must be here
|
|
32
32
|
- `src/simulations/` - Simulation files must be here
|
|
33
|
-
- Resource file naming: `*-resource.tsx` (e.g., `
|
|
34
|
-
- Simulation file naming: `*-simulation.
|
|
33
|
+
- Resource file naming: `*-resource.tsx` (e.g., `review-resource.tsx`)
|
|
34
|
+
- Simulation file naming: `*-simulation.json` (e.g., `review-purchase-simulation.json`)
|
|
35
35
|
- `src/index-resource.tsx` - Build template (must have `// RESOURCE_IMPORT` and `// RESOURCE_MOUNT` comments)
|
|
36
36
|
|
|
37
37
|
**You can customize:**
|
|
@@ -71,7 +71,7 @@ ngrok http 6766
|
|
|
71
71
|
|
|
72
72
|
You can then connect to the tunnel forwarding URL at the `/mcp` path from ChatGPT **in developer mode** to see your UI in action: `User > Settings > Apps & Connectors > Create`
|
|
73
73
|
|
|
74
|
-
Once your app is connected, send the name of the app and a tool, like `/sunpeak show
|
|
74
|
+
Once your app is connected, send the name of the app and a tool, like `/sunpeak show review`, to ChatGPT.
|
|
75
75
|
|
|
76
76
|
When you make changes to the UI, refresh your app in ChatGPT after the MCP server has finished rebuilding your app: `User > Settings > Apps & Connectors > My App > Refresh`
|
|
77
77
|
|
|
@@ -87,8 +87,8 @@ This creates optimized builds in `dist/`:
|
|
|
87
87
|
|
|
88
88
|
- `dist/albums.js`
|
|
89
89
|
- `dist/albums.json`
|
|
90
|
-
- `dist/
|
|
91
|
-
- `dist/
|
|
90
|
+
- `dist/review.js`
|
|
91
|
+
- `dist/review.json`
|
|
92
92
|
- _(One .js file per resource in src/resources/)_
|
|
93
93
|
- _(One .json file per resource in src/resources/)_
|
|
94
94
|
|