@tpitre/story-ui 4.4.1 → 4.4.2
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/README.md
CHANGED
|
@@ -67,9 +67,9 @@ Story UI will guide you through:
|
|
|
67
67
|
|
|
68
68
|
| Provider | Models | Best For |
|
|
69
69
|
|----------|--------|----------|
|
|
70
|
-
| **Claude** (Anthropic) | claude-opus-4-5, claude-sonnet-4-5, claude-haiku-4-5 | Complex reasoning, code quality |
|
|
70
|
+
| **Claude** (Anthropic) | claude-opus-4-5-20251101, claude-sonnet-4-5-20250929, claude-haiku-4-5-20251001, claude-sonnet-4-20250514 | Complex reasoning, code quality |
|
|
71
71
|
| **GPT** (OpenAI) | gpt-5.2, gpt-5.1, gpt-4o, gpt-4o-mini | Versatility, latest capabilities |
|
|
72
|
-
| **Gemini** (Google) | gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash |
|
|
72
|
+
| **Gemini** (Google) | gemini-3-pro-preview, gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash | Advanced reasoning, fast generation |
|
|
73
73
|
|
|
74
74
|
### Production Deployment
|
|
75
75
|
- **Railway**: Node.js backend with file-based story persistence
|
|
@@ -467,17 +467,20 @@ npx story-ui mcp
|
|
|
467
467
|
|
|
468
468
|
| Method | Endpoint | Description |
|
|
469
469
|
|--------|----------|-------------|
|
|
470
|
+
| `POST` | `/story-ui/generate` | Generate story (specify provider in body) |
|
|
471
|
+
| `POST` | `/story-ui/generate-stream` | Generate story with streaming |
|
|
470
472
|
| `GET` | `/story-ui/providers` | List available LLM providers and models |
|
|
471
|
-
| `
|
|
472
|
-
| `POST` | `/story-ui/openai` | Generate with OpenAI |
|
|
473
|
-
| `POST` | `/story-ui/gemini` | Generate with Gemini |
|
|
473
|
+
| `GET` | `/story-ui/components` | List discovered components |
|
|
474
474
|
| `GET` | `/story-ui/considerations` | Get design system context |
|
|
475
|
+
| `GET` | `/mcp/stories` | List generated stories |
|
|
476
|
+
| `DELETE` | `/mcp/stories/:storyId` | Delete a story |
|
|
475
477
|
|
|
476
478
|
### Request Format
|
|
477
479
|
|
|
478
480
|
```typescript
|
|
479
481
|
{
|
|
480
482
|
prompt: string; // User's request
|
|
483
|
+
provider?: string; // LLM provider: 'claude' | 'openai' | 'gemini'
|
|
481
484
|
model?: string; // Specific model to use
|
|
482
485
|
previousCode?: string; // For iterations
|
|
483
486
|
history?: Message[]; // Conversation history
|
package/dist/mcp-server/index.js
CHANGED
|
@@ -268,7 +268,8 @@ app.delete('/mcp/stories/:storyId', async (req, res) => {
|
|
|
268
268
|
return res.status(500).json({ error: 'Failed to delete story' });
|
|
269
269
|
}
|
|
270
270
|
});
|
|
271
|
-
// File-based story routes - stories are generated as
|
|
271
|
+
// File-based story routes - stories are generated as framework-specific files
|
|
272
|
+
// (.stories.tsx for React, .stories.ts for Vue/Angular, .stories.svelte for Svelte, .stories.js for Web Components)
|
|
272
273
|
// Storybook discovers these automatically via its native file system watching
|
|
273
274
|
// Proxy routes for frontend compatibility (maps /story-ui/ to /mcp/)
|
|
274
275
|
app.post('/story-ui/generate', generateStoryFromPrompt);
|
|
@@ -31,6 +31,26 @@ const HTTP_PORT = process.env.VITE_STORY_UI_PORT || process.env.STORY_UI_HTTP_PO
|
|
|
31
31
|
const HTTP_BASE_URL = process.env.STORY_UI_HTTP_BASE_URL || `http://localhost:${HTTP_PORT}`;
|
|
32
32
|
// Initialize configuration
|
|
33
33
|
const config = loadUserConfig();
|
|
34
|
+
// Framework-agnostic story file extensions
|
|
35
|
+
const STORY_EXTENSIONS = ['.stories.tsx', '.stories.ts', '.stories.svelte', '.stories.js'];
|
|
36
|
+
// Helper function to check if a file is a story file
|
|
37
|
+
function isStoryFile(filename) {
|
|
38
|
+
return STORY_EXTENSIONS.some(ext => filename.endsWith(ext));
|
|
39
|
+
}
|
|
40
|
+
// Helper function to remove any story extension from a filename
|
|
41
|
+
function removeStoryExtension(filename) {
|
|
42
|
+
for (const ext of STORY_EXTENSIONS) {
|
|
43
|
+
if (filename.endsWith(ext)) {
|
|
44
|
+
return filename.slice(0, -ext.length);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return filename;
|
|
48
|
+
}
|
|
49
|
+
// Helper function to extract hash from story filename (e.g., "Button-a1b2c3d4.stories.tsx" -> "a1b2c3d4")
|
|
50
|
+
function getHashFromFilename(filename) {
|
|
51
|
+
const match = filename.match(/-([a-f0-9]{8})\.stories\./);
|
|
52
|
+
return match ? match[1] : null;
|
|
53
|
+
}
|
|
34
54
|
// Create MCP server instance
|
|
35
55
|
const server = new Server({
|
|
36
56
|
name: "story-ui",
|
|
@@ -230,12 +250,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
230
250
|
if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
|
|
231
251
|
const files = fs.readdirSync(config.generatedStoriesPath);
|
|
232
252
|
files
|
|
233
|
-
.filter((file) => file
|
|
253
|
+
.filter((file) => isStoryFile(file))
|
|
234
254
|
.forEach((file) => {
|
|
235
|
-
const hash = file
|
|
236
|
-
const storyId = hash ? `story-${hash}` : file
|
|
255
|
+
const hash = getHashFromFilename(file) || '';
|
|
256
|
+
const storyId = hash ? `story-${hash}` : removeStoryExtension(file);
|
|
237
257
|
// Try to read title from file
|
|
238
|
-
let title = file
|
|
258
|
+
let title = removeStoryExtension(file).replace(/-/g, ' ');
|
|
239
259
|
try {
|
|
240
260
|
const filePath = path.join(config.generatedStoriesPath, file);
|
|
241
261
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
@@ -325,11 +345,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
325
345
|
// Extract hash from story ID
|
|
326
346
|
const hashMatch = storyId.match(/^story-([a-f0-9]{8})$/);
|
|
327
347
|
const hash = hashMatch ? hashMatch[1] : null;
|
|
328
|
-
// Find matching file
|
|
348
|
+
// Find matching file (framework-agnostic)
|
|
329
349
|
const matchingFile = files.find(file => {
|
|
330
|
-
if (
|
|
350
|
+
if (!isStoryFile(file))
|
|
351
|
+
return false;
|
|
352
|
+
if (hash && file.includes(`-${hash}.stories.`))
|
|
331
353
|
return true;
|
|
332
|
-
if (file ===
|
|
354
|
+
if (removeStoryExtension(file) === storyId)
|
|
333
355
|
return true;
|
|
334
356
|
if (file === storyId)
|
|
335
357
|
return true;
|
|
@@ -402,7 +424,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
402
424
|
if (!storyId) {
|
|
403
425
|
if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
|
|
404
426
|
const files = fs.readdirSync(config.generatedStoriesPath)
|
|
405
|
-
.filter((file) => file
|
|
427
|
+
.filter((file) => isStoryFile(file))
|
|
406
428
|
.map((file) => {
|
|
407
429
|
const filePath = path.join(config.generatedStoriesPath, file);
|
|
408
430
|
const stats = fs.statSync(filePath);
|
|
@@ -412,8 +434,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
412
434
|
if (files.length > 0) {
|
|
413
435
|
// Use the most recently modified story
|
|
414
436
|
const mostRecent = files[0].file;
|
|
415
|
-
const
|
|
416
|
-
storyId =
|
|
437
|
+
const hash = getHashFromFilename(mostRecent);
|
|
438
|
+
storyId = hash ? `story-${hash}` : removeStoryExtension(mostRecent);
|
|
417
439
|
console.error(`[MCP] Using most recent story: ${mostRecent} (${storyId})`);
|
|
418
440
|
}
|
|
419
441
|
}
|
|
@@ -436,11 +458,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
436
458
|
// Extract hash from story ID
|
|
437
459
|
const hashMatch = storyId.match(/^story-([a-f0-9]{8})$/);
|
|
438
460
|
const hash = hashMatch ? hashMatch[1] : null;
|
|
439
|
-
// Find matching file
|
|
461
|
+
// Find matching file (framework-agnostic)
|
|
440
462
|
const matchingFile = files.find(file => {
|
|
441
|
-
if (
|
|
463
|
+
if (!isStoryFile(file))
|
|
464
|
+
return false;
|
|
465
|
+
if (hash && file.includes(`-${hash}.stories.`))
|
|
442
466
|
return true;
|
|
443
|
-
if (file ===
|
|
467
|
+
if (removeStoryExtension(file) === storyId)
|
|
444
468
|
return true;
|
|
445
469
|
return false;
|
|
446
470
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini-provider.d.ts","sourceRoot":"","sources":["../../../story-generator/llm-providers/gemini-provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,YAAY,EACZ,cAAc,EACd,SAAS,EACT,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"gemini-provider.d.ts","sourceRoot":"","sources":["../../../story-generator/llm-providers/gemini-provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,YAAY,EACZ,cAAc,EACd,SAAS,EACT,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAkHrD,qBAAa,cAAe,SAAQ,eAAe;IACjD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAY;IACvC,QAAQ,CAAC,eAAe,cAAiB;gBAE7B,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC;IAU5C,OAAO,CAAC,SAAS;IAKX,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA6D1E,UAAU,CACf,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,WAAW,CAAC;IA+GvB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8C/D,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,cAAc;IAqCtB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,eAAe;IAiBvB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAKrC;AAGD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAErF"}
|
|
@@ -6,8 +6,22 @@
|
|
|
6
6
|
import { BaseLLMProvider } from './base-provider.js';
|
|
7
7
|
import { logger } from '../logger.js';
|
|
8
8
|
// Gemini model definitions - Updated December 2025
|
|
9
|
-
// Top
|
|
9
|
+
// Top 5 models - Reference: https://ai.google.dev/gemini-api/docs/models
|
|
10
10
|
const GEMINI_MODELS = [
|
|
11
|
+
{
|
|
12
|
+
id: 'gemini-3-pro-preview',
|
|
13
|
+
name: 'Gemini 3 Pro Preview',
|
|
14
|
+
provider: 'gemini',
|
|
15
|
+
contextWindow: 1048576,
|
|
16
|
+
maxOutputTokens: 65536,
|
|
17
|
+
supportsVision: true,
|
|
18
|
+
supportsDocuments: true,
|
|
19
|
+
supportsFunctionCalling: true,
|
|
20
|
+
supportsStreaming: true,
|
|
21
|
+
supportsReasoning: true,
|
|
22
|
+
inputPricePer1kTokens: 0.002,
|
|
23
|
+
outputPricePer1kTokens: 0.012,
|
|
24
|
+
},
|
|
11
25
|
{
|
|
12
26
|
id: 'gemini-2.5-pro',
|
|
13
27
|
name: 'Gemini 2.5 Pro',
|
|
@@ -63,8 +77,8 @@ const GEMINI_MODELS = [
|
|
|
63
77
|
outputPricePer1kTokens: 0.005,
|
|
64
78
|
},
|
|
65
79
|
];
|
|
66
|
-
// Default model - Gemini
|
|
67
|
-
const DEFAULT_MODEL = 'gemini-
|
|
80
|
+
// Default model - Gemini 3 Pro Preview (most capable)
|
|
81
|
+
const DEFAULT_MODEL = 'gemini-3-pro-preview';
|
|
68
82
|
// API configuration
|
|
69
83
|
const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta/models';
|
|
70
84
|
export class GeminiProvider extends BaseLLMProvider {
|