@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.
- package/dist/cli/index.js +0 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cloudflare-edge/src/mcp-session.js +462 -0
- package/dist/cloudflare-edge/src/types.js +4 -0
- package/dist/cloudflare-edge/src/worker.js +106 -0
- package/dist/cloudflare-pages/vite.config.js +14 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/mcp-server/mcp-stdio-server.js.map +1 -0
- package/dist/mcp-server/routes/claude.js.map +1 -0
- package/dist/mcp-server/routes/components.js.map +1 -0
- package/dist/mcp-server/routes/generateStory.js.map +1 -0
- package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
- package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/hybridStories.js +216 -0
- package/dist/mcp-server/routes/hybridStories.js.map +1 -0
- package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
- package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/memoryStories.js +158 -0
- package/dist/mcp-server/routes/memoryStories.js.map +1 -0
- package/dist/mcp-server/routes/storySync.d.ts +26 -0
- package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
- package/dist/mcp-server/routes/storySync.js +147 -0
- package/dist/mcp-server/routes/storySync.js.map +1 -0
- package/dist/mcp-server/routes/updateStory.js +246 -0
- package/dist/mcp-server/sessionManager.d.ts +50 -0
- package/dist/mcp-server/sessionManager.d.ts.map +1 -0
- package/dist/mcp-server/sessionManager.js +125 -0
- package/dist/mcp-server/sessionManager.js.map +1 -0
- package/dist/playground/components/AIAssistant/AIAssistant.d.ts +6 -0
- package/dist/playground/components/AIAssistant/AIAssistant.d.ts.map +1 -0
- package/dist/playground/components/AIAssistant/AIAssistant.js +109 -0
- package/dist/playground/components/AIAssistant/AIAssistant.js.map +1 -0
- package/dist/playground/components/AIAssistant/AIAssistant.module.css +166 -0
- package/dist/playground/components/Canvas/Canvas.d.ts +9 -0
- package/dist/playground/components/Canvas/Canvas.d.ts.map +1 -0
- package/dist/playground/components/Canvas/Canvas.js +58 -0
- package/dist/playground/components/Canvas/Canvas.js.map +1 -0
- package/dist/playground/components/Canvas/Canvas.module.css +189 -0
- package/dist/playground/components/Canvas/CanvasWithDnd.d.ts +9 -0
- package/dist/playground/components/Canvas/CanvasWithDnd.d.ts.map +1 -0
- package/dist/playground/components/Canvas/CanvasWithDnd.js +158 -0
- package/dist/playground/components/Canvas/CanvasWithDnd.js.map +1 -0
- package/dist/playground/components/Canvas/ComponentRenderer.d.ts +15 -0
- package/dist/playground/components/Canvas/ComponentRenderer.d.ts.map +1 -0
- package/dist/playground/components/Canvas/ComponentRenderer.js +177 -0
- package/dist/playground/components/Canvas/ComponentRenderer.js.map +1 -0
- package/dist/playground/components/Canvas/DraggableComponent.d.ts +15 -0
- package/dist/playground/components/Canvas/DraggableComponent.d.ts.map +1 -0
- package/dist/playground/components/Canvas/DraggableComponent.js +49 -0
- package/dist/playground/components/Canvas/DraggableComponent.js.map +1 -0
- package/dist/playground/components/Canvas/index.d.ts +9 -0
- package/dist/playground/components/Canvas/index.d.ts.map +1 -0
- package/dist/playground/components/Canvas/index.js +5 -0
- package/dist/playground/components/Canvas/index.js.map +1 -0
- package/dist/playground/components/CodeView/CodeView.d.ts +12 -0
- package/dist/playground/components/CodeView/CodeView.d.ts.map +1 -0
- package/dist/playground/components/CodeView/CodeView.js +77 -0
- package/dist/playground/components/CodeView/CodeView.js.map +1 -0
- package/dist/playground/components/CodeView/CodeView.module.css +178 -0
- package/dist/playground/components/ComponentPalette/ComponentPalette.d.ts +17 -0
- package/dist/playground/components/ComponentPalette/ComponentPalette.d.ts.map +1 -0
- package/dist/playground/components/ComponentPalette/ComponentPalette.js +138 -0
- package/dist/playground/components/ComponentPalette/ComponentPalette.js.map +1 -0
- package/dist/playground/components/ComponentPalette/ComponentPalette.module.css +217 -0
- package/dist/playground/components/ComponentPalette/index.d.ts +3 -0
- package/dist/playground/components/ComponentPalette/index.d.ts.map +1 -0
- package/dist/playground/components/ComponentPalette/index.js +2 -0
- package/dist/playground/components/ComponentPalette/index.js.map +1 -0
- package/dist/playground/components/DropZone/DropZone.d.ts +17 -0
- package/dist/playground/components/DropZone/DropZone.d.ts.map +1 -0
- package/dist/playground/components/DropZone/DropZone.js +73 -0
- package/dist/playground/components/DropZone/DropZone.js.map +1 -0
- package/dist/playground/components/DropZone/DropZone.module.css +86 -0
- package/dist/playground/components/ExportDialog/ExportDialog.d.ts +10 -0
- package/dist/playground/components/ExportDialog/ExportDialog.d.ts.map +1 -0
- package/dist/playground/components/ExportDialog/ExportDialog.js +57 -0
- package/dist/playground/components/ExportDialog/ExportDialog.js.map +1 -0
- package/dist/playground/components/ExportDialog/ExportDialog.module.css +328 -0
- package/dist/playground/components/LayoutHelpers/LayoutHelpers.d.ts +134 -0
- package/dist/playground/components/LayoutHelpers/LayoutHelpers.d.ts.map +1 -0
- package/dist/playground/components/LayoutHelpers/LayoutHelpers.js +254 -0
- package/dist/playground/components/LayoutHelpers/LayoutHelpers.js.map +1 -0
- package/dist/playground/components/LayoutHelpers/index.d.ts +3 -0
- package/dist/playground/components/LayoutHelpers/index.d.ts.map +1 -0
- package/dist/playground/components/LayoutHelpers/index.js +2 -0
- package/dist/playground/components/LayoutHelpers/index.js.map +1 -0
- package/dist/playground/components/Playground/Playground.d.ts +10 -0
- package/dist/playground/components/Playground/Playground.d.ts.map +1 -0
- package/dist/playground/components/Playground/Playground.js +128 -0
- package/dist/playground/components/Playground/Playground.js.map +1 -0
- package/dist/playground/components/Playground/Playground.module.css +308 -0
- package/dist/playground/components/PropertiesPanel/PropertiesPanel.d.ts +10 -0
- package/dist/playground/components/PropertiesPanel/PropertiesPanel.d.ts.map +1 -0
- package/dist/playground/components/PropertiesPanel/PropertiesPanel.js +150 -0
- package/dist/playground/components/PropertiesPanel/PropertiesPanel.js.map +1 -0
- package/dist/playground/components/PropertiesPanel/PropertiesPanel.module.css +155 -0
- package/dist/playground/components/PropertiesPanel/index.d.ts +3 -0
- package/dist/playground/components/PropertiesPanel/index.d.ts.map +1 -0
- package/dist/playground/components/PropertiesPanel/index.js +2 -0
- package/dist/playground/components/PropertiesPanel/index.js.map +1 -0
- package/dist/playground/components/PropertyEditors/BooleanEditor.d.ts +12 -0
- package/dist/playground/components/PropertyEditors/BooleanEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/BooleanEditor.js +14 -0
- package/dist/playground/components/PropertyEditors/BooleanEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/ColorEditor.d.ts +12 -0
- package/dist/playground/components/PropertyEditors/ColorEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/ColorEditor.js +62 -0
- package/dist/playground/components/PropertyEditors/ColorEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/IconEditor.d.ts +12 -0
- package/dist/playground/components/PropertyEditors/IconEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/IconEditor.js +123 -0
- package/dist/playground/components/PropertyEditors/IconEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/NumberEditor.d.ts +15 -0
- package/dist/playground/components/PropertyEditors/NumberEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/NumberEditor.js +46 -0
- package/dist/playground/components/PropertyEditors/NumberEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/PropertyEditors.module.css +432 -0
- package/dist/playground/components/PropertyEditors/SelectEditor.d.ts +19 -0
- package/dist/playground/components/PropertyEditors/SelectEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/SelectEditor.js +17 -0
- package/dist/playground/components/PropertyEditors/SelectEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/SpacingEditor.d.ts +19 -0
- package/dist/playground/components/PropertyEditors/SpacingEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/SpacingEditor.js +162 -0
- package/dist/playground/components/PropertyEditors/SpacingEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/SpacingEditor.module.css +214 -0
- package/dist/playground/components/PropertyEditors/TextEditor.d.ts +14 -0
- package/dist/playground/components/PropertyEditors/TextEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/TextEditor.js +38 -0
- package/dist/playground/components/PropertyEditors/TextEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/TokenEditor.d.ts +23 -0
- package/dist/playground/components/PropertyEditors/TokenEditor.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/TokenEditor.js +50 -0
- package/dist/playground/components/PropertyEditors/TokenEditor.js.map +1 -0
- package/dist/playground/components/PropertyEditors/index.d.ts +20 -0
- package/dist/playground/components/PropertyEditors/index.d.ts.map +1 -0
- package/dist/playground/components/PropertyEditors/index.js +12 -0
- package/dist/playground/components/PropertyEditors/index.js.map +1 -0
- package/dist/playground/components/TreeView/TreeView.d.ts +10 -0
- package/dist/playground/components/TreeView/TreeView.d.ts.map +1 -0
- package/dist/playground/components/TreeView/TreeView.js +146 -0
- package/dist/playground/components/TreeView/TreeView.js.map +1 -0
- package/dist/playground/components/TreeView/TreeView.module.css +214 -0
- package/dist/playground/components/TreeView/index.d.ts +3 -0
- package/dist/playground/components/TreeView/index.d.ts.map +1 -0
- package/dist/playground/components/TreeView/index.js +2 -0
- package/dist/playground/components/TreeView/index.js.map +1 -0
- package/dist/playground/config/propertyDefinitions.d.ts +73 -0
- package/dist/playground/config/propertyDefinitions.d.ts.map +1 -0
- package/dist/playground/config/propertyDefinitions.js +809 -0
- package/dist/playground/config/propertyDefinitions.js.map +1 -0
- package/dist/playground/hooks/useKeyboardShortcuts.d.ts +38 -0
- package/dist/playground/hooks/useKeyboardShortcuts.d.ts.map +1 -0
- package/dist/playground/hooks/useKeyboardShortcuts.js +191 -0
- package/dist/playground/hooks/useKeyboardShortcuts.js.map +1 -0
- package/dist/playground/index.d.ts +21 -0
- package/dist/playground/index.d.ts.map +1 -0
- package/dist/playground/index.js +23 -0
- package/dist/playground/index.js.map +1 -0
- package/dist/playground/services/CodeGenerator.d.ts +73 -0
- package/dist/playground/services/CodeGenerator.d.ts.map +1 -0
- package/dist/playground/services/CodeGenerator.js +359 -0
- package/dist/playground/services/CodeGenerator.js.map +1 -0
- package/dist/playground/services/DragDropManager.d.ts +95 -0
- package/dist/playground/services/DragDropManager.d.ts.map +1 -0
- package/dist/playground/services/DragDropManager.js +408 -0
- package/dist/playground/services/DragDropManager.js.map +1 -0
- package/dist/playground/services/StoryParser.d.ts +73 -0
- package/dist/playground/services/StoryParser.d.ts.map +1 -0
- package/dist/playground/services/StoryParser.js +419 -0
- package/dist/playground/services/StoryParser.js.map +1 -0
- package/dist/playground/store/playgroundStore.d.ts +86 -0
- package/dist/playground/store/playgroundStore.d.ts.map +1 -0
- package/dist/playground/store/playgroundStore.js +337 -0
- package/dist/playground/store/playgroundStore.js.map +1 -0
- package/dist/playground/stories/PlaygroundDragDrop.stories.d.ts +13 -0
- package/dist/playground/stories/PlaygroundDragDrop.stories.d.ts.map +1 -0
- package/dist/playground/stories/PlaygroundDragDrop.stories.js +227 -0
- package/dist/playground/stories/PlaygroundDragDrop.stories.js.map +1 -0
- package/dist/playground/stories/PlaygroundPhase4.stories.d.ts +13 -0
- package/dist/playground/stories/PlaygroundPhase4.stories.d.ts.map +1 -0
- package/dist/playground/stories/PlaygroundPhase4.stories.js +334 -0
- package/dist/playground/stories/PlaygroundPhase4.stories.js.map +1 -0
- package/dist/playground/stories/PlaygroundPhase5.stories.d.ts +14 -0
- package/dist/playground/stories/PlaygroundPhase5.stories.d.ts.map +1 -0
- package/dist/playground/stories/PlaygroundPhase5.stories.js +512 -0
- package/dist/playground/stories/PlaygroundPhase5.stories.js.map +1 -0
- package/dist/playground/stories/PlaygroundProperties.stories.d.ts +13 -0
- package/dist/playground/stories/PlaygroundProperties.stories.d.ts.map +1 -0
- package/dist/playground/stories/PlaygroundProperties.stories.js +342 -0
- package/dist/playground/stories/PlaygroundProperties.stories.js.map +1 -0
- package/dist/playground/types/index.d.ts +251 -0
- package/dist/playground/types/index.d.ts.map +1 -0
- package/dist/playground/types/index.js +5 -0
- package/dist/playground/types/index.js.map +1 -0
- package/dist/scripts/verify-framework-adapters.js +105 -0
- package/dist/story-generator/componentBlacklist.js.map +1 -0
- package/dist/story-generator/componentDiscovery.js.map +1 -0
- package/dist/story-generator/configLoader.js.map +1 -0
- package/dist/story-generator/considerationsLoader.js.map +1 -0
- package/dist/story-generator/documentation-sources.js.map +1 -0
- package/dist/story-generator/documentationLoader.js.map +1 -0
- package/dist/story-generator/dynamicPackageDiscovery.js.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.js.map +1 -0
- package/dist/story-generator/generateStory.js.map +1 -0
- package/dist/story-generator/gitignoreManager.js.map +1 -0
- package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
- package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
- package/dist/story-generator/inMemoryStoryService.js +128 -0
- package/dist/story-generator/inMemoryStoryService.js.map +1 -0
- package/dist/story-generator/logger.js.map +1 -0
- package/dist/story-generator/postProcessStory.js.map +1 -0
- package/dist/story-generator/postgresStoryService.d.ts +56 -0
- package/dist/story-generator/postgresStoryService.d.ts.map +1 -0
- package/dist/story-generator/postgresStoryService.js +240 -0
- package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
- package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/productionGitignoreManager.js +340 -0
- package/dist/story-generator/productionGitignoreManager.js.map +1 -0
- package/dist/story-generator/promptGenerator.js.map +1 -0
- package/dist/story-generator/providerPresets.d.ts +54 -0
- package/dist/story-generator/providerPresets.d.ts.map +1 -0
- package/dist/story-generator/providerPresets.js +214 -0
- package/dist/story-generator/storyHistory.js.map +1 -0
- package/dist/story-generator/storyServiceFactory.d.ts +22 -0
- package/dist/story-generator/storyServiceFactory.d.ts.map +1 -0
- package/dist/story-generator/storyServiceFactory.js +97 -0
- package/dist/story-generator/storyServiceInterface.d.ts +85 -0
- package/dist/story-generator/storyServiceInterface.d.ts.map +1 -0
- package/dist/story-generator/storyServiceInterface.js +5 -0
- package/dist/story-generator/storySync.d.ts +68 -0
- package/dist/story-generator/storySync.d.ts.map +1 -0
- package/dist/story-generator/storySync.js +201 -0
- package/dist/story-generator/storySync.js.map +1 -0
- package/dist/story-generator/storyTracker.js.map +1 -0
- package/dist/story-generator/storyValidator.js.map +1 -0
- package/dist/story-generator/test_validation.d.ts +2 -0
- package/dist/story-generator/test_validation.d.ts.map +1 -0
- package/dist/story-generator/test_validation.js +51 -0
- package/dist/story-generator/universalDesignSystemAdapter.js.map +1 -0
- package/dist/story-generator/urlRedirectService.js.map +1 -0
- package/dist/story-generator/validateStory.js.map +1 -0
- package/dist/story-ui.config.js.map +1 -0
- package/dist/story-ui.config.loader.d.ts +36 -0
- package/dist/story-ui.config.loader.d.ts.map +1 -0
- package/dist/story-ui.config.loader.js +205 -0
- package/dist/story-ui.config.loader.js.map +1 -0
- package/dist/temp/package/templates/StoryUI/StoryUIPanel.js +807 -0
- package/dist/temp/package/templates/StoryUI/StoryUIPanel.stories.js +37 -0
- package/dist/temp/package/templates/StoryUI/index.js +2 -0
- package/dist/templates/StoryUI/StoryUIPanel.js.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.js.map +1 -0
- package/dist/templates/StoryUI/index.js.map +1 -0
- package/dist/templates/StoryUI/manager.d.ts +14 -0
- package/dist/templates/StoryUI/manager.d.ts.map +1 -0
- package/dist/templates/production-app/src/App.d.ts +10 -0
- package/dist/templates/production-app/src/App.d.ts.map +1 -0
- package/dist/templates/production-app/src/App.js +653 -0
- package/dist/templates/production-app/src/LivePreviewRenderer.d.ts +24 -0
- package/dist/templates/production-app/src/LivePreviewRenderer.d.ts.map +1 -0
- package/dist/templates/production-app/src/LivePreviewRenderer.js +199 -0
- package/dist/templates/production-app/src/componentRegistry.d.ts +20 -0
- package/dist/templates/production-app/src/componentRegistry.d.ts.map +1 -0
- package/dist/templates/production-app/src/componentRegistry.js +316 -0
- package/dist/templates/production-app/src/main.d.ts +9 -0
- package/dist/templates/production-app/src/main.d.ts.map +1 -0
- package/dist/templates/production-app/src/main.js +18 -0
- package/dist/templates/production-app/vite.config.d.ts +3 -0
- package/dist/templates/production-app/vite.config.d.ts.map +1 -0
- package/dist/templates/production-app/vite.config.js +71 -0
- package/dist/test-storybooks/angular-material-storybook/src/main.js +66 -0
- package/dist/test-storybooks/chakra-storybook/vite.config.js +6 -0
- package/dist/test-storybooks/mantine-storybook/vite.config.js +93 -0
- package/dist/test-storybooks/web-components-shoelace/vite.config.js +9 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- 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
|
+
}
|