sunpeak 0.5.36 → 0.5.41
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/dist/chatgpt/mock-openai.d.ts +2 -2
- package/dist/chatgpt/simple-sidebar.d.ts +2 -1
- package/dist/hooks/use-max-height.d.ts +1 -1
- package/dist/index.cjs +176 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +176 -121
- package/dist/index.js.map +1 -1
- package/dist/providers/openai/types.d.ts +1 -1
- package/dist/providers/types.d.ts +1 -1
- package/dist/style.css +155 -36
- package/package.json +1 -1
- package/template/dist/chatgpt/albums.js +10 -10
- package/template/dist/chatgpt/carousel.js +2 -2
- package/template/dist/chatgpt/counter.js +7 -7
- 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 +19 -19
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Textarea.js +3 -3
- package/template/node_modules/.vite/deps/_metadata.json +30 -30
- package/template/node_modules/.vite/deps/{chunk-675LFNY2.js → chunk-EVJ3DVH5.js} +8 -8
- package/template/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/template/src/components/album/albums.test.tsx +7 -2
- package/template/src/components/album/albums.tsx +1 -1
- package/template/src/components/album/fullscreen-viewer.test.tsx +12 -24
- package/template/src/components/album/fullscreen-viewer.tsx +55 -34
- package/template/src/components/carousel/carousel.tsx +1 -1
- package/template/src/components/resources/albums-resource.tsx +1 -0
- package/template/src/components/resources/counter-resource.tsx +8 -0
- package/template/src/simulations/albums-simulation.ts +5 -1
- package/template/src/simulations/carousel-simulation.ts +5 -1
- package/template/src/simulations/counter-simulation.ts +6 -1
- package/template/src/simulations/widget-config.ts +42 -0
- /package/template/node_modules/.vite/deps/{chunk-675LFNY2.js.map → chunk-EVJ3DVH5.js.map} +0 -0
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
Button,
|
|
3
3
|
ButtonLink,
|
|
4
4
|
CopyButton
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-QPJAV452.js";
|
|
7
|
-
import "./chunk-XB525PXG.js";
|
|
5
|
+
} from "./chunk-EVJ3DVH5.js";
|
|
8
6
|
import "./chunk-YOJ6QPGS.js";
|
|
9
7
|
import "./chunk-BAG6OO6S.js";
|
|
8
|
+
import "./chunk-XB525PXG.js";
|
|
9
|
+
import "./chunk-QPJAV452.js";
|
|
10
10
|
import "./chunk-EGRHWZRV.js";
|
|
11
11
|
import "./chunk-CNYJBM5F.js";
|
|
12
12
|
import "./chunk-PTVT3RFX.js";
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
dist_exports4 as dist_exports
|
|
3
|
-
} from "./chunk-SGWD4VEU.js";
|
|
4
|
-
import "./chunk-KFGKZMLK.js";
|
|
5
1
|
import {
|
|
6
2
|
useResizeObserver
|
|
7
3
|
} from "./chunk-YOJ6QPGS.js";
|
|
@@ -9,6 +5,10 @@ import {
|
|
|
9
5
|
handlePressableMouseEnter,
|
|
10
6
|
waitForAnimationFrame
|
|
11
7
|
} from "./chunk-BAG6OO6S.js";
|
|
8
|
+
import {
|
|
9
|
+
dist_exports4 as dist_exports
|
|
10
|
+
} from "./chunk-SGWD4VEU.js";
|
|
11
|
+
import "./chunk-KFGKZMLK.js";
|
|
12
12
|
import "./chunk-EGRHWZRV.js";
|
|
13
13
|
import {
|
|
14
14
|
clsx_default
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Input
|
|
3
|
-
} from "./chunk-CQ3GYAYB.js";
|
|
4
1
|
import {
|
|
5
2
|
Button,
|
|
6
3
|
LoadingIndicator,
|
|
7
4
|
TransitionGroup
|
|
8
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-EVJ3DVH5.js";
|
|
9
6
|
import {
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
7
|
+
useTimeout
|
|
8
|
+
} from "./chunk-YOJ6QPGS.js";
|
|
9
|
+
import {
|
|
10
|
+
handlePressableMouseEnter,
|
|
11
|
+
preventDefaultHandler,
|
|
12
|
+
toCssVariables,
|
|
13
|
+
waitForAnimationFrame
|
|
14
|
+
} from "./chunk-BAG6OO6S.js";
|
|
15
|
+
import {
|
|
16
|
+
dist_exports,
|
|
17
|
+
dist_exports3 as dist_exports2,
|
|
18
|
+
dist_exports5 as dist_exports3
|
|
19
|
+
} from "./chunk-SGWD4VEU.js";
|
|
20
|
+
import "./chunk-KFGKZMLK.js";
|
|
12
21
|
import {
|
|
13
22
|
Check_default,
|
|
14
23
|
ChevronDownVector_default,
|
|
@@ -18,20 +27,11 @@ import {
|
|
|
18
27
|
X_default
|
|
19
28
|
} from "./chunk-XB525PXG.js";
|
|
20
29
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
dist_exports5 as dist_exports3
|
|
24
|
-
} from "./chunk-SGWD4VEU.js";
|
|
25
|
-
import "./chunk-KFGKZMLK.js";
|
|
26
|
-
import {
|
|
27
|
-
useTimeout
|
|
28
|
-
} from "./chunk-YOJ6QPGS.js";
|
|
30
|
+
Input
|
|
31
|
+
} from "./chunk-CQ3GYAYB.js";
|
|
29
32
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
toCssVariables,
|
|
33
|
-
waitForAnimationFrame
|
|
34
|
-
} from "./chunk-BAG6OO6S.js";
|
|
33
|
+
o
|
|
34
|
+
} from "./chunk-QPJAV452.js";
|
|
35
35
|
import "./chunk-EGRHWZRV.js";
|
|
36
36
|
import {
|
|
37
37
|
clsx_default
|
|
@@ -7,118 +7,115 @@
|
|
|
7
7
|
"react": {
|
|
8
8
|
"src": "../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/index.js",
|
|
9
9
|
"file": "react.js",
|
|
10
|
-
"fileHash": "
|
|
10
|
+
"fileHash": "264ae3aa",
|
|
11
11
|
"needsInterop": true
|
|
12
12
|
},
|
|
13
13
|
"react-dom": {
|
|
14
14
|
"src": "../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/index.js",
|
|
15
15
|
"file": "react-dom.js",
|
|
16
|
-
"fileHash": "
|
|
16
|
+
"fileHash": "565f0670",
|
|
17
17
|
"needsInterop": true
|
|
18
18
|
},
|
|
19
19
|
"react/jsx-dev-runtime": {
|
|
20
20
|
"src": "../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-dev-runtime.js",
|
|
21
21
|
"file": "react_jsx-dev-runtime.js",
|
|
22
|
-
"fileHash": "
|
|
22
|
+
"fileHash": "dc93890d",
|
|
23
23
|
"needsInterop": true
|
|
24
24
|
},
|
|
25
25
|
"react/jsx-runtime": {
|
|
26
26
|
"src": "../../../../node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-runtime.js",
|
|
27
27
|
"file": "react_jsx-runtime.js",
|
|
28
|
-
"fileHash": "
|
|
28
|
+
"fileHash": "769723ed",
|
|
29
29
|
"needsInterop": true
|
|
30
30
|
},
|
|
31
31
|
"@openai/apps-sdk-ui/components/Button": {
|
|
32
32
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/components/Button/index.js",
|
|
33
33
|
"file": "@openai_apps-sdk-ui_components_Button.js",
|
|
34
|
-
"fileHash": "
|
|
34
|
+
"fileHash": "2aaa39fe",
|
|
35
35
|
"needsInterop": false
|
|
36
36
|
},
|
|
37
37
|
"@openai/apps-sdk-ui/components/Checkbox": {
|
|
38
38
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/components/Checkbox/index.js",
|
|
39
39
|
"file": "@openai_apps-sdk-ui_components_Checkbox.js",
|
|
40
|
-
"fileHash": "
|
|
40
|
+
"fileHash": "9bc36d10",
|
|
41
41
|
"needsInterop": false
|
|
42
42
|
},
|
|
43
43
|
"@openai/apps-sdk-ui/components/Icon": {
|
|
44
44
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/components/Icon/index.js",
|
|
45
45
|
"file": "@openai_apps-sdk-ui_components_Icon.js",
|
|
46
|
-
"fileHash": "
|
|
46
|
+
"fileHash": "0c8a7bb7",
|
|
47
47
|
"needsInterop": false
|
|
48
48
|
},
|
|
49
49
|
"@openai/apps-sdk-ui/components/Input": {
|
|
50
50
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/components/Input/index.js",
|
|
51
51
|
"file": "@openai_apps-sdk-ui_components_Input.js",
|
|
52
|
-
"fileHash": "
|
|
52
|
+
"fileHash": "66aa5c5a",
|
|
53
53
|
"needsInterop": false
|
|
54
54
|
},
|
|
55
55
|
"@openai/apps-sdk-ui/components/SegmentedControl": {
|
|
56
56
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/components/SegmentedControl/index.js",
|
|
57
57
|
"file": "@openai_apps-sdk-ui_components_SegmentedControl.js",
|
|
58
|
-
"fileHash": "
|
|
58
|
+
"fileHash": "97a2aa5f",
|
|
59
59
|
"needsInterop": false
|
|
60
60
|
},
|
|
61
61
|
"@openai/apps-sdk-ui/components/Select": {
|
|
62
62
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/components/Select/index.js",
|
|
63
63
|
"file": "@openai_apps-sdk-ui_components_Select.js",
|
|
64
|
-
"fileHash": "
|
|
64
|
+
"fileHash": "be97a54f",
|
|
65
65
|
"needsInterop": false
|
|
66
66
|
},
|
|
67
67
|
"@openai/apps-sdk-ui/components/Textarea": {
|
|
68
68
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/components/Textarea/index.js",
|
|
69
69
|
"file": "@openai_apps-sdk-ui_components_Textarea.js",
|
|
70
|
-
"fileHash": "
|
|
70
|
+
"fileHash": "cb2abaca",
|
|
71
71
|
"needsInterop": false
|
|
72
72
|
},
|
|
73
73
|
"@openai/apps-sdk-ui/theme": {
|
|
74
74
|
"src": "../../../../node_modules/.pnpm/@openai+apps-sdk-ui@0.2.0_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@19._60630c8dcc43ec213b3e346c9e26579b/node_modules/@openai/apps-sdk-ui/dist/es/lib/theme.js",
|
|
75
75
|
"file": "@openai_apps-sdk-ui_theme.js",
|
|
76
|
-
"fileHash": "
|
|
76
|
+
"fileHash": "446e068b",
|
|
77
77
|
"needsInterop": false
|
|
78
78
|
},
|
|
79
79
|
"clsx": {
|
|
80
80
|
"src": "../../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs",
|
|
81
81
|
"file": "clsx.js",
|
|
82
|
-
"fileHash": "
|
|
82
|
+
"fileHash": "07860a30",
|
|
83
83
|
"needsInterop": false
|
|
84
84
|
},
|
|
85
85
|
"embla-carousel-react": {
|
|
86
86
|
"src": "../../../../node_modules/.pnpm/embla-carousel-react@8.6.0_react@19.2.0/node_modules/embla-carousel-react/esm/embla-carousel-react.esm.js",
|
|
87
87
|
"file": "embla-carousel-react.js",
|
|
88
|
-
"fileHash": "
|
|
88
|
+
"fileHash": "361056e7",
|
|
89
89
|
"needsInterop": false
|
|
90
90
|
},
|
|
91
91
|
"embla-carousel-wheel-gestures": {
|
|
92
92
|
"src": "../../../../node_modules/.pnpm/embla-carousel-wheel-gestures@8.1.0_embla-carousel@8.6.0/node_modules/embla-carousel-wheel-gestures/dist/embla-carousel-wheel-gestures.esm.js",
|
|
93
93
|
"file": "embla-carousel-wheel-gestures.js",
|
|
94
|
-
"fileHash": "
|
|
94
|
+
"fileHash": "a42e64fa",
|
|
95
95
|
"needsInterop": false
|
|
96
96
|
},
|
|
97
97
|
"react-dom/client": {
|
|
98
98
|
"src": "../../../../node_modules/.pnpm/react-dom@19.2.0_react@19.2.0/node_modules/react-dom/client.js",
|
|
99
99
|
"file": "react-dom_client.js",
|
|
100
|
-
"fileHash": "
|
|
100
|
+
"fileHash": "39ff4ecc",
|
|
101
101
|
"needsInterop": true
|
|
102
102
|
},
|
|
103
103
|
"tailwind-merge": {
|
|
104
104
|
"src": "../../../../node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.mjs",
|
|
105
105
|
"file": "tailwind-merge.js",
|
|
106
|
-
"fileHash": "
|
|
106
|
+
"fileHash": "01cd3e08",
|
|
107
107
|
"needsInterop": false
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
"chunks": {
|
|
111
|
-
"chunk-
|
|
112
|
-
"file": "chunk-
|
|
113
|
-
},
|
|
114
|
-
"chunk-675LFNY2": {
|
|
115
|
-
"file": "chunk-675LFNY2.js"
|
|
111
|
+
"chunk-EVJ3DVH5": {
|
|
112
|
+
"file": "chunk-EVJ3DVH5.js"
|
|
116
113
|
},
|
|
117
|
-
"chunk-
|
|
118
|
-
"file": "chunk-
|
|
114
|
+
"chunk-YOJ6QPGS": {
|
|
115
|
+
"file": "chunk-YOJ6QPGS.js"
|
|
119
116
|
},
|
|
120
|
-
"chunk-
|
|
121
|
-
"file": "chunk-
|
|
117
|
+
"chunk-BAG6OO6S": {
|
|
118
|
+
"file": "chunk-BAG6OO6S.js"
|
|
122
119
|
},
|
|
123
120
|
"chunk-SGWD4VEU": {
|
|
124
121
|
"file": "chunk-SGWD4VEU.js"
|
|
@@ -126,11 +123,14 @@
|
|
|
126
123
|
"chunk-KFGKZMLK": {
|
|
127
124
|
"file": "chunk-KFGKZMLK.js"
|
|
128
125
|
},
|
|
129
|
-
"chunk-
|
|
130
|
-
"file": "chunk-
|
|
126
|
+
"chunk-XB525PXG": {
|
|
127
|
+
"file": "chunk-XB525PXG.js"
|
|
131
128
|
},
|
|
132
|
-
"chunk-
|
|
133
|
-
"file": "chunk-
|
|
129
|
+
"chunk-CQ3GYAYB": {
|
|
130
|
+
"file": "chunk-CQ3GYAYB.js"
|
|
131
|
+
},
|
|
132
|
+
"chunk-QPJAV452": {
|
|
133
|
+
"file": "chunk-QPJAV452.js"
|
|
134
134
|
},
|
|
135
135
|
"chunk-EGRHWZRV": {
|
|
136
136
|
"file": "chunk-EGRHWZRV.js"
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
o
|
|
3
|
-
} from "./chunk-QPJAV452.js";
|
|
4
|
-
import {
|
|
5
|
-
Check_default,
|
|
6
|
-
Copy_default
|
|
7
|
-
} from "./chunk-XB525PXG.js";
|
|
8
1
|
import {
|
|
9
2
|
useTimeout
|
|
10
3
|
} from "./chunk-YOJ6QPGS.js";
|
|
@@ -19,6 +12,13 @@ import {
|
|
|
19
12
|
toTransformProperty,
|
|
20
13
|
waitForAnimationFrame
|
|
21
14
|
} from "./chunk-BAG6OO6S.js";
|
|
15
|
+
import {
|
|
16
|
+
Check_default,
|
|
17
|
+
Copy_default
|
|
18
|
+
} from "./chunk-XB525PXG.js";
|
|
19
|
+
import {
|
|
20
|
+
o
|
|
21
|
+
} from "./chunk-QPJAV452.js";
|
|
22
22
|
import {
|
|
23
23
|
clsx_default
|
|
24
24
|
} from "./chunk-CNYJBM5F.js";
|
|
@@ -625,4 +625,4 @@ export {
|
|
|
625
625
|
ButtonLink,
|
|
626
626
|
CopyButton
|
|
627
627
|
};
|
|
628
|
-
//# sourceMappingURL=chunk-
|
|
628
|
+
//# sourceMappingURL=chunk-EVJ3DVH5.js.map
|
package/template/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"4.0.13","results":[[":src/components/
|
|
1
|
+
{"version":"4.0.13","results":[[":src/components/album/albums.test.tsx",{"duration":370.11665100000005,"failed":false}],[":src/components/resources/counter-resource.test.tsx",{"duration":311.2705619999997,"failed":false}],[":src/components/resources/carousel-resource.test.tsx",{"duration":257.46737600000006,"failed":false}],[":src/components/album/fullscreen-viewer.test.tsx",{"duration":232.20117000000027,"failed":false}],[":src/components/carousel/carousel.test.tsx",{"duration":86.45302899999979,"failed":false}],[":src/components/resources/albums-resource.test.tsx",{"duration":273.0157959999999,"failed":false}],[":src/components/album/film-strip.test.tsx",{"duration":458.94508499999984,"failed":false}],[":src/components/album/album-card.test.tsx",{"duration":290.17094999999995,"failed":false}],[":src/components/card/card.test.tsx",{"duration":54.771146000000044,"failed":false}]]}
|
|
@@ -91,8 +91,13 @@ describe('Albums', () => {
|
|
|
91
91
|
const firstAlbum = screen.getByText('Summer Vacation').closest('button')!;
|
|
92
92
|
fireEvent.click(firstAlbum);
|
|
93
93
|
|
|
94
|
-
// Should update widget state with selected album ID
|
|
95
|
-
expect(mockSetWidgetState).
|
|
94
|
+
// Should update widget state with selected album ID using function updater
|
|
95
|
+
expect(mockSetWidgetState).toHaveBeenCalledTimes(1);
|
|
96
|
+
const updateFn = mockSetWidgetState.mock.calls[0][0];
|
|
97
|
+
expect(typeof updateFn).toBe('function');
|
|
98
|
+
// Test the updater function
|
|
99
|
+
const result = updateFn({ currentIndex: 0 });
|
|
100
|
+
expect(result).toEqual({ currentIndex: 0, selectedAlbumId: 'album-1' });
|
|
96
101
|
|
|
97
102
|
// Should request fullscreen mode
|
|
98
103
|
expect(mockRequestDisplayMode).toHaveBeenCalledWith({ mode: 'fullscreen' });
|
|
@@ -48,7 +48,7 @@ export const Albums = React.forwardRef<HTMLDivElement, AlbumsProps>(({ className
|
|
|
48
48
|
|
|
49
49
|
const handleSelectAlbum = React.useCallback(
|
|
50
50
|
(album: Album) => {
|
|
51
|
-
setWidgetState({ selectedAlbumId: album.id });
|
|
51
|
+
setWidgetState((prev) => ({ ...prev, selectedAlbumId: album.id }));
|
|
52
52
|
api?.requestDisplayMode?.({ mode: 'fullscreen' });
|
|
53
53
|
},
|
|
54
54
|
[setWidgetState, api]
|
|
@@ -4,11 +4,9 @@ import { FullscreenViewer } from './fullscreen-viewer';
|
|
|
4
4
|
import type { Album } from './albums';
|
|
5
5
|
|
|
6
6
|
// Mock sunpeak hooks
|
|
7
|
-
let mockMaxHeight = 800;
|
|
8
7
|
let mockSafeArea = { insets: { top: 0, bottom: 0, left: 0, right: 0 } };
|
|
9
8
|
|
|
10
9
|
vi.mock('sunpeak', () => ({
|
|
11
|
-
useMaxHeight: () => mockMaxHeight,
|
|
12
10
|
useSafeArea: () => mockSafeArea,
|
|
13
11
|
}));
|
|
14
12
|
|
|
@@ -27,8 +25,8 @@ describe('FullscreenViewer', () => {
|
|
|
27
25
|
it('resets to first photo when album changes', () => {
|
|
28
26
|
const { rerender, container } = render(<FullscreenViewer album={mockAlbum} />);
|
|
29
27
|
|
|
30
|
-
// Get the main photo area
|
|
31
|
-
const mainPhotoArea = container.querySelector('.flex-1
|
|
28
|
+
// Get the main photo area
|
|
29
|
+
const mainPhotoArea = container.querySelector('.flex-1');
|
|
32
30
|
let mainPhoto = mainPhotoArea?.querySelector('img');
|
|
33
31
|
expect(mainPhoto).toHaveAttribute('alt', 'First Photo');
|
|
34
32
|
expect(mainPhoto).toHaveAttribute('src', 'https://example.com/1.jpg');
|
|
@@ -56,8 +54,8 @@ describe('FullscreenViewer', () => {
|
|
|
56
54
|
it('displays correct photo based on selected index from FilmStrip', () => {
|
|
57
55
|
const { container } = render(<FullscreenViewer album={mockAlbum} />);
|
|
58
56
|
|
|
59
|
-
// Get the main photo
|
|
60
|
-
const mainPhotoArea = container.querySelector('.flex-1
|
|
57
|
+
// Get the main photo
|
|
58
|
+
const mainPhotoArea = container.querySelector('.flex-1');
|
|
61
59
|
const firstPhoto = mainPhotoArea?.querySelector('img');
|
|
62
60
|
|
|
63
61
|
expect(firstPhoto).toHaveAttribute('alt', 'First Photo');
|
|
@@ -79,28 +77,18 @@ describe('FullscreenViewer', () => {
|
|
|
79
77
|
expect(images.length).toBe(0);
|
|
80
78
|
});
|
|
81
79
|
|
|
82
|
-
it('respects safe area insets
|
|
80
|
+
it('respects safe area insets', () => {
|
|
83
81
|
mockSafeArea = { insets: { top: 20, bottom: 30, left: 10, right: 15 } };
|
|
84
82
|
|
|
85
83
|
const { container } = render(<FullscreenViewer album={mockAlbum} />);
|
|
86
84
|
|
|
87
|
-
//
|
|
88
|
-
const
|
|
89
|
-
expect(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
it('respects maxHeight constraint', () => {
|
|
96
|
-
mockMaxHeight = 600;
|
|
97
|
-
|
|
98
|
-
const { container } = render(<FullscreenViewer album={mockAlbum} />);
|
|
99
|
-
|
|
100
|
-
const mainDiv = container.firstChild as HTMLElement;
|
|
101
|
-
expect(mainDiv).toHaveStyle({
|
|
102
|
-
maxHeight: '600px',
|
|
103
|
-
height: '600px',
|
|
85
|
+
// Check root div has safe area padding
|
|
86
|
+
const rootDiv = container.firstChild as HTMLElement;
|
|
87
|
+
expect(rootDiv).toHaveStyle({
|
|
88
|
+
paddingTop: '20px',
|
|
89
|
+
paddingBottom: '30px',
|
|
90
|
+
paddingLeft: '10px',
|
|
91
|
+
paddingRight: '15px',
|
|
104
92
|
});
|
|
105
93
|
});
|
|
106
94
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useSafeArea } from 'sunpeak';
|
|
3
3
|
import { cn } from '../../lib/index';
|
|
4
4
|
import { FilmStrip } from './film-strip';
|
|
5
5
|
import type { Album } from './albums';
|
|
@@ -11,56 +11,77 @@ export type FullscreenViewerProps = {
|
|
|
11
11
|
|
|
12
12
|
export const FullscreenViewer = React.forwardRef<HTMLDivElement, FullscreenViewerProps>(
|
|
13
13
|
({ album, className }, ref) => {
|
|
14
|
-
const maxHeight = useMaxHeight();
|
|
15
14
|
const safeArea = useSafeArea();
|
|
16
15
|
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
|
16
|
+
const [width, setWidth] = React.useState(0);
|
|
17
|
+
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
17
18
|
|
|
18
19
|
React.useEffect(() => {
|
|
19
20
|
setSelectedIndex(0);
|
|
20
21
|
}, [album?.id]);
|
|
21
22
|
|
|
23
|
+
// Measure component width to determine mobile vs desktop layout
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
const element = containerRef.current;
|
|
26
|
+
if (!element) return;
|
|
27
|
+
|
|
28
|
+
const updateWidth = () => {
|
|
29
|
+
setWidth(element.getBoundingClientRect().width);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
updateWidth();
|
|
33
|
+
|
|
34
|
+
const resizeObserver = new ResizeObserver(updateWidth);
|
|
35
|
+
resizeObserver.observe(element);
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
resizeObserver.disconnect();
|
|
39
|
+
};
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
// Combine refs
|
|
43
|
+
React.useImperativeHandle(ref, () => containerRef.current!);
|
|
44
|
+
|
|
22
45
|
const selectedPhoto = album?.photos?.[selectedIndex];
|
|
46
|
+
const isMobile = width > 0 && width < 768;
|
|
23
47
|
|
|
24
48
|
return (
|
|
25
49
|
<div
|
|
26
|
-
ref={
|
|
27
|
-
className={cn('
|
|
50
|
+
ref={containerRef}
|
|
51
|
+
className={cn('flex w-full bg-surface', isMobile ? 'flex-col' : 'flex-row', className)}
|
|
28
52
|
style={{
|
|
29
|
-
|
|
30
|
-
|
|
53
|
+
paddingTop: `${safeArea?.insets.top ?? 0}px`,
|
|
54
|
+
paddingBottom: `${safeArea?.insets.bottom ?? 0}px`,
|
|
55
|
+
paddingLeft: `${safeArea?.insets.left ?? 0}px`,
|
|
56
|
+
paddingRight: `${safeArea?.insets.right ?? 0}px`,
|
|
31
57
|
}}
|
|
32
58
|
>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<div
|
|
36
|
-
className="
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
>
|
|
41
|
-
<FilmStrip album={album} selectedIndex={selectedIndex} onSelect={setSelectedIndex} />
|
|
59
|
+
{/* Album header - mobile only */}
|
|
60
|
+
{isMobile && (
|
|
61
|
+
<div className="border-b border-subtle bg-surface/95 backdrop-blur-sm px-4 py-3">
|
|
62
|
+
<h2 className="text-base font-semibold text-primary">{album.title}</h2>
|
|
63
|
+
<p className="text-sm text-secondary">
|
|
64
|
+
{selectedIndex + 1} / {album.photos.length}
|
|
65
|
+
</p>
|
|
42
66
|
</div>
|
|
67
|
+
)}
|
|
43
68
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
paddingTop: `calc(2.5rem + ${safeArea?.insets.top ?? 0}px)`,
|
|
49
|
-
paddingBottom: `calc(2.5rem + ${safeArea?.insets.bottom ?? 0}px)`,
|
|
50
|
-
paddingLeft: `calc(10rem + ${safeArea?.insets.left ?? 0}px)`,
|
|
51
|
-
paddingRight: `calc(10rem + ${safeArea?.insets.right ?? 0}px)`,
|
|
52
|
-
}}
|
|
53
|
-
>
|
|
54
|
-
<div className="relative w-full h-full">
|
|
55
|
-
{selectedPhoto ? (
|
|
56
|
-
<img
|
|
57
|
-
src={selectedPhoto.url}
|
|
58
|
-
alt={selectedPhoto.title || album.title}
|
|
59
|
-
className="absolute inset-0 m-auto rounded-3xl shadow-sm border border-primary/10 max-w-full max-h-full object-contain"
|
|
60
|
-
/>
|
|
61
|
-
) : null}
|
|
62
|
-
</div>
|
|
69
|
+
{/* Film strip - desktop only */}
|
|
70
|
+
{!isMobile && (
|
|
71
|
+
<div className="w-40 flex-shrink-0">
|
|
72
|
+
<FilmStrip album={album} selectedIndex={selectedIndex} onSelect={setSelectedIndex} />
|
|
63
73
|
</div>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
{/* Main photo */}
|
|
77
|
+
<div className="flex-1 flex items-center justify-center p-4 md:p-10">
|
|
78
|
+
{selectedPhoto ? (
|
|
79
|
+
<img
|
|
80
|
+
src={selectedPhoto.url}
|
|
81
|
+
alt={selectedPhoto.title || album.title}
|
|
82
|
+
className="rounded-3xl shadow-sm border border-primary/10 max-w-full max-h-full object-contain"
|
|
83
|
+
/>
|
|
84
|
+
) : null}
|
|
64
85
|
</div>
|
|
65
86
|
</div>
|
|
66
87
|
);
|
|
@@ -57,7 +57,7 @@ export const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
|
|
|
57
57
|
|
|
58
58
|
const currentIndex = emblaApi.selectedScrollSnap();
|
|
59
59
|
if (widgetState?.currentIndex !== currentIndex) {
|
|
60
|
-
setWidgetState({ currentIndex });
|
|
60
|
+
setWidgetState((prev) => ({ ...prev, currentIndex }));
|
|
61
61
|
}
|
|
62
62
|
}, [emblaApi, widgetState?.currentIndex, setWidgetState]);
|
|
63
63
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useWidgetState, useSafeArea, useMaxHeight, useUserAgent } from 'sunpeak';
|
|
2
2
|
import { Button } from '@openai/apps-sdk-ui/components/Button';
|
|
3
|
+
import { useEffect } from 'react';
|
|
3
4
|
|
|
4
5
|
interface CounterState extends Record<string, unknown> {
|
|
5
6
|
count?: number;
|
|
@@ -22,6 +23,13 @@ export function CounterResource() {
|
|
|
22
23
|
const count = widgetState?.count ?? 0;
|
|
23
24
|
const hasTouch = userAgent?.capabilities.touch ?? false;
|
|
24
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
|
+
|
|
25
33
|
const increment = () => {
|
|
26
34
|
setWidgetState({ count: count + 1 });
|
|
27
35
|
};
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* This file contains only metadata and doesn't import React components or CSS.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { defaultWidgetMeta } from './widget-config';
|
|
7
|
+
|
|
6
8
|
const albumsData = {
|
|
7
9
|
albums: [
|
|
8
10
|
{
|
|
@@ -144,7 +146,9 @@ export const albumsSimulation = {
|
|
|
144
146
|
title: 'Albums',
|
|
145
147
|
description: 'Show photo albums widget markup',
|
|
146
148
|
mimeType: 'text/html+skybridge',
|
|
147
|
-
_meta: {
|
|
149
|
+
_meta: {
|
|
150
|
+
...defaultWidgetMeta,
|
|
151
|
+
},
|
|
148
152
|
},
|
|
149
153
|
|
|
150
154
|
// MCP CallTool protocol - data for CallTool response
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* This file contains only metadata and doesn't import React components or CSS.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { defaultWidgetMeta } from './widget-config';
|
|
7
|
+
|
|
6
8
|
const placesData = {
|
|
7
9
|
places: [
|
|
8
10
|
{
|
|
@@ -81,7 +83,9 @@ export const carouselSimulation = {
|
|
|
81
83
|
title: 'Carousel',
|
|
82
84
|
description: 'Show popular places to visit widget markup',
|
|
83
85
|
mimeType: 'text/html+skybridge',
|
|
84
|
-
_meta: {
|
|
86
|
+
_meta: {
|
|
87
|
+
...defaultWidgetMeta,
|
|
88
|
+
},
|
|
85
89
|
},
|
|
86
90
|
|
|
87
91
|
// MCP CallTool protocol - data for CallTool response
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* Server-safe configuration for the counter simulation.
|
|
3
3
|
* This file contains only metadata and doesn't import React components or CSS.
|
|
4
4
|
*/
|
|
5
|
+
|
|
6
|
+
import { defaultWidgetMeta } from './widget-config';
|
|
7
|
+
|
|
5
8
|
export const counterSimulation = {
|
|
6
9
|
userMessage: 'Help me count something',
|
|
7
10
|
|
|
@@ -30,7 +33,9 @@ export const counterSimulation = {
|
|
|
30
33
|
title: 'Counter',
|
|
31
34
|
description: 'Show a simple counter tool widget markup',
|
|
32
35
|
mimeType: 'text/html+skybridge',
|
|
33
|
-
_meta: {
|
|
36
|
+
_meta: {
|
|
37
|
+
...defaultWidgetMeta,
|
|
38
|
+
},
|
|
34
39
|
},
|
|
35
40
|
|
|
36
41
|
// MCP CallTool protocol - data for CallTool response
|