@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 | Fast generation, cost efficiency |
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
- | `POST` | `/story-ui/claude` | Generate with Claude |
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
@@ -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 .stories.tsx files
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.endsWith('.stories.tsx'))
253
+ .filter((file) => isStoryFile(file))
234
254
  .forEach((file) => {
235
- const hash = file.match(/-([a-f0-9]{8})\.stories\.tsx$/)?.[1] || '';
236
- const storyId = hash ? `story-${hash}` : file.replace('.stories.tsx', '');
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.replace('.stories.tsx', '').replace(/-/g, ' ');
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 (hash && file.includes(`-${hash}.stories.tsx`))
350
+ if (!isStoryFile(file))
351
+ return false;
352
+ if (hash && file.includes(`-${hash}.stories.`))
331
353
  return true;
332
- if (file === `${storyId}.stories.tsx`)
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.endsWith('.stories.tsx'))
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 hashMatch = mostRecent.match(/-([a-f0-9]{8})\.stories\.tsx$/);
416
- storyId = hashMatch ? `story-${hashMatch[1]}` : mostRecent.replace('.stories.tsx', '');
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 (hash && file.includes(`-${hash}.stories.tsx`))
463
+ if (!isStoryFile(file))
464
+ return false;
465
+ if (hash && file.includes(`-${hash}.stories.`))
442
466
  return true;
443
- if (file === `${storyId}.stories.tsx`)
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;AAoGrD,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"}
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 4 models only - Reference: https://ai.google.dev/gemini-api/docs/models
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 2.5 Pro (recommended)
67
- const DEFAULT_MODEL = 'gemini-2.5-pro';
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpitre/story-ui",
3
- "version": "4.4.1",
3
+ "version": "4.4.2",
4
4
  "description": "AI-powered Storybook story generator with dynamic component discovery",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",