sunpeak 0.16.3 → 0.16.5

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.
Files changed (35) hide show
  1. package/README.md +3 -1
  2. package/bin/commands/build.mjs +1 -0
  3. package/bin/commands/dev.mjs +2 -0
  4. package/dist/chatgpt/globals.css +0 -8
  5. package/dist/mcp/index.cjs +61 -48
  6. package/dist/mcp/index.cjs.map +1 -1
  7. package/dist/mcp/index.js +61 -48
  8. package/dist/mcp/index.js.map +1 -1
  9. package/dist/style.css +0 -8
  10. package/package.json +1 -1
  11. package/template/package.json +0 -3
  12. package/template/src/components/avatar.tsx +1 -1
  13. package/template/src/resources/albums/components/album-card.tsx +2 -2
  14. package/template/src/resources/albums/components/album-carousel.tsx +3 -3
  15. package/template/src/resources/albums/components/film-strip.tsx +2 -2
  16. package/template/src/resources/albums/components/fullscreen-viewer.tsx +1 -1
  17. package/template/src/resources/carousel/components/card.tsx +2 -2
  18. package/template/src/resources/carousel/components/carousel.tsx +3 -3
  19. package/template/src/resources/map/components/map-view.tsx +1 -1
  20. package/template/src/resources/map/components/map.tsx +3 -3
  21. package/template/src/resources/map/components/place-card.tsx +2 -2
  22. package/template/src/resources/map/components/place-carousel.tsx +1 -1
  23. package/template/src/resources/map/components/place-inspector.tsx +4 -4
  24. package/template/src/resources/map/components/place-list.tsx +2 -2
  25. package/template/src/resources/review/review.tsx +59 -29
  26. package/template/src/tools/review-diff.ts +3 -1
  27. package/template/src/tools/review-post.ts +3 -1
  28. package/template/src/tools/review-purchase.ts +3 -1
  29. package/template/src/tools/show-albums.ts +3 -1
  30. package/template/src/tools/show-carousel.ts +3 -1
  31. package/template/src/tools/show-map.ts +3 -1
  32. package/template/tsconfig.json +7 -2
  33. package/template/vitest.config.ts +1 -0
  34. package/template/node_modules/.bin/nodemon +0 -21
  35. package/template/node_modules/.bin/tsx +0 -21
package/dist/style.css CHANGED
@@ -1283,10 +1283,6 @@
1283
1283
  background-color: #0000;
1284
1284
  }
1285
1285
 
1286
- .bg-white {
1287
- background-color: var(--color-white);
1288
- }
1289
-
1290
1286
  .bg-gradient-to-l {
1291
1287
  --tw-gradient-position: to left in oklab;
1292
1288
  background-image: linear-gradient(var(--tw-gradient-stops));
@@ -1587,10 +1583,6 @@
1587
1583
  white-space: pre-wrap;
1588
1584
  }
1589
1585
 
1590
- .text-\[\#000000\] {
1591
- color: #000;
1592
- }
1593
-
1594
1586
  .text-\[var\(--color-text-danger\)\] {
1595
1587
  color: var(--color-text-danger);
1596
1588
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sunpeak",
3
- "version": "0.16.3",
3
+ "version": "0.16.5",
4
4
  "description": "Local-first MCP Apps framework. Quickstart, build, test, and ship your Claude or ChatGPT App!",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -30,12 +30,9 @@
30
30
  "@types/react-dom": "^19.0.0",
31
31
  "@vitejs/plugin-react": "^4.5.2",
32
32
  "jsdom": "^27.3.0",
33
- "nodemon": "^3.1.11",
34
- "postcss": "^8.5.6",
35
33
  "react": "^19.0.0",
36
34
  "react-dom": "^19.0.0",
37
35
  "tailwindcss": "^4.1.18",
38
- "tsx": "^4.21.0",
39
36
  "typescript": "^5.9.3",
40
37
  "vite": "^5.4.21",
41
38
  "vitest": "^4.0.12"
@@ -19,7 +19,7 @@ export function Avatar({ imageUrl, name, size = 32, className }: AvatarProps) {
19
19
  {imageUrl && !imgError ? (
20
20
  <img
21
21
  src={imageUrl}
22
- alt={name ?? ''}
22
+ alt={name ?? 'Avatar'}
23
23
  className="w-full h-full object-cover"
24
24
  onError={() => setImgError(true)}
25
25
  />
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { Button } from '../../../components/button';
3
- import { cn } from '../../../lib/index';
2
+ import { Button } from '@/components/button';
3
+ import { cn } from '@/lib/index';
4
4
  import type { Album } from './albums';
5
5
 
6
6
  export type AlbumCardProps = {
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import useEmblaCarousel from 'embla-carousel-react';
3
3
  import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures';
4
- import { ArrowLeft, ArrowRight } from '../../../components/icon';
5
- import { Button } from '../../../components/button';
6
- import { cn } from '../../../lib/index';
4
+ import { ArrowLeft, ArrowRight } from '@/components/icon';
5
+ import { Button } from '@/components/button';
6
+ import { cn } from '@/lib/index';
7
7
 
8
8
  export type AlbumCarouselProps = {
9
9
  children?: React.ReactNode;
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { Button } from '../../../components/button';
3
- import { cn } from '../../../lib/index';
2
+ import { Button } from '@/components/button';
3
+ import { cn } from '@/lib/index';
4
4
  import type { Album } from './albums';
5
5
 
6
6
  export type FilmStripProps = {
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { SafeArea } from 'sunpeak';
3
- import { cn } from '../../../lib/index';
3
+ import { cn } from '@/lib/index';
4
4
  import { FilmStrip } from './film-strip';
5
5
  import type { Album } from './albums';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { Button } from '../../../components/button';
3
- import { cn } from '../../../lib/index';
2
+ import { Button } from '@/components/button';
3
+ import { cn } from '@/lib/index';
4
4
 
5
5
  export interface CardButtonProps {
6
6
  isPrimary?: boolean;
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import useEmblaCarousel from 'embla-carousel-react';
3
3
  import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures';
4
- import { ArrowLeft, ArrowRight } from '../../../components/icon';
5
- import { Button } from '../../../components/button';
6
- import { cn } from '../../../lib/index';
4
+ import { ArrowLeft, ArrowRight } from '@/components/icon';
5
+ import { Button } from '@/components/button';
6
+ import { cn } from '@/lib/index';
7
7
 
8
8
  export type CarouselProps = {
9
9
  children?: React.ReactNode;
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import mapboxgl from 'mapbox-gl';
3
3
  import 'mapbox-gl/dist/mapbox-gl.css';
4
- import { cn } from '../../../lib/index';
4
+ import { cn } from '@/lib/index';
5
5
  import type { Place } from './types';
6
6
 
7
7
  // Public Mapbox token for demo purposes
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { useApp, useAppState, useDisplayMode, useToolData, useViewport } from 'sunpeak';
3
- import { Button } from '../../../components/button';
4
- import { ExpandLg } from '../../../components/icon';
5
- import { cn } from '../../../lib/index';
3
+ import { Button } from '@/components/button';
4
+ import { ExpandLg } from '@/components/icon';
5
+ import { cn } from '@/lib/index';
6
6
  import { PlaceList } from './place-list';
7
7
  import { PlaceCarousel } from './place-carousel';
8
8
  import { PlaceInspector } from './place-inspector';
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { Star } from '../../../components/icon';
3
- import { cn } from '../../../lib/index';
2
+ import { Star } from '@/components/icon';
3
+ import { cn } from '@/lib/index';
4
4
  import type { Place } from './types';
5
5
 
6
6
  export type PlaceCardProps = {
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import useEmblaCarousel from 'embla-carousel-react';
3
- import { cn } from '../../../lib/index';
3
+ import { cn } from '@/lib/index';
4
4
  import { PlaceCard } from './place-card';
5
5
  import type { Place } from './types';
6
6
 
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react';
2
- import { Button } from '../../../components/button';
3
- import { Avatar } from '../../../components/avatar';
4
- import { X, Star } from '../../../components/icon';
5
- import { cn } from '../../../lib/index';
2
+ import { Button } from '@/components/button';
3
+ import { Avatar } from '@/components/avatar';
4
+ import { X, Star } from '@/components/icon';
5
+ import { cn } from '@/lib/index';
6
6
  import type { Place } from './types';
7
7
 
8
8
  export type PlaceInspectorProps = {
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { Settings } from '../../../components/icon';
3
- import { cn } from '../../../lib/index';
2
+ import { Settings } from '@/components/icon';
3
+ import { cn } from '@/lib/index';
4
4
  import { PlaceCard } from './place-card';
5
5
  import type { Place } from './types';
6
6
 
@@ -7,8 +7,8 @@ import {
7
7
  SafeArea,
8
8
  } from 'sunpeak';
9
9
  import type { ResourceConfig } from 'sunpeak';
10
- import { Button } from '../../components/button';
11
- import { ExpandLg } from '../../components/icon';
10
+ import { Button } from '@/components/button';
11
+ import { ExpandLg } from '@/components/icon';
12
12
 
13
13
  export const resource: ResourceConfig = {
14
14
  title: 'Review',
@@ -77,15 +77,14 @@ interface Alert {
77
77
  message: string;
78
78
  }
79
79
 
80
- /** Content section - supports multiple display types */
81
- interface Section {
82
- /** Optional section title */
83
- title?: string;
84
- /** Section content type */
85
- type: 'details' | 'items' | 'changes' | 'preview' | 'summary';
86
- /** Content data (type depends on section type) */
87
- content: Detail[] | Item[] | Change[] | string;
88
- }
80
+ /** Content section - discriminated union ensures type-safe content access */
81
+ type Section = { title?: string } & (
82
+ | { type: 'details'; content: Detail[] }
83
+ | { type: 'items'; content: Item[] }
84
+ | { type: 'changes'; content: Change[] }
85
+ | { type: 'preview'; content: string }
86
+ | { type: 'summary'; content: Detail[] }
87
+ );
89
88
 
90
89
  /** Tool call configuration for domain-specific review actions */
91
90
  interface ReviewTool {
@@ -128,17 +127,45 @@ interface ReviewState {
128
127
  // ============================================================================
129
128
 
130
129
  const changeTypeConfig = {
131
- create: { icon: '+', color: '#16a34a', bg: '#f0fdf4' },
132
- modify: { icon: '~', color: '#ca8a04', bg: '#fefce8' },
133
- delete: { icon: '\u2212', color: '#dc2626', bg: '#fef2f2' },
134
- action: { icon: '\u2192', color: '#2563eb', bg: '#eff6ff' },
130
+ create: { icon: '+', color: 'light-dark(#16a34a, #4ade80)', bg: 'light-dark(#f0fdf4, #052e16)' },
131
+ modify: { icon: '~', color: 'light-dark(#ca8a04, #facc15)', bg: 'light-dark(#fefce8, #422006)' },
132
+ delete: {
133
+ icon: '\u2212',
134
+ color: 'light-dark(#dc2626, #f87171)',
135
+ bg: 'light-dark(#fef2f2, #450a0a)',
136
+ },
137
+ action: {
138
+ icon: '\u2192',
139
+ color: 'light-dark(#2563eb, #60a5fa)',
140
+ bg: 'light-dark(#eff6ff, #172554)',
141
+ },
135
142
  };
136
143
 
137
144
  const alertTypeConfig = {
138
- info: { icon: '\u2139', bg: '#eff6ff', border: '#bfdbfe', text: '#1e40af' },
139
- warning: { icon: '\u26A0', bg: '#fefce8', border: '#fde047', text: '#a16207' },
140
- error: { icon: '\u2715', bg: '#fef2f2', border: '#fecaca', text: '#b91c1c' },
141
- success: { icon: '\u2713', bg: '#f0fdf4', border: '#bbf7d0', text: '#15803d' },
145
+ info: {
146
+ icon: '\u2139',
147
+ bg: 'light-dark(#eff6ff, #172554)',
148
+ border: 'light-dark(#bfdbfe, #1e3a5f)',
149
+ text: 'light-dark(#1e40af, #93c5fd)',
150
+ },
151
+ warning: {
152
+ icon: '\u26A0',
153
+ bg: 'light-dark(#fefce8, #422006)',
154
+ border: 'light-dark(#fde047, #854d0e)',
155
+ text: 'light-dark(#a16207, #fde047)',
156
+ },
157
+ error: {
158
+ icon: '\u2715',
159
+ bg: 'light-dark(#fef2f2, #450a0a)',
160
+ border: 'light-dark(#fecaca, #7f1d1d)',
161
+ text: 'light-dark(#b91c1c, #fca5a5)',
162
+ },
163
+ success: {
164
+ icon: '\u2713',
165
+ bg: 'light-dark(#f0fdf4, #052e16)',
166
+ border: 'light-dark(#bbf7d0, #14532d)',
167
+ text: 'light-dark(#15803d, #86efac)',
168
+ },
142
169
  };
143
170
 
144
171
  function DetailsSection({ content }: { content: Detail[] }) {
@@ -228,7 +255,7 @@ function ChangesSection({ content }: { content: Change[] }) {
228
255
  >
229
256
  <div className="flex items-start gap-3">
230
257
  <span
231
- className="flex-shrink-0 w-6 h-6 flex items-center justify-center rounded font-mono font-bold bg-white"
258
+ className="flex-shrink-0 w-6 h-6 flex items-center justify-center rounded font-mono font-bold bg-[var(--color-background-primary)]"
232
259
  style={{
233
260
  color: config.color,
234
261
  borderWidth: 1,
@@ -244,7 +271,7 @@ function ChangesSection({ content }: { content: Change[] }) {
244
271
  {change.path}
245
272
  </code>
246
273
  )}
247
- <p className="text-sm text-[#000000]">{change.description}</p>
274
+ <p className="text-sm text-[var(--color-text-primary)]">{change.description}</p>
248
275
  {change.details && (
249
276
  <p className="mt-1 text-xs text-[var(--color-text-secondary)]">
250
277
  {change.details}
@@ -299,17 +326,15 @@ function SectionRenderer({ section }: { section: Section }) {
299
326
  const renderContent = () => {
300
327
  switch (section.type) {
301
328
  case 'details':
302
- return <DetailsSection content={section.content as Detail[]} />;
329
+ return <DetailsSection content={section.content} />;
303
330
  case 'items':
304
- return <ItemsSection content={section.content as Item[]} />;
331
+ return <ItemsSection content={section.content} />;
305
332
  case 'changes':
306
- return <ChangesSection content={section.content as Change[]} />;
333
+ return <ChangesSection content={section.content} />;
307
334
  case 'preview':
308
- return <PreviewSection content={section.content as string} />;
335
+ return <PreviewSection content={section.content} />;
309
336
  case 'summary':
310
- return <SummarySection content={section.content as Detail[]} />;
311
- default:
312
- return null;
337
+ return <SummarySection content={section.content} />;
313
338
  }
314
339
  };
315
340
 
@@ -500,7 +525,12 @@ export function ReviewResource() {
500
525
  <div className="flex flex-col items-center gap-1">
501
526
  <div
502
527
  className="flex items-center justify-center gap-2"
503
- style={{ color: decision === 'accepted' ? '#16a34a' : '#dc2626' }}
528
+ style={{
529
+ color:
530
+ decision === 'accepted'
531
+ ? 'light-dark(#16a34a, #4ade80)'
532
+ : 'light-dark(#dc2626, #f87171)',
533
+ }}
504
534
  >
505
535
  <span className="text-lg">{decision === 'accepted' ? '\u2713' : '\u2717'}</span>
506
536
  <span className="font-medium">
@@ -19,6 +19,8 @@ export const schema = {
19
19
  runMigrations: z.boolean().describe('Whether to run database migrations as part of the change'),
20
20
  };
21
21
 
22
- export default async function (_args: Record<string, unknown>, _extra: ToolHandlerExtra) {
22
+ type Args = z.infer<z.ZodObject<typeof schema>>;
23
+
24
+ export default async function (_args: Args, _extra: ToolHandlerExtra) {
23
25
  return { structuredContent: { title: 'Review', sections: [] } };
24
26
  }
@@ -21,6 +21,8 @@ export const schema = {
21
21
  visibility: z.enum(['public', 'connections', 'private']).describe('Post visibility setting'),
22
22
  };
23
23
 
24
- export default async function (_args: Record<string, unknown>, _extra: ToolHandlerExtra) {
24
+ type Args = z.infer<z.ZodObject<typeof schema>>;
25
+
26
+ export default async function (_args: Args, _extra: ToolHandlerExtra) {
25
27
  return { structuredContent: { title: 'Review Your Post', sections: [] } };
26
28
  }
@@ -26,6 +26,8 @@ export const schema = {
26
26
  paymentMethodId: z.string().describe('ID of the saved payment method'),
27
27
  };
28
28
 
29
- export default async function (_args: Record<string, unknown>, _extra: ToolHandlerExtra) {
29
+ type Args = z.infer<z.ZodObject<typeof schema>>;
30
+
31
+ export default async function (_args: Args, _extra: ToolHandlerExtra) {
30
32
  return { structuredContent: { title: 'Confirm Your Order', sections: [] } };
31
33
  }
@@ -17,6 +17,8 @@ export const schema = {
17
17
  limit: z.number().describe('Maximum number of albums to return'),
18
18
  };
19
19
 
20
- export default async function (_args: Record<string, unknown>, _extra: ToolHandlerExtra) {
20
+ type Args = z.infer<z.ZodObject<typeof schema>>;
21
+
22
+ export default async function (_args: Args, _extra: ToolHandlerExtra) {
21
23
  return { structuredContent: { albums: [] } };
22
24
  }
@@ -20,6 +20,8 @@ export const schema = {
20
20
  limit: z.number().describe('Maximum number of places to return'),
21
21
  };
22
22
 
23
- export default async function (_args: Record<string, unknown>, _extra: ToolHandlerExtra) {
23
+ type Args = z.infer<z.ZodObject<typeof schema>>;
24
+
25
+ export default async function (_args: Args, _extra: ToolHandlerExtra) {
24
26
  return { structuredContent: { places: [] } };
25
27
  }
@@ -24,6 +24,8 @@ export const schema = {
24
24
  priceRange: z.array(z.enum(['$', '$$', '$$$', '$$$$'])).describe('Price range filter'),
25
25
  };
26
26
 
27
- export default async function (_args: Record<string, unknown>, _extra: ToolHandlerExtra) {
27
+ type Args = z.infer<z.ZodObject<typeof schema>>;
28
+
29
+ export default async function (_args: Args, _extra: ToolHandlerExtra) {
28
30
  return { structuredContent: { places: [] } };
29
31
  }
@@ -16,7 +16,12 @@
16
16
  "strict": true,
17
17
  "noUnusedLocals": true,
18
18
  "noUnusedParameters": true,
19
- "noFallthroughCasesInSwitch": true
19
+ "noFallthroughCasesInSwitch": true,
20
+
21
+ "baseUrl": ".",
22
+ "paths": {
23
+ "@/*": ["./src/*"]
24
+ }
20
25
  },
21
- "include": ["src", ".sunpeak"]
26
+ "include": ["src", "tests", ".sunpeak"]
22
27
  }
@@ -14,6 +14,7 @@ export default defineConfig({
14
14
  },
15
15
  resolve: {
16
16
  alias: {
17
+ '@': path.resolve(__dirname, 'src'),
17
18
  // In workspace dev mode, use local sunpeak source
18
19
  ...(isTemplate && {
19
20
  sunpeak: parentSrc,
@@ -1,21 +0,0 @@
1
- #!/bin/sh
2
- basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
-
4
- case `uname` in
5
- *CYGWIN*|*MINGW*|*MSYS*)
6
- if command -v cygpath > /dev/null 2>&1; then
7
- basedir=`cygpath -w "$basedir"`
8
- fi
9
- ;;
10
- esac
11
-
12
- if [ -z "$NODE_PATH" ]; then
13
- export NODE_PATH="/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/nodemon@3.1.11/node_modules/nodemon/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/nodemon@3.1.11/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/node_modules"
14
- else
15
- export NODE_PATH="/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/nodemon@3.1.11/node_modules/nodemon/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/nodemon@3.1.11/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/node_modules:$NODE_PATH"
16
- fi
17
- if [ -x "$basedir/node" ]; then
18
- exec "$basedir/node" "$basedir/../nodemon/bin/nodemon.js" "$@"
19
- else
20
- exec node "$basedir/../nodemon/bin/nodemon.js" "$@"
21
- fi
@@ -1,21 +0,0 @@
1
- #!/bin/sh
2
- basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
-
4
- case `uname` in
5
- *CYGWIN*|*MINGW*|*MSYS*)
6
- if command -v cygpath > /dev/null 2>&1; then
7
- basedir=`cygpath -w "$basedir"`
8
- fi
9
- ;;
10
- esac
11
-
12
- if [ -z "$NODE_PATH" ]; then
13
- export NODE_PATH="/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/tsx@4.21.0/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/node_modules"
14
- else
15
- export NODE_PATH="/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/tsx@4.21.0/node_modules:/home/runner/work/sunpeak/sunpeak/node_modules/.pnpm/node_modules:$NODE_PATH"
16
- fi
17
- if [ -x "$basedir/node" ]; then
18
- exec "$basedir/node" "$basedir/../tsx/dist/cli.mjs" "$@"
19
- else
20
- exec node "$basedir/../tsx/dist/cli.mjs" "$@"
21
- fi