sunpeak 0.4.2 → 0.5.1
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 +2 -2
- package/bin/sunpeak.js +1 -1
- package/dist/chatgpt/chatgpt-simulator-types.d.ts +1 -1
- package/dist/chatgpt/chatgpt-simulator.d.ts +4 -15
- package/dist/chatgpt/index.d.ts +1 -1
- package/dist/chatgpt/mock-openai.d.ts +4 -16
- package/dist/index.cjs +70 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +70 -42
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.cjs +45 -112
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.d.ts +2 -1
- package/dist/mcp/index.js +45 -112
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -4
- package/dist/mcp/types.d.ts +16 -62
- package/dist/providers/index.d.ts +1 -3
- package/dist/providers/openai/index.d.ts +7 -0
- package/dist/{chatgpt/openai-provider.d.ts → providers/openai/provider.d.ts} +1 -1
- package/dist/{chatgpt/openai-types.d.ts → providers/openai/types.d.ts} +3 -32
- package/dist/providers/types.d.ts +4 -5
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/provider-detection.d.ts +17 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/runtime.d.ts +34 -0
- package/dist/types/simulation.d.ts +47 -0
- package/package.json +2 -2
- package/template/README.md +1 -1
- package/template/dev/main.tsx +6 -2
- package/template/mcp/server.ts +8 -9
- package/template/package.json +1 -1
- package/template/scripts/build-all.mjs +43 -0
- package/template/scripts/validate.mjs +16 -7
- package/template/src/components/album/albums.tsx +7 -7
- package/template/src/components/card/card.tsx +4 -0
- package/template/src/components/index.ts +1 -1
- package/template/src/components/resources/AlbumsResource.tsx +13 -0
- package/template/src/{App.tsx → components/resources/CounterResource.tsx} +5 -14
- package/template/src/components/{simulations/carousel-simulation.tsx → resources/PlacesResource.tsx} +11 -17
- package/template/src/components/resources/index.ts +3 -0
- package/template/src/index-albums.tsx +9 -0
- package/template/src/index-carousel.tsx +9 -0
- package/template/src/index-counter.tsx +9 -0
- package/template/src/simulations/albums-simulation.ts +157 -0
- package/template/src/simulations/carousel-simulation.ts +94 -0
- package/template/src/simulations/counter-simulation.ts +43 -0
- package/template/src/simulations/index.ts +11 -0
- package/template/src/simulations/simulation-configs.ts +23 -0
- package/template/src/simulations/simulations.ts +36 -0
- package/template/src/simulations/types.ts +12 -0
- package/template/vite.config.build.ts +20 -14
- package/dist/chatgpt/mcp-provider.d.ts +0 -25
- package/template/data/albums.json +0 -112
- package/template/data/places.json +0 -49
- package/template/src/components/simulations/albums-simulation.tsx +0 -20
- package/template/src/components/simulations/app-simulation.tsx +0 -13
- package/template/src/components/simulations/index.tsx +0 -14
- package/template/src/index.chatgpt.tsx +0 -8
- package/template/src/index.ts +0 -3
package/dist/mcp/types.d.ts
CHANGED
|
@@ -1,74 +1,28 @@
|
|
|
1
1
|
import { Resource, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* MCP CallTool response data (subset used in simulations)
|
|
4
4
|
*/
|
|
5
|
-
export
|
|
6
|
-
|
|
5
|
+
export interface SimulationCallToolResult {
|
|
6
|
+
structuredContent?: Record<string, unknown> | null;
|
|
7
|
+
_meta?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Simulation configuration for MCP server.
|
|
11
|
+
* Must include distPath for the built widget file.
|
|
12
|
+
*/
|
|
13
|
+
export interface SimulationWithDist {
|
|
14
|
+
distPath: string;
|
|
15
|
+
tool: Tool;
|
|
16
|
+
resource: Resource;
|
|
17
|
+
toolCall?: SimulationCallToolResult;
|
|
7
18
|
}
|
|
8
19
|
/**
|
|
9
20
|
* Configuration for the MCP server.
|
|
21
|
+
* Takes an array of simulations with distPath for each built widget.
|
|
10
22
|
*/
|
|
11
23
|
export interface MCPServerConfig {
|
|
12
24
|
name?: string;
|
|
13
25
|
version?: string;
|
|
14
26
|
port?: number;
|
|
15
|
-
|
|
16
|
-
toolName?: string;
|
|
17
|
-
toolDescription?: string;
|
|
18
|
-
dummyData?: Record<string, unknown>;
|
|
19
|
-
provider?: MCPProvider;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Provider-specific metadata for widget descriptors.
|
|
23
|
-
*/
|
|
24
|
-
export interface WidgetDescriptorMeta {
|
|
25
|
-
[key: string]: unknown;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Provider-specific metadata for tool invocations.
|
|
29
|
-
*/
|
|
30
|
-
export interface WidgetInvocationMeta {
|
|
31
|
-
[key: string]: unknown;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Interface for platform-specific MCP provider implementations.
|
|
35
|
-
*/
|
|
36
|
-
export interface MCPProviderImplementation {
|
|
37
|
-
/**
|
|
38
|
-
* Get metadata for widget descriptors (tools and resources).
|
|
39
|
-
*/
|
|
40
|
-
getWidgetDescriptorMeta(): WidgetDescriptorMeta;
|
|
41
|
-
/**
|
|
42
|
-
* Get metadata for tool invocation responses.
|
|
43
|
-
*/
|
|
44
|
-
getWidgetInvocationMeta(): WidgetInvocationMeta;
|
|
45
|
-
/**
|
|
46
|
-
* Read and wrap the widget content for the platform.
|
|
47
|
-
* @param distPath - Path to the built widget JS file.
|
|
48
|
-
* @returns The wrapped HTML content ready for the platform.
|
|
49
|
-
*/
|
|
50
|
-
readWidgetContent(distPath: string): string;
|
|
51
|
-
/**
|
|
52
|
-
* Get the MIME type for widget resources.
|
|
53
|
-
*/
|
|
54
|
-
getWidgetMimeType(): string;
|
|
55
|
-
/**
|
|
56
|
-
* Get the resource URI for the widget.
|
|
57
|
-
*/
|
|
58
|
-
getWidgetResourceUri(): string;
|
|
59
|
-
/**
|
|
60
|
-
* Create the tool definition for the provider.
|
|
61
|
-
*/
|
|
62
|
-
createTool(config: {
|
|
63
|
-
name: string;
|
|
64
|
-
description: string;
|
|
65
|
-
inputSchema: Tool["inputSchema"];
|
|
66
|
-
}): Tool;
|
|
67
|
-
/**
|
|
68
|
-
* Create the resource definition for the provider.
|
|
69
|
-
*/
|
|
70
|
-
createResource(config: {
|
|
71
|
-
name: string;
|
|
72
|
-
description: string;
|
|
73
|
-
}): Resource;
|
|
27
|
+
simulations: SimulationWithDist[];
|
|
74
28
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { WidgetProvider, WidgetGlobals, WidgetAPI } from './types';
|
|
2
2
|
export type { WidgetGlobals, WidgetAPI, WidgetProvider } from './types';
|
|
3
|
-
export { isOpenAiAvailable, getOpenAiProvider } from '../chatgpt/openai-provider';
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
6
|
-
* This function caches the result, so detection only happens once.
|
|
4
|
+
* Get the detected provider for the current environment.
|
|
7
5
|
*
|
|
8
6
|
* @returns The detected provider, or null if no provider is available.
|
|
9
7
|
*/
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI/ChatGPT provider implementation.
|
|
3
|
+
* This module provides the OpenAI-specific implementation of the WidgetProvider interface.
|
|
4
|
+
*/
|
|
5
|
+
export { isOpenAiAvailable, getOpenAiProvider } from './provider';
|
|
6
|
+
export type { OpenAiGlobals, OpenAiAPI, CallTool, RequestDisplayMode, RequestModal, NotifyIntrinsicHeight, SetGlobalsEvent, } from './types';
|
|
7
|
+
export { SET_GLOBALS_EVENT_TYPE } from './types';
|
|
@@ -1,38 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export type Theme = 'light' | 'dark';
|
|
3
|
-
export type SafeAreaInsets = {
|
|
4
|
-
top: number;
|
|
5
|
-
bottom: number;
|
|
6
|
-
left: number;
|
|
7
|
-
right: number;
|
|
8
|
-
};
|
|
9
|
-
export type SafeArea = {
|
|
10
|
-
insets: SafeAreaInsets;
|
|
11
|
-
};
|
|
12
|
-
export type DeviceType = 'mobile' | 'tablet' | 'desktop' | 'unknown';
|
|
13
|
-
export type UserAgent = {
|
|
14
|
-
device: {
|
|
15
|
-
type: DeviceType;
|
|
16
|
-
};
|
|
17
|
-
capabilities: {
|
|
18
|
-
hover: boolean;
|
|
19
|
-
touch: boolean;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
export type DisplayMode = 'pip' | 'inline' | 'fullscreen';
|
|
1
|
+
import { UnknownObject, Theme, UserAgent, DisplayMode, ViewMode, View, SafeArea, CallToolResponse } from '../../types/runtime';
|
|
23
2
|
export type RequestDisplayMode = (args: {
|
|
24
3
|
mode: DisplayMode;
|
|
25
4
|
}) => Promise<{
|
|
26
5
|
mode: DisplayMode;
|
|
27
6
|
}>;
|
|
28
|
-
export type ViewMode = 'modal' | 'default';
|
|
29
|
-
export type View = {
|
|
30
|
-
mode: ViewMode;
|
|
31
|
-
params?: UnknownObject;
|
|
32
|
-
};
|
|
33
|
-
export type CallToolResponse = {
|
|
34
|
-
result: string;
|
|
35
|
-
};
|
|
36
7
|
export type CallTool = (name: string, args: Record<string, unknown>) => Promise<CallToolResponse>;
|
|
37
8
|
export type OpenAiGlobals<ToolInput = UnknownObject, ToolOutput = UnknownObject, ToolResponseMetadata = UnknownObject, WidgetState = UnknownObject> = {
|
|
38
9
|
theme: Theme;
|
|
@@ -46,14 +17,13 @@ export type OpenAiGlobals<ToolInput = UnknownObject, ToolOutput = UnknownObject,
|
|
|
46
17
|
toolOutput: ToolOutput | null;
|
|
47
18
|
toolResponseMetadata: ToolResponseMetadata | null;
|
|
48
19
|
widgetState: WidgetState | null;
|
|
49
|
-
setWidgetState: (state: WidgetState) => Promise<void>;
|
|
50
20
|
};
|
|
51
21
|
export type RequestModal = (args: {
|
|
52
22
|
mode: ViewMode;
|
|
53
23
|
params?: UnknownObject;
|
|
54
24
|
}) => Promise<void>;
|
|
55
25
|
export type NotifyIntrinsicHeight = (height: number) => void;
|
|
56
|
-
export type OpenAiAPI = {
|
|
26
|
+
export type OpenAiAPI<WidgetState = UnknownObject> = {
|
|
57
27
|
callTool: CallTool;
|
|
58
28
|
sendFollowUpMessage: (args: {
|
|
59
29
|
prompt: string;
|
|
@@ -64,6 +34,7 @@ export type OpenAiAPI = {
|
|
|
64
34
|
requestDisplayMode: RequestDisplayMode;
|
|
65
35
|
requestModal: RequestModal;
|
|
66
36
|
notifyIntrinsicHeight: NotifyIntrinsicHeight;
|
|
37
|
+
setWidgetState: (state: WidgetState) => Promise<void>;
|
|
67
38
|
};
|
|
68
39
|
export declare const SET_GLOBALS_EVENT_TYPE = "openai:set_globals";
|
|
69
40
|
export declare class SetGlobalsEvent extends CustomEvent<{
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { UnknownObject, Theme, UserAgent, DisplayMode, SafeArea, View, ViewMode, CallToolResponse } from '../
|
|
1
|
+
import { UnknownObject, Theme, UserAgent, DisplayMode, SafeArea, View, ViewMode, CallToolResponse } from '../types/runtime';
|
|
2
2
|
/**
|
|
3
3
|
* Provider-agnostic types for widget runtime environments.
|
|
4
4
|
* These types abstract away the specific host (OpenAI/ChatGPT, etc.)
|
|
5
5
|
*/
|
|
6
|
-
export type { UnknownObject, Theme, SafeAreaInsets, SafeArea, DeviceType, UserAgent, DisplayMode, ViewMode, View, CallToolResponse, } from '../
|
|
6
|
+
export type { UnknownObject, Theme, SafeAreaInsets, SafeArea, DeviceType, UserAgent, DisplayMode, ViewMode, View, CallToolResponse, } from '../types/runtime';
|
|
7
7
|
/**
|
|
8
8
|
* Global state available from the widget runtime environment.
|
|
9
|
-
*
|
|
9
|
+
* Provider-agnostic interface that abstracts widget globals.
|
|
10
10
|
*/
|
|
11
11
|
export type WidgetGlobals<ToolInput = UnknownObject, ToolOutput = UnknownObject, ToolResponseMetadata = UnknownObject, WidgetState = UnknownObject> = {
|
|
12
12
|
theme: Theme;
|
|
@@ -20,11 +20,10 @@ export type WidgetGlobals<ToolInput = UnknownObject, ToolOutput = UnknownObject,
|
|
|
20
20
|
toolOutput: ToolOutput | null;
|
|
21
21
|
toolResponseMetadata: ToolResponseMetadata | null;
|
|
22
22
|
widgetState: WidgetState | null;
|
|
23
|
-
setWidgetState: (state: WidgetState) => Promise<void>;
|
|
24
23
|
};
|
|
25
24
|
/**
|
|
26
25
|
* API methods available from the widget runtime environment.
|
|
27
|
-
*
|
|
26
|
+
* Provider-agnostic interface that abstracts widget API methods.
|
|
28
27
|
*/
|
|
29
28
|
export type WidgetAPI = {
|
|
30
29
|
callTool?: (name: string, args: Record<string, unknown>) => Promise<CallToolResponse>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime utilities for widget environments.
|
|
3
|
+
*
|
|
4
|
+
* This module contains runtime-specific logic like provider detection
|
|
5
|
+
* that needs to know about concrete provider implementations.
|
|
6
|
+
*/
|
|
7
|
+
export { detectProvider, isProviderAvailable, resetProviderCache, } from './provider-detection';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WidgetProvider } from '../providers/types';
|
|
2
|
+
/**
|
|
3
|
+
* Detect and return the appropriate provider for the current environment.
|
|
4
|
+
* This function caches the result, so detection only happens once.
|
|
5
|
+
*
|
|
6
|
+
* @returns The detected provider, or null if no provider is available.
|
|
7
|
+
*/
|
|
8
|
+
export declare function detectProvider(): WidgetProvider | null;
|
|
9
|
+
/**
|
|
10
|
+
* Check if any provider is available.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isProviderAvailable(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Reset the provider detection cache.
|
|
15
|
+
* Useful for testing or when the environment changes.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resetProviderCache(): void;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic runtime types for widget environments.
|
|
3
|
+
* These types are provider-agnostic and can be used across different widget platforms.
|
|
4
|
+
*/
|
|
5
|
+
export type UnknownObject = Record<string, unknown>;
|
|
6
|
+
export type Theme = 'light' | 'dark';
|
|
7
|
+
export type SafeAreaInsets = {
|
|
8
|
+
top: number;
|
|
9
|
+
bottom: number;
|
|
10
|
+
left: number;
|
|
11
|
+
right: number;
|
|
12
|
+
};
|
|
13
|
+
export type SafeArea = {
|
|
14
|
+
insets: SafeAreaInsets;
|
|
15
|
+
};
|
|
16
|
+
export type DeviceType = 'mobile' | 'tablet' | 'desktop' | 'unknown';
|
|
17
|
+
export type UserAgent = {
|
|
18
|
+
device: {
|
|
19
|
+
type: DeviceType;
|
|
20
|
+
};
|
|
21
|
+
capabilities: {
|
|
22
|
+
hover: boolean;
|
|
23
|
+
touch: boolean;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export type DisplayMode = 'pip' | 'inline' | 'fullscreen';
|
|
27
|
+
export type ViewMode = 'modal' | 'default';
|
|
28
|
+
export type View = {
|
|
29
|
+
mode: ViewMode;
|
|
30
|
+
params?: UnknownObject;
|
|
31
|
+
};
|
|
32
|
+
export type CallToolResponse = {
|
|
33
|
+
result: string;
|
|
34
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Tool, Resource } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { Theme, DisplayMode, UserAgent, SafeArea, View } from './index';
|
|
3
|
+
/**
|
|
4
|
+
* Core simulation types for development and testing.
|
|
5
|
+
* These types define how simulations are configured and used in both
|
|
6
|
+
* the dev simulator and MCP server contexts.
|
|
7
|
+
*/
|
|
8
|
+
import type * as React from 'react';
|
|
9
|
+
/**
|
|
10
|
+
* Simulation globals that configure the simulator environment.
|
|
11
|
+
* These values are passed to the mock runtime to set initial values for development/testing.
|
|
12
|
+
* All fields are optional as simulations can use defaults.
|
|
13
|
+
* Globals initialized based on tool responses are instead set in SimulationCallToolResult:
|
|
14
|
+
* (structuredContent > toolOutput, _meta > toolResponseMetadata)
|
|
15
|
+
*/
|
|
16
|
+
export interface SimulationGlobals {
|
|
17
|
+
theme?: Theme;
|
|
18
|
+
userAgent?: UserAgent;
|
|
19
|
+
locale?: string;
|
|
20
|
+
maxHeight?: number;
|
|
21
|
+
displayMode?: DisplayMode;
|
|
22
|
+
safeArea?: SafeArea;
|
|
23
|
+
view?: View | null;
|
|
24
|
+
toolInput?: Record<string, unknown>;
|
|
25
|
+
widgetState?: Record<string, unknown> | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* MCP CallTool response data (subset used in simulations).
|
|
29
|
+
* Note: toolOutput (structuredContent) and toolResponseMetadata (_meta)
|
|
30
|
+
* are set here for use by the MCP server as well, not in SimulationGlobals.
|
|
31
|
+
*/
|
|
32
|
+
export interface SimulationCallToolResult {
|
|
33
|
+
structuredContent?: Record<string, unknown> | null;
|
|
34
|
+
_meta?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A simulation packages a component with its example data and metadata.
|
|
38
|
+
* Each simulation represents a complete tool experience in the simulator.
|
|
39
|
+
*/
|
|
40
|
+
export interface Simulation {
|
|
41
|
+
resourceComponent: React.ComponentType;
|
|
42
|
+
userMessage?: string;
|
|
43
|
+
simulationGlobals?: SimulationGlobals;
|
|
44
|
+
tool: Tool;
|
|
45
|
+
resource: Resource;
|
|
46
|
+
toolCall?: SimulationCallToolResult;
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sunpeak",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "The MCP App SDK. Quickstart, build, & test your ChatGPT App locally!",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
package/template/README.md
CHANGED
|
@@ -10,7 +10,7 @@ For an initial overview of your new app and the sunpeak API, refer to the [docum
|
|
|
10
10
|
pnpm dev
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Edit [src/
|
|
13
|
+
Edit the resource files in [./src/components/resources/](./src/components/resources/) to build your resource UI.
|
|
14
14
|
|
|
15
15
|
## Development
|
|
16
16
|
|
package/template/dev/main.tsx
CHANGED
|
@@ -3,10 +3,14 @@ import '../src/styles/globals.css';
|
|
|
3
3
|
import { StrictMode } from 'react';
|
|
4
4
|
import { createRoot } from 'react-dom/client';
|
|
5
5
|
import { ChatGPTSimulator } from 'sunpeak';
|
|
6
|
-
import { simulations } from '../src/
|
|
6
|
+
import { simulations } from '../src/simulations';
|
|
7
7
|
|
|
8
8
|
createRoot(document.getElementById('root')!).render(
|
|
9
9
|
<StrictMode>
|
|
10
|
-
<ChatGPTSimulator
|
|
10
|
+
<ChatGPTSimulator
|
|
11
|
+
simulations={simulations}
|
|
12
|
+
appName="Sunpeak App"
|
|
13
|
+
appIcon="🌄"
|
|
14
|
+
/>
|
|
11
15
|
</StrictMode>
|
|
12
16
|
);
|
package/template/mcp/server.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { runMCPServer } from 'sunpeak/mcp';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import {
|
|
4
|
+
import { SIMULATIONS } from '../src/simulations/simulation-configs.js';
|
|
5
5
|
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
const
|
|
10
|
-
|
|
8
|
+
// Add distPath to each simulation for the MCP server
|
|
9
|
+
const simulations = Object.entries(SIMULATIONS).map(([simulationKey, simulation]) => ({
|
|
10
|
+
...simulation,
|
|
11
|
+
distPath: path.resolve(__dirname, `../dist/chatgpt/${simulationKey}.js`),
|
|
12
|
+
}));
|
|
11
13
|
|
|
12
14
|
runMCPServer({
|
|
13
|
-
name: '
|
|
15
|
+
name: 'Sunpeak App',
|
|
14
16
|
version: '0.1.0',
|
|
15
|
-
|
|
16
|
-
toolName: 'show-places',
|
|
17
|
-
toolDescription: 'Show popular places in Austin',
|
|
18
|
-
dummyData: toolOutput,
|
|
17
|
+
simulations,
|
|
19
18
|
port: 6766,
|
|
20
19
|
});
|
package/template/package.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { existsSync, rmSync } from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const distDir = path.join(__dirname, '../dist/chatgpt');
|
|
9
|
+
|
|
10
|
+
// Clean dist directory
|
|
11
|
+
if (existsSync(distDir)) {
|
|
12
|
+
rmSync(distDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const tools = [
|
|
16
|
+
{ entry: 'src/index-counter.tsx', output: 'counter.js' },
|
|
17
|
+
{ entry: 'src/index-albums.tsx', output: 'albums.js' },
|
|
18
|
+
{ entry: 'src/index-carousel.tsx', output: 'carousel.js' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
console.log('Building all tools...\n');
|
|
22
|
+
|
|
23
|
+
tools.forEach(({ entry, output }, index) => {
|
|
24
|
+
console.log(`[${index + 1}/${tools.length}] Building ${output}...`);
|
|
25
|
+
try {
|
|
26
|
+
execSync(
|
|
27
|
+
`vite build --config vite.config.build.ts`,
|
|
28
|
+
{
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
env: {
|
|
31
|
+
...process.env,
|
|
32
|
+
ENTRY_FILE: entry,
|
|
33
|
+
OUTPUT_FILE: output,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(`Failed to build ${output}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log('\n✓ All tools built successfully!');
|
|
@@ -100,17 +100,26 @@ try {
|
|
|
100
100
|
throw new Error('pnpm build failed');
|
|
101
101
|
}
|
|
102
102
|
const chatgptDir = join(PROJECT_ROOT, 'dist', 'chatgpt');
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
const expectedFiles = ['counter.js', 'albums.js', 'carousel.js'];
|
|
104
|
+
|
|
105
|
+
// Check all expected files exist
|
|
106
|
+
for (const file of expectedFiles) {
|
|
107
|
+
const filePath = join(chatgptDir, file);
|
|
108
|
+
if (!existsSync(filePath)) {
|
|
109
|
+
printError(`Missing expected file: ./dist/chatgpt/${file}`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
107
112
|
}
|
|
113
|
+
|
|
114
|
+
// Verify only expected files are present
|
|
108
115
|
const files = readdirSync(chatgptDir);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
printError(
|
|
116
|
+
const unexpectedFiles = files.filter(f => !expectedFiles.includes(f));
|
|
117
|
+
if (unexpectedFiles.length > 0) {
|
|
118
|
+
printError(`Unexpected files in ./dist/chatgpt/: ${unexpectedFiles.join(', ')}`);
|
|
119
|
+
printError(`Expected only: ${expectedFiles.join(', ')}`);
|
|
112
120
|
process.exit(1);
|
|
113
121
|
}
|
|
122
|
+
|
|
114
123
|
console.log()
|
|
115
124
|
printSuccess('pnpm build');
|
|
116
125
|
|
|
@@ -15,22 +15,22 @@ export interface Album {
|
|
|
15
15
|
}>
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export interface
|
|
18
|
+
export interface AlbumsData extends Record<string, unknown> {
|
|
19
19
|
albums: Album[]
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export interface
|
|
22
|
+
export interface AlbumsState extends Record<string, unknown> {
|
|
23
23
|
selectedAlbumId?: string | null
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export type
|
|
26
|
+
export type AlbumsProps = {
|
|
27
27
|
className?: string
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export const
|
|
30
|
+
export const Albums = React.forwardRef<HTMLDivElement, AlbumsProps>(
|
|
31
31
|
({ className }, ref) => {
|
|
32
|
-
const data = useWidgetProps<
|
|
33
|
-
const [widgetState, setWidgetState] = useWidgetState<
|
|
32
|
+
const data = useWidgetProps<AlbumsData>(() => ({ albums: [] }))
|
|
33
|
+
const [widgetState, setWidgetState] = useWidgetState<AlbumsState>(() => ({
|
|
34
34
|
selectedAlbumId: null,
|
|
35
35
|
}))
|
|
36
36
|
const displayMode = useDisplayMode()
|
|
@@ -74,4 +74,4 @@ export const OpenAIAlbums = React.forwardRef<HTMLDivElement, OpenAIAlbumsProps>(
|
|
|
74
74
|
)
|
|
75
75
|
}
|
|
76
76
|
)
|
|
77
|
-
|
|
77
|
+
Albums.displayName = "Albums"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Albums } from "../album"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Production-ready Albums Resource
|
|
6
|
+
*
|
|
7
|
+
* This resource displays photo albums in a carousel layout with fullscreen viewing capability.
|
|
8
|
+
* Can be dropped into any production environment without changes.
|
|
9
|
+
*/
|
|
10
|
+
export const AlbumsResource = React.forwardRef<HTMLDivElement>((_props, ref) => {
|
|
11
|
+
return <Albums ref={ref} />
|
|
12
|
+
})
|
|
13
|
+
AlbumsResource.displayName = "AlbumsResource"
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import './styles/globals.css';
|
|
2
|
-
|
|
3
1
|
import { useWidgetState } from 'sunpeak';
|
|
4
2
|
import { Button } from '@openai/apps-sdk-ui/components/Button';
|
|
5
3
|
|
|
@@ -8,19 +6,12 @@ interface CounterState extends Record<string, unknown> {
|
|
|
8
6
|
}
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* This is a simple counter app to get you started.
|
|
14
|
-
* Try building your own app here!
|
|
9
|
+
* Production-ready Counter Resource
|
|
15
10
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* - Check out the components folder for reusable components
|
|
19
|
-
* - Use sunpeak hooks for state management and display modes
|
|
20
|
-
* - Edit this file and see your changes live
|
|
21
|
-
* - Edit ./components/simulations/app-simulation.tsx to customize your simulation
|
|
11
|
+
* This resource displays a counter to demonstrate useWidgetState.
|
|
12
|
+
* Can be dropped into any production environment without changes.
|
|
22
13
|
*/
|
|
23
|
-
export function
|
|
14
|
+
export function CounterResource() {
|
|
24
15
|
const [widgetState, setWidgetState] = useWidgetState<CounterState>(() => ({
|
|
25
16
|
count: 0,
|
|
26
17
|
}));
|
|
@@ -46,7 +37,7 @@ export function App() {
|
|
|
46
37
|
Welcome to Sunpeak!
|
|
47
38
|
</h1>
|
|
48
39
|
<p className="text-secondary">
|
|
49
|
-
Build your ChatGPT
|
|
40
|
+
Build your ChatGPT tool here
|
|
50
41
|
</p>
|
|
51
42
|
</div>
|
|
52
43
|
|
package/template/src/components/{simulations/carousel-simulation.tsx → resources/PlacesResource.tsx}
RENAMED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
-
import type { Simulation } from "sunpeak"
|
|
3
2
|
import { useWidgetProps } from "sunpeak"
|
|
4
3
|
import { Carousel, Card } from ".."
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Production-ready Places Resource
|
|
7
|
+
*
|
|
8
|
+
* This resource displays places in a carousel layout with cards.
|
|
9
|
+
* Can be dropped into any production environment without changes.
|
|
10
|
+
*/
|
|
6
11
|
|
|
7
12
|
export interface Place {
|
|
8
13
|
id: string
|
|
@@ -14,12 +19,12 @@ export interface Place {
|
|
|
14
19
|
description: string
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
export interface
|
|
22
|
+
export interface PlacesData extends Record<string, unknown> {
|
|
18
23
|
places: Place[]
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
const
|
|
22
|
-
const data = useWidgetProps<
|
|
26
|
+
export const PlacesResource = React.forwardRef<HTMLDivElement>((_props, ref) => {
|
|
27
|
+
const data = useWidgetProps<PlacesData>(() => ({ places: [] }))
|
|
23
28
|
|
|
24
29
|
return (
|
|
25
30
|
<div ref={ref}>
|
|
@@ -49,15 +54,4 @@ const CarouselComponent = React.forwardRef<HTMLDivElement>((_props, ref) => {
|
|
|
49
54
|
</div>
|
|
50
55
|
)
|
|
51
56
|
})
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
export const carouselSimulation: Simulation = {
|
|
55
|
-
value: 'carousel',
|
|
56
|
-
label: 'Carousel',
|
|
57
|
-
component: CarouselComponent,
|
|
58
|
-
appName: 'Splorin',
|
|
59
|
-
appIcon: '✈️',
|
|
60
|
-
userMessage: 'Show me popular places to visit in Austin Texas',
|
|
61
|
-
toolOutput: placesData,
|
|
62
|
-
widgetState: null,
|
|
63
|
-
}
|
|
57
|
+
PlacesResource.displayName = "PlacesResource"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import './styles/globals.css';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { AlbumsResource } from './components/resources/AlbumsResource';
|
|
4
|
+
|
|
5
|
+
// Mount the AlbumsResource
|
|
6
|
+
const root = document.getElementById('root');
|
|
7
|
+
if (root) {
|
|
8
|
+
createRoot(root).render(<AlbumsResource />);
|
|
9
|
+
}
|