nitrostack 1.0.55 → 1.0.57

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 (222) hide show
  1. package/dist/cli/commands/init.d.ts.map +1 -1
  2. package/dist/cli/commands/init.js +14 -9
  3. package/dist/cli/commands/init.js.map +1 -1
  4. package/dist/widgets/hooks/index.d.ts +7 -0
  5. package/dist/widgets/hooks/index.d.ts.map +1 -0
  6. package/dist/widgets/hooks/index.js +7 -0
  7. package/dist/widgets/hooks/index.js.map +1 -0
  8. package/dist/widgets/hooks/use-display-mode.d.ts +11 -0
  9. package/dist/widgets/hooks/use-display-mode.d.ts.map +1 -0
  10. package/dist/widgets/hooks/use-display-mode.js +13 -0
  11. package/dist/widgets/hooks/use-display-mode.js.map +1 -0
  12. package/dist/widgets/hooks/use-max-height.d.ts +10 -0
  13. package/dist/widgets/hooks/use-max-height.d.ts.map +1 -0
  14. package/dist/widgets/hooks/use-max-height.js +13 -0
  15. package/dist/widgets/hooks/use-max-height.js.map +1 -0
  16. package/dist/widgets/hooks/use-openai-global.d.ts +12 -0
  17. package/dist/widgets/hooks/use-openai-global.d.ts.map +1 -0
  18. package/dist/widgets/hooks/use-openai-global.js +32 -0
  19. package/dist/widgets/hooks/use-openai-global.js.map +1 -0
  20. package/dist/widgets/hooks/use-theme.d.ts +10 -0
  21. package/dist/widgets/hooks/use-theme.d.ts.map +1 -0
  22. package/dist/widgets/hooks/use-theme.js +12 -0
  23. package/dist/widgets/hooks/use-theme.js.map +1 -0
  24. package/dist/widgets/hooks/use-widget-state.d.ts +18 -0
  25. package/dist/widgets/hooks/use-widget-state.d.ts.map +1 -0
  26. package/dist/widgets/hooks/use-widget-state.js +27 -0
  27. package/dist/widgets/hooks/use-widget-state.js.map +1 -0
  28. package/dist/widgets/hooks/useWidgetSDK.d.ts +47 -0
  29. package/dist/widgets/hooks/useWidgetSDK.d.ts.map +1 -0
  30. package/dist/widgets/hooks/useWidgetSDK.js +67 -0
  31. package/dist/widgets/hooks/useWidgetSDK.js.map +1 -0
  32. package/dist/widgets/index.d.ts +7 -1
  33. package/dist/widgets/index.d.ts.map +1 -1
  34. package/dist/widgets/index.js +11 -1
  35. package/dist/widgets/index.js.map +1 -1
  36. package/dist/widgets/runtime/WidgetLayout.d.ts +32 -0
  37. package/dist/widgets/runtime/WidgetLayout.d.ts.map +1 -0
  38. package/dist/widgets/runtime/WidgetLayout.js +143 -0
  39. package/dist/widgets/runtime/WidgetLayout.js.map +1 -0
  40. package/dist/widgets/runtime/widget-polyfill.d.ts +1 -0
  41. package/dist/widgets/runtime/widget-polyfill.d.ts.map +1 -0
  42. package/dist/widgets/runtime/widget-polyfill.js +28 -0
  43. package/dist/widgets/runtime/widget-polyfill.js.map +1 -0
  44. package/dist/widgets/sdk.d.ts +109 -0
  45. package/dist/widgets/sdk.d.ts.map +1 -0
  46. package/dist/widgets/sdk.js +221 -0
  47. package/dist/widgets/sdk.js.map +1 -0
  48. package/dist/widgets/types.d.ts +89 -0
  49. package/dist/widgets/types.d.ts.map +1 -0
  50. package/dist/widgets/types.js +8 -0
  51. package/dist/widgets/types.js.map +1 -0
  52. package/dist/widgets/utils/media-queries.d.ts +34 -0
  53. package/dist/widgets/utils/media-queries.d.ts.map +1 -0
  54. package/dist/widgets/utils/media-queries.js +42 -0
  55. package/dist/widgets/utils/media-queries.js.map +1 -0
  56. package/package.json +2 -2
  57. package/src/studio/app/chat/page.tsx +274 -137
  58. package/src/studio/app/globals.css +140 -64
  59. package/src/studio/branding.md +807 -0
  60. package/src/studio/components/WidgetRenderer.tsx +222 -16
  61. package/src/studio/lib/llm-service.ts +39 -39
  62. package/templates/typescript-oauth/{env.example → .env.example} +4 -10
  63. package/templates/typescript-oauth/README.md +226 -306
  64. package/templates/typescript-oauth/package-lock.json +4253 -0
  65. package/templates/typescript-oauth/package.json +10 -5
  66. package/templates/typescript-oauth/src/app.module.ts +39 -36
  67. package/templates/typescript-oauth/src/guards/oauth.guard.ts +0 -1
  68. package/templates/typescript-oauth/src/index.ts +22 -30
  69. package/templates/typescript-oauth/src/modules/flights/booking.tools.ts +323 -0
  70. package/templates/typescript-oauth/src/modules/flights/flights.module.ts +14 -0
  71. package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +231 -0
  72. package/templates/typescript-oauth/src/modules/flights/flights.resources.ts +215 -0
  73. package/templates/typescript-oauth/src/modules/flights/flights.tools.ts +457 -0
  74. package/templates/typescript-oauth/src/services/duffel.service.ts +285 -0
  75. package/templates/typescript-oauth/src/widgets/app/airport-search/page.tsx +270 -0
  76. package/templates/typescript-oauth/src/widgets/app/flight-details/page.tsx +261 -0
  77. package/templates/typescript-oauth/src/widgets/app/flight-search-results/page.tsx +378 -0
  78. package/templates/typescript-oauth/src/widgets/app/globals.css +167 -0
  79. package/templates/typescript-oauth/src/widgets/app/layout.tsx +6 -2
  80. package/templates/typescript-oauth/src/widgets/app/order-cancellation/page.tsx +207 -0
  81. package/templates/typescript-oauth/src/widgets/app/order-summary/page.tsx +245 -0
  82. package/templates/typescript-oauth/src/widgets/app/payment-confirmation/page.tsx +152 -0
  83. package/templates/typescript-oauth/src/widgets/app/seat-selection/page.tsx +486 -0
  84. package/templates/typescript-oauth/src/widgets/next-env.d.ts +5 -0
  85. package/templates/typescript-oauth/src/widgets/package-lock.json +155 -126
  86. package/templates/typescript-oauth/src/widgets/widget-manifest.json +374 -27
  87. package/templates/typescript-pizzaz/IMPLEMENTATION.md +98 -0
  88. package/templates/typescript-pizzaz/README.md +233 -0
  89. package/templates/typescript-pizzaz/package.json +31 -0
  90. package/templates/typescript-pizzaz/src/app.module.ts +28 -0
  91. package/templates/typescript-pizzaz/src/index.ts +30 -0
  92. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.data.ts +106 -0
  93. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.module.ts +11 -0
  94. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.service.ts +60 -0
  95. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.tools.ts +197 -0
  96. package/templates/typescript-pizzaz/src/widgets/app/layout.tsx +18 -0
  97. package/templates/typescript-pizzaz/src/widgets/app/pizza-list/page.tsx +272 -0
  98. package/templates/typescript-pizzaz/src/widgets/app/pizza-map/page.tsx +216 -0
  99. package/templates/typescript-pizzaz/src/widgets/app/pizza-shop/page.tsx +374 -0
  100. package/templates/typescript-pizzaz/src/widgets/components/CompactShopCard.tsx +144 -0
  101. package/templates/typescript-pizzaz/src/widgets/components/PizzaCard.tsx +191 -0
  102. package/templates/typescript-pizzaz/src/widgets/package.json +30 -0
  103. package/templates/typescript-pizzaz/src/widgets/widget-manifest.json +253 -0
  104. package/templates/typescript-pizzaz/tsconfig.json +30 -0
  105. package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +0 -1
  106. package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +102 -56
  107. package/templates/typescript-starter/src/widgets/app/layout.tsx +6 -2
  108. package/templates/typescript-auth/AI_AGENT_CLI_REFERENCE.md +0 -702
  109. package/templates/typescript-auth/AI_AGENT_SDK_REFERENCE.md +0 -1260
  110. package/templates/typescript-auth/README.md +0 -402
  111. package/templates/typescript-auth/env.example +0 -25
  112. package/templates/typescript-auth/package.json +0 -36
  113. package/templates/typescript-auth/src/app.module.ts +0 -103
  114. package/templates/typescript-auth/src/db/database.ts +0 -160
  115. package/templates/typescript-auth/src/db/seed.ts +0 -374
  116. package/templates/typescript-auth/src/db/setup.ts +0 -87
  117. package/templates/typescript-auth/src/events/analytics.service.ts +0 -52
  118. package/templates/typescript-auth/src/events/notification.service.ts +0 -40
  119. package/templates/typescript-auth/src/filters/global-exception.filter.ts +0 -28
  120. package/templates/typescript-auth/src/guards/README.md +0 -75
  121. package/templates/typescript-auth/src/guards/jwt.guard.ts +0 -105
  122. package/templates/typescript-auth/src/health/database.health.ts +0 -41
  123. package/templates/typescript-auth/src/index.ts +0 -29
  124. package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +0 -24
  125. package/templates/typescript-auth/src/middleware/logging.middleware.ts +0 -42
  126. package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +0 -16
  127. package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +0 -114
  128. package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +0 -40
  129. package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +0 -284
  130. package/templates/typescript-auth/src/modules/auth/auth.module.ts +0 -16
  131. package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +0 -147
  132. package/templates/typescript-auth/src/modules/auth/auth.resources.ts +0 -84
  133. package/templates/typescript-auth/src/modules/auth/auth.tools.ts +0 -139
  134. package/templates/typescript-auth/src/modules/cart/cart.module.ts +0 -16
  135. package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +0 -95
  136. package/templates/typescript-auth/src/modules/cart/cart.resources.ts +0 -44
  137. package/templates/typescript-auth/src/modules/cart/cart.tools.ts +0 -277
  138. package/templates/typescript-auth/src/modules/orders/orders.module.ts +0 -16
  139. package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +0 -88
  140. package/templates/typescript-auth/src/modules/orders/orders.resources.ts +0 -48
  141. package/templates/typescript-auth/src/modules/orders/orders.tools.ts +0 -303
  142. package/templates/typescript-auth/src/modules/products/products.module.ts +0 -16
  143. package/templates/typescript-auth/src/modules/products/products.prompts.ts +0 -146
  144. package/templates/typescript-auth/src/modules/products/products.resources.ts +0 -98
  145. package/templates/typescript-auth/src/modules/products/products.tools.ts +0 -266
  146. package/templates/typescript-auth/src/pipes/validation.pipe.ts +0 -42
  147. package/templates/typescript-auth/src/services/database.service.ts +0 -90
  148. package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +0 -122
  149. package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +0 -116
  150. package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +0 -105
  151. package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +0 -139
  152. package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +0 -153
  153. package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +0 -86
  154. package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +0 -116
  155. package/templates/typescript-auth/src/widgets/app/categories/page.tsx +0 -134
  156. package/templates/typescript-auth/src/widgets/app/layout.tsx +0 -21
  157. package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +0 -129
  158. package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +0 -231
  159. package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +0 -225
  160. package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +0 -218
  161. package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +0 -121
  162. package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +0 -198
  163. package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +0 -187
  164. package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +0 -165
  165. package/templates/typescript-auth/src/widgets/next.config.js +0 -38
  166. package/templates/typescript-auth/src/widgets/package.json +0 -18
  167. package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +0 -169
  168. package/templates/typescript-auth/src/widgets/tsconfig.json +0 -28
  169. package/templates/typescript-auth/src/widgets/types/tool-data.ts +0 -141
  170. package/templates/typescript-auth/src/widgets/widget-manifest.json +0 -464
  171. package/templates/typescript-auth/tsconfig.json +0 -27
  172. package/templates/typescript-auth-api-key/AI_AGENT_CLI_REFERENCE.md +0 -701
  173. package/templates/typescript-auth-api-key/AI_AGENT_SDK_REFERENCE.md +0 -1260
  174. package/templates/typescript-auth-api-key/README.md +0 -485
  175. package/templates/typescript-auth-api-key/env.example +0 -17
  176. package/templates/typescript-auth-api-key/package.json +0 -21
  177. package/templates/typescript-auth-api-key/src/app.module.ts +0 -38
  178. package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +0 -47
  179. package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +0 -157
  180. package/templates/typescript-auth-api-key/src/index.ts +0 -47
  181. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +0 -12
  182. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +0 -73
  183. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +0 -60
  184. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +0 -71
  185. package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +0 -18
  186. package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +0 -155
  187. package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +0 -123
  188. package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +0 -133
  189. package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +0 -134
  190. package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +0 -14
  191. package/templates/typescript-auth-api-key/src/widgets/package.json +0 -24
  192. package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +0 -48
  193. package/templates/typescript-auth-api-key/tsconfig.json +0 -23
  194. package/templates/typescript-oauth/OAUTH_SETUP.md +0 -592
  195. package/templates/typescript-oauth/src/modules/demo/demo.module.ts +0 -16
  196. package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +0 -190
  197. package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +0 -133
  198. package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +0 -134
  199. package/templates/typescript-oauth/src/widgets/out/404.html +0 -1
  200. package/templates/typescript-oauth/src/widgets/out/_next/static/WU9THacVqL52RZbrZOLS1/_buildManifest.js +0 -1
  201. package/templates/typescript-oauth/src/widgets/out/_next/static/WU9THacVqL52RZbrZOLS1/_ssgManifest.js +0 -1
  202. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/117-eb57c7ef86f964a4.js +0 -2
  203. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/_not-found/page-dcb83ba3e4d0aafd.js +0 -1
  204. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/calculator-operations/page-b8913a740073ea8a.js +0 -1
  205. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/calculator-result/page-ddaaab2fce95dea2.js +0 -1
  206. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/layout-cbd3ebdc4ecc5247.js +0 -1
  207. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/fd9d1056-749e5812300142af.js +0 -1
  208. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
  209. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/main-76df43fcef3db344.js +0 -1
  210. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/main-app-f9c40224d04023c5.js +0 -1
  211. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
  212. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
  213. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  214. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/webpack-100b9e646d9c912e.js +0 -1
  215. package/templates/typescript-oauth/src/widgets/out/calculator-operations.html +0 -1
  216. package/templates/typescript-oauth/src/widgets/out/calculator-operations.txt +0 -7
  217. package/templates/typescript-oauth/src/widgets/out/calculator-result.html +0 -1
  218. package/templates/typescript-oauth/src/widgets/out/calculator-result.txt +0 -7
  219. package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +0 -133
  220. /package/templates/{typescript-auth-api-key → typescript-oauth}/src/health/system.health.ts +0 -0
  221. /package/templates/{typescript-auth-api-key → typescript-pizzaz}/src/widgets/next.config.js +0 -0
  222. /package/templates/{typescript-auth-api-key → typescript-pizzaz}/src/widgets/tsconfig.json +0 -0
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+
3
+ import { WidgetLayout } from 'nitrostack/widgets';
4
+ import 'mapbox-gl/dist/mapbox-gl.css';
5
+
6
+ export default function RootLayout({
7
+ children,
8
+ }: {
9
+ children: React.ReactNode;
10
+ }) {
11
+ return (
12
+ <html lang="en">
13
+ <body style={{ margin: 0, padding: 0, fontFamily: 'system-ui, sans-serif' }}>
14
+ <WidgetLayout>{children}</WidgetLayout>
15
+ </body>
16
+ </html>
17
+ );
18
+ }
@@ -0,0 +1,272 @@
1
+ 'use client';
2
+
3
+ import { useTheme, useWidgetState, useMaxHeight, useWidgetSDK } from 'nitrostack/widgets';
4
+
5
+ // Disable static generation - this is a dynamic widget
6
+ export const dynamic = 'force-dynamic';
7
+ import { PizzaCard } from '../../components/PizzaCard';
8
+ import { SlidersHorizontal } from 'lucide-react';
9
+ import { useState } from 'react';
10
+
11
+ interface PizzaShop {
12
+ id: string;
13
+ name: string;
14
+ description: string;
15
+ address: string;
16
+ coords: [number, number];
17
+ rating: number;
18
+ reviews: number;
19
+ priceLevel: 1 | 2 | 3;
20
+ cuisine: string[];
21
+ hours: { open: string; close: string };
22
+ phone: string;
23
+ website?: string;
24
+ image: string;
25
+ specialties: string[];
26
+ openNow: boolean;
27
+ }
28
+
29
+ interface WidgetData {
30
+ shops: PizzaShop[];
31
+ filters: any;
32
+ totalShops: number;
33
+ }
34
+
35
+ export default function PizzaListWidget() {
36
+ const theme = useTheme();
37
+ const maxHeight = useMaxHeight();
38
+ const isDark = theme === 'dark';
39
+
40
+ const { isReady, getToolOutput, callTool } = useWidgetSDK();
41
+
42
+ // Access tool output
43
+ const data = getToolOutput<WidgetData>();
44
+
45
+ console.log('🍕 PizzaListWidget render:', { isReady, hasData: !!data, data });
46
+
47
+ // Persistent state for view mode and favorites
48
+ const [state, setState] = useWidgetState<{
49
+ viewMode: 'grid' | 'list';
50
+ favorites: string[];
51
+ sortBy: 'rating' | 'name' | 'price';
52
+ }>(() => ({
53
+ viewMode: 'grid',
54
+ favorites: [],
55
+ sortBy: 'rating',
56
+ }));
57
+
58
+ const [showFilters, setShowFilters] = useState(false);
59
+
60
+ if (!data) {
61
+ return (
62
+ <div style={{
63
+ padding: '40px',
64
+ textAlign: 'center',
65
+ color: isDark ? '#fff' : '#000',
66
+ }}>
67
+ Loading pizza shops... {isReady ? '(SDK ready but no data)' : '(waiting for SDK)'}
68
+ </div>
69
+ );
70
+ }
71
+
72
+ // Check if shops array exists
73
+ if (!data.shops || !Array.isArray(data.shops)) {
74
+ console.error('❌ Invalid data structure:', data);
75
+ return (
76
+ <div style={{
77
+ padding: '40px',
78
+ textAlign: 'center',
79
+ color: isDark ? '#fff' : '#000',
80
+ }}>
81
+ Error: Invalid data structure. Expected shops array.
82
+ <pre style={{ marginTop: '16px', fontSize: '12px', textAlign: 'left' }}>
83
+ {JSON.stringify(data, null, 2)}
84
+ </pre>
85
+ </div>
86
+ );
87
+ }
88
+
89
+ const toggleFavorite = (shopId: string) => {
90
+ const favorites = state?.favorites || [];
91
+ const newFavorites = favorites.includes(shopId)
92
+ ? favorites.filter(id => id !== shopId)
93
+ : [...favorites, shopId];
94
+
95
+ setState({ ...state, favorites: newFavorites });
96
+ };
97
+
98
+ const handleShopClick = async (shopId: string) => {
99
+ // Call the show_pizza_shop tool to show shop details
100
+ await callTool('show_pizza_shop', { shopId });
101
+ };
102
+
103
+ // Sort shops
104
+ let sortedShops = [...data.shops];
105
+ switch (state?.sortBy) {
106
+ case 'rating':
107
+ sortedShops.sort((a, b) => b.rating - a.rating);
108
+ break;
109
+ case 'name':
110
+ sortedShops.sort((a, b) => a.name.localeCompare(b.name));
111
+ break;
112
+ case 'price':
113
+ sortedShops.sort((a, b) => a.priceLevel - b.priceLevel);
114
+ break;
115
+ }
116
+
117
+ return (
118
+ <div style={{
119
+ background: isDark ? '#0a0a0a' : '#f9fafb',
120
+ minHeight: '100vh',
121
+ maxHeight: maxHeight || '100vh',
122
+ overflow: 'auto',
123
+ }}>
124
+ {/* Header */}
125
+ <div style={{
126
+ background: isDark ? '#1a1a1a' : '#ffffff',
127
+ borderBottom: `1px solid ${isDark ? '#333' : '#e5e7eb'}`,
128
+ padding: '12px 16px',
129
+ position: 'sticky',
130
+ top: 0,
131
+ zIndex: 10,
132
+ }}>
133
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
134
+ <div>
135
+ <h1 style={{
136
+ margin: '0 0 2px 0',
137
+ fontSize: '18px',
138
+ fontWeight: '700',
139
+ color: isDark ? '#fff' : '#111',
140
+ }}>
141
+ 🍕 Pizza Shops
142
+ </h1>
143
+ <p style={{
144
+ margin: 0,
145
+ fontSize: '12px',
146
+ color: isDark ? '#999' : '#666',
147
+ }}>
148
+ {data.totalShops} shops found
149
+ </p>
150
+ </div>
151
+
152
+ {/* Sort and Filter Controls */}
153
+ <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
154
+ <select
155
+ value={state?.sortBy || 'rating'}
156
+ onChange={(e) => setState({ ...state, sortBy: e.target.value as any })}
157
+ style={{
158
+ padding: '6px 10px',
159
+ background: isDark ? '#1a1a1a' : '#fff',
160
+ border: `1px solid ${isDark ? '#444' : '#d1d5db'}`,
161
+ borderRadius: '6px',
162
+ color: isDark ? '#fff' : '#111',
163
+ fontSize: '12px',
164
+ cursor: 'pointer',
165
+ outline: 'none',
166
+ }}
167
+ >
168
+ <option value="rating">⭐ Rating</option>
169
+ <option value="name">🔤 Name</option>
170
+ <option value="price">💰 Price</option>
171
+ </select>
172
+
173
+ <button
174
+ onClick={() => setShowFilters(!showFilters)}
175
+ style={{
176
+ padding: '8px 12px',
177
+ background: showFilters
178
+ ? (isDark ? '#333' : '#f3f4f6')
179
+ : 'transparent',
180
+ border: `1px solid ${isDark ? '#444' : '#d1d5db'}`,
181
+ borderRadius: '8px',
182
+ cursor: 'pointer',
183
+ color: isDark ? '#fff' : '#111',
184
+ }}
185
+ >
186
+ <SlidersHorizontal size={18} />
187
+ </button>
188
+ </div>
189
+ </div>
190
+
191
+ {/* Filters */}
192
+ {showFilters && (
193
+ <div style={{
194
+ padding: '16px',
195
+ background: isDark ? '#111' : '#f9fafb',
196
+ borderRadius: '8px',
197
+ marginTop: '12px',
198
+ }}>
199
+ <p style={{
200
+ margin: '0 0 8px 0',
201
+ fontSize: '12px',
202
+ color: isDark ? '#999' : '#666',
203
+ }}>
204
+ Filters coming soon...
205
+ </p>
206
+ </div>
207
+ )}
208
+
209
+ {/* Favorites Count */}
210
+ {state?.favorites && state.favorites.length > 0 && (
211
+ <div style={{
212
+ marginTop: '12px',
213
+ padding: '8px 12px',
214
+ background: isDark ? '#1a1a1a' : '#fef3c7',
215
+ border: `1px solid ${isDark ? '#333' : '#fbbf24'}`,
216
+ borderRadius: '8px',
217
+ fontSize: '14px',
218
+ color: isDark ? '#fbbf24' : '#92400e',
219
+ }}>
220
+ ❤️ {state.favorites.length} favorite{state.favorites.length !== 1 ? 's' : ''}
221
+ </div>
222
+ )}
223
+ </div>
224
+
225
+ {/* Horizontal Scrolling Shop Cards */}
226
+ <div style={{
227
+ padding: '12px 16px',
228
+ overflowX: 'auto',
229
+ overflowY: 'hidden',
230
+ scrollSnapType: 'x mandatory',
231
+ WebkitOverflowScrolling: 'touch',
232
+ scrollbarWidth: 'thin',
233
+ }}>
234
+ <div style={{
235
+ display: 'flex',
236
+ gap: '12px',
237
+ paddingBottom: '4px',
238
+ }}>
239
+ {sortedShops.map(shop => (
240
+ <div
241
+ key={shop.id}
242
+ style={{
243
+ minWidth: '280px',
244
+ maxWidth: '280px',
245
+ scrollSnapAlign: 'start',
246
+ flexShrink: 0,
247
+ }}
248
+ >
249
+ <PizzaCard
250
+ shop={shop}
251
+ isFavorite={state?.favorites?.includes(shop.id)}
252
+ onToggleFavorite={toggleFavorite}
253
+ onSelect={() => handleShopClick(shop.id)}
254
+ />
255
+ </div>
256
+ ))}
257
+ </div>
258
+ </div>
259
+
260
+ {/* Footer */}
261
+ <div style={{
262
+ padding: '20px',
263
+ textAlign: 'center',
264
+ fontSize: '12px',
265
+ color: isDark ? '#666' : '#999',
266
+ borderTop: `1px solid ${isDark ? '#333' : '#e5e7eb'}`,
267
+ }}>
268
+ Powered by NitroStack • Theme: {theme || 'light'} • Scroll horizontally →
269
+ </div>
270
+ </div>
271
+ );
272
+ }
@@ -0,0 +1,216 @@
1
+ 'use client';
2
+
3
+ import { useTheme, useWidgetState, useMaxHeight, useDisplayMode, useWidgetSDK } from 'nitrostack/widgets';
4
+
5
+ // Disable static generation - this is a dynamic widget
6
+ export const dynamic = 'force-dynamic';
7
+ import { useEffect, useRef, useState } from 'react';
8
+ import mapboxgl from 'mapbox-gl';
9
+ import 'mapbox-gl/dist/mapbox-gl.css';
10
+ import { CompactShopCard } from '../../components/CompactShopCard';
11
+ import { Maximize2 } from 'lucide-react';
12
+
13
+ interface PizzaShop {
14
+ id: string;
15
+ name: string;
16
+ description: string;
17
+ address: string;
18
+ coords: [number, number];
19
+ rating: number;
20
+ reviews: number;
21
+ priceLevel: 1 | 2 | 3;
22
+ cuisine: string[];
23
+ hours: { open: string; close: string };
24
+ phone: string;
25
+ website?: string;
26
+ image: string;
27
+ specialties: string[];
28
+ openNow: boolean;
29
+ }
30
+
31
+ interface WidgetData {
32
+ shops: PizzaShop[];
33
+ filter: string;
34
+ totalShops: number;
35
+ }
36
+
37
+ export default function PizzaMapWidget() {
38
+ const theme = useTheme();
39
+ const maxHeight = useMaxHeight();
40
+ const displayMode = useDisplayMode();
41
+ const isDark = theme === 'dark';
42
+ const mapContainer = useRef<HTMLDivElement>(null);
43
+ const map = useRef<mapboxgl.Map | null>(null);
44
+ const [selectedShop, setSelectedShop] = useState<PizzaShop | null>(null);
45
+
46
+ const { isReady, getToolOutput, callTool, requestFullscreen } = useWidgetSDK();
47
+
48
+ // Access tool output
49
+ const data = getToolOutput<WidgetData>();
50
+
51
+ // Persistent state
52
+ const [state, setState] = useWidgetState<{
53
+ favorites: string[];
54
+ }>(() => ({
55
+ favorites: [],
56
+ }));
57
+
58
+ useEffect(() => {
59
+ if (!mapContainer.current || !data || map.current) return;
60
+
61
+ // Initialize Mapbox
62
+ const initMap = async () => {
63
+ try {
64
+ // Set your Mapbox token here
65
+ mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN || 'pk.eyJ1IjoiZXJpY25pbmciLCJhIjoiY21icXlubWM1MDRiczJvb2xwM2p0amNyayJ9.n-3O6JI5nOp_Lw96ZO5vJQ';
66
+
67
+ const mapInstance = new mapboxgl.Map({
68
+ container: mapContainer.current!,
69
+ style: isDark ? 'mapbox://styles/mapbox/dark-v11' : 'mapbox://styles/mapbox/streets-v12',
70
+ center: data.shops[0]?.coords || [-122.4194, 37.7749],
71
+ zoom: 12,
72
+ });
73
+
74
+ // Add markers
75
+ data.shops.forEach(shop => {
76
+ const el = document.createElement('div');
77
+ el.className = 'marker';
78
+ el.style.backgroundImage = 'url(https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png)';
79
+ el.style.width = '30px';
80
+ el.style.height = '40px';
81
+ el.style.backgroundSize = '100%';
82
+ el.style.cursor = 'pointer';
83
+
84
+ el.addEventListener('click', () => {
85
+ setSelectedShop(shop);
86
+ });
87
+
88
+ new mapboxgl.Marker(el)
89
+ .setLngLat(shop.coords)
90
+ .addTo(mapInstance);
91
+ });
92
+
93
+ // Fit bounds to show all markers
94
+ if (data.shops.length > 1) {
95
+ const bounds = new mapboxgl.LngLatBounds();
96
+ data.shops.forEach(shop => bounds.extend(shop.coords));
97
+ mapInstance.fitBounds(bounds, { padding: 50 });
98
+ }
99
+
100
+ map.current = mapInstance;
101
+ } catch (error) {
102
+ console.error('Failed to load Mapbox:', error);
103
+ }
104
+ };
105
+
106
+ initMap();
107
+
108
+ return () => {
109
+ if (map.current) {
110
+ map.current.remove();
111
+ }
112
+ };
113
+ }, [data, isDark]);
114
+
115
+ if (!data) {
116
+ return (
117
+ <div style={{
118
+ padding: '40px',
119
+ textAlign: 'center',
120
+ color: isDark ? '#fff' : '#000',
121
+ }}>
122
+ Loading map... {isReady ? '(SDK ready but no data)' : '(waiting for SDK)'}
123
+ </div>
124
+ );
125
+ }
126
+
127
+ const handleShopClick = async (shopId: string) => {
128
+ // Call the show_pizza_shop tool to show shop details
129
+ await callTool('show_pizza_shop', { shopId });
130
+ };
131
+
132
+ const requestFullscreenMode = async () => {
133
+ await requestFullscreen();
134
+ };
135
+
136
+ return (
137
+ <div style={{
138
+ position: 'relative',
139
+ height: maxHeight || '650px',
140
+ background: isDark ? '#0a0a0a' : '#f9fafb',
141
+ overflow: 'hidden',
142
+ }}>
143
+ {/* Map Container - Full Screen */}
144
+ <div
145
+ ref={mapContainer}
146
+ style={{
147
+ position: 'absolute',
148
+ inset: 0,
149
+ }}
150
+ />
151
+
152
+ {/* Enlarge Button */}
153
+ <button
154
+ onClick={requestFullscreen}
155
+ style={{
156
+ position: 'absolute',
157
+ top: '16px',
158
+ right: '16px',
159
+ padding: '10px',
160
+ background: 'rgba(255, 255, 255, 0.9)',
161
+ border: 'none',
162
+ borderRadius: '8px',
163
+ cursor: 'pointer',
164
+ boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
165
+ zIndex: 10,
166
+ display: 'flex',
167
+ alignItems: 'center',
168
+ justifyContent: 'center',
169
+ }}
170
+ >
171
+ <Maximize2 size={18} style={{ color: '#111' }} />
172
+ </button>
173
+
174
+ {/* Overlay Shop Cards - Bottom */}
175
+ <div style={{
176
+ position: 'absolute',
177
+ bottom: '8px',
178
+ left: '16px',
179
+ right: '16px',
180
+ zIndex: 5,
181
+ overflowX: 'auto',
182
+ overflowY: 'hidden',
183
+ scrollSnapType: 'x mandatory',
184
+ WebkitOverflowScrolling: 'touch',
185
+ scrollbarWidth: 'none',
186
+ msOverflowStyle: 'none',
187
+ }}>
188
+ <div style={{
189
+ display: 'flex',
190
+ gap: '12px',
191
+ paddingBottom: '8px',
192
+ }}>
193
+ {data.shops.map(shop => (
194
+ <div
195
+ key={shop.id}
196
+ style={{
197
+ scrollSnapAlign: 'start',
198
+ flexShrink: 0,
199
+ }}
200
+ >
201
+ <CompactShopCard
202
+ shop={shop}
203
+ isSelected={selectedShop?.id === shop.id}
204
+ onClick={() => {
205
+ setSelectedShop(shop);
206
+ handleShopClick(shop.id);
207
+ }}
208
+ isDark={isDark}
209
+ />
210
+ </div>
211
+ ))}
212
+ </div>
213
+ </div>
214
+ </div>
215
+ );
216
+ }