sunpeak 0.8.6 → 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 +4 -6
- 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 +1 -1
- package/template/dist/review.json +1 -1
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Button.js +3 -3
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_SegmentedControl.js +4 -4
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Select.js +20 -20
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Textarea.js +3 -3
- package/template/node_modules/.vite/deps/_metadata.json +35 -35
- package/template/node_modules/.vite/deps/{chunk-SPYXUHEY.js → chunk-N6DVYEXK.js} +8 -8
- 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/review-resource.test.tsx +538 -0
- 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/template/node_modules/.vite/deps/{chunk-SPYXUHEY.js.map → chunk-N6DVYEXK.js.map} +0 -0
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "counter",
|
|
3
|
-
"title": "Counter",
|
|
4
|
-
"description": "Show a simple counter tool widget",
|
|
5
|
-
"mimeType": "text/html+skybridge",
|
|
6
|
-
"_meta": {
|
|
7
|
-
"openai/widgetDomain": "https://sunpeak.ai",
|
|
8
|
-
"openai/widgetCSP": {
|
|
9
|
-
"resource_domains": [
|
|
10
|
-
"https://cdn.openai.com"
|
|
11
|
-
]
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"uri": "ui://counter-mjt2f5f3"
|
|
15
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "counter",
|
|
3
|
-
"title": "Counter",
|
|
4
|
-
"description": "Show a simple counter tool widget",
|
|
5
|
-
"mimeType": "text/html+skybridge",
|
|
6
|
-
"_meta": {
|
|
7
|
-
"openai/widgetDomain": "https://sunpeak.ai",
|
|
8
|
-
"openai/widgetCSP": {
|
|
9
|
-
"resource_domains": ["https://cdn.openai.com"]
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { CounterResource } from './counter-resource';
|
|
4
|
-
|
|
5
|
-
// Mock sunpeak hooks
|
|
6
|
-
const mockSetWidgetState = vi.fn();
|
|
7
|
-
let mockSafeArea = { insets: { top: 0, bottom: 0, left: 0, right: 0 } };
|
|
8
|
-
let mockMaxHeight = 600;
|
|
9
|
-
let mockUserAgent: {
|
|
10
|
-
device: { type: 'desktop' | 'mobile' | 'tablet' | 'unknown' };
|
|
11
|
-
capabilities: { hover: boolean; touch: boolean };
|
|
12
|
-
} = {
|
|
13
|
-
device: { type: 'desktop' },
|
|
14
|
-
capabilities: { hover: true, touch: false },
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
vi.mock('sunpeak', () => ({
|
|
18
|
-
useWidgetState: () => [{ count: 0 }, mockSetWidgetState],
|
|
19
|
-
useSafeArea: () => mockSafeArea,
|
|
20
|
-
useMaxHeight: () => mockMaxHeight,
|
|
21
|
-
useUserAgent: () => mockUserAgent,
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
describe('CounterResource', () => {
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
vi.clearAllMocks();
|
|
27
|
-
mockSafeArea = { insets: { top: 0, bottom: 0, left: 0, right: 0 } };
|
|
28
|
-
mockMaxHeight = 600;
|
|
29
|
-
mockUserAgent = { device: { type: 'desktop' }, capabilities: { hover: true, touch: false } };
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('renders counter with initial count', () => {
|
|
33
|
-
render(<CounterResource />);
|
|
34
|
-
|
|
35
|
-
expect(screen.getByText('Welcome to Sunpeak!')).toBeInTheDocument();
|
|
36
|
-
expect(screen.getByText('0')).toBeInTheDocument();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('increments counter when + button is clicked', () => {
|
|
40
|
-
render(<CounterResource />);
|
|
41
|
-
|
|
42
|
-
const incrementButton = screen.getByLabelText('Increment');
|
|
43
|
-
fireEvent.click(incrementButton);
|
|
44
|
-
|
|
45
|
-
expect(mockSetWidgetState).toHaveBeenCalledWith({ count: 1 });
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('decrements counter when - button is clicked', () => {
|
|
49
|
-
render(<CounterResource />);
|
|
50
|
-
|
|
51
|
-
const decrementButton = screen.getByLabelText('Decrement');
|
|
52
|
-
fireEvent.click(decrementButton);
|
|
53
|
-
|
|
54
|
-
expect(mockSetWidgetState).toHaveBeenCalledWith({ count: -1 });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('resets counter when Reset button is clicked', () => {
|
|
58
|
-
render(<CounterResource />);
|
|
59
|
-
|
|
60
|
-
const resetButton = screen.getByText('Reset');
|
|
61
|
-
fireEvent.click(resetButton);
|
|
62
|
-
|
|
63
|
-
expect(mockSetWidgetState).toHaveBeenCalledWith({ count: 0 });
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('respects safe area insets', () => {
|
|
67
|
-
mockSafeArea = { insets: { top: 20, bottom: 30, left: 10, right: 15 } };
|
|
68
|
-
|
|
69
|
-
const { container } = render(<CounterResource />);
|
|
70
|
-
const mainDiv = container.firstChild as HTMLElement;
|
|
71
|
-
|
|
72
|
-
expect(mainDiv).toHaveStyle({
|
|
73
|
-
paddingTop: '20px',
|
|
74
|
-
paddingBottom: '30px',
|
|
75
|
-
paddingLeft: '10px',
|
|
76
|
-
paddingRight: '15px',
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('respects maxHeight constraint', () => {
|
|
81
|
-
mockMaxHeight = 400;
|
|
82
|
-
|
|
83
|
-
const { container } = render(<CounterResource />);
|
|
84
|
-
const mainDiv = container.firstChild as HTMLElement;
|
|
85
|
-
|
|
86
|
-
expect(mainDiv).toHaveStyle({
|
|
87
|
-
maxHeight: '400px',
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('renders larger buttons for touch devices', () => {
|
|
92
|
-
mockUserAgent = { device: { type: 'mobile' }, capabilities: { hover: false, touch: true } };
|
|
93
|
-
|
|
94
|
-
render(<CounterResource />);
|
|
95
|
-
|
|
96
|
-
const incrementButton = screen.getByLabelText('Increment');
|
|
97
|
-
const resetButton = screen.getByText('Reset');
|
|
98
|
-
|
|
99
|
-
// Buttons should have larger size for touch
|
|
100
|
-
expect(incrementButton).toBeInTheDocument();
|
|
101
|
-
expect(resetButton).toBeInTheDocument();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('renders standard-sized buttons for non-touch devices', () => {
|
|
105
|
-
mockUserAgent = { device: { type: 'desktop' }, capabilities: { hover: true, touch: false } };
|
|
106
|
-
|
|
107
|
-
render(<CounterResource />);
|
|
108
|
-
|
|
109
|
-
const incrementButton = screen.getByLabelText('Increment');
|
|
110
|
-
const resetButton = screen.getByText('Reset');
|
|
111
|
-
|
|
112
|
-
// Buttons should have standard size for non-touch
|
|
113
|
-
expect(incrementButton).toBeInTheDocument();
|
|
114
|
-
expect(resetButton).toBeInTheDocument();
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { useWidgetState, useSafeArea, useMaxHeight, useUserAgent } from 'sunpeak';
|
|
2
|
-
import { Button } from '@openai/apps-sdk-ui/components/Button';
|
|
3
|
-
import { useEffect } from 'react';
|
|
4
|
-
|
|
5
|
-
interface CounterState extends Record<string, unknown> {
|
|
6
|
-
count?: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Production-ready Counter Resource
|
|
11
|
-
*
|
|
12
|
-
* This resource displays a counter to demonstrate useWidgetState.
|
|
13
|
-
* Can be dropped into any production environment without changes.
|
|
14
|
-
*/
|
|
15
|
-
export function CounterResource() {
|
|
16
|
-
const [widgetState, setWidgetState] = useWidgetState<CounterState>(() => ({
|
|
17
|
-
count: 0,
|
|
18
|
-
}));
|
|
19
|
-
const safeArea = useSafeArea();
|
|
20
|
-
const maxHeight = useMaxHeight();
|
|
21
|
-
const userAgent = useUserAgent();
|
|
22
|
-
|
|
23
|
-
const count = widgetState?.count ?? 0;
|
|
24
|
-
const hasTouch = userAgent?.capabilities.touch ?? false;
|
|
25
|
-
|
|
26
|
-
// Initialize count to 0 if not set
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
if (widgetState?.count === undefined) {
|
|
29
|
-
setWidgetState({ count: 0 });
|
|
30
|
-
}
|
|
31
|
-
}, [widgetState?.count, setWidgetState]);
|
|
32
|
-
|
|
33
|
-
const increment = () => {
|
|
34
|
-
setWidgetState({ count: count + 1 });
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const decrement = () => {
|
|
38
|
-
setWidgetState({ count: count - 1 });
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const reset = () => {
|
|
42
|
-
setWidgetState({ count: 0 });
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<div
|
|
47
|
-
className="flex flex-col items-center justify-center space-y-6"
|
|
48
|
-
style={{
|
|
49
|
-
paddingTop: `${safeArea?.insets.top ?? 0}px`,
|
|
50
|
-
paddingBottom: `${safeArea?.insets.bottom ?? 0}px`,
|
|
51
|
-
paddingLeft: `${safeArea?.insets.left ?? 0}px`,
|
|
52
|
-
paddingRight: `${safeArea?.insets.right ?? 0}px`,
|
|
53
|
-
maxHeight: maxHeight ?? undefined,
|
|
54
|
-
}}
|
|
55
|
-
>
|
|
56
|
-
<div className="text-center space-y-2">
|
|
57
|
-
<h1 className="text-3xl font-bold text-primary">Welcome to Sunpeak!</h1>
|
|
58
|
-
<p className="text-secondary">Build your MCP resource here</p>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div className="flex flex-col items-center space-y-4 p-8 border border-subtle rounded-2xl bg-surface shadow-sm">
|
|
62
|
-
<div className="text-6xl font-bold text-primary">{count}</div>
|
|
63
|
-
|
|
64
|
-
<div className="flex gap-2">
|
|
65
|
-
<Button
|
|
66
|
-
variant="soft"
|
|
67
|
-
color="secondary"
|
|
68
|
-
onClick={decrement}
|
|
69
|
-
aria-label="Decrement"
|
|
70
|
-
size={hasTouch ? 'lg' : 'md'}
|
|
71
|
-
>
|
|
72
|
-
−
|
|
73
|
-
</Button>
|
|
74
|
-
<Button
|
|
75
|
-
variant="solid"
|
|
76
|
-
color="primary"
|
|
77
|
-
onClick={increment}
|
|
78
|
-
aria-label="Increment"
|
|
79
|
-
size={hasTouch ? 'lg' : 'md'}
|
|
80
|
-
>
|
|
81
|
-
+
|
|
82
|
-
</Button>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<Button variant="outline" color="secondary" onClick={reset} size={hasTouch ? 'md' : 'sm'}>
|
|
86
|
-
Reset
|
|
87
|
-
</Button>
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
<div className="text-center text-sm text-secondary max-w-md space-y-2">
|
|
91
|
-
<p>
|
|
92
|
-
This counter persists its state using{' '}
|
|
93
|
-
<code className="px-1.5 py-0.5 rounded bg-surface-secondary text-primary">
|
|
94
|
-
useWidgetState
|
|
95
|
-
</code>
|
|
96
|
-
</p>
|
|
97
|
-
<p>Try switching between examples in the sidebar!</p>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"userMessage": "Help me count something",
|
|
3
|
-
"tool": {
|
|
4
|
-
"name": "show-counter",
|
|
5
|
-
"description": "Show a simple counter tool",
|
|
6
|
-
"inputSchema": { "type": "object", "properties": {}, "additionalProperties": false },
|
|
7
|
-
"title": "Show Counter",
|
|
8
|
-
"annotations": { "readOnlyHint": true },
|
|
9
|
-
"_meta": {
|
|
10
|
-
"openai/toolInvocation/invoking": "Counting beans",
|
|
11
|
-
"openai/toolInvocation/invoked": "Beans counted",
|
|
12
|
-
"openai/widgetAccessible": true,
|
|
13
|
-
"openai/resultCanProduceWidget": true
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"toolCall": {
|
|
17
|
-
"structuredContent": null,
|
|
18
|
-
"_meta": {}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
File without changes
|