@tpitre/story-ui 2.2.0 → 2.3.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/.env.sample +82 -11
- package/README.md +89 -0
- package/dist/cli/deploy.d.ts +17 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +696 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +26 -2
- package/dist/cli/setup.d.ts +11 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +437 -110
- package/dist/mcp-server/index.d.ts +2 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +120 -2
- package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
- package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
- package/dist/mcp-server/mcp-stdio-server.js +8 -1
- package/dist/mcp-server/routes/claude.d.ts +3 -0
- package/dist/mcp-server/routes/claude.d.ts.map +1 -0
- package/dist/mcp-server/routes/claude.js +60 -23
- package/dist/mcp-server/routes/components.d.ts +4 -0
- package/dist/mcp-server/routes/components.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.d.ts +38 -0
- package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.js +183 -0
- package/dist/mcp-server/routes/generateStory.d.ts +3 -0
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStory.js +160 -76
- package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
- package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStoryStream.js +947 -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/mcpRemote.d.ts +14 -0
- package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
- package/dist/mcp-server/routes/mcpRemote.js +489 -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/providers.d.ts +89 -0
- package/dist/mcp-server/routes/providers.d.ts.map +1 -0
- package/dist/mcp-server/routes/providers.js +369 -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/streamTypes.d.ts +110 -0
- package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
- package/dist/mcp-server/routes/streamTypes.js +18 -0
- package/dist/mcp-server/sessionManager.d.ts +50 -0
- package/dist/mcp-server/sessionManager.d.ts.map +1 -0
- package/dist/story-generator/componentBlacklist.d.ts +21 -0
- package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
- package/dist/story-generator/componentDiscovery.d.ts +28 -0
- package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.js +205 -0
- package/dist/story-generator/configLoader.d.ts +33 -0
- package/dist/story-generator/configLoader.d.ts.map +1 -0
- package/dist/story-generator/considerationsLoader.d.ts +32 -0
- package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
- package/dist/story-generator/documentation-sources.d.ts +28 -0
- package/dist/story-generator/documentation-sources.d.ts.map +1 -0
- package/dist/story-generator/documentationLoader.d.ts +64 -0
- package/dist/story-generator/documentationLoader.d.ts.map +1 -0
- package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
- package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.js +111 -11
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
- package/dist/story-generator/framework-adapters/index.d.ts +97 -0
- package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/index.js +198 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
- package/dist/story-generator/framework-adapters/types.d.ts +182 -0
- package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/types.js +8 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
- package/dist/story-generator/generateStory.d.ts +7 -0
- package/dist/story-generator/generateStory.d.ts.map +1 -0
- package/dist/story-generator/gitignoreManager.d.ts +50 -0
- package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/imageProcessor.d.ts +80 -0
- package/dist/story-generator/imageProcessor.d.ts.map +1 -0
- package/dist/story-generator/imageProcessor.js +391 -0
- package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
- package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.js +135 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/claude-provider.js +414 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
- package/dist/story-generator/llm-providers/index.d.ts +63 -0
- package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/index.js +169 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/openai-provider.js +458 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/settings-manager.js +173 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
- package/dist/story-generator/llm-providers/types.d.ts +153 -0
- package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/types.js +8 -0
- package/dist/story-generator/logger.d.ts +14 -0
- package/dist/story-generator/logger.d.ts.map +1 -0
- package/dist/story-generator/logger.js +96 -29
- package/dist/story-generator/postProcessStory.d.ts +6 -0
- package/dist/story-generator/postProcessStory.d.ts.map +1 -0
- package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
- package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/promptGenerator.d.ts +48 -0
- package/dist/story-generator/promptGenerator.d.ts.map +1 -0
- package/dist/story-generator/promptGenerator.js +186 -1
- package/dist/story-generator/storyHistory.d.ts +44 -0
- package/dist/story-generator/storyHistory.d.ts.map +1 -0
- package/dist/story-generator/storySync.d.ts +68 -0
- package/dist/story-generator/storySync.d.ts.map +1 -0
- package/dist/story-generator/storyTracker.d.ts +48 -0
- package/dist/story-generator/storyTracker.d.ts.map +1 -0
- package/dist/story-generator/storyValidator.d.ts +6 -0
- package/dist/story-generator/storyValidator.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.js +138 -1
- package/dist/story-generator/urlRedirectService.d.ts +21 -0
- package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
- package/dist/story-generator/validateStory.d.ts +19 -0
- package/dist/story-generator/validateStory.d.ts.map +1 -0
- package/dist/story-generator/validateStory.js +6 -2
- package/dist/story-generator/visionPrompts.d.ts +88 -0
- package/dist/story-generator/visionPrompts.d.ts.map +1 -0
- package/dist/story-generator/visionPrompts.js +462 -0
- package/dist/story-ui.config.d.ts +78 -0
- package/dist/story-ui.config.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
- package/dist/templates/StoryUI/index.d.ts +3 -0
- package/dist/templates/StoryUI/index.d.ts.map +1 -0
- package/dist/templates/StoryUI/index.js +2 -0
- package/package.json +17 -3
- package/templates/StoryUI/StoryUIPanel.tsx +1960 -384
- package/templates/StoryUI/index.tsx +1 -1
- package/templates/StoryUI/manager.tsx +264 -0
- package/templates/production-app/.env.example +11 -0
- package/templates/production-app/index.html +66 -0
- package/templates/production-app/package.json +30 -0
- package/templates/production-app/public/favicon.svg +5 -0
- package/templates/production-app/src/App.tsx +1157 -0
- package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
- package/templates/production-app/src/componentRegistry.ts +315 -0
- package/templates/production-app/src/considerations.ts +16 -0
- package/templates/production-app/src/index.css +284 -0
- package/templates/production-app/src/main.tsx +25 -0
- package/templates/production-app/tsconfig.json +32 -0
- package/templates/production-app/tsconfig.node.json +11 -0
- package/templates/production-app/vite.config.ts +83 -0
- package/templates/react-import-rule.json +2 -2
- package/dist/index.js +0 -12
- package/dist/story-ui.config.loader.js +0 -205
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import fetch from 'node-fetch';
|
|
2
1
|
import { generateStory } from '../../story-generator/generateStory.js';
|
|
3
2
|
import * as crypto from 'crypto';
|
|
4
3
|
import * as path from 'path';
|
|
5
|
-
import { buildClaudePrompt as buildFlexiblePrompt } from '../../story-generator/promptGenerator.js';
|
|
4
|
+
import { buildClaudePrompt as buildFlexiblePrompt, buildFrameworkAwarePrompt, detectProjectFramework, } from '../../story-generator/promptGenerator.js';
|
|
5
|
+
import { getAdapter } from '../../story-generator/framework-adapters/index.js';
|
|
6
6
|
import { loadUserConfig, validateConfig } from '../../story-generator/configLoader.js';
|
|
7
7
|
import { setupProductionGitignore } from '../../story-generator/productionGitignoreManager.js';
|
|
8
8
|
import { getInMemoryStoryService } from '../../story-generator/inMemoryStoryService.js';
|
|
9
|
-
import { extractAndValidateCodeBlock, createFallbackStory } from '../../story-generator/validateStory.js';
|
|
9
|
+
import { extractAndValidateCodeBlock, createFallbackStory, validateStoryCode } from '../../story-generator/validateStory.js';
|
|
10
10
|
import { isBlacklistedComponent, isBlacklistedIcon, getBlacklistErrorMessage, ICON_CORRECTIONS } from '../../story-generator/componentBlacklist.js';
|
|
11
11
|
import { StoryTracker } from '../../story-generator/storyTracker.js';
|
|
12
12
|
import { EnhancedComponentDiscovery } from '../../story-generator/enhancedComponentDiscovery.js';
|
|
@@ -16,13 +16,9 @@ import { validateStory } from '../../story-generator/storyValidator.js';
|
|
|
16
16
|
import { StoryHistoryManager } from '../../story-generator/storyHistory.js';
|
|
17
17
|
import { logger } from '../../story-generator/logger.js';
|
|
18
18
|
import { UrlRedirectService } from '../../story-generator/urlRedirectService.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const COMPONENT_LIST = [];
|
|
23
|
-
const SAMPLE_STORY = '';
|
|
24
|
-
// Legacy component reference - now using dynamic discovery
|
|
25
|
-
const COMPONENT_REFERENCE = '';
|
|
19
|
+
import { chatCompletion, generateTitle as llmGenerateTitle, isProviderConfigured, getProviderInfo, chatCompletionWithImages, buildMessageWithImages } from '../../story-generator/llm-providers/story-llm-service.js';
|
|
20
|
+
import { processImageInputs } from '../../story-generator/imageProcessor.js';
|
|
21
|
+
import { buildVisionAwarePrompt } from '../../story-generator/visionPrompts.js';
|
|
26
22
|
// Legacy function - now uses flexible system with enhanced discovery
|
|
27
23
|
async function buildClaudePrompt(userPrompt) {
|
|
28
24
|
const config = loadUserConfig();
|
|
@@ -31,15 +27,58 @@ async function buildClaudePrompt(userPrompt) {
|
|
|
31
27
|
return await buildFlexiblePrompt(userPrompt, config, components);
|
|
32
28
|
}
|
|
33
29
|
// Enhanced function that includes conversation context and previous code
|
|
34
|
-
|
|
30
|
+
// Now supports multi-framework prompt generation and vision-aware prompts
|
|
31
|
+
async function buildClaudePromptWithContext(userPrompt, config, conversation, previousCode, options) {
|
|
35
32
|
const discovery = new EnhancedComponentDiscovery(config);
|
|
36
33
|
const components = await discovery.discoverAll();
|
|
34
|
+
// Determine if we should use framework-aware prompts
|
|
35
|
+
let useFrameworkAware = false;
|
|
36
|
+
let frameworkOptions;
|
|
37
|
+
if (options?.framework) {
|
|
38
|
+
// Explicit framework specified
|
|
39
|
+
useFrameworkAware = true;
|
|
40
|
+
frameworkOptions = { framework: options.framework };
|
|
41
|
+
logger.log(`📦 Using specified framework: ${options.framework}`);
|
|
42
|
+
}
|
|
43
|
+
else if (options?.autoDetectFramework) {
|
|
44
|
+
// Auto-detect framework from project
|
|
45
|
+
try {
|
|
46
|
+
const detectedFramework = await detectProjectFramework(process.cwd());
|
|
47
|
+
useFrameworkAware = true;
|
|
48
|
+
frameworkOptions = { framework: detectedFramework };
|
|
49
|
+
logger.log(`📦 Auto-detected framework: ${detectedFramework}`);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.warn('Failed to auto-detect framework, using React default', { error });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
37
55
|
// Always start with component discovery as the authoritative source
|
|
38
56
|
logger.log(`📦 Discovered ${components.length} components from ${config.importPath}`);
|
|
39
57
|
const availableComponents = components.map(c => c.name).join(', ');
|
|
40
58
|
logger.log(`✅ Available components: ${availableComponents}`);
|
|
41
59
|
// Build base prompt with discovered components (always required)
|
|
42
|
-
|
|
60
|
+
// Use framework-aware prompt if configured, otherwise use legacy React prompt
|
|
61
|
+
let prompt;
|
|
62
|
+
if (useFrameworkAware && frameworkOptions) {
|
|
63
|
+
prompt = await buildFrameworkAwarePrompt(userPrompt, config, components, frameworkOptions);
|
|
64
|
+
logger.log(`🔧 Built framework-aware prompt for ${frameworkOptions.framework}`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
prompt = await buildFlexiblePrompt(userPrompt, config, components);
|
|
68
|
+
}
|
|
69
|
+
// Enhance prompt with vision-aware context if vision mode is provided
|
|
70
|
+
if (options?.visionMode) {
|
|
71
|
+
logger.log(`🔍 Enhancing prompt with vision mode: ${options.visionMode}`);
|
|
72
|
+
const visionPrompts = buildVisionAwarePrompt({
|
|
73
|
+
promptType: options.visionMode,
|
|
74
|
+
userDescription: userPrompt,
|
|
75
|
+
availableComponents: components.map(c => c.name),
|
|
76
|
+
framework: frameworkOptions?.framework || 'react',
|
|
77
|
+
designSystem: options.designSystem,
|
|
78
|
+
});
|
|
79
|
+
// Combine the vision system prompt with the existing prompt and add the user prompt
|
|
80
|
+
prompt = `${visionPrompts.systemPrompt}\n\n---\n\n${prompt}\n\n---\n\n${visionPrompts.userPrompt}`;
|
|
81
|
+
}
|
|
43
82
|
// Try to enhance with bundled documentation for usage patterns and design tokens
|
|
44
83
|
logger.log('📋 Using bundled documentation for enhancement');
|
|
45
84
|
const documentation = getDocumentation(config.importPath);
|
|
@@ -115,26 +154,32 @@ function extractCodeBlock(text) {
|
|
|
115
154
|
const codeBlock = text.match(/```(?:tsx|jsx|typescript|ts|js|javascript)?([\s\S]*?)```/i);
|
|
116
155
|
return codeBlock ? codeBlock[1].trim() : null;
|
|
117
156
|
}
|
|
118
|
-
async function
|
|
119
|
-
|
|
120
|
-
if (!
|
|
121
|
-
throw new Error('
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
157
|
+
async function callLLM(messages, images) {
|
|
158
|
+
// Check if any provider is configured
|
|
159
|
+
if (!isProviderConfigured()) {
|
|
160
|
+
throw new Error('No LLM provider configured. Please set CLAUDE_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY.');
|
|
161
|
+
}
|
|
162
|
+
const providerInfo = getProviderInfo();
|
|
163
|
+
logger.debug(`Using ${providerInfo.currentProvider} (${providerInfo.currentModel}) for story generation`);
|
|
164
|
+
// If images are provided, use vision-capable chat
|
|
165
|
+
if (images && images.length > 0) {
|
|
166
|
+
if (!providerInfo.supportsVision) {
|
|
167
|
+
throw new Error(`${providerInfo.currentProvider} does not support vision. Please configure a vision-capable provider.`);
|
|
168
|
+
}
|
|
169
|
+
logger.log(`🖼️ Using vision-capable chat with ${images.length} image(s)`);
|
|
170
|
+
// Convert messages to include images in the first user message
|
|
171
|
+
const messagesWithImages = messages.map((msg, index) => {
|
|
172
|
+
if (msg.role === 'user' && index === 0) {
|
|
173
|
+
return {
|
|
174
|
+
role: msg.role,
|
|
175
|
+
content: buildMessageWithImages(msg.content, images)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return msg;
|
|
179
|
+
});
|
|
180
|
+
return await chatCompletionWithImages(messagesWithImages, { maxTokens: 8192 });
|
|
181
|
+
}
|
|
182
|
+
return await chatCompletion(messages, { maxTokens: 8192 });
|
|
138
183
|
}
|
|
139
184
|
function cleanPromptForTitle(prompt) {
|
|
140
185
|
if (!prompt || typeof prompt !== 'string') {
|
|
@@ -171,34 +216,15 @@ function cleanPromptForTitle(prompt) {
|
|
|
171
216
|
.trim()
|
|
172
217
|
.replace(/\b\w/g, c => c.toUpperCase()); // Capitalize words
|
|
173
218
|
}
|
|
174
|
-
async function
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
'',
|
|
183
|
-
'UI description:',
|
|
184
|
-
userPrompt,
|
|
185
|
-
'',
|
|
186
|
-
'Title:'
|
|
187
|
-
].join('\n');
|
|
188
|
-
const aiText = await callClaude([{ role: 'user', content: titlePrompt }]);
|
|
189
|
-
// Take the first non-empty line, trim, and remove quotes if present
|
|
190
|
-
const lines = aiText.split('\n').map(l => l.trim()).filter(Boolean);
|
|
191
|
-
if (lines.length > 0) {
|
|
192
|
-
let title = lines[0].replace(/^['\"]|['\"]$/g, '').trim();
|
|
193
|
-
// Additional sanitization for safety
|
|
194
|
-
title = title
|
|
195
|
-
.replace(/[^\w\s'"?!-]/g, ' ') // Remove problematic characters
|
|
196
|
-
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
197
|
-
.trim()
|
|
198
|
-
.slice(0, 50); // Limit length
|
|
199
|
-
return title;
|
|
219
|
+
async function getLLMTitle(userPrompt) {
|
|
220
|
+
// Use the LLM service's built-in title generation
|
|
221
|
+
try {
|
|
222
|
+
return await llmGenerateTitle(userPrompt);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
logger.warn('Failed to generate title via LLM, using fallback', { error });
|
|
226
|
+
return '';
|
|
200
227
|
}
|
|
201
|
-
return '';
|
|
202
228
|
}
|
|
203
229
|
function escapeTitleForTS(title) {
|
|
204
230
|
// Escape all characters that could break TypeScript string literals
|
|
@@ -316,7 +342,12 @@ function fileNameFromTitle(title, hash) {
|
|
|
316
342
|
return `${base}-${hash}.stories.tsx`;
|
|
317
343
|
}
|
|
318
344
|
export async function generateStoryFromPrompt(req, res) {
|
|
319
|
-
const { prompt, fileName, conversation, isUpdate, originalTitle, storyId: providedStoryId
|
|
345
|
+
const { prompt, fileName, conversation, isUpdate, originalTitle, storyId: providedStoryId, framework, // Explicit framework (react, vue, angular, svelte, web-components)
|
|
346
|
+
autoDetectFramework, // Auto-detect from project (default: false)
|
|
347
|
+
images, // Array of images for vision-based generation
|
|
348
|
+
visionMode, // Vision mode: 'screenshot_to_story', 'design_to_story', 'component_analysis', 'layout_analysis'
|
|
349
|
+
designSystem // Design system being used (chakra-ui, mantine, etc.)
|
|
350
|
+
} = req.body;
|
|
320
351
|
if (!prompt)
|
|
321
352
|
return res.status(400).json({ error: 'Missing prompt' });
|
|
322
353
|
try {
|
|
@@ -329,6 +360,21 @@ export async function generateStoryFromPrompt(req, res) {
|
|
|
329
360
|
details: validation.errors
|
|
330
361
|
});
|
|
331
362
|
}
|
|
363
|
+
// Process images if provided
|
|
364
|
+
let processedImages = [];
|
|
365
|
+
if (images && Array.isArray(images) && images.length > 0) {
|
|
366
|
+
logger.log(`📸 Processing ${images.length} image(s) for vision-based story generation`);
|
|
367
|
+
try {
|
|
368
|
+
processedImages = await processImageInputs(images);
|
|
369
|
+
logger.log(`✅ Successfully processed ${processedImages.length} image(s)`);
|
|
370
|
+
}
|
|
371
|
+
catch (imageError) {
|
|
372
|
+
return res.status(400).json({
|
|
373
|
+
error: 'Image processing failed',
|
|
374
|
+
details: imageError instanceof Error ? imageError.message : String(imageError)
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
332
378
|
// Set up production-ready environment
|
|
333
379
|
const gitignoreManager = setupProductionGitignore(config);
|
|
334
380
|
const storyService = getInMemoryStoryService(config);
|
|
@@ -374,12 +420,19 @@ export async function generateStoryFromPrompt(req, res) {
|
|
|
374
420
|
let validationErrors = [];
|
|
375
421
|
const maxRetries = 3;
|
|
376
422
|
let attempts = 0;
|
|
377
|
-
|
|
423
|
+
// Build framework-aware options with vision support
|
|
424
|
+
const frameworkOptions = {
|
|
425
|
+
framework: framework,
|
|
426
|
+
autoDetectFramework: autoDetectFramework === true,
|
|
427
|
+
visionMode: visionMode,
|
|
428
|
+
designSystem: designSystem,
|
|
429
|
+
};
|
|
430
|
+
const initialPrompt = await buildClaudePromptWithContext(prompt, config, conversation, previousCode, frameworkOptions);
|
|
378
431
|
const messages = [{ role: 'user', content: initialPrompt }];
|
|
379
432
|
while (attempts < maxRetries) {
|
|
380
433
|
attempts++;
|
|
381
434
|
logger.log(`--- Story Generation Attempt ${attempts} ---`);
|
|
382
|
-
const claudeResponse = await
|
|
435
|
+
const claudeResponse = await callLLM(messages, processedImages.length > 0 ? processedImages : undefined);
|
|
383
436
|
const extractedCode = extractCodeBlock(claudeResponse);
|
|
384
437
|
if (!extractedCode) {
|
|
385
438
|
aiText = claudeResponse; // Use raw response if no code block
|
|
@@ -484,12 +537,24 @@ export async function generateStoryFromPrompt(req, res) {
|
|
|
484
537
|
console.error('No valid code could be extracted or generated.');
|
|
485
538
|
return res.status(500).json({ error: 'Failed to generate valid TypeScript code.' });
|
|
486
539
|
}
|
|
487
|
-
//
|
|
488
|
-
|
|
540
|
+
// Determine the framework being used (priority: request > config > auto-detect > default)
|
|
541
|
+
const detectedFramework = frameworkOptions.framework ||
|
|
542
|
+
config.framework ||
|
|
543
|
+
(frameworkOptions.autoDetectFramework ? await detectProjectFramework(process.cwd()).catch(() => 'react') : 'react');
|
|
544
|
+
logger.log(`🎯 Framework detection: request=${frameworkOptions.framework}, config=${config.framework}, detected=${detectedFramework}`);
|
|
545
|
+
// Get the framework adapter for post-processing
|
|
546
|
+
const frameworkAdapter = getAdapter(detectedFramework);
|
|
547
|
+
// Only add React import for React framework
|
|
548
|
+
if (detectedFramework === 'react' && !fileContents.includes("import React from 'react';")) {
|
|
489
549
|
fileContents = "import React from 'react';\n" + fileContents;
|
|
490
550
|
}
|
|
491
551
|
// Post-processing is now consolidated to run once on the final code
|
|
492
552
|
let fixedFileContents = postProcessStory(fileContents, config.importPath);
|
|
553
|
+
// Apply framework-specific post-processing if adapter is available
|
|
554
|
+
if (frameworkAdapter) {
|
|
555
|
+
logger.log(`🔧 Applying ${detectedFramework} framework post-processing`);
|
|
556
|
+
fixedFileContents = frameworkAdapter.postProcess(fixedFileContents);
|
|
557
|
+
}
|
|
493
558
|
// Generate title based on conversation context
|
|
494
559
|
let aiTitle;
|
|
495
560
|
if (isActualUpdate && originalTitle) {
|
|
@@ -500,11 +565,11 @@ export async function generateStoryFromPrompt(req, res) {
|
|
|
500
565
|
else if (isActualUpdate) {
|
|
501
566
|
// For updates without original title, try to keep the original title or modify it slightly
|
|
502
567
|
const originalPrompt = conversation.find((msg) => msg.role === 'user')?.content || prompt;
|
|
503
|
-
aiTitle = await
|
|
568
|
+
aiTitle = await getLLMTitle(originalPrompt);
|
|
504
569
|
}
|
|
505
570
|
else {
|
|
506
571
|
// For new stories, generate a new title
|
|
507
|
-
aiTitle = await
|
|
572
|
+
aiTitle = await getLLMTitle(prompt);
|
|
508
573
|
}
|
|
509
574
|
if (!aiTitle || aiTitle.length < 2) {
|
|
510
575
|
// Fallback to cleaned prompt if Claude fails
|
|
@@ -530,6 +595,35 @@ export async function generateStoryFromPrompt(req, res) {
|
|
|
530
595
|
return p1 + titleToUse + p3;
|
|
531
596
|
});
|
|
532
597
|
}
|
|
598
|
+
// FIX #5: Final validation after ALL post-processing
|
|
599
|
+
// This catches any syntax errors introduced by post-processing (e.g., buggy regex replacements)
|
|
600
|
+
const finalValidation = validateStoryCode(fixedFileContents, 'story.tsx', config);
|
|
601
|
+
if (!finalValidation.isValid) {
|
|
602
|
+
logger.log('⚠️ Post-processing introduced syntax errors:', finalValidation.errors);
|
|
603
|
+
// If we have fixed code from validation, use it
|
|
604
|
+
if (finalValidation.fixedCode) {
|
|
605
|
+
logger.log('✅ Auto-fixed post-processing errors');
|
|
606
|
+
fixedFileContents = finalValidation.fixedCode;
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
// Post-processing broke the code and we can't fix it
|
|
610
|
+
// Return an error rather than serving broken code
|
|
611
|
+
console.error('Post-processing introduced unfixable syntax errors:', finalValidation.errors);
|
|
612
|
+
return res.status(500).json({
|
|
613
|
+
error: 'Story generation failed due to post-processing errors',
|
|
614
|
+
details: finalValidation.errors,
|
|
615
|
+
suggestion: 'This is a bug in Story UI. Please report this issue with your prompt.',
|
|
616
|
+
validation: {
|
|
617
|
+
hasWarnings: true,
|
|
618
|
+
errors: finalValidation.errors,
|
|
619
|
+
warnings: ['Post-processing introduced syntax errors that could not be automatically fixed']
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
logger.log('✅ Final validation passed after post-processing');
|
|
626
|
+
}
|
|
533
627
|
// Check if this is an update to an existing story
|
|
534
628
|
// ONLY consider it an update if we're in the same conversation context
|
|
535
629
|
let existingStory = null;
|
|
@@ -688,16 +782,6 @@ export async function generateStoryFromPrompt(req, res) {
|
|
|
688
782
|
res.status(500).json({ error: err.message || 'Story generation failed' });
|
|
689
783
|
}
|
|
690
784
|
}
|
|
691
|
-
/**
|
|
692
|
-
* Fixes inline styles in the generated story content
|
|
693
|
-
* Converts React camelCase style properties to kebab-case CSS properties
|
|
694
|
-
*/
|
|
695
|
-
function fixInlineStyles(content) {
|
|
696
|
-
// This function is now superseded by the validator and postProcessStory
|
|
697
|
-
// but can be kept for other potential style cleanups if needed.
|
|
698
|
-
// For now, the main logic is in the validator.
|
|
699
|
-
return content;
|
|
700
|
-
}
|
|
701
785
|
/**
|
|
702
786
|
* Extracts component names from story content
|
|
703
787
|
*/
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming Story Generation with Two-Way Communication
|
|
3
|
+
*
|
|
4
|
+
* This endpoint provides real-time feedback during story generation via SSE.
|
|
5
|
+
* It enables the chat to show:
|
|
6
|
+
* 1. Intent preview - what the AI plans to do
|
|
7
|
+
* 2. Progress updates - step-by-step execution
|
|
8
|
+
* 3. Execution feedback - detailed completion with reasoning
|
|
9
|
+
*/
|
|
10
|
+
import { Request, Response } from 'express';
|
|
11
|
+
export declare function generateStoryFromPromptStream(req: Request, res: Response): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=generateStoryStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateStoryStream.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/generateStoryStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAqa5C,wBAAsB,6BAA6B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBAge9E"}
|