@tpitre/story-ui 4.1.1 → 4.2.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 CHANGED
File without changes
@@ -399,6 +399,125 @@ app.delete('/story-ui/stories', async (req, res) => {
399
399
  return res.status(500).json({ error: 'Failed to clear stories' });
400
400
  }
401
401
  });
402
+ // Orphan stories management - find and delete stories without associated chats
403
+ // POST to get list of orphans, DELETE to remove them
404
+ app.post('/story-ui/orphan-stories', async (req, res) => {
405
+ try {
406
+ const { chatFileNames } = req.body;
407
+ if (!chatFileNames || !Array.isArray(chatFileNames)) {
408
+ return res.status(400).json({ error: 'chatFileNames array is required' });
409
+ }
410
+ const storiesPath = config.generatedStoriesPath;
411
+ if (!fs.existsSync(storiesPath)) {
412
+ return res.json({ orphans: [], count: 0 });
413
+ }
414
+ const files = fs.readdirSync(storiesPath);
415
+ const storyFiles = files.filter(file => file.endsWith('.stories.tsx') ||
416
+ file.endsWith('.stories.ts') ||
417
+ file.endsWith('.stories.svelte'));
418
+ // Find orphans: stories that don't match any chat fileName
419
+ const orphans = storyFiles.filter(storyFile => {
420
+ // Extract the base name without extension for comparison
421
+ const storyBase = storyFile
422
+ .replace('.stories.tsx', '')
423
+ .replace('.stories.ts', '')
424
+ .replace('.stories.svelte', '');
425
+ // Check if any chat fileName matches this story
426
+ return !chatFileNames.some(chatFileName => {
427
+ if (!chatFileName)
428
+ return false;
429
+ const chatBase = chatFileName
430
+ .replace('.stories.tsx', '')
431
+ .replace('.stories.ts', '')
432
+ .replace('.stories.svelte', '');
433
+ return storyBase === chatBase || storyFile === chatFileName;
434
+ });
435
+ });
436
+ // Get details for each orphan
437
+ const orphanDetails = orphans.map(fileName => {
438
+ const filePath = path.join(storiesPath, fileName);
439
+ const content = fs.readFileSync(filePath, 'utf-8');
440
+ const stats = fs.statSync(filePath);
441
+ // Extract title from story file
442
+ const titleMatch = content.match(/title:\s*['"]([^'"]+)['"]/);
443
+ let title = titleMatch ? titleMatch[1].replace('Generated/', '') : fileName.replace(/\.stories\.[a-z]+$/, '');
444
+ // Remove hash suffix from display title
445
+ title = title.replace(/\s*\([a-f0-9]{8}\)$/i, '');
446
+ return {
447
+ fileName,
448
+ title,
449
+ lastUpdated: stats.mtime.getTime()
450
+ };
451
+ });
452
+ console.log(`📋 Found ${orphans.length} orphan stories out of ${storyFiles.length} total`);
453
+ return res.json({
454
+ orphans: orphanDetails,
455
+ count: orphans.length,
456
+ totalStories: storyFiles.length
457
+ });
458
+ }
459
+ catch (error) {
460
+ console.error('Error finding orphan stories:', error);
461
+ return res.status(500).json({ error: 'Failed to find orphan stories' });
462
+ }
463
+ });
464
+ app.delete('/story-ui/orphan-stories', async (req, res) => {
465
+ try {
466
+ const { chatFileNames } = req.body;
467
+ if (!chatFileNames || !Array.isArray(chatFileNames)) {
468
+ return res.status(400).json({ error: 'chatFileNames array is required' });
469
+ }
470
+ const storiesPath = config.generatedStoriesPath;
471
+ if (!fs.existsSync(storiesPath)) {
472
+ return res.json({ deleted: [], count: 0 });
473
+ }
474
+ const files = fs.readdirSync(storiesPath);
475
+ const storyFiles = files.filter(file => file.endsWith('.stories.tsx') ||
476
+ file.endsWith('.stories.ts') ||
477
+ file.endsWith('.stories.svelte'));
478
+ // Find orphans: stories that don't match any chat fileName
479
+ const orphans = storyFiles.filter(storyFile => {
480
+ const storyBase = storyFile
481
+ .replace('.stories.tsx', '')
482
+ .replace('.stories.ts', '')
483
+ .replace('.stories.svelte', '');
484
+ return !chatFileNames.some(chatFileName => {
485
+ if (!chatFileName)
486
+ return false;
487
+ const chatBase = chatFileName
488
+ .replace('.stories.tsx', '')
489
+ .replace('.stories.ts', '')
490
+ .replace('.stories.svelte', '');
491
+ return storyBase === chatBase || storyFile === chatFileName;
492
+ });
493
+ });
494
+ // Delete orphans
495
+ const deleted = [];
496
+ const errors = [];
497
+ for (const fileName of orphans) {
498
+ try {
499
+ const filePath = path.join(storiesPath, fileName);
500
+ fs.unlinkSync(filePath);
501
+ deleted.push(fileName);
502
+ console.log(`🗑️ Deleted orphan story: ${fileName}`);
503
+ }
504
+ catch (err) {
505
+ errors.push(fileName);
506
+ console.error(`❌ Error deleting orphan ${fileName}:`, err);
507
+ }
508
+ }
509
+ console.log(`✅ Deleted ${deleted.length} orphan stories`);
510
+ return res.json({
511
+ deleted,
512
+ count: deleted.length,
513
+ errors: errors.length > 0 ? errors : undefined
514
+ });
515
+ }
516
+ catch (error) {
517
+ console.error('Error deleting orphan stories:', error);
518
+ return res.status(500).json({ error: 'Failed to delete orphan stories' });
519
+ }
520
+ });
402
521
  // MCP Remote HTTP transport routes (for Claude Desktop remote connections)
403
522
  // Provides Streamable HTTP and legacy SSE endpoints for remote MCP access
404
523
  app.use('/mcp-remote', mcpRemoteRouter);
@@ -1 +1 @@
1
- {"version":3,"file":"claude-provider.d.ts","sourceRoot":"","sources":["../../../story-generator/llm-providers/claude-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;AAwJrD,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;IAUtC,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAkE1E,UAAU,CACf,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,WAAW,CAAC;IAuGvB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgD/D,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,aAAa;IAiBrB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAMrC;AAGD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAErF"}
1
+ {"version":3,"file":"claude-provider.d.ts","sourceRoot":"","sources":["../../../story-generator/llm-providers/claude-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;AAiGrD,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;IAUtC,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAkE1E,UAAU,CACf,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,WAAW,CAAC;IAuGvB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgD/D,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,aAAa;IAiBrB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAMrC;AAGD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAErF"}
@@ -5,10 +5,9 @@
5
5
  */
6
6
  import { BaseLLMProvider } from './base-provider.js';
7
7
  import { logger } from '../logger.js';
8
- // Claude model definitions - Updated November 2025
9
- // Based on Anthropic API documentation via Context7
8
+ // Claude model definitions - Updated December 2025
9
+ // Top 4 models only - Reference: Anthropic API documentation
10
10
  const CLAUDE_MODELS = [
11
- // Latest Claude 4.x models (Recommended)
12
11
  {
13
12
  id: 'claude-opus-4-5-20251101',
14
13
  name: 'Claude Opus 4.5',
@@ -48,7 +47,6 @@ const CLAUDE_MODELS = [
48
47
  inputPricePer1kTokens: 0.0008,
49
48
  outputPricePer1kTokens: 0.004,
50
49
  },
51
- // Claude 4.0 models
52
50
  {
53
51
  id: 'claude-sonnet-4-20250514',
54
52
  name: 'Claude Sonnet 4',
@@ -62,62 +60,9 @@ const CLAUDE_MODELS = [
62
60
  inputPricePer1kTokens: 0.003,
63
61
  outputPricePer1kTokens: 0.015,
64
62
  },
65
- {
66
- id: 'claude-opus-4-20250514',
67
- name: 'Claude Opus 4',
68
- provider: 'claude',
69
- contextWindow: 200000,
70
- maxOutputTokens: 32000,
71
- supportsVision: true,
72
- supportsDocuments: true,
73
- supportsFunctionCalling: true,
74
- supportsStreaming: true,
75
- inputPricePer1kTokens: 0.015,
76
- outputPricePer1kTokens: 0.075,
77
- },
78
- // Legacy models (deprecated - use newer versions)
79
- {
80
- id: 'claude-3-7-sonnet-20250219',
81
- name: 'Claude 3.7 Sonnet (Legacy)',
82
- provider: 'claude',
83
- contextWindow: 200000,
84
- maxOutputTokens: 16000,
85
- supportsVision: true,
86
- supportsDocuments: true,
87
- supportsFunctionCalling: true,
88
- supportsStreaming: true,
89
- inputPricePer1kTokens: 0.003,
90
- outputPricePer1kTokens: 0.015,
91
- },
92
- {
93
- id: 'claude-3-5-sonnet-20241022',
94
- name: 'Claude 3.5 Sonnet (Deprecated)',
95
- provider: 'claude',
96
- contextWindow: 200000,
97
- maxOutputTokens: 8192,
98
- supportsVision: true,
99
- supportsDocuments: true,
100
- supportsFunctionCalling: true,
101
- supportsStreaming: true,
102
- inputPricePer1kTokens: 0.003,
103
- outputPricePer1kTokens: 0.015,
104
- },
105
- {
106
- id: 'claude-3-5-haiku-20241022',
107
- name: 'Claude 3.5 Haiku (Deprecated)',
108
- provider: 'claude',
109
- contextWindow: 200000,
110
- maxOutputTokens: 8192,
111
- supportsVision: true,
112
- supportsDocuments: false,
113
- supportsFunctionCalling: true,
114
- supportsStreaming: true,
115
- inputPricePer1kTokens: 0.0008,
116
- outputPricePer1kTokens: 0.004,
117
- },
118
63
  ];
119
64
  // Default model - Claude Sonnet 4.5 (recommended for agents and coding)
120
- const DEFAULT_MODEL = 'claude-sonnet-4-5-20250929';
65
+ const DEFAULT_MODEL = 'claude-opus-4-5-20251101';
121
66
  // API configuration
122
67
  const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
123
68
  const ANTHROPIC_VERSION = '2023-06-01';
@@ -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;AAgKrD,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;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"}
@@ -6,25 +6,8 @@
6
6
  import { BaseLLMProvider } from './base-provider.js';
7
7
  import { logger } from '../logger.js';
8
8
  // Gemini model definitions - Updated December 2025
9
- // Reference: https://ai.google.dev/gemini-api/docs/models
9
+ // Top 4 models only - Reference: https://ai.google.dev/gemini-api/docs/models
10
10
  const GEMINI_MODELS = [
11
- // Gemini 3 Series - Latest
12
- {
13
- id: 'gemini-3-pro-preview',
14
- name: 'Gemini 3 Pro (Preview)',
15
- provider: 'gemini',
16
- contextWindow: 1048576, // 1M tokens input
17
- maxOutputTokens: 65536,
18
- supportsVision: true,
19
- supportsDocuments: true,
20
- supportsFunctionCalling: true,
21
- supportsStreaming: true,
22
- supportsReasoning: true,
23
- inputPricePer1kTokens: 0.002,
24
- outputPricePer1kTokens: 0.012,
25
- description: 'Most capable Gemini model with advanced reasoning.',
26
- },
27
- // Gemini 2.5 Series
28
11
  {
29
12
  id: 'gemini-2.5-pro',
30
13
  name: 'Gemini 2.5 Pro',
@@ -38,7 +21,6 @@ const GEMINI_MODELS = [
38
21
  supportsReasoning: true,
39
22
  inputPricePer1kTokens: 0.00125,
40
23
  outputPricePer1kTokens: 0.01,
41
- description: 'Gemini 2.5 Pro with enhanced reasoning.',
42
24
  },
43
25
  {
44
26
  id: 'gemini-2.5-flash',
@@ -53,21 +35,6 @@ const GEMINI_MODELS = [
53
35
  supportsReasoning: true,
54
36
  inputPricePer1kTokens: 0.00015,
55
37
  outputPricePer1kTokens: 0.0006,
56
- description: 'Fast Gemini 2.5 Flash with adaptive thinking.',
57
- },
58
- // Gemini 2.0 Series
59
- {
60
- id: 'gemini-2.0-flash-exp',
61
- name: 'Gemini 2.0 Flash Experimental',
62
- provider: 'gemini',
63
- contextWindow: 1048576,
64
- maxOutputTokens: 8192,
65
- supportsVision: true,
66
- supportsDocuments: true,
67
- supportsFunctionCalling: true,
68
- supportsStreaming: true,
69
- inputPricePer1kTokens: 0.00,
70
- outputPricePer1kTokens: 0.00,
71
38
  },
72
39
  {
73
40
  id: 'gemini-2.0-flash',
@@ -82,7 +49,6 @@ const GEMINI_MODELS = [
82
49
  inputPricePer1kTokens: 0.00,
83
50
  outputPricePer1kTokens: 0.00,
84
51
  },
85
- // Gemini 1.5 Series (Legacy)
86
52
  {
87
53
  id: 'gemini-1.5-pro',
88
54
  name: 'Gemini 1.5 Pro',
@@ -96,35 +62,9 @@ const GEMINI_MODELS = [
96
62
  inputPricePer1kTokens: 0.00125,
97
63
  outputPricePer1kTokens: 0.005,
98
64
  },
99
- {
100
- id: 'gemini-1.5-flash',
101
- name: 'Gemini 1.5 Flash',
102
- provider: 'gemini',
103
- contextWindow: 1048576,
104
- maxOutputTokens: 8192,
105
- supportsVision: true,
106
- supportsDocuments: true,
107
- supportsFunctionCalling: true,
108
- supportsStreaming: true,
109
- inputPricePer1kTokens: 0.000075,
110
- outputPricePer1kTokens: 0.0003,
111
- },
112
- {
113
- id: 'gemini-1.5-flash-8b',
114
- name: 'Gemini 1.5 Flash 8B',
115
- provider: 'gemini',
116
- contextWindow: 1048576,
117
- maxOutputTokens: 8192,
118
- supportsVision: true,
119
- supportsDocuments: true,
120
- supportsFunctionCalling: true,
121
- supportsStreaming: true,
122
- inputPricePer1kTokens: 0.0000375,
123
- outputPricePer1kTokens: 0.00015,
124
- },
125
65
  ];
126
- // Default model
127
- const DEFAULT_MODEL = 'gemini-2.0-flash';
66
+ // Default model - Gemini 2.5 Pro (recommended)
67
+ const DEFAULT_MODEL = 'gemini-2.5-pro';
128
68
  // API configuration
129
69
  const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta/models';
130
70
  export class GeminiProvider extends BaseLLMProvider {
@@ -1 +1 @@
1
- {"version":3,"file":"openai-provider.d.ts","sourceRoot":"","sources":["../../../story-generator/llm-providers/openai-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;AA+LrD,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;IAUtC,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAmE1E,UAAU,CACf,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,WAAW,CAAC;IA6GvB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+C/D,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,eAAe;IAmBvB,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":"openai-provider.d.ts","sourceRoot":"","sources":["../../../story-generator/llm-providers/openai-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;AAsGrD,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;IAUtC,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAmE1E,UAAU,CACf,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,WAAW,CAAC;IA6GvB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+C/D,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,eAAe;IAmBvB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAKrC;AAGD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAErF"}
@@ -5,13 +5,12 @@
5
5
  */
6
6
  import { BaseLLMProvider } from './base-provider.js';
7
7
  import { logger } from '../logger.js';
8
- // OpenAI model definitions - Updated November 2025
9
- // Reference: https://openai.com/index/introducing-gpt-5/
8
+ // OpenAI model definitions - Updated December 2025
9
+ // Reference: https://platform.openai.com/docs/guides/latest-model
10
10
  const OPENAI_MODELS = [
11
- // GPT-5.1 Series - Latest (November 2025)
12
11
  {
13
- id: 'gpt-5.1',
14
- name: 'GPT-5.1',
12
+ id: 'gpt-5.2',
13
+ name: 'GPT-5.2',
15
14
  provider: 'openai',
16
15
  contextWindow: 256000,
17
16
  maxOutputTokens: 32768,
@@ -19,47 +18,15 @@ const OPENAI_MODELS = [
19
18
  supportsDocuments: true,
20
19
  supportsFunctionCalling: true,
21
20
  supportsStreaming: true,
22
- supportsReasoning: true, // Adaptive reasoning with reasoning_effort parameter
21
+ supportsReasoning: true,
23
22
  inputPricePer1kTokens: 0.005,
24
23
  outputPricePer1kTokens: 0.015,
25
- description: 'Latest GPT-5 series. 76.3% on SWE-bench. Adaptive reasoning capability.',
26
- },
27
- {
28
- id: 'gpt-5.1-chat-latest',
29
- name: 'GPT-5.1 Instant',
30
- provider: 'openai',
31
- contextWindow: 256000,
32
- maxOutputTokens: 32768,
33
- supportsVision: true,
34
- supportsDocuments: true,
35
- supportsFunctionCalling: true,
36
- supportsStreaming: true,
37
- supportsReasoning: true,
38
- inputPricePer1kTokens: 0.003,
39
- outputPricePer1kTokens: 0.012,
40
- description: 'More conversational GPT-5.1 with improved instruction following.',
41
24
  },
42
25
  {
43
- id: 'gpt-5.1-thinking',
44
- name: 'GPT-5.1 Thinking',
26
+ id: 'gpt-5.1',
27
+ name: 'GPT-5.1',
45
28
  provider: 'openai',
46
29
  contextWindow: 256000,
47
- maxOutputTokens: 65536,
48
- supportsVision: true,
49
- supportsDocuments: true,
50
- supportsFunctionCalling: true,
51
- supportsStreaming: true,
52
- supportsReasoning: true,
53
- inputPricePer1kTokens: 0.008,
54
- outputPricePer1kTokens: 0.024,
55
- description: 'Extended thinking mode for complex reasoning tasks.',
56
- },
57
- // GPT-5 Original (August 2025)
58
- {
59
- id: 'gpt-5',
60
- name: 'GPT-5',
61
- provider: 'openai',
62
- contextWindow: 200000,
63
30
  maxOutputTokens: 32768,
64
31
  supportsVision: true,
65
32
  supportsDocuments: true,
@@ -68,9 +35,7 @@ const OPENAI_MODELS = [
68
35
  supportsReasoning: true,
69
36
  inputPricePer1kTokens: 0.005,
70
37
  outputPricePer1kTokens: 0.015,
71
- description: 'Multimodal foundation model combining reasoning and general capabilities.',
72
38
  },
73
- // GPT-4o Series
74
39
  {
75
40
  id: 'gpt-4o',
76
41
  name: 'GPT-4o',
@@ -97,63 +62,9 @@ const OPENAI_MODELS = [
97
62
  inputPricePer1kTokens: 0.00015,
98
63
  outputPricePer1kTokens: 0.0006,
99
64
  },
100
- // o1 Reasoning Series
101
- {
102
- id: 'o1',
103
- name: 'o1 (Reasoning)',
104
- provider: 'openai',
105
- contextWindow: 200000,
106
- maxOutputTokens: 100000,
107
- supportsVision: true,
108
- supportsDocuments: false,
109
- supportsFunctionCalling: true,
110
- supportsStreaming: true,
111
- inputPricePer1kTokens: 0.015,
112
- outputPricePer1kTokens: 0.06,
113
- },
114
- {
115
- id: 'o1-mini',
116
- name: 'o1 Mini (Reasoning)',
117
- provider: 'openai',
118
- contextWindow: 128000,
119
- maxOutputTokens: 65536,
120
- supportsVision: false,
121
- supportsDocuments: false,
122
- supportsFunctionCalling: true,
123
- supportsStreaming: true,
124
- inputPricePer1kTokens: 0.003,
125
- outputPricePer1kTokens: 0.012,
126
- },
127
- // Legacy GPT-4 Series
128
- {
129
- id: 'gpt-4-turbo',
130
- name: 'GPT-4 Turbo',
131
- provider: 'openai',
132
- contextWindow: 128000,
133
- maxOutputTokens: 4096,
134
- supportsVision: true,
135
- supportsDocuments: false,
136
- supportsFunctionCalling: true,
137
- supportsStreaming: true,
138
- inputPricePer1kTokens: 0.01,
139
- outputPricePer1kTokens: 0.03,
140
- },
141
- {
142
- id: 'gpt-3.5-turbo',
143
- name: 'GPT-3.5 Turbo (Legacy)',
144
- provider: 'openai',
145
- contextWindow: 16385,
146
- maxOutputTokens: 4096,
147
- supportsVision: false,
148
- supportsDocuments: false,
149
- supportsFunctionCalling: true,
150
- supportsStreaming: true,
151
- inputPricePer1kTokens: 0.0005,
152
- outputPricePer1kTokens: 0.0015,
153
- },
154
65
  ];
155
66
  // Default model - Updated to latest GPT-5.1 (December 2025)
156
- const DEFAULT_MODEL = 'gpt-5.1';
67
+ const DEFAULT_MODEL = 'gpt-5.2';
157
68
  // API configuration
158
69
  const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
159
70
  export class OpenAIProvider extends BaseLLMProvider {
@@ -1 +1 @@
1
- {"version":3,"file":"StoryUIPanel.d.ts","sourceRoot":"","sources":["../../../templates/StoryUI/StoryUIPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,oBAAoB,CAAC;AAuwB5B,UAAU,iBAAiB;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,iBAAS,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,iBAAiB,2CAsiCnD;AAED,eAAe,YAAY,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"StoryUIPanel.d.ts","sourceRoot":"","sources":["../../../templates/StoryUI/StoryUIPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,oBAAoB,CAAC;AAwwB5B,UAAU,iBAAiB;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,iBAAS,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,iBAAiB,2CAwnCnD;AAED,eAAe,YAAY,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -151,6 +151,7 @@ const MCP_API = `${API_BASE}/mcp/generate-story`;
151
151
  const MCP_STREAM_API = `${API_BASE}/mcp/generate-story-stream`;
152
152
  const PROVIDERS_API = `${API_BASE}/mcp/providers`;
153
153
  const STORIES_API = `${API_BASE}/story-ui/stories`;
154
+ const ORPHAN_STORIES_API = `${API_BASE}/story-ui/orphan-stories`;
154
155
  const CONSIDERATIONS_API = `${API_BASE}/mcp/considerations`;
155
156
  function isEdgeMode() {
156
157
  if (typeof window !== 'undefined') {
@@ -444,6 +445,8 @@ function StoryUIPanel({ mcpPort }) {
444
445
  const [contextMenuId, setContextMenuId] = useState(null);
445
446
  const [renamingChatId, setRenamingChatId] = useState(null);
446
447
  const [renameValue, setRenameValue] = useState('');
448
+ const [orphanCount, setOrphanCount] = useState(0);
449
+ const [isDeletingOrphans, setIsDeletingOrphans] = useState(false);
447
450
  const chatEndRef = useRef(null);
448
451
  const inputRef = useRef(null);
449
452
  const fileInputRef = useRef(null);
@@ -1144,6 +1147,65 @@ function StoryUIPanel({ mcpPort }) {
1144
1147
  }
1145
1148
  }
1146
1149
  };
1150
+ // Check for orphan stories (stories without associated chats)
1151
+ const checkOrphanStories = useCallback(async () => {
1152
+ if (!state.connectionStatus.connected)
1153
+ return;
1154
+ try {
1155
+ const chatFileNames = state.recentChats.map(chat => chat.fileName);
1156
+ const response = await fetch(ORPHAN_STORIES_API, {
1157
+ method: 'POST',
1158
+ headers: { 'Content-Type': 'application/json' },
1159
+ body: JSON.stringify({ chatFileNames }),
1160
+ });
1161
+ if (response.ok) {
1162
+ const data = await response.json();
1163
+ setOrphanCount(data.count || 0);
1164
+ }
1165
+ }
1166
+ catch (error) {
1167
+ console.error('Failed to check orphan stories:', error);
1168
+ }
1169
+ }, [state.connectionStatus.connected, state.recentChats]);
1170
+ // Delete all orphan stories
1171
+ const handleDeleteOrphans = async () => {
1172
+ if (orphanCount === 0)
1173
+ return;
1174
+ if (!confirm(`Delete ${orphanCount} orphan ${orphanCount === 1 ? 'story' : 'stories'}? These are generated story files without associated chats.`)) {
1175
+ return;
1176
+ }
1177
+ setIsDeletingOrphans(true);
1178
+ try {
1179
+ const chatFileNames = state.recentChats.map(chat => chat.fileName);
1180
+ const response = await fetch(ORPHAN_STORIES_API, {
1181
+ method: 'DELETE',
1182
+ headers: { 'Content-Type': 'application/json' },
1183
+ body: JSON.stringify({ chatFileNames }),
1184
+ });
1185
+ if (response.ok) {
1186
+ const data = await response.json();
1187
+ setOrphanCount(0);
1188
+ if (data.count > 0) {
1189
+ // Show success message briefly
1190
+ alert(`Deleted ${data.count} orphan ${data.count === 1 ? 'story' : 'stories'}.`);
1191
+ }
1192
+ }
1193
+ else {
1194
+ alert('Failed to delete orphan stories. Please try again.');
1195
+ }
1196
+ }
1197
+ catch (error) {
1198
+ console.error('Failed to delete orphan stories:', error);
1199
+ alert('Failed to delete orphan stories. Please try again.');
1200
+ }
1201
+ finally {
1202
+ setIsDeletingOrphans(false);
1203
+ }
1204
+ };
1205
+ // Check for orphans when chats change or connection is established
1206
+ useEffect(() => {
1207
+ checkOrphanStories();
1208
+ }, [checkOrphanStories]);
1147
1209
  const handleStartRename = (chatId, currentTitle, e) => {
1148
1210
  if (e)
1149
1211
  e.stopPropagation();
@@ -1255,7 +1317,7 @@ function StoryUIPanel({ mcpPort }) {
1255
1317
  handleConfirmRename(chat.id);
1256
1318
  if (e.key === 'Escape')
1257
1319
  handleCancelRename();
1258
- }, onClick: e => e.stopPropagation(), autoFocus: true }), _jsx("button", { className: "sui-button sui-button-icon sui-button-sm", onClick: e => { e.stopPropagation(); handleConfirmRename(chat.id); }, "aria-label": "Save", children: Icons.check }), _jsx("button", { className: "sui-button sui-button-icon sui-button-sm", onClick: e => { e.stopPropagation(); handleCancelRename(); }, "aria-label": "Cancel", children: Icons.x })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "sui-chat-item-title", children: chat.title }), _jsxs("div", { className: "sui-chat-item-actions", children: [_jsx("button", { className: "sui-chat-item-menu sui-button sui-button-icon sui-button-sm", onClick: e => { e.stopPropagation(); setContextMenuId(contextMenuId === chat.id ? null : chat.id); }, "aria-label": "More options", children: Icons.moreVertical }), contextMenuId === chat.id && (_jsxs("div", { className: "sui-context-menu", children: [_jsxs("button", { className: "sui-context-menu-item", onClick: e => handleStartRename(chat.id, chat.title, e), children: [Icons.pencil, _jsx("span", { children: "Rename" })] }), _jsxs("button", { className: "sui-context-menu-item sui-context-menu-item-danger", onClick: e => handleDeleteChat(chat.id, e), children: [Icons.trash, _jsx("span", { children: "Delete" })] })] }))] })] })) }, chat.id))) })] })), !state.sidebarOpen && (_jsx("div", { style: { padding: '12px', display: 'flex', justifyContent: 'center' }, children: _jsx("button", { className: "sui-button sui-button-ghost sui-button-icon", onClick: () => dispatch({ type: 'SET_SIDEBAR', payload: true }), "aria-label": "Show sidebar", children: Icons.panelLeft }) }))] }), _jsxs("main", { className: "sui-main", onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, children: [state.isDragging && (_jsx("div", { className: "sui-drop-overlay", children: _jsxs("div", { className: "sui-drop-overlay-text", children: [Icons.image, _jsx("span", { children: "Drop images here" })] }) })), _jsxs("header", { className: "sui-header", children: [_jsxs("div", { className: "sui-header-left", children: [_jsx("span", { className: "sui-header-title", children: "Story UI" }), _jsxs(Badge, { variant: state.connectionStatus.connected ? 'success' : 'destructive', children: [_jsx("span", { className: "sui-badge-dot" }), state.connectionStatus.connected ? getConnectionDisplayText() : 'Disconnected'] })] }), _jsx("div", { className: "sui-header-right", children: state.connectionStatus.connected && state.availableProviders.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sui-select", children: [_jsxs("div", { className: "sui-select-trigger", children: [_jsx("span", { children: state.availableProviders.find(p => p.type === state.selectedProvider)?.name || 'Provider' }), Icons.chevronDown] }), _jsx("select", { className: "sui-select-native", value: state.selectedProvider, onChange: e => {
1320
+ }, onClick: e => e.stopPropagation(), autoFocus: true }), _jsx("button", { className: "sui-button sui-button-icon sui-button-sm", onClick: e => { e.stopPropagation(); handleConfirmRename(chat.id); }, "aria-label": "Save", children: Icons.check }), _jsx("button", { className: "sui-button sui-button-icon sui-button-sm", onClick: e => { e.stopPropagation(); handleCancelRename(); }, "aria-label": "Cancel", children: Icons.x })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "sui-chat-item-title", children: chat.title }), _jsxs("div", { className: "sui-chat-item-actions", children: [_jsx("button", { className: "sui-chat-item-menu sui-button sui-button-icon sui-button-sm", onClick: e => { e.stopPropagation(); setContextMenuId(contextMenuId === chat.id ? null : chat.id); }, "aria-label": "More options", children: Icons.moreVertical }), contextMenuId === chat.id && (_jsxs("div", { className: "sui-context-menu", children: [_jsxs("button", { className: "sui-context-menu-item", onClick: e => handleStartRename(chat.id, chat.title, e), children: [Icons.pencil, _jsx("span", { children: "Rename" })] }), _jsxs("button", { className: "sui-context-menu-item sui-context-menu-item-danger", onClick: e => handleDeleteChat(chat.id, e), children: [Icons.trash, _jsx("span", { children: "Delete" })] })] }))] })] })) }, chat.id))) }), orphanCount > 0 && (_jsx("div", { className: "sui-orphan-footer", children: _jsx("button", { className: "sui-orphan-delete-btn", onClick: handleDeleteOrphans, disabled: isDeletingOrphans, title: `${orphanCount} story ${orphanCount === 1 ? 'file has' : 'files have'} no associated chat`, children: isDeletingOrphans ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "sui-orphan-spinner" }), _jsx("span", { children: "Deleting..." })] })) : (_jsxs(_Fragment, { children: [Icons.trash, _jsxs("span", { children: [orphanCount, " orphan ", orphanCount === 1 ? 'story' : 'stories'] })] })) }) }))] })), !state.sidebarOpen && (_jsx("div", { style: { padding: '12px', display: 'flex', justifyContent: 'center' }, children: _jsx("button", { className: "sui-button sui-button-ghost sui-button-icon", onClick: () => dispatch({ type: 'SET_SIDEBAR', payload: true }), "aria-label": "Show sidebar", children: Icons.panelLeft }) }))] }), _jsxs("main", { className: "sui-main", onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, children: [state.isDragging && (_jsx("div", { className: "sui-drop-overlay", children: _jsxs("div", { className: "sui-drop-overlay-text", children: [Icons.image, _jsx("span", { children: "Drop images here" })] }) })), _jsxs("header", { className: "sui-header", children: [_jsxs("div", { className: "sui-header-left", children: [_jsx("span", { className: "sui-header-title", children: "Story UI" }), _jsxs(Badge, { variant: state.connectionStatus.connected ? 'success' : 'destructive', children: [_jsx("span", { className: "sui-badge-dot" }), state.connectionStatus.connected ? getConnectionDisplayText() : 'Disconnected'] })] }), _jsx("div", { className: "sui-header-right", children: state.connectionStatus.connected && state.availableProviders.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sui-select", children: [_jsxs("div", { className: "sui-select-trigger", children: [_jsx("span", { children: state.availableProviders.find(p => p.type === state.selectedProvider)?.name || 'Provider' }), Icons.chevronDown] }), _jsx("select", { className: "sui-select-native", value: state.selectedProvider, onChange: e => {
1259
1321
  const newProvider = e.target.value;
1260
1322
  dispatch({ type: 'SET_SELECTED_PROVIDER', payload: newProvider });
1261
1323
  const provider = state.availableProviders.find(p => p.type === newProvider);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpitre/story-ui",
3
- "version": "4.1.1",
3
+ "version": "4.2.0",
4
4
  "description": "AI-powered Storybook story generator with dynamic component discovery",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1467,3 +1467,59 @@
1467
1467
  .sui-hidden {
1468
1468
  display: none;
1469
1469
  }
1470
+
1471
+ /* ============================================
1472
+ Orphan Stories Footer
1473
+ ============================================ */
1474
+ .sui-orphan-footer {
1475
+ flex-shrink: 0;
1476
+ padding-top: var(--space-2);
1477
+ border-top: 1px solid hsl(var(--border));
1478
+ margin-top: auto;
1479
+ }
1480
+
1481
+ .sui-orphan-delete-btn {
1482
+ display: flex;
1483
+ align-items: center;
1484
+ gap: var(--space-2);
1485
+ width: 100%;
1486
+ padding: var(--space-2) var(--space-3);
1487
+ border: none;
1488
+ background: transparent;
1489
+ color: hsl(var(--muted-foreground));
1490
+ font-size: 0.75rem;
1491
+ cursor: pointer;
1492
+ border-radius: var(--radius-sm);
1493
+ transition: all var(--transition-fast);
1494
+ }
1495
+
1496
+ .sui-orphan-delete-btn:hover:not(:disabled) {
1497
+ background: hsl(var(--destructive) / 0.1);
1498
+ color: hsl(var(--destructive));
1499
+ }
1500
+
1501
+ .sui-orphan-delete-btn:disabled {
1502
+ cursor: not-allowed;
1503
+ opacity: 0.6;
1504
+ }
1505
+
1506
+ .sui-orphan-delete-btn svg {
1507
+ width: 14px;
1508
+ height: 14px;
1509
+ flex-shrink: 0;
1510
+ }
1511
+
1512
+ .sui-orphan-spinner {
1513
+ width: 14px;
1514
+ height: 14px;
1515
+ border: 2px solid hsl(var(--muted-foreground) / 0.3);
1516
+ border-top-color: hsl(var(--muted-foreground));
1517
+ border-radius: 50%;
1518
+ animation: sui-spin 0.6s linear infinite;
1519
+ }
1520
+
1521
+ @keyframes sui-spin {
1522
+ to {
1523
+ transform: rotate(360deg);
1524
+ }
1525
+ }