@tpitre/story-ui 3.3.0 → 3.4.0

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 (281) hide show
  1. package/dist/cli/index.js +0 -0
  2. package/dist/cli/index.js.map +1 -0
  3. package/dist/cli/setup.js.map +1 -0
  4. package/dist/cloudflare-edge/src/mcp-session.js +462 -0
  5. package/dist/cloudflare-edge/src/types.js +4 -0
  6. package/dist/cloudflare-edge/src/worker.js +106 -0
  7. package/dist/cloudflare-pages/vite.config.js +14 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +12 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/mcp-server/index.js.map +1 -0
  13. package/dist/mcp-server/mcp-stdio-server.js.map +1 -0
  14. package/dist/mcp-server/routes/claude.js.map +1 -0
  15. package/dist/mcp-server/routes/components.js.map +1 -0
  16. package/dist/mcp-server/routes/generateStory.js.map +1 -0
  17. package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
  18. package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
  19. package/dist/mcp-server/routes/hybridStories.js +216 -0
  20. package/dist/mcp-server/routes/hybridStories.js.map +1 -0
  21. package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
  22. package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
  23. package/dist/mcp-server/routes/memoryStories.js +158 -0
  24. package/dist/mcp-server/routes/memoryStories.js.map +1 -0
  25. package/dist/mcp-server/routes/storySync.d.ts +26 -0
  26. package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
  27. package/dist/mcp-server/routes/storySync.js +147 -0
  28. package/dist/mcp-server/routes/storySync.js.map +1 -0
  29. package/dist/mcp-server/routes/updateStory.js +246 -0
  30. package/dist/mcp-server/sessionManager.d.ts +50 -0
  31. package/dist/mcp-server/sessionManager.d.ts.map +1 -0
  32. package/dist/mcp-server/sessionManager.js +125 -0
  33. package/dist/mcp-server/sessionManager.js.map +1 -0
  34. package/dist/playground/components/AIAssistant/AIAssistant.d.ts +6 -0
  35. package/dist/playground/components/AIAssistant/AIAssistant.d.ts.map +1 -0
  36. package/dist/playground/components/AIAssistant/AIAssistant.js +109 -0
  37. package/dist/playground/components/AIAssistant/AIAssistant.js.map +1 -0
  38. package/dist/playground/components/AIAssistant/AIAssistant.module.css +166 -0
  39. package/dist/playground/components/Canvas/Canvas.d.ts +9 -0
  40. package/dist/playground/components/Canvas/Canvas.d.ts.map +1 -0
  41. package/dist/playground/components/Canvas/Canvas.js +58 -0
  42. package/dist/playground/components/Canvas/Canvas.js.map +1 -0
  43. package/dist/playground/components/Canvas/Canvas.module.css +189 -0
  44. package/dist/playground/components/Canvas/CanvasWithDnd.d.ts +9 -0
  45. package/dist/playground/components/Canvas/CanvasWithDnd.d.ts.map +1 -0
  46. package/dist/playground/components/Canvas/CanvasWithDnd.js +158 -0
  47. package/dist/playground/components/Canvas/CanvasWithDnd.js.map +1 -0
  48. package/dist/playground/components/Canvas/ComponentRenderer.d.ts +15 -0
  49. package/dist/playground/components/Canvas/ComponentRenderer.d.ts.map +1 -0
  50. package/dist/playground/components/Canvas/ComponentRenderer.js +177 -0
  51. package/dist/playground/components/Canvas/ComponentRenderer.js.map +1 -0
  52. package/dist/playground/components/Canvas/DraggableComponent.d.ts +15 -0
  53. package/dist/playground/components/Canvas/DraggableComponent.d.ts.map +1 -0
  54. package/dist/playground/components/Canvas/DraggableComponent.js +49 -0
  55. package/dist/playground/components/Canvas/DraggableComponent.js.map +1 -0
  56. package/dist/playground/components/Canvas/index.d.ts +9 -0
  57. package/dist/playground/components/Canvas/index.d.ts.map +1 -0
  58. package/dist/playground/components/Canvas/index.js +5 -0
  59. package/dist/playground/components/Canvas/index.js.map +1 -0
  60. package/dist/playground/components/CodeView/CodeView.d.ts +12 -0
  61. package/dist/playground/components/CodeView/CodeView.d.ts.map +1 -0
  62. package/dist/playground/components/CodeView/CodeView.js +77 -0
  63. package/dist/playground/components/CodeView/CodeView.js.map +1 -0
  64. package/dist/playground/components/CodeView/CodeView.module.css +178 -0
  65. package/dist/playground/components/ComponentPalette/ComponentPalette.d.ts +17 -0
  66. package/dist/playground/components/ComponentPalette/ComponentPalette.d.ts.map +1 -0
  67. package/dist/playground/components/ComponentPalette/ComponentPalette.js +138 -0
  68. package/dist/playground/components/ComponentPalette/ComponentPalette.js.map +1 -0
  69. package/dist/playground/components/ComponentPalette/ComponentPalette.module.css +217 -0
  70. package/dist/playground/components/ComponentPalette/index.d.ts +3 -0
  71. package/dist/playground/components/ComponentPalette/index.d.ts.map +1 -0
  72. package/dist/playground/components/ComponentPalette/index.js +2 -0
  73. package/dist/playground/components/ComponentPalette/index.js.map +1 -0
  74. package/dist/playground/components/DropZone/DropZone.d.ts +17 -0
  75. package/dist/playground/components/DropZone/DropZone.d.ts.map +1 -0
  76. package/dist/playground/components/DropZone/DropZone.js +73 -0
  77. package/dist/playground/components/DropZone/DropZone.js.map +1 -0
  78. package/dist/playground/components/DropZone/DropZone.module.css +86 -0
  79. package/dist/playground/components/ExportDialog/ExportDialog.d.ts +10 -0
  80. package/dist/playground/components/ExportDialog/ExportDialog.d.ts.map +1 -0
  81. package/dist/playground/components/ExportDialog/ExportDialog.js +57 -0
  82. package/dist/playground/components/ExportDialog/ExportDialog.js.map +1 -0
  83. package/dist/playground/components/ExportDialog/ExportDialog.module.css +328 -0
  84. package/dist/playground/components/LayoutHelpers/LayoutHelpers.d.ts +134 -0
  85. package/dist/playground/components/LayoutHelpers/LayoutHelpers.d.ts.map +1 -0
  86. package/dist/playground/components/LayoutHelpers/LayoutHelpers.js +254 -0
  87. package/dist/playground/components/LayoutHelpers/LayoutHelpers.js.map +1 -0
  88. package/dist/playground/components/LayoutHelpers/index.d.ts +3 -0
  89. package/dist/playground/components/LayoutHelpers/index.d.ts.map +1 -0
  90. package/dist/playground/components/LayoutHelpers/index.js +2 -0
  91. package/dist/playground/components/LayoutHelpers/index.js.map +1 -0
  92. package/dist/playground/components/Playground/Playground.d.ts +10 -0
  93. package/dist/playground/components/Playground/Playground.d.ts.map +1 -0
  94. package/dist/playground/components/Playground/Playground.js +128 -0
  95. package/dist/playground/components/Playground/Playground.js.map +1 -0
  96. package/dist/playground/components/Playground/Playground.module.css +308 -0
  97. package/dist/playground/components/PropertiesPanel/PropertiesPanel.d.ts +10 -0
  98. package/dist/playground/components/PropertiesPanel/PropertiesPanel.d.ts.map +1 -0
  99. package/dist/playground/components/PropertiesPanel/PropertiesPanel.js +150 -0
  100. package/dist/playground/components/PropertiesPanel/PropertiesPanel.js.map +1 -0
  101. package/dist/playground/components/PropertiesPanel/PropertiesPanel.module.css +155 -0
  102. package/dist/playground/components/PropertiesPanel/index.d.ts +3 -0
  103. package/dist/playground/components/PropertiesPanel/index.d.ts.map +1 -0
  104. package/dist/playground/components/PropertiesPanel/index.js +2 -0
  105. package/dist/playground/components/PropertiesPanel/index.js.map +1 -0
  106. package/dist/playground/components/PropertyEditors/BooleanEditor.d.ts +12 -0
  107. package/dist/playground/components/PropertyEditors/BooleanEditor.d.ts.map +1 -0
  108. package/dist/playground/components/PropertyEditors/BooleanEditor.js +14 -0
  109. package/dist/playground/components/PropertyEditors/BooleanEditor.js.map +1 -0
  110. package/dist/playground/components/PropertyEditors/ColorEditor.d.ts +12 -0
  111. package/dist/playground/components/PropertyEditors/ColorEditor.d.ts.map +1 -0
  112. package/dist/playground/components/PropertyEditors/ColorEditor.js +62 -0
  113. package/dist/playground/components/PropertyEditors/ColorEditor.js.map +1 -0
  114. package/dist/playground/components/PropertyEditors/IconEditor.d.ts +12 -0
  115. package/dist/playground/components/PropertyEditors/IconEditor.d.ts.map +1 -0
  116. package/dist/playground/components/PropertyEditors/IconEditor.js +123 -0
  117. package/dist/playground/components/PropertyEditors/IconEditor.js.map +1 -0
  118. package/dist/playground/components/PropertyEditors/NumberEditor.d.ts +15 -0
  119. package/dist/playground/components/PropertyEditors/NumberEditor.d.ts.map +1 -0
  120. package/dist/playground/components/PropertyEditors/NumberEditor.js +46 -0
  121. package/dist/playground/components/PropertyEditors/NumberEditor.js.map +1 -0
  122. package/dist/playground/components/PropertyEditors/PropertyEditors.module.css +432 -0
  123. package/dist/playground/components/PropertyEditors/SelectEditor.d.ts +19 -0
  124. package/dist/playground/components/PropertyEditors/SelectEditor.d.ts.map +1 -0
  125. package/dist/playground/components/PropertyEditors/SelectEditor.js +17 -0
  126. package/dist/playground/components/PropertyEditors/SelectEditor.js.map +1 -0
  127. package/dist/playground/components/PropertyEditors/SpacingEditor.d.ts +19 -0
  128. package/dist/playground/components/PropertyEditors/SpacingEditor.d.ts.map +1 -0
  129. package/dist/playground/components/PropertyEditors/SpacingEditor.js +162 -0
  130. package/dist/playground/components/PropertyEditors/SpacingEditor.js.map +1 -0
  131. package/dist/playground/components/PropertyEditors/SpacingEditor.module.css +214 -0
  132. package/dist/playground/components/PropertyEditors/TextEditor.d.ts +14 -0
  133. package/dist/playground/components/PropertyEditors/TextEditor.d.ts.map +1 -0
  134. package/dist/playground/components/PropertyEditors/TextEditor.js +38 -0
  135. package/dist/playground/components/PropertyEditors/TextEditor.js.map +1 -0
  136. package/dist/playground/components/PropertyEditors/TokenEditor.d.ts +23 -0
  137. package/dist/playground/components/PropertyEditors/TokenEditor.d.ts.map +1 -0
  138. package/dist/playground/components/PropertyEditors/TokenEditor.js +50 -0
  139. package/dist/playground/components/PropertyEditors/TokenEditor.js.map +1 -0
  140. package/dist/playground/components/PropertyEditors/index.d.ts +20 -0
  141. package/dist/playground/components/PropertyEditors/index.d.ts.map +1 -0
  142. package/dist/playground/components/PropertyEditors/index.js +12 -0
  143. package/dist/playground/components/PropertyEditors/index.js.map +1 -0
  144. package/dist/playground/components/TreeView/TreeView.d.ts +10 -0
  145. package/dist/playground/components/TreeView/TreeView.d.ts.map +1 -0
  146. package/dist/playground/components/TreeView/TreeView.js +146 -0
  147. package/dist/playground/components/TreeView/TreeView.js.map +1 -0
  148. package/dist/playground/components/TreeView/TreeView.module.css +214 -0
  149. package/dist/playground/components/TreeView/index.d.ts +3 -0
  150. package/dist/playground/components/TreeView/index.d.ts.map +1 -0
  151. package/dist/playground/components/TreeView/index.js +2 -0
  152. package/dist/playground/components/TreeView/index.js.map +1 -0
  153. package/dist/playground/config/propertyDefinitions.d.ts +73 -0
  154. package/dist/playground/config/propertyDefinitions.d.ts.map +1 -0
  155. package/dist/playground/config/propertyDefinitions.js +809 -0
  156. package/dist/playground/config/propertyDefinitions.js.map +1 -0
  157. package/dist/playground/hooks/useKeyboardShortcuts.d.ts +38 -0
  158. package/dist/playground/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  159. package/dist/playground/hooks/useKeyboardShortcuts.js +191 -0
  160. package/dist/playground/hooks/useKeyboardShortcuts.js.map +1 -0
  161. package/dist/playground/index.d.ts +21 -0
  162. package/dist/playground/index.d.ts.map +1 -0
  163. package/dist/playground/index.js +23 -0
  164. package/dist/playground/index.js.map +1 -0
  165. package/dist/playground/services/CodeGenerator.d.ts +73 -0
  166. package/dist/playground/services/CodeGenerator.d.ts.map +1 -0
  167. package/dist/playground/services/CodeGenerator.js +359 -0
  168. package/dist/playground/services/CodeGenerator.js.map +1 -0
  169. package/dist/playground/services/DragDropManager.d.ts +95 -0
  170. package/dist/playground/services/DragDropManager.d.ts.map +1 -0
  171. package/dist/playground/services/DragDropManager.js +408 -0
  172. package/dist/playground/services/DragDropManager.js.map +1 -0
  173. package/dist/playground/services/StoryParser.d.ts +73 -0
  174. package/dist/playground/services/StoryParser.d.ts.map +1 -0
  175. package/dist/playground/services/StoryParser.js +419 -0
  176. package/dist/playground/services/StoryParser.js.map +1 -0
  177. package/dist/playground/store/playgroundStore.d.ts +86 -0
  178. package/dist/playground/store/playgroundStore.d.ts.map +1 -0
  179. package/dist/playground/store/playgroundStore.js +337 -0
  180. package/dist/playground/store/playgroundStore.js.map +1 -0
  181. package/dist/playground/stories/PlaygroundDragDrop.stories.d.ts +13 -0
  182. package/dist/playground/stories/PlaygroundDragDrop.stories.d.ts.map +1 -0
  183. package/dist/playground/stories/PlaygroundDragDrop.stories.js +227 -0
  184. package/dist/playground/stories/PlaygroundDragDrop.stories.js.map +1 -0
  185. package/dist/playground/stories/PlaygroundPhase4.stories.d.ts +13 -0
  186. package/dist/playground/stories/PlaygroundPhase4.stories.d.ts.map +1 -0
  187. package/dist/playground/stories/PlaygroundPhase4.stories.js +334 -0
  188. package/dist/playground/stories/PlaygroundPhase4.stories.js.map +1 -0
  189. package/dist/playground/stories/PlaygroundPhase5.stories.d.ts +14 -0
  190. package/dist/playground/stories/PlaygroundPhase5.stories.d.ts.map +1 -0
  191. package/dist/playground/stories/PlaygroundPhase5.stories.js +512 -0
  192. package/dist/playground/stories/PlaygroundPhase5.stories.js.map +1 -0
  193. package/dist/playground/stories/PlaygroundProperties.stories.d.ts +13 -0
  194. package/dist/playground/stories/PlaygroundProperties.stories.d.ts.map +1 -0
  195. package/dist/playground/stories/PlaygroundProperties.stories.js +342 -0
  196. package/dist/playground/stories/PlaygroundProperties.stories.js.map +1 -0
  197. package/dist/playground/types/index.d.ts +251 -0
  198. package/dist/playground/types/index.d.ts.map +1 -0
  199. package/dist/playground/types/index.js +5 -0
  200. package/dist/playground/types/index.js.map +1 -0
  201. package/dist/scripts/verify-framework-adapters.js +105 -0
  202. package/dist/story-generator/componentBlacklist.js.map +1 -0
  203. package/dist/story-generator/componentDiscovery.js.map +1 -0
  204. package/dist/story-generator/configLoader.js.map +1 -0
  205. package/dist/story-generator/considerationsLoader.js.map +1 -0
  206. package/dist/story-generator/documentation-sources.js.map +1 -0
  207. package/dist/story-generator/documentationLoader.js.map +1 -0
  208. package/dist/story-generator/dynamicPackageDiscovery.js.map +1 -0
  209. package/dist/story-generator/enhancedComponentDiscovery.js.map +1 -0
  210. package/dist/story-generator/generateStory.js.map +1 -0
  211. package/dist/story-generator/gitignoreManager.js.map +1 -0
  212. package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
  213. package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
  214. package/dist/story-generator/inMemoryStoryService.js +128 -0
  215. package/dist/story-generator/inMemoryStoryService.js.map +1 -0
  216. package/dist/story-generator/logger.js.map +1 -0
  217. package/dist/story-generator/postProcessStory.js.map +1 -0
  218. package/dist/story-generator/postgresStoryService.d.ts +56 -0
  219. package/dist/story-generator/postgresStoryService.d.ts.map +1 -0
  220. package/dist/story-generator/postgresStoryService.js +240 -0
  221. package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
  222. package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
  223. package/dist/story-generator/productionGitignoreManager.js +340 -0
  224. package/dist/story-generator/productionGitignoreManager.js.map +1 -0
  225. package/dist/story-generator/promptGenerator.js.map +1 -0
  226. package/dist/story-generator/providerPresets.d.ts +54 -0
  227. package/dist/story-generator/providerPresets.d.ts.map +1 -0
  228. package/dist/story-generator/providerPresets.js +214 -0
  229. package/dist/story-generator/storyHistory.js.map +1 -0
  230. package/dist/story-generator/storyServiceFactory.d.ts +22 -0
  231. package/dist/story-generator/storyServiceFactory.d.ts.map +1 -0
  232. package/dist/story-generator/storyServiceFactory.js +97 -0
  233. package/dist/story-generator/storyServiceInterface.d.ts +85 -0
  234. package/dist/story-generator/storyServiceInterface.d.ts.map +1 -0
  235. package/dist/story-generator/storyServiceInterface.js +5 -0
  236. package/dist/story-generator/storySync.d.ts +68 -0
  237. package/dist/story-generator/storySync.d.ts.map +1 -0
  238. package/dist/story-generator/storySync.js +201 -0
  239. package/dist/story-generator/storySync.js.map +1 -0
  240. package/dist/story-generator/storyTracker.js.map +1 -0
  241. package/dist/story-generator/storyValidator.js.map +1 -0
  242. package/dist/story-generator/test_validation.d.ts +2 -0
  243. package/dist/story-generator/test_validation.d.ts.map +1 -0
  244. package/dist/story-generator/test_validation.js +51 -0
  245. package/dist/story-generator/universalDesignSystemAdapter.js.map +1 -0
  246. package/dist/story-generator/urlRedirectService.js.map +1 -0
  247. package/dist/story-generator/validateStory.js.map +1 -0
  248. package/dist/story-ui.config.js.map +1 -0
  249. package/dist/story-ui.config.loader.d.ts +36 -0
  250. package/dist/story-ui.config.loader.d.ts.map +1 -0
  251. package/dist/story-ui.config.loader.js +205 -0
  252. package/dist/story-ui.config.loader.js.map +1 -0
  253. package/dist/temp/package/templates/StoryUI/StoryUIPanel.js +807 -0
  254. package/dist/temp/package/templates/StoryUI/StoryUIPanel.stories.js +37 -0
  255. package/dist/temp/package/templates/StoryUI/index.js +2 -0
  256. package/dist/templates/StoryUI/StoryUIPanel.js.map +1 -0
  257. package/dist/templates/StoryUI/StoryUIPanel.stories.js.map +1 -0
  258. package/dist/templates/StoryUI/index.js.map +1 -0
  259. package/dist/templates/StoryUI/manager.d.ts +14 -0
  260. package/dist/templates/StoryUI/manager.d.ts.map +1 -0
  261. package/dist/templates/production-app/src/App.d.ts +10 -0
  262. package/dist/templates/production-app/src/App.d.ts.map +1 -0
  263. package/dist/templates/production-app/src/App.js +653 -0
  264. package/dist/templates/production-app/src/LivePreviewRenderer.d.ts +24 -0
  265. package/dist/templates/production-app/src/LivePreviewRenderer.d.ts.map +1 -0
  266. package/dist/templates/production-app/src/LivePreviewRenderer.js +199 -0
  267. package/dist/templates/production-app/src/componentRegistry.d.ts +20 -0
  268. package/dist/templates/production-app/src/componentRegistry.d.ts.map +1 -0
  269. package/dist/templates/production-app/src/componentRegistry.js +316 -0
  270. package/dist/templates/production-app/src/main.d.ts +9 -0
  271. package/dist/templates/production-app/src/main.d.ts.map +1 -0
  272. package/dist/templates/production-app/src/main.js +18 -0
  273. package/dist/templates/production-app/vite.config.d.ts +3 -0
  274. package/dist/templates/production-app/vite.config.d.ts.map +1 -0
  275. package/dist/templates/production-app/vite.config.js +71 -0
  276. package/dist/test-storybooks/angular-material-storybook/src/main.js +66 -0
  277. package/dist/test-storybooks/chakra-storybook/vite.config.js +6 -0
  278. package/dist/test-storybooks/mantine-storybook/vite.config.js +93 -0
  279. package/dist/test-storybooks/web-components-shoelace/vite.config.js +9 -0
  280. package/dist/tsconfig.tsbuildinfo +1 -0
  281. package/package.json +1 -1
@@ -0,0 +1,807 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect } from 'react';
3
+ // Determine the MCP API port.
4
+ // 1. Check multiple possible environment variables/overrides in order of preference
5
+ // 2. Check VITE_STORY_UI_PORT from environment
6
+ // 3. Check window.__STORY_UI_PORT__ set by host application
7
+ // 4. Otherwise fall back to the default 4001.
8
+ const getApiPort = () => {
9
+ // Check for Vite environment variable
10
+ const vitePort = import.meta.env?.VITE_STORY_UI_PORT;
11
+ if (vitePort)
12
+ return String(vitePort);
13
+ // Check for window override (legacy support)
14
+ const windowOverride = window.__STORY_UI_PORT__;
15
+ if (windowOverride)
16
+ return String(windowOverride);
17
+ // Check for MCP port override set by stories file
18
+ const mcpOverride = window.STORY_UI_MCP_PORT;
19
+ if (mcpOverride)
20
+ return String(mcpOverride);
21
+ return '4001';
22
+ };
23
+ const MCP_API = `http://localhost:${getApiPort()}/story-ui/generate`;
24
+ const STORIES_API = `http://localhost:${getApiPort()}/story-ui/stories`;
25
+ const DELETE_API_BASE = `http://localhost:${getApiPort()}/story-ui/stories`;
26
+ const STORAGE_KEY = `story-ui-chats-${window.location.port}`;
27
+ const MAX_RECENT_CHATS = 20;
28
+ // Load from localStorage
29
+ const loadChats = () => {
30
+ try {
31
+ const stored = localStorage.getItem(STORAGE_KEY);
32
+ if (!stored)
33
+ return [];
34
+ const chats = JSON.parse(stored);
35
+ // Sort by lastUpdated and limit
36
+ return chats
37
+ .sort((a, b) => b.lastUpdated - a.lastUpdated)
38
+ .slice(0, MAX_RECENT_CHATS);
39
+ }
40
+ catch (e) {
41
+ console.error('Failed to load chats:', e);
42
+ return [];
43
+ }
44
+ };
45
+ // Save to localStorage
46
+ const saveChats = (chats) => {
47
+ try {
48
+ // Keep only the most recent chats
49
+ const toSave = chats
50
+ .sort((a, b) => b.lastUpdated - a.lastUpdated)
51
+ .slice(0, MAX_RECENT_CHATS);
52
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
53
+ }
54
+ catch (e) {
55
+ console.error('Failed to save chats:', e);
56
+ }
57
+ };
58
+ // Sync with memory stories from backend
59
+ const syncWithActualStories = async () => {
60
+ try {
61
+ const response = await fetch(STORIES_API);
62
+ if (!response.ok) {
63
+ console.error('Failed to fetch stories from backend');
64
+ return loadChats();
65
+ }
66
+ // Check if response is JSON
67
+ const contentType = response.headers.get('content-type');
68
+ if (!contentType || !contentType.includes('application/json')) {
69
+ console.error('Server returned non-JSON response, likely server not running or wrong port');
70
+ return loadChats();
71
+ }
72
+ const data = await response.json();
73
+ const memoryStories = data.stories || [];
74
+ // Load existing chats
75
+ const existingChats = loadChats();
76
+ // Create a map for quick lookup
77
+ const chatMap = new Map();
78
+ existingChats.forEach(chat => {
79
+ chatMap.set(chat.id, chat);
80
+ if (chat.fileName) {
81
+ chatMap.set(chat.fileName, chat);
82
+ }
83
+ });
84
+ // Update or add memory stories
85
+ memoryStories.forEach((story) => {
86
+ const storyId = story.storyId || story.fileName;
87
+ const existingChat = chatMap.get(storyId) || chatMap.get(story.fileName);
88
+ if (existingChat) {
89
+ // Update existing chat with latest info
90
+ existingChat.title = story.title || existingChat.title;
91
+ existingChat.fileName = story.fileName || existingChat.fileName;
92
+ existingChat.lastUpdated = new Date(story.updatedAt || story.createdAt).getTime();
93
+ }
94
+ else {
95
+ // Create new chat from memory story
96
+ const newChat = {
97
+ id: storyId,
98
+ title: story.title || story.fileName,
99
+ fileName: story.fileName,
100
+ conversation: [{
101
+ role: 'user',
102
+ content: story.prompt || `Generate ${story.title}`
103
+ }, {
104
+ role: 'ai',
105
+ content: `✅ Created story: "${story.title}"\n\nThis story was recovered from memory. You can continue updating it or view it in Storybook.`
106
+ }],
107
+ lastUpdated: new Date(story.updatedAt || story.createdAt).getTime()
108
+ };
109
+ chatMap.set(storyId, newChat);
110
+ }
111
+ });
112
+ // Convert back to array and save
113
+ const syncedChats = Array.from(chatMap.values());
114
+ saveChats(syncedChats);
115
+ return syncedChats;
116
+ }
117
+ catch (error) {
118
+ console.error('Error syncing with backend:', error);
119
+ return loadChats();
120
+ }
121
+ };
122
+ // Delete story and chat
123
+ const deleteStoryAndChat = async (chatId) => {
124
+ try {
125
+ // First try to delete from backend
126
+ const response = await fetch(`${DELETE_API_BASE}/${chatId}`, {
127
+ method: 'DELETE',
128
+ headers: { 'Content-Type': 'application/json' }
129
+ });
130
+ if (!response.ok) {
131
+ console.error('Failed to delete story from backend');
132
+ return false;
133
+ }
134
+ // Check if response is JSON
135
+ const contentType = response.headers.get('content-type');
136
+ if (!contentType || !contentType.includes('application/json')) {
137
+ console.error('Server returned non-JSON response, likely server not running or wrong port');
138
+ return false;
139
+ }
140
+ // Then remove from local storage
141
+ const chats = loadChats().filter(chat => chat.id !== chatId);
142
+ saveChats(chats);
143
+ return true;
144
+ }
145
+ catch (error) {
146
+ console.error('Error deleting story:', error);
147
+ return false;
148
+ }
149
+ };
150
+ // Test connection to MCP server
151
+ const testMCPConnection = async () => {
152
+ try {
153
+ const response = await fetch(STORIES_API, {
154
+ method: 'GET',
155
+ headers: { 'Content-Type': 'application/json' },
156
+ });
157
+ if (!response.ok) {
158
+ return { connected: false, error: `HTTP ${response.status}: ${response.statusText}` };
159
+ }
160
+ const contentType = response.headers.get('content-type');
161
+ if (!contentType || !contentType.includes('application/json')) {
162
+ return { connected: false, error: 'Server returned non-JSON response (likely wrong port or server not running)' };
163
+ }
164
+ return { connected: true };
165
+ }
166
+ catch (error) {
167
+ return { connected: false, error: error instanceof Error ? error.message : 'Unknown error' };
168
+ }
169
+ };
170
+ // Component styles
171
+ const STYLES = {
172
+ container: {
173
+ display: 'flex',
174
+ flexDirection: 'row',
175
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
176
+ height: '100vh',
177
+ overflow: 'hidden',
178
+ background: 'linear-gradient(135deg, #1e293b 0%, #334155 100%)',
179
+ color: '#e2e8f0',
180
+ },
181
+ // Sidebar
182
+ sidebar: {
183
+ width: '280px',
184
+ background: 'rgba(255, 255, 255, 0.05)',
185
+ borderRight: '1px solid rgba(255, 255, 255, 0.1)',
186
+ display: 'flex',
187
+ flexDirection: 'column',
188
+ backdropFilter: 'blur(10px)',
189
+ transition: 'width 0.3s ease',
190
+ position: 'relative',
191
+ },
192
+ sidebarCollapsed: {
193
+ width: '60px',
194
+ },
195
+ sidebarToggle: {
196
+ width: '100%',
197
+ padding: '10px 16px',
198
+ background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
199
+ color: 'white',
200
+ border: 'none',
201
+ borderRadius: '8px',
202
+ fontSize: '14px',
203
+ fontWeight: '500',
204
+ cursor: 'pointer',
205
+ marginBottom: '8px',
206
+ transition: 'all 0.2s ease',
207
+ boxShadow: '0 2px 8px rgba(59, 130, 246, 0.3)',
208
+ display: 'flex',
209
+ alignItems: 'center',
210
+ justifyContent: 'center',
211
+ gap: '8px',
212
+ },
213
+ newChatButton: {
214
+ width: '100%',
215
+ padding: '10px 16px',
216
+ background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
217
+ color: 'white',
218
+ border: 'none',
219
+ borderRadius: '8px',
220
+ fontSize: '14px',
221
+ fontWeight: '500',
222
+ cursor: 'pointer',
223
+ marginBottom: '16px',
224
+ transition: 'all 0.2s ease',
225
+ boxShadow: '0 2px 8px rgba(59, 130, 246, 0.2)',
226
+ },
227
+ chatItem: {
228
+ padding: '12px 16px',
229
+ marginBottom: '8px',
230
+ background: 'rgba(255, 255, 255, 0.08)',
231
+ borderRadius: '8px',
232
+ cursor: 'pointer',
233
+ transition: 'all 0.2s ease',
234
+ position: 'relative',
235
+ paddingRight: '40px',
236
+ },
237
+ chatItemActive: {
238
+ background: 'rgba(59, 130, 246, 0.2)',
239
+ borderLeft: '3px solid #3b82f6',
240
+ },
241
+ chatItemTitle: {
242
+ fontSize: '14px',
243
+ fontWeight: '500',
244
+ marginBottom: '4px',
245
+ whiteSpace: 'nowrap',
246
+ overflow: 'hidden',
247
+ textOverflow: 'ellipsis',
248
+ },
249
+ chatItemTime: {
250
+ fontSize: '12px',
251
+ color: '#94a3b8',
252
+ },
253
+ deleteButton: {
254
+ position: 'absolute',
255
+ right: '8px',
256
+ top: '50%',
257
+ transform: 'translateY(-50%)',
258
+ background: 'rgba(239, 68, 68, 0.8)',
259
+ color: 'white',
260
+ border: 'none',
261
+ borderRadius: '4px',
262
+ padding: '4px 8px',
263
+ fontSize: '12px',
264
+ cursor: 'pointer',
265
+ opacity: 0,
266
+ transition: 'opacity 0.2s ease',
267
+ },
268
+ // Main content
269
+ mainContent: {
270
+ flex: 1,
271
+ display: 'flex',
272
+ flexDirection: 'column',
273
+ overflow: 'hidden',
274
+ },
275
+ chatHeader: {
276
+ padding: '20px 24px',
277
+ borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
278
+ background: 'rgba(255, 255, 255, 0.05)',
279
+ backdropFilter: 'blur(10px)',
280
+ },
281
+ chatContainer: {
282
+ flex: 1,
283
+ padding: '24px',
284
+ overflowY: 'auto',
285
+ scrollBehavior: 'smooth',
286
+ },
287
+ emptyState: {
288
+ color: '#94a3b8',
289
+ textAlign: 'center',
290
+ marginTop: '60px',
291
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
292
+ },
293
+ emptyStateTitle: {
294
+ fontSize: '16px',
295
+ fontWeight: '500',
296
+ marginBottom: '8px',
297
+ color: '#cbd5e1',
298
+ },
299
+ emptyStateSubtitle: {
300
+ fontSize: '13px',
301
+ color: '#64748b',
302
+ },
303
+ // Message bubbles
304
+ messageContainer: {
305
+ display: 'flex',
306
+ marginBottom: '16px',
307
+ },
308
+ userMessage: {
309
+ background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
310
+ color: '#ffffff',
311
+ borderRadius: '18px 18px 4px 18px',
312
+ padding: '12px 16px',
313
+ maxWidth: '80%',
314
+ marginLeft: 'auto',
315
+ fontSize: '14px',
316
+ lineHeight: '1.5',
317
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
318
+ boxShadow: '0 2px 12px rgba(59, 130, 246, 0.3)',
319
+ wordWrap: 'break-word',
320
+ },
321
+ aiMessage: {
322
+ background: 'rgba(255, 255, 255, 0.95)',
323
+ color: '#1f2937',
324
+ borderRadius: '18px 18px 18px 4px',
325
+ padding: '12px 16px',
326
+ maxWidth: '80%',
327
+ fontSize: '14px',
328
+ lineHeight: '1.5',
329
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
330
+ boxShadow: '0 2px 12px rgba(0, 0, 0, 0.1)',
331
+ wordWrap: 'break-word',
332
+ whiteSpace: 'pre-wrap',
333
+ },
334
+ loadingMessage: {
335
+ background: 'rgba(255, 255, 255, 0.9)',
336
+ color: '#6b7280',
337
+ borderRadius: '18px 18px 18px 4px',
338
+ padding: '12px 16px',
339
+ fontSize: '14px',
340
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
341
+ display: 'flex',
342
+ alignItems: 'center',
343
+ gap: '8px',
344
+ },
345
+ // Input form
346
+ inputForm: {
347
+ display: 'flex',
348
+ alignItems: 'center',
349
+ gap: '12px',
350
+ margin: '0 24px 24px 24px',
351
+ padding: '16px',
352
+ background: 'rgba(255, 255, 255, 0.05)',
353
+ borderRadius: '12px',
354
+ border: '1px solid rgba(255, 255, 255, 0.1)',
355
+ backdropFilter: 'blur(10px)',
356
+ },
357
+ textInput: {
358
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
359
+ flex: 1,
360
+ padding: '12px 16px',
361
+ borderRadius: '8px',
362
+ border: '1px solid rgba(255, 255, 255, 0.2)',
363
+ fontSize: '14px',
364
+ color: '#1f2937',
365
+ background: '#ffffff',
366
+ outline: 'none',
367
+ transition: 'all 0.2s ease',
368
+ boxSizing: 'border-box',
369
+ },
370
+ sendButton: {
371
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
372
+ padding: '12px 20px',
373
+ borderRadius: '8px',
374
+ border: 'none',
375
+ background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
376
+ color: '#ffffff',
377
+ fontSize: '14px',
378
+ fontWeight: '500',
379
+ cursor: 'pointer',
380
+ display: 'flex',
381
+ alignItems: 'center',
382
+ gap: '6px',
383
+ transition: 'all 0.2s ease',
384
+ boxShadow: '0 2px 8px rgba(16, 185, 129, 0.3)',
385
+ },
386
+ errorMessage: {
387
+ background: 'rgba(248, 113, 113, 0.1)',
388
+ color: '#f87171',
389
+ padding: '12px 16px',
390
+ borderRadius: '8px',
391
+ fontSize: '14px',
392
+ marginBottom: '16px',
393
+ border: '1px solid rgba(248, 113, 113, 0.3)',
394
+ },
395
+ loadingDots: {
396
+ display: 'inline-block',
397
+ animation: 'loadingDots 1.4s infinite',
398
+ },
399
+ '@keyframes loadingDots': {
400
+ '0%': { content: '""' },
401
+ '25%': { content: '"."' },
402
+ '50%': { content: '".."' },
403
+ '75%': { content: '"..."' },
404
+ },
405
+ codeBlock: {
406
+ background: '#1e293b',
407
+ padding: '12px 16px',
408
+ borderRadius: '8px',
409
+ fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
410
+ fontSize: '13px',
411
+ lineHeight: '1.6',
412
+ overflowX: 'auto',
413
+ marginTop: '8px',
414
+ border: '1px solid rgba(255, 255, 255, 0.1)',
415
+ },
416
+ };
417
+ // Add custom style for loading animation
418
+ const styleSheet = document.createElement('style');
419
+ styleSheet.textContent = `
420
+ @keyframes loadingDots {
421
+ 0%, 20% { content: "."; }
422
+ 40% { content: ".."; }
423
+ 60%, 100% { content: "..."; }
424
+ }
425
+
426
+ .loading-dots::after {
427
+ content: ".";
428
+ animation: loadingDots 1.4s infinite;
429
+ }
430
+ `;
431
+ document.head.appendChild(styleSheet);
432
+ // Helper function to format timestamp
433
+ const formatTime = (timestamp) => {
434
+ const date = new Date(timestamp);
435
+ const now = new Date();
436
+ const diffMs = now.getTime() - date.getTime();
437
+ const diffMins = Math.floor(diffMs / 60000);
438
+ const diffHours = Math.floor(diffMs / 3600000);
439
+ const diffDays = Math.floor(diffMs / 86400000);
440
+ if (diffMins < 1)
441
+ return 'just now';
442
+ if (diffMins < 60)
443
+ return `${diffMins}m ago`;
444
+ if (diffHours < 24)
445
+ return `${diffHours}h ago`;
446
+ if (diffDays < 7)
447
+ return `${diffDays}d ago`;
448
+ return date.toLocaleDateString();
449
+ };
450
+ // Main component
451
+ export function StoryUIPanel() {
452
+ const [input, setInput] = useState('');
453
+ const [conversation, setConversation] = useState([]);
454
+ const [loading, setLoading] = useState(false);
455
+ const [error, setError] = useState(null);
456
+ const [recentChats, setRecentChats] = useState([]);
457
+ const [activeChatId, setActiveChatId] = useState(null);
458
+ const [activeTitle, setActiveTitle] = useState('');
459
+ const [sidebarOpen, setSidebarOpen] = useState(true);
460
+ const [connectionStatus, setConnectionStatus] = useState({ connected: false });
461
+ const chatEndRef = useRef(null);
462
+ const inputRef = useRef(null);
463
+ // Load and sync chats on mount
464
+ useEffect(() => {
465
+ const initializeChats = async () => {
466
+ // Test connection first
467
+ const connectionTest = await testMCPConnection();
468
+ setConnectionStatus(connectionTest);
469
+ if (connectionTest.connected) {
470
+ const syncedChats = await syncWithActualStories();
471
+ const sortedChats = syncedChats.sort((a, b) => b.lastUpdated - a.lastUpdated).slice(0, MAX_RECENT_CHATS);
472
+ setRecentChats(sortedChats);
473
+ if (sortedChats.length > 0) {
474
+ setConversation(sortedChats[0].conversation);
475
+ setActiveChatId(sortedChats[0].id);
476
+ setActiveTitle(sortedChats[0].title);
477
+ }
478
+ }
479
+ else {
480
+ // Load from local storage if server is not available
481
+ const localChats = loadChats();
482
+ setRecentChats(localChats);
483
+ }
484
+ };
485
+ initializeChats();
486
+ }, []);
487
+ // Scroll to bottom on new message
488
+ useEffect(() => {
489
+ if (chatEndRef.current) {
490
+ chatEndRef.current.scrollIntoView({ behavior: 'smooth' });
491
+ }
492
+ }, [conversation, loading]);
493
+ const handleSend = async (e) => {
494
+ if (e)
495
+ e.preventDefault();
496
+ if (!input.trim())
497
+ return;
498
+ setError(null);
499
+ setLoading(true);
500
+ // Test connection before sending
501
+ const connectionTest = await testMCPConnection();
502
+ setConnectionStatus(connectionTest);
503
+ if (!connectionTest.connected) {
504
+ setError(`Cannot connect to MCP server: ${connectionTest.error || 'Server not running'}`);
505
+ setLoading(false);
506
+ return;
507
+ }
508
+ const newConversation = [...conversation, { role: 'user', content: input }];
509
+ setConversation(newConversation);
510
+ setInput('');
511
+ try {
512
+ const res = await fetch(MCP_API, {
513
+ method: 'POST',
514
+ headers: { 'Content-Type': 'application/json' },
515
+ body: JSON.stringify({
516
+ prompt: input,
517
+ conversation: newConversation,
518
+ fileName: activeChatId || undefined,
519
+ }),
520
+ });
521
+ // Check if response is JSON
522
+ const contentType = res.headers.get('content-type');
523
+ if (!contentType || !contentType.includes('application/json')) {
524
+ const text = await res.text();
525
+ throw new Error(`Server returned non-JSON response (likely server not running or wrong port). Response: ${text.substring(0, 200)}...`);
526
+ }
527
+ const data = await res.json();
528
+ if (!res.ok || !data.success)
529
+ throw new Error(data.error || 'Story generation failed');
530
+ // Create user-friendly response message instead of showing raw markup
531
+ let responseMessage;
532
+ let statusIcon = '✅';
533
+ // Check for validation issues
534
+ if (data.validation && data.validation.hasWarnings) {
535
+ statusIcon = '⚠️';
536
+ const warningCount = data.validation.warnings.length;
537
+ const errorCount = data.validation.errors.length;
538
+ if (errorCount > 0) {
539
+ statusIcon = '🔧';
540
+ }
541
+ }
542
+ if (data.isUpdate) {
543
+ responseMessage = `${statusIcon} Updated your story: "${data.title}"\n\nI've made the requested changes while keeping the same layout structure. You can view the updated component in Storybook.`;
544
+ }
545
+ else {
546
+ responseMessage = `${statusIcon} Created new story: "${data.title}"\n\nI've generated the component with the requested features. You can view it in Storybook where you'll see both the rendered component and its markup in the Docs tab.`;
547
+ // IMPORTANT: Add a note about refreshing for new stories
548
+ responseMessage += '\n\n💡 **Note**: If you don\'t see the story immediately, you may need to refresh your Storybook page (Cmd/Ctrl + R) for new stories to appear in the sidebar.';
549
+ }
550
+ // Add validation information if there are issues
551
+ if (data.validation && data.validation.hasWarnings) {
552
+ responseMessage += '\n\n';
553
+ if (data.validation.errors.length > 0) {
554
+ responseMessage += `🔧 **Auto-fixed ${data.validation.errors.length} syntax error(s):**\n`;
555
+ responseMessage += data.validation.errors.slice(0, 3).map(error => ` • ${error}`).join('\n');
556
+ if (data.validation.errors.length > 3) {
557
+ responseMessage += `\n • ... and ${data.validation.errors.length - 3} more`;
558
+ }
559
+ responseMessage += '\n';
560
+ }
561
+ if (data.validation.warnings.length > 0) {
562
+ responseMessage += `⚠️ **Warnings:**\n`;
563
+ responseMessage += data.validation.warnings.slice(0, 2).map(warning => ` • ${warning}`).join('\n');
564
+ if (data.validation.warnings.length > 2) {
565
+ responseMessage += `\n • ... and ${data.validation.warnings.length - 2} more`;
566
+ }
567
+ }
568
+ }
569
+ const aiMsg = { role: 'ai', content: responseMessage };
570
+ const updatedConversation = [...newConversation, aiMsg];
571
+ setConversation(updatedConversation);
572
+ // Determine if this is an update or new chat
573
+ // Check if we have an active chat AND the backend indicates this is an update
574
+ const isUpdate = activeChatId && conversation.length > 0 && (data.isUpdate ||
575
+ data.fileName === activeChatId ||
576
+ // Also check if fileName matches any existing chat's fileName
577
+ recentChats.some(chat => chat.fileName === data.fileName && chat.id === activeChatId));
578
+ console.log('Update detection:', {
579
+ activeChatId,
580
+ conversationLength: conversation.length,
581
+ dataIsUpdate: data.isUpdate,
582
+ dataFileName: data.fileName,
583
+ isUpdate
584
+ });
585
+ if (isUpdate) {
586
+ // Update existing chat session
587
+ const chatTitle = activeTitle; // Keep existing title for updates
588
+ const updatedSession = {
589
+ id: activeChatId,
590
+ title: chatTitle,
591
+ fileName: data.fileName || activeChatId,
592
+ conversation: updatedConversation,
593
+ lastUpdated: Date.now(),
594
+ };
595
+ const chats = loadChats();
596
+ const chatIndex = chats.findIndex(c => c.id === activeChatId);
597
+ if (chatIndex !== -1) {
598
+ chats[chatIndex] = updatedSession;
599
+ }
600
+ saveChats(chats);
601
+ setRecentChats(chats);
602
+ console.log('Updated existing chat:', activeChatId);
603
+ }
604
+ else {
605
+ // Create new chat session - use storyId from backend for consistency
606
+ const chatId = data.storyId || data.fileName || data.outPath || Date.now().toString();
607
+ const chatTitle = data.title || input;
608
+ setActiveChatId(chatId);
609
+ setActiveTitle(chatTitle);
610
+ const newSession = {
611
+ id: chatId,
612
+ title: chatTitle,
613
+ fileName: data.fileName || '',
614
+ conversation: updatedConversation,
615
+ lastUpdated: Date.now(),
616
+ };
617
+ const chats = loadChats().filter(c => c.id !== chatId);
618
+ chats.unshift(newSession);
619
+ if (chats.length > MAX_RECENT_CHATS) {
620
+ chats.splice(MAX_RECENT_CHATS);
621
+ }
622
+ saveChats(chats);
623
+ setRecentChats(chats);
624
+ console.log('Created new chat:', chatId);
625
+ }
626
+ }
627
+ catch (err) {
628
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
629
+ setError(errorMessage);
630
+ const errorConversation = [...newConversation, { role: 'ai', content: `Error: ${errorMessage}` }];
631
+ setConversation(errorConversation);
632
+ // IMPORTANT: Create/update chat session even on error so retries continue the same conversation
633
+ const isUpdate = activeChatId && conversation.length > 0;
634
+ if (isUpdate) {
635
+ // Update existing chat with error
636
+ const updatedSession = {
637
+ id: activeChatId,
638
+ title: activeTitle,
639
+ fileName: activeChatId,
640
+ conversation: errorConversation,
641
+ lastUpdated: Date.now(),
642
+ };
643
+ const chats = loadChats();
644
+ const chatIndex = chats.findIndex(c => c.id === activeChatId);
645
+ if (chatIndex !== -1) {
646
+ chats[chatIndex] = updatedSession;
647
+ }
648
+ saveChats(chats);
649
+ setRecentChats(chats);
650
+ }
651
+ else {
652
+ // Create new chat session for error (so retries can continue it)
653
+ const chatId = `error-${Date.now()}`;
654
+ const chatTitle = input.length > 30 ? input.substring(0, 30) + '...' : input;
655
+ setActiveChatId(chatId);
656
+ setActiveTitle(chatTitle);
657
+ const newSession = {
658
+ id: chatId,
659
+ title: chatTitle,
660
+ fileName: '',
661
+ conversation: errorConversation,
662
+ lastUpdated: Date.now(),
663
+ };
664
+ const chats = loadChats();
665
+ chats.unshift(newSession);
666
+ if (chats.length > MAX_RECENT_CHATS) {
667
+ chats.splice(MAX_RECENT_CHATS);
668
+ }
669
+ saveChats(chats);
670
+ setRecentChats(chats);
671
+ }
672
+ }
673
+ finally {
674
+ setLoading(false);
675
+ }
676
+ };
677
+ const handleSelectChat = (chat) => {
678
+ setConversation(chat.conversation);
679
+ setActiveChatId(chat.id);
680
+ setActiveTitle(chat.title);
681
+ };
682
+ const handleNewChat = () => {
683
+ setConversation([]);
684
+ setActiveChatId(null);
685
+ setActiveTitle('');
686
+ };
687
+ const handleDeleteChat = async (chatId, e) => {
688
+ e.stopPropagation(); // Prevent selecting the chat
689
+ if (confirm('Delete this story and chat? This action cannot be undone.')) {
690
+ const success = await deleteStoryAndChat(chatId);
691
+ if (success) {
692
+ // Update local state
693
+ const updatedChats = recentChats.filter(chat => chat.id !== chatId);
694
+ setRecentChats(updatedChats);
695
+ // If we deleted the active chat, switch to another or clear
696
+ if (activeChatId === chatId) {
697
+ if (updatedChats.length > 0) {
698
+ setConversation(updatedChats[0].conversation);
699
+ setActiveChatId(updatedChats[0].id);
700
+ setActiveTitle(updatedChats[0].title);
701
+ }
702
+ else {
703
+ handleNewChat();
704
+ }
705
+ }
706
+ }
707
+ else {
708
+ alert('Failed to delete story. Please try again.');
709
+ }
710
+ }
711
+ };
712
+ return (_jsxs("div", { style: STYLES.container, children: [_jsxs("div", { style: {
713
+ ...STYLES.sidebar,
714
+ ...(sidebarOpen ? {} : STYLES.sidebarCollapsed),
715
+ }, children: [sidebarOpen && (_jsxs("div", { style: { flex: 1, overflowY: 'auto', padding: '16px' }, children: [_jsx("button", { onClick: () => setSidebarOpen(false), style: STYLES.sidebarToggle, title: "Collapse sidebar", onMouseEnter: (e) => {
716
+ e.currentTarget.style.transform = 'translateY(-1px)';
717
+ e.currentTarget.style.boxShadow = '0 4px 16px rgba(59, 130, 246, 0.4)';
718
+ }, onMouseLeave: (e) => {
719
+ e.currentTarget.style.transform = 'translateY(0)';
720
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(59, 130, 246, 0.3)';
721
+ }, children: "\u2630 Chats" }), _jsx("button", { onClick: handleNewChat, style: STYLES.newChatButton, onMouseEnter: (e) => {
722
+ e.currentTarget.style.transform = 'translateY(-1px)';
723
+ e.currentTarget.style.boxShadow = '0 4px 16px rgba(59, 130, 246, 0.4)';
724
+ }, onMouseLeave: (e) => {
725
+ e.currentTarget.style.transform = 'translateY(0)';
726
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(59, 130, 246, 0.2)';
727
+ }, children: "+ New Chat" }), recentChats.length > 0 && (_jsx("div", { style: {
728
+ color: '#64748b',
729
+ fontSize: '12px',
730
+ marginBottom: '8px',
731
+ fontWeight: '500',
732
+ textTransform: 'uppercase',
733
+ letterSpacing: '0.05em',
734
+ }, children: "Recent Chats" })), recentChats.map(chat => (_jsxs("div", { onClick: () => handleSelectChat(chat), style: {
735
+ ...STYLES.chatItem,
736
+ ...(activeChatId === chat.id ? STYLES.chatItemActive : {}),
737
+ }, onMouseEnter: (e) => {
738
+ if (activeChatId !== chat.id) {
739
+ e.currentTarget.style.background = 'rgba(255, 255, 255, 0.12)';
740
+ }
741
+ const deleteBtn = e.currentTarget.querySelector('.delete-btn');
742
+ if (deleteBtn)
743
+ deleteBtn.style.opacity = '1';
744
+ }, onMouseLeave: (e) => {
745
+ if (activeChatId !== chat.id) {
746
+ e.currentTarget.style.background = 'rgba(255, 255, 255, 0.08)';
747
+ }
748
+ const deleteBtn = e.currentTarget.querySelector('.delete-btn');
749
+ if (deleteBtn)
750
+ deleteBtn.style.opacity = '0';
751
+ }, children: [_jsx("div", { style: STYLES.chatItemTitle, children: chat.title }), _jsx("div", { style: STYLES.chatItemTime, children: formatTime(chat.lastUpdated) }), _jsx("button", { className: "delete-btn", onClick: (e) => handleDeleteChat(chat.id, e), style: STYLES.deleteButton, title: "Delete chat", children: "\u2715" })] }, chat.id)))] })), !sidebarOpen && (_jsx("div", { style: { padding: '16px' }, children: _jsx("button", { onClick: () => setSidebarOpen(true), style: {
752
+ ...STYLES.sidebarToggle,
753
+ width: '40px',
754
+ height: '40px',
755
+ padding: '0',
756
+ fontSize: '16px',
757
+ }, title: "Expand sidebar", onMouseEnter: (e) => {
758
+ e.currentTarget.style.transform = 'scale(1.05)';
759
+ e.currentTarget.style.boxShadow = '0 4px 16px rgba(59, 130, 246, 0.4)';
760
+ }, onMouseLeave: (e) => {
761
+ e.currentTarget.style.transform = 'scale(1)';
762
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(59, 130, 246, 0.3)';
763
+ }, children: "\u2630" }) }))] }), _jsxs("div", { style: STYLES.mainContent, children: [_jsxs("div", { style: STYLES.chatHeader, children: [_jsx("h1", { style: {
764
+ fontSize: '24px',
765
+ margin: 0,
766
+ fontWeight: '600',
767
+ background: 'linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%)',
768
+ WebkitBackgroundClip: 'text',
769
+ WebkitTextFillColor: 'transparent',
770
+ display: 'inline-block'
771
+ }, children: "Story UI" }), _jsx("p", { style: { fontSize: '14px', margin: '4px 0 0 0', color: '#94a3b8' }, children: "Generate Storybook stories with AI" }), _jsxs("div", { style: {
772
+ display: 'flex',
773
+ alignItems: 'center',
774
+ gap: '8px',
775
+ marginTop: '8px',
776
+ fontSize: '12px'
777
+ }, children: [_jsx("div", { style: {
778
+ width: '8px',
779
+ height: '8px',
780
+ borderRadius: '50%',
781
+ backgroundColor: connectionStatus.connected ? '#10b981' : '#f87171'
782
+ } }), _jsx("span", { style: { color: connectionStatus.connected ? '#10b981' : '#f87171' }, children: connectionStatus.connected
783
+ ? `Connected to MCP server (port ${getApiPort()})`
784
+ : `Disconnected: ${connectionStatus.error || 'Server not running'}` })] })] }), _jsxs("div", { style: STYLES.chatContainer, children: [error && (_jsx("div", { style: STYLES.errorMessage, children: error })), conversation.length === 0 && !loading && (_jsxs("div", { style: STYLES.emptyState, children: [_jsx("div", { style: STYLES.emptyStateTitle, children: "Start a new conversation" }), _jsx("div", { style: STYLES.emptyStateSubtitle, children: "Describe the UI component you'd like to create" })] })), conversation.map((msg, i) => (_jsx("div", { style: STYLES.messageContainer, children: _jsx("div", { style: msg.role === 'user' ? STYLES.userMessage : STYLES.aiMessage, children: msg.content }) }, i))), loading && (_jsx("div", { style: STYLES.messageContainer, children: _jsxs("div", { style: STYLES.loadingMessage, children: [_jsx("span", { children: "Generating story" }), _jsx("span", { className: "loading-dots" })] }) })), _jsx("div", { ref: chatEndRef })] }), _jsxs("form", { onSubmit: handleSend, style: STYLES.inputForm, children: [_jsx("input", { ref: inputRef, type: "text", value: input, onChange: e => setInput(e.target.value), placeholder: "Describe a UI component...", style: STYLES.textInput, onFocus: (e) => {
785
+ e.currentTarget.style.borderColor = 'rgba(59, 130, 246, 0.5)';
786
+ e.currentTarget.style.boxShadow = '0 0 0 3px rgba(59, 130, 246, 0.1)';
787
+ }, onBlur: (e) => {
788
+ e.currentTarget.style.borderColor = 'rgba(255, 255, 255, 0.2)';
789
+ e.currentTarget.style.boxShadow = 'none';
790
+ } }), _jsxs("button", { type: "submit", disabled: loading || !input.trim(), style: {
791
+ ...STYLES.sendButton,
792
+ ...(loading || !input.trim() ? {
793
+ opacity: 0.5,
794
+ cursor: 'not-allowed',
795
+ background: '#6b7280',
796
+ boxShadow: 'none'
797
+ } : {})
798
+ }, onMouseEnter: (e) => {
799
+ if (!loading && input.trim()) {
800
+ e.currentTarget.style.transform = 'scale(1.05)';
801
+ e.currentTarget.style.boxShadow = '0 4px 16px rgba(16, 185, 129, 0.4)';
802
+ }
803
+ }, onMouseLeave: (e) => {
804
+ e.currentTarget.style.transform = 'scale(1)';
805
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(16, 185, 129, 0.3)';
806
+ }, children: [_jsx("span", { children: "Send" }), _jsx("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })] })] })] })] }));
807
+ }