nitrostack 1.0.55 → 1.0.56

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 +1 -1
  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
@@ -6,20 +6,21 @@ import { api } from '@/lib/api';
6
6
  import { WidgetRenderer } from '@/components/WidgetRenderer';
7
7
  import { MarkdownRenderer } from '@/components/MarkdownRenderer';
8
8
  import type { ChatMessage, Tool, ToolCall, Prompt } from '@/lib/types';
9
- import {
10
- Bot,
11
- Settings,
12
- Trash2,
13
- Image as ImageIcon,
14
- Send,
15
- Wrench,
9
+ import {
10
+ Bot,
11
+ Settings,
12
+ Trash2,
13
+ Image as ImageIcon,
14
+ Send,
15
+ Wrench,
16
16
  Save,
17
17
  X,
18
18
  Sparkles,
19
19
  FileText,
20
20
  Play,
21
21
  ExternalLink,
22
- Info
22
+ Info,
23
+ MoreVertical
23
24
  } from 'lucide-react';
24
25
 
25
26
  export default function ChatPage() {
@@ -34,7 +35,7 @@ export default function ChatPage() {
34
35
  tools,
35
36
  setTools,
36
37
  } = useStudioStore();
37
-
38
+
38
39
  // Get jwtToken and apiKey dynamically to ensure we always have the latest value
39
40
  const getAuthTokens = () => {
40
41
  const state = useStudioStore.getState();
@@ -52,6 +53,7 @@ export default function ChatPage() {
52
53
  const [prompts, setPrompts] = useState<Prompt[]>([]);
53
54
  const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
54
55
  const [promptArgs, setPromptArgs] = useState<Record<string, string>>({});
56
+ const [fullscreenWidget, setFullscreenWidget] = useState<{ uri: string, data: any } | null>(null);
55
57
  const messagesEndRef = useRef<HTMLDivElement>(null);
56
58
  const fileInputRef = useRef<HTMLInputElement>(null);
57
59
  const textareaRef = useRef<HTMLTextAreaElement>(null);
@@ -59,7 +61,7 @@ export default function ChatPage() {
59
61
  useEffect(() => {
60
62
  loadTools();
61
63
  loadPrompts();
62
-
64
+
63
65
  // Check if there's a suggested message from localStorage
64
66
  if (typeof window !== 'undefined') {
65
67
  const chatInput = window.localStorage.getItem('chatInput');
@@ -91,6 +93,120 @@ export default function ChatPage() {
91
93
  }
92
94
  }, [inputValue]);
93
95
 
96
+ // Listen for widget fullscreen requests
97
+ useEffect(() => {
98
+ const handleFullscreenRequest = (event: CustomEvent) => {
99
+ const { uri, data } = event.detail;
100
+ setFullscreenWidget({ uri, data });
101
+ };
102
+
103
+ window.addEventListener('widget-fullscreen-request', handleFullscreenRequest as EventListener);
104
+ return () => window.removeEventListener('widget-fullscreen-request', handleFullscreenRequest as EventListener);
105
+ }, []);
106
+
107
+ // Listen for widget tool call requests
108
+ useEffect(() => {
109
+ let isProcessingToolCall = false;
110
+
111
+ const handleToolCall = async (event: any) => {
112
+ // Prevent multiple simultaneous calls
113
+ if (isProcessingToolCall) {
114
+ console.log('⏭️ Skipping duplicate tool call');
115
+ return;
116
+ }
117
+
118
+ const { toolName, toolArgs } = event.detail;
119
+ console.log('📞 Chat received tool call from widget:', toolName, toolArgs);
120
+
121
+ isProcessingToolCall = true;
122
+
123
+ try {
124
+ // Get current state directly from store to avoid stale closure
125
+ const currentMessages = useStudioStore.getState().chatMessages;
126
+ const currentProv = useStudioStore.getState().currentProvider;
127
+
128
+ // Directly send the tool call message without showing in input
129
+ const toolCallMessage = `Use the ${toolName} tool with these arguments: ${JSON.stringify(toolArgs)}`;
130
+
131
+ // Add user message
132
+ const userMessage: ChatMessage = {
133
+ role: 'user',
134
+ content: toolCallMessage,
135
+ };
136
+ addChatMessage(userMessage);
137
+
138
+ // Call LLM
139
+ setLoading(true);
140
+ try {
141
+ const { jwtToken, mcpApiKey } = getAuthTokens();
142
+ const apiKey = localStorage.getItem(`${currentProv}_api_key`);
143
+ const response = await api.chat({
144
+ provider: currentProv,
145
+ messages: [...currentMessages, userMessage],
146
+ apiKey: apiKey || '',
147
+ jwtToken: jwtToken || undefined,
148
+ mcpApiKey: mcpApiKey || undefined,
149
+ });
150
+
151
+ // Handle tool calls (same as handleSend)
152
+ if (response.toolCalls && response.toolResults) {
153
+ // Attach results to tool calls for widget rendering
154
+ const toolCallsWithResults = response.toolCalls.map((tc: any, i: any) => {
155
+ const toolResult = response.toolResults[i];
156
+ let parsedResult;
157
+ if (toolResult.content) {
158
+ try {
159
+ parsedResult = JSON.parse(toolResult.content);
160
+ } catch (e) {
161
+ parsedResult = { raw: toolResult.content };
162
+ }
163
+ }
164
+ return { ...tc, result: parsedResult };
165
+ });
166
+
167
+ if (response.message) {
168
+ response.message.toolCalls = toolCallsWithResults;
169
+ addChatMessage(response.message);
170
+ }
171
+
172
+ // Add tool results
173
+ const toolResultMessages: ChatMessage[] = [];
174
+ for (const result of response.toolResults) {
175
+ addChatMessage(result);
176
+ toolResultMessages.push(result);
177
+ }
178
+
179
+ // Continue conversation
180
+ const messagesForContinuation = [
181
+ ...currentMessages,
182
+ userMessage,
183
+ response.message!,
184
+ ...toolResultMessages,
185
+ ];
186
+
187
+ // Call continueChatWithToolResults
188
+ await continueChatWithToolResults(apiKey || '', messagesForContinuation);
189
+ } else if (response.message) {
190
+ addChatMessage(response.message);
191
+ }
192
+
193
+ setLoading(false);
194
+ } catch (error) {
195
+ console.error('Tool call failed:', error);
196
+ setLoading(false);
197
+ }
198
+ } finally {
199
+ // Reset flag after a short delay to allow next call
200
+ setTimeout(() => {
201
+ isProcessingToolCall = false;
202
+ }, 1000);
203
+ }
204
+ };
205
+
206
+ window.addEventListener('widget-tool-call', handleToolCall);
207
+ return () => window.removeEventListener('widget-tool-call', handleToolCall);
208
+ }, []); // Empty dependency array - only register once
209
+
94
210
  const loadTools = async () => {
95
211
  try {
96
212
  const data = await api.getTools();
@@ -111,43 +227,43 @@ export default function ChatPage() {
111
227
 
112
228
  const handleExecutePrompt = async () => {
113
229
  if (!selectedPrompt) return;
114
-
230
+
115
231
  // Close modal
116
232
  const prompt = selectedPrompt;
117
233
  const args = { ...promptArgs };
118
234
  setSelectedPrompt(null);
119
235
  setPromptArgs({});
120
-
236
+
121
237
  // Build user message showing what prompt was executed
122
238
  let userMessageContent = `Execute prompt: ${prompt.name}`;
123
239
  if (Object.keys(args).length > 0) {
124
240
  userMessageContent += `\nArguments: ${JSON.stringify(args, null, 2)}`;
125
241
  }
126
-
242
+
127
243
  // Add user message to chat
128
244
  addChatMessage({
129
245
  role: 'user',
130
246
  content: userMessageContent,
131
247
  });
132
-
248
+
133
249
  setLoading(true);
134
-
250
+
135
251
  try {
136
252
  // Execute the prompt directly via API
137
253
  const result = await api.executePrompt(prompt.name, args);
138
-
254
+
139
255
  // Add the prompt result as an assistant message
140
256
  if (result.messages && result.messages.length > 0) {
141
257
  // Combine all prompt messages into one assistant message
142
258
  const combinedContent = result.messages
143
259
  .map((msg: any) => {
144
- const content = typeof msg.content === 'string'
145
- ? msg.content
260
+ const content = typeof msg.content === 'string'
261
+ ? msg.content
146
262
  : msg.content?.text || JSON.stringify(msg.content);
147
263
  return `[${msg.role.toUpperCase()}]\n${content}`;
148
264
  })
149
265
  .join('\n\n');
150
-
266
+
151
267
  addChatMessage({
152
268
  role: 'assistant',
153
269
  content: combinedContent,
@@ -216,38 +332,38 @@ export default function ChatPage() {
216
332
 
217
333
  try {
218
334
  const messagesToSend = [...chatMessages, userMessage];
219
-
335
+
220
336
  // Clean messages to ensure they're serializable
221
337
  const cleanedMessages = messagesToSend.map(msg => {
222
338
  const cleaned: any = {
223
339
  role: msg.role,
224
340
  content: msg.content || '',
225
341
  };
226
-
342
+
227
343
  if (msg.toolCalls && msg.toolCalls.length > 0) {
228
344
  cleaned.toolCalls = msg.toolCalls;
229
345
  }
230
-
346
+
231
347
  if (msg.toolCallId) {
232
348
  cleaned.toolCallId = msg.toolCallId;
233
349
  }
234
-
350
+
235
351
  // Skip image property for now (not supported by OpenAI chat completions)
236
352
  // if (msg.image) {
237
353
  // cleaned.image = msg.image;
238
354
  // }
239
-
355
+
240
356
  return cleaned;
241
357
  });
242
-
358
+
243
359
  // Get fresh auth tokens from store
244
360
  const { jwtToken, mcpApiKey } = getAuthTokens();
245
-
361
+
246
362
  console.log('Sending messages to API:', cleanedMessages);
247
363
  console.log('Auth tokens:', { hasJwtToken: !!jwtToken, hasMcpApiKey: !!mcpApiKey });
248
364
  console.log('Original messages:', messagesToSend);
249
365
  console.log('Cleaned messages JSON:', JSON.stringify(cleanedMessages));
250
-
366
+
251
367
  const response = await api.chat({
252
368
  provider: currentProvider,
253
369
  messages: cleanedMessages,
@@ -261,13 +377,13 @@ export default function ChatPage() {
261
377
  // Attach results to tool calls for widget rendering
262
378
  const toolCallsWithResults = response.toolCalls.map((tc, i) => {
263
379
  const toolResult = response.toolResults[i];
264
-
380
+
265
381
  // Parse the result content
266
382
  let parsedResult;
267
383
  if (toolResult.content) {
268
384
  try {
269
385
  parsedResult = JSON.parse(toolResult.content);
270
-
386
+
271
387
  // Unwrap if response was wrapped by TransformInterceptor
272
388
  if (parsedResult.success !== undefined && parsedResult.data !== undefined) {
273
389
  parsedResult = parsedResult.data;
@@ -276,19 +392,19 @@ export default function ChatPage() {
276
392
  parsedResult = { content: toolResult.content };
277
393
  }
278
394
  }
279
-
395
+
280
396
  return {
281
397
  ...tc,
282
398
  result: parsedResult,
283
399
  };
284
400
  });
285
-
401
+
286
402
  // Add assistant message with tool calls
287
403
  if (response.message) {
288
404
  response.message.toolCalls = toolCallsWithResults;
289
405
  addChatMessage(response.message);
290
406
  }
291
-
407
+
292
408
  // Extract JWT token from ANY tool response (not just 'login')
293
409
  for (let i = 0; i < response.toolCalls.length; i++) {
294
410
  const toolCall = response.toolCalls[i];
@@ -331,7 +447,7 @@ export default function ChatPage() {
331
447
  addChatMessage(response.message);
332
448
  }
333
449
  }
334
-
450
+
335
451
  // Set loading to false AFTER all async operations complete
336
452
  setLoading(false);
337
453
  } catch (error) {
@@ -348,31 +464,31 @@ export default function ChatPage() {
348
464
  try {
349
465
  // Use provided messages or fall back to store (for recursive calls)
350
466
  const messagesToUse = messages || chatMessages;
351
-
467
+
352
468
  // Get fresh auth tokens from store (token may have been updated by login)
353
469
  const { jwtToken, mcpApiKey } = getAuthTokens();
354
-
470
+
355
471
  // Clean messages before sending
356
472
  const cleanedMessages = messagesToUse.map(msg => {
357
473
  const cleaned: any = {
358
474
  role: msg.role,
359
475
  content: msg.content || '',
360
476
  };
361
-
477
+
362
478
  if (msg.toolCalls && msg.toolCalls.length > 0) {
363
479
  cleaned.toolCalls = msg.toolCalls;
364
480
  }
365
-
481
+
366
482
  if (msg.toolCallId) {
367
483
  cleaned.toolCallId = msg.toolCallId;
368
484
  }
369
-
485
+
370
486
  return cleaned;
371
487
  });
372
-
488
+
373
489
  console.log('Continue with cleaned messages:', JSON.stringify(cleanedMessages));
374
490
  console.log('Continue auth tokens:', { hasJwtToken: !!jwtToken, hasMcpApiKey: !!mcpApiKey });
375
-
491
+
376
492
  const response = await api.chat({
377
493
  provider: currentProvider,
378
494
  messages: cleanedMessages,
@@ -392,7 +508,7 @@ export default function ChatPage() {
392
508
  addChatMessage(result);
393
509
  newToolResults.push(result);
394
510
  }
395
-
511
+
396
512
  // Build messages for next iteration
397
513
  const nextMessages = [
398
514
  ...(messages || chatMessages),
@@ -421,7 +537,7 @@ export default function ChatPage() {
421
537
  };
422
538
 
423
539
  return (
424
- <div className="fixed inset-0 flex flex-col bg-background" style={{ left: 'var(--sidebar-width, 15rem)' }}>
540
+ <div className="fixed inset-0 flex flex-col" style={{ left: 'var(--sidebar-width, 15rem)', backgroundColor: '#0a0a0a' }}>
425
541
  {/* Sticky Header */}
426
542
  <div className="sticky top-0 z-10 border-b border-border/50 px-3 sm:px-6 py-3 flex flex-col sm:flex-row items-start sm:items-center justify-between bg-card/80 backdrop-blur-md shadow-sm gap-3 sm:gap-0">
427
543
  <div className="flex items-center gap-3">
@@ -442,19 +558,18 @@ export default function ChatPage() {
442
558
  <option value="gemini">Gemini</option>
443
559
  <option value="openai">OpenAI</option>
444
560
  </select>
445
- <button
446
- onClick={() => setShowSettings(!showSettings)}
447
- className={`w-8 h-8 rounded-lg flex items-center justify-center transition-all flex-shrink-0 ${
448
- showSettings
449
- ? 'bg-primary/10 text-primary ring-1 ring-primary/30'
450
- : 'bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground'
451
- }`}
561
+ <button
562
+ onClick={() => setShowSettings(!showSettings)}
563
+ className={`w-8 h-8 rounded-lg flex items-center justify-center transition-all flex-shrink-0 ${showSettings
564
+ ? 'bg-primary/10 text-primary ring-1 ring-primary/30'
565
+ : 'bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground'
566
+ }`}
452
567
  title="Settings"
453
568
  >
454
569
  <Settings className="w-4 h-4" />
455
570
  </button>
456
- <button
457
- onClick={clearChat}
571
+ <button
572
+ onClick={clearChat}
458
573
  className="w-8 h-8 rounded-lg flex items-center justify-center bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground transition-all flex-shrink-0"
459
574
  title="Clear chat"
460
575
  >
@@ -476,8 +591,8 @@ export default function ChatPage() {
476
591
  <p className="text-xs text-muted-foreground mt-1">Configure your AI provider API keys to enable chat functionality</p>
477
592
  </div>
478
593
  </div>
479
-
480
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
594
+
595
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
481
596
  {/* OpenAI Section */}
482
597
  <div className="card p-4">
483
598
  <div className="flex items-center justify-between mb-3">
@@ -487,9 +602,9 @@ export default function ChatPage() {
487
602
  </div>
488
603
  OpenAI API Key
489
604
  </label>
490
- <a
491
- href="https://platform.openai.com/api-keys"
492
- target="_blank"
605
+ <a
606
+ href="https://platform.openai.com/api-keys"
607
+ target="_blank"
493
608
  rel="noopener noreferrer"
494
609
  className="text-xs text-primary hover:text-primary/80 flex items-center gap-1 transition-colors"
495
610
  >
@@ -497,11 +612,11 @@ export default function ChatPage() {
497
612
  </a>
498
613
  </div>
499
614
  <div className="flex gap-2 mb-3">
500
- <input
501
- id="openai-api-key"
502
- type="password"
503
- className="input flex-1 text-sm py-2"
504
- placeholder="sk-proj-..."
615
+ <input
616
+ id="openai-api-key"
617
+ type="password"
618
+ className="input flex-1 text-sm py-2"
619
+ placeholder="sk-proj-..."
505
620
  />
506
621
  <button onClick={() => saveApiKey('openai')} className="btn btn-primary text-xs px-4 py-2">
507
622
  <Save className="w-3 h-3 mr-1" />
@@ -518,9 +633,9 @@ export default function ChatPage() {
518
633
  </a>
519
634
  , navigate to API Keys, and create a new secret key.
520
635
  </p>
521
- <a
522
- href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key"
523
- target="_blank"
636
+ <a
637
+ href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key"
638
+ target="_blank"
524
639
  rel="noopener noreferrer"
525
640
  className="text-primary hover:underline inline-flex items-center gap-1"
526
641
  >
@@ -539,9 +654,9 @@ export default function ChatPage() {
539
654
  </div>
540
655
  Gemini API Key
541
656
  </label>
542
- <a
543
- href="https://aistudio.google.com/app/apikey"
544
- target="_blank"
657
+ <a
658
+ href="https://aistudio.google.com/app/apikey"
659
+ target="_blank"
545
660
  rel="noopener noreferrer"
546
661
  className="text-xs text-primary hover:text-primary/80 flex items-center gap-1 transition-colors"
547
662
  >
@@ -549,11 +664,11 @@ export default function ChatPage() {
549
664
  </a>
550
665
  </div>
551
666
  <div className="flex gap-2 mb-3">
552
- <input
553
- id="gemini-api-key"
554
- type="password"
555
- className="input flex-1 text-sm py-2"
556
- placeholder="AIza..."
667
+ <input
668
+ id="gemini-api-key"
669
+ type="password"
670
+ className="input flex-1 text-sm py-2"
671
+ placeholder="AIza..."
557
672
  />
558
673
  <button onClick={() => saveApiKey('gemini')} className="btn btn-primary text-xs px-4 py-2">
559
674
  <Save className="w-3 h-3 mr-1" />
@@ -570,9 +685,9 @@ export default function ChatPage() {
570
685
  </a>
571
686
  , sign in with your Google account, and click "Get API key".
572
687
  </p>
573
- <a
574
- href="https://ai.google.dev/gemini-api/docs/api-key"
575
- target="_blank"
688
+ <a
689
+ href="https://ai.google.dev/gemini-api/docs/api-key"
690
+ target="_blank"
576
691
  rel="noopener noreferrer"
577
692
  className="text-primary hover:underline inline-flex items-center gap-1"
578
693
  >
@@ -588,7 +703,7 @@ export default function ChatPage() {
588
703
  <div className="flex items-start gap-2">
589
704
  <Info className="w-4 h-4 text-amber-500 mt-0.5 flex-shrink-0" />
590
705
  <div className="text-xs text-muted-foreground">
591
- <strong className="text-foreground">Security Note:</strong> Your API keys are stored locally in your browser and never sent to our servers.
706
+ <strong className="text-foreground">Security Note:</strong> Your API keys are stored locally in your browser and never sent to our servers.
592
707
  Keep them confidential and avoid sharing them publicly.
593
708
  </div>
594
709
  </div>
@@ -599,14 +714,14 @@ export default function ChatPage() {
599
714
 
600
715
  {/* ChatGPT-style Messages Container - ONLY this scrolls */}
601
716
  <div className="flex-1 overflow-y-auto overflow-x-hidden">
602
- <div className="max-w-3xl mx-auto px-4 py-6 space-y-6 min-h-full">
717
+ <div className="max-w-5xl mx-auto px-4 py-6 space-y-6 min-h-full">
603
718
  {chatMessages.length === 0 && !loading ? (
604
719
  /* Welcome Screen */
605
720
  <div className="flex flex-col items-center justify-center min-h-[calc(100vh-300px)] animate-fade-in">
606
721
  <div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-primary to-amber-500 flex items-center justify-center shadow-xl mb-6">
607
722
  <Bot className="w-10 h-10 text-white" strokeWidth={2.5} />
608
723
  </div>
609
-
724
+
610
725
  <h2 className="text-3xl font-bold text-foreground mb-3">Welcome to NitroStudio</h2>
611
726
  <p className="text-muted-foreground text-center max-w-md mb-8">
612
727
  Your AI-powered development environment for Model Context Protocol (MCP) servers.
@@ -621,7 +736,7 @@ export default function ChatPage() {
621
736
  <h3 className="text-lg font-semibold text-foreground">Available Prompts</h3>
622
737
  <span className="text-sm text-muted-foreground">({prompts.length})</span>
623
738
  </div>
624
-
739
+
625
740
  <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
626
741
  {prompts.slice(0, 6).map((prompt) => (
627
742
  <button
@@ -716,7 +831,7 @@ export default function ChatPage() {
716
831
 
717
832
  {/* ChatGPT-style Input Area - Fixed at bottom */}
718
833
  <div className="sticky bottom-0 border-t border-border/50 bg-background/95 backdrop-blur-md shadow-[0_-2px_10px_rgba(0,0,0,0.1)]">
719
- <div className="max-w-3xl mx-auto px-3 sm:px-4 py-3 sm:py-4">
834
+ <div className="max-w-5xl mx-auto px-3 sm:px-4 py-3 sm:py-4">
720
835
  {currentImage && (
721
836
  <div className="mb-3 p-3 bg-card rounded-xl flex items-start gap-3 border border-border/50 animate-fade-in">
722
837
  <img
@@ -773,8 +888,8 @@ export default function ChatPage() {
773
888
  }}
774
889
  />
775
890
  </div>
776
- <button
777
- onClick={handleSend}
891
+ <button
892
+ onClick={handleSend}
778
893
  disabled={loading || (!inputValue.trim() && !currentImage)}
779
894
  className="h-11 w-11 rounded-xl flex items-center justify-center bg-gradient-to-br from-primary to-amber-500 text-white shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed transition-all flex-shrink-0 hover:scale-105 active:scale-95"
780
895
  title="Send message (Enter)"
@@ -782,7 +897,7 @@ export default function ChatPage() {
782
897
  <Send className="w-5 h-5" strokeWidth={2.5} />
783
898
  </button>
784
899
  </div>
785
-
900
+
786
901
  </div>
787
902
  </div>
788
903
 
@@ -845,17 +960,41 @@ export default function ChatPage() {
845
960
  </div>
846
961
  )}
847
962
 
848
- <button
849
- onClick={handleExecutePrompt}
963
+ <button
964
+ onClick={handleExecutePrompt}
850
965
  className="btn btn-primary w-full gap-2"
851
966
  >
852
967
  <Play className="w-4 h-4" />
853
968
  Execute Prompt
854
- </button>
969
+ </button>
970
+ </div>
971
+
972
+ </div>
973
+ </div>
974
+ )}
975
+
976
+ {/* Fullscreen Widget Modal */}
977
+ {fullscreenWidget && (
978
+ <div
979
+ className="fixed inset-0 z-50 flex items-center justify-center"
980
+ style={{ backgroundColor: 'rgba(0, 0, 0, 0.9)' }}
981
+ >
982
+ {/* Close Button */}
983
+ <button
984
+ onClick={() => setFullscreenWidget(null)}
985
+ className="absolute top-4 right-4 z-60 p-3 rounded-lg bg-white/10 hover:bg-white/20 backdrop-blur-sm border border-white/20 transition-all"
986
+ title="Exit fullscreen"
987
+ >
988
+ <X className="w-6 h-6 text-white" />
989
+ </button>
990
+
991
+ {/* Widget Container */}
992
+ <div className="w-full h-full p-8">
993
+ <div className="w-full h-full rounded-xl overflow-hidden shadow-2xl">
994
+ <WidgetRenderer uri={fullscreenWidget.uri} data={fullscreenWidget.data} className="widget-fullscreen" />
995
+ </div>
855
996
  </div>
856
-
857
997
  </div>
858
- </div>
859
998
  )}
860
999
  </div>
861
1000
  );
@@ -892,7 +1031,7 @@ function ChatMessageComponent({ message, tools }: { message: ChatMessage; tools:
892
1031
  />
893
1032
  </div>
894
1033
  )}
895
-
1034
+
896
1035
  {/* Text content with markdown rendering */}
897
1036
  {message.content && (
898
1037
  <div className="text-sm leading-relaxed mb-4">
@@ -920,73 +1059,71 @@ function ChatMessageComponent({ message, tools }: { message: ChatMessage; tools:
920
1059
  function ToolCallComponent({ toolCall, tools }: { toolCall: ToolCall; tools: Tool[] }) {
921
1060
  const [showArgs, setShowArgs] = useState(false);
922
1061
  const tool = tools.find((t) => t.name === toolCall.name);
923
-
1062
+
924
1063
  // Get widget URI from multiple possible sources
925
- const componentUri =
926
- tool?.widget?.route ||
927
- tool?.outputTemplate ||
928
- tool?._meta?.['openai/outputTemplate'] ||
1064
+ const componentUri =
1065
+ tool?.widget?.route ||
1066
+ tool?.outputTemplate ||
1067
+ tool?._meta?.['openai/outputTemplate'] ||
929
1068
  tool?._meta?.['ui/template'];
930
-
1069
+
931
1070
  // Get result data from toolCall and unwrap if needed
932
1071
  let widgetData = toolCall.result || toolCall.arguments;
933
-
1072
+
934
1073
  // Unwrap if response was wrapped by TransformInterceptor
935
1074
  // Check if it has the interceptor's structure: { success, data, metadata }
936
- if (widgetData && typeof widgetData === 'object' &&
937
- widgetData.success !== undefined && widgetData.data !== undefined) {
1075
+ if (widgetData && typeof widgetData === 'object' &&
1076
+ widgetData.success !== undefined && widgetData.data !== undefined) {
938
1077
  widgetData = widgetData.data; // Return the unwrapped data
939
1078
  }
940
1079
 
941
- console.log('ToolCallComponent:', {
942
- toolName: toolCall.name,
943
- componentUri,
1080
+ console.log('ToolCallComponent:', {
1081
+ toolName: toolCall.name,
1082
+ componentUri,
944
1083
  hasData: !!widgetData,
945
- tool
1084
+ tool
946
1085
  });
947
1086
 
948
1087
  return (
949
- <div className="rounded-2xl overflow-hidden border border-border/50 bg-card/50 backdrop-blur-sm shadow-sm hover:shadow-md transition-shadow">
950
- {/* Tool Header - Collapsible */}
1088
+ <div className="relative group/widget">
1089
+ {/* Widget - No frame, just the widget */}
1090
+ {componentUri && widgetData && (
1091
+ <div className="rounded-lg overflow-hidden max-w-5xl">
1092
+ <WidgetRenderer uri={componentUri} data={widgetData} className="widget-in-chat" />
1093
+ </div>
1094
+ )}
1095
+
1096
+ {/* 3-dots menu button - positioned absolutely in top-right */}
951
1097
  <button
952
1098
  onClick={() => setShowArgs(!showArgs)}
953
- className="w-full flex items-center justify-between px-4 py-3 hover:bg-muted/30 transition-colors group"
1099
+ className="absolute top-2 right-2 w-8 h-8 rounded-lg flex items-center justify-center bg-background/80 backdrop-blur-sm border border-border/50 hover:bg-background hover:border-border transition-all opacity-0 group-hover/widget:opacity-100 shadow-sm z-10"
1100
+ title="View tool details"
954
1101
  >
955
- <div className="flex items-center gap-2">
956
- <div className="w-6 h-6 rounded-md bg-primary/10 flex items-center justify-center group-hover:bg-primary/20 transition-colors">
957
- <Wrench className="w-3.5 h-3.5 text-primary" />
958
- </div>
959
- <span className="font-semibold text-sm text-foreground">{toolCall.name}</span>
960
- </div>
961
- <svg
962
- className={`w-4 h-4 text-muted-foreground transition-transform ${showArgs ? 'rotate-180' : ''}`}
963
- fill="none"
964
- viewBox="0 0 24 24"
965
- stroke="currentColor"
966
- >
967
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
968
- </svg>
1102
+ <MoreVertical className="w-4 h-4 text-muted-foreground" />
969
1103
  </button>
970
1104
 
971
- {/* Arguments - Expandable */}
1105
+ {/* Arguments Modal/Dropdown - appears when 3-dots clicked */}
972
1106
  {showArgs && (
973
- <div className="px-4 pb-3 border-t border-border/30 pt-3 animate-fade-in">
974
- <p className="text-xs font-medium text-muted-foreground mb-2">Arguments:</p>
975
- <pre className="p-3 rounded-lg overflow-auto bg-background/50 border border-border/30 font-mono text-xs text-foreground max-h-40">
976
- {JSON.stringify(toolCall.arguments, null, 2)}
977
- </pre>
978
- </div>
979
- )}
980
-
981
- {/* Widget - ChatGPT-style embedded card */}
982
- {componentUri && widgetData && (
983
- <div className="border-t border-border/30 pt-3 flex justify-center">
984
- <div className="rounded-lg overflow-hidden bg-background" style={{
985
- width: 'fit-content',
986
- maxWidth: '100%',
987
- height: '450px',
988
- }}>
989
- <WidgetRenderer uri={componentUri} data={widgetData} className="widget-in-chat" />
1107
+ <div className="absolute top-12 right-2 w-96 max-w-[calc(100%-1rem)] bg-card rounded-xl border border-border shadow-2xl p-4 animate-fade-in z-20">
1108
+ <div className="flex items-center justify-between mb-3">
1109
+ <div className="flex items-center gap-2">
1110
+ <div className="w-6 h-6 rounded-md bg-primary/10 flex items-center justify-center">
1111
+ <Wrench className="w-3.5 h-3.5 text-primary" />
1112
+ </div>
1113
+ <span className="font-semibold text-sm text-foreground">{toolCall.name}</span>
1114
+ </div>
1115
+ <button
1116
+ onClick={() => setShowArgs(false)}
1117
+ className="w-6 h-6 rounded-md flex items-center justify-center hover:bg-muted transition-colors"
1118
+ >
1119
+ <X className="w-4 h-4 text-muted-foreground" />
1120
+ </button>
1121
+ </div>
1122
+ <div>
1123
+ <p className="text-xs font-medium text-muted-foreground mb-2">Arguments:</p>
1124
+ <pre className="p-3 rounded-lg overflow-auto bg-background border border-border/30 font-mono text-xs text-foreground max-h-60">
1125
+ {JSON.stringify(toolCall.arguments, null, 2)}
1126
+ </pre>
990
1127
  </div>
991
1128
  </div>
992
1129
  )}