@tpitre/story-ui 1.7.0 → 2.0.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.
Files changed (31) hide show
  1. package/.env.sample +3 -1
  2. package/README.md +160 -606
  3. package/dist/cli/index.js +23 -24
  4. package/dist/cli/setup.js +256 -36
  5. package/dist/mcp-server/index.js +67 -0
  6. package/dist/mcp-server/routes/generateStory.js +323 -56
  7. package/dist/story-generator/componentBlacklist.js +181 -0
  8. package/dist/story-generator/componentDiscovery.js +9 -2
  9. package/dist/story-generator/configLoader.js +109 -39
  10. package/dist/story-generator/considerationsLoader.js +204 -0
  11. package/dist/story-generator/documentation-sources.js +36 -0
  12. package/dist/story-generator/documentationLoader.js +214 -0
  13. package/dist/story-generator/dynamicPackageDiscovery.js +527 -0
  14. package/dist/story-generator/enhancedComponentDiscovery.js +369 -118
  15. package/dist/story-generator/generateStory.js +7 -3
  16. package/dist/story-generator/postProcessStory.js +71 -0
  17. package/dist/story-generator/promptGenerator.js +286 -37
  18. package/dist/story-generator/storyHistory.js +118 -0
  19. package/dist/story-generator/storyTracker.js +33 -18
  20. package/dist/story-generator/storyValidator.js +39 -0
  21. package/dist/story-generator/universalDesignSystemAdapter.js +209 -0
  22. package/dist/story-generator/validateStory.js +82 -7
  23. package/dist/story-ui.config.js +12 -5
  24. package/package.json +11 -6
  25. package/templates/StoryUI/StoryUIPanel.stories.tsx +29 -13
  26. package/templates/StoryUI/StoryUIPanel.tsx +489 -359
  27. package/templates/react-import-rule.json +36 -0
  28. package/templates/story-generation-rules.json +29 -0
  29. package/templates/story-ui-considerations.json +156 -0
  30. package/templates/story-ui-considerations.md +109 -0
  31. package/templates/story-ui-docs-README.md +55 -0
package/dist/cli/index.js CHANGED
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'url';
7
7
  import { setupProductionGitignore } from '../story-generator/productionGitignoreManager.js';
8
8
  import { createStoryUIConfig } from '../story-ui.config.js';
9
9
  import { setupCommand } from './setup.js';
10
+ import net from 'net';
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
12
13
  const program = new Command();
@@ -25,16 +26,34 @@ program
25
26
  .description('Start the Story UI server')
26
27
  .option('-p, --port <port>', 'Port to run the server on', '4001')
27
28
  .option('-c, --config <config>', 'Path to configuration file')
28
- .action((options) => {
29
+ .action(async (options) => {
29
30
  console.log('šŸš€ Starting Story UI server...');
30
31
  // Use absolute path to avoid dist/dist issue when package is linked
31
32
  const pkgRoot = path.resolve(__dirname, '..');
32
33
  const serverPath = path.join(pkgRoot, 'mcp-server/index.js');
33
34
  console.log(`āœ… Using MCP server at: ${serverPath}`);
34
- const env = { ...process.env };
35
- if (options.port) {
36
- env.PORT = options.port;
35
+ // FIRST_EDIT: determine an available port
36
+ const requestedPort = parseInt(options.port || '4001', 10);
37
+ const isPortFree = (port) => {
38
+ return new Promise((resolve) => {
39
+ const tester = net.createServer()
40
+ .once('error', () => resolve(false))
41
+ .once('listening', () => {
42
+ tester.close();
43
+ resolve(true);
44
+ })
45
+ .listen(port);
46
+ });
47
+ };
48
+ let finalPort = requestedPort;
49
+ // eslint-disable-next-line no-await-in-loop
50
+ while (!(await isPortFree(finalPort))) {
51
+ finalPort += 1;
37
52
  }
53
+ if (finalPort !== requestedPort) {
54
+ console.log(`āš ļø Port ${requestedPort} is in use. Using ${finalPort} instead.`);
55
+ }
56
+ const env = { ...process.env, PORT: String(finalPort) };
38
57
  if (options.config) {
39
58
  env.STORY_UI_CONFIG_PATH = options.config;
40
59
  }
@@ -131,26 +150,6 @@ async function autoDetectAndCreateConfig() {
131
150
  async function createTemplateConfig(template) {
132
151
  let config;
133
152
  switch (template) {
134
- case 'material-ui':
135
- config = {
136
- importPath: '@mui/material',
137
- componentPrefix: '',
138
- layoutRules: {
139
- multiColumnWrapper: 'Grid',
140
- columnComponent: 'Grid',
141
- layoutExamples: {
142
- twoColumn: `<Grid container spacing={2}>
143
- <Grid item xs={6}>
144
- <Card>Left content</Card>
145
- </Grid>
146
- <Grid item xs={6}>
147
- <Card>Right content</Card>
148
- </Grid>
149
- </Grid>`
150
- }
151
- }
152
- };
153
- break;
154
153
  case 'chakra-ui':
155
154
  config = {
156
155
  importPath: '@chakra-ui/react',
package/dist/cli/setup.js CHANGED
@@ -4,8 +4,91 @@ import inquirer from 'inquirer';
4
4
  import chalk from 'chalk';
5
5
  import { autoDetectDesignSystem } from '../story-generator/configLoader.js';
6
6
  import { fileURLToPath } from 'url';
7
+ import net from 'net';
8
+ import { execSync } from 'child_process';
7
9
  const __filename = fileURLToPath(import.meta.url);
8
10
  const __dirname = path.dirname(__filename);
11
+ // FIRST_EDIT: helper functions to check for free ports
12
+ async function isPortAvailable(port) {
13
+ return new Promise((resolve) => {
14
+ const tester = net.createServer()
15
+ .once('error', () => resolve(false))
16
+ .once('listening', () => {
17
+ tester.close();
18
+ resolve(true);
19
+ })
20
+ .listen(port);
21
+ });
22
+ }
23
+ async function findAvailablePort(startPort) {
24
+ let port = startPort;
25
+ // eslint-disable-next-line no-await-in-loop
26
+ while (!(await isPortAvailable(port))) {
27
+ port += 1;
28
+ }
29
+ return port;
30
+ }
31
+ // Design system installation configurations
32
+ const DESIGN_SYSTEM_CONFIGS = {
33
+ antd: {
34
+ packages: ['antd'],
35
+ name: 'Ant Design',
36
+ importPath: 'antd',
37
+ additionalSetup: 'import "antd/dist/reset.css";'
38
+ },
39
+ mantine: {
40
+ packages: ['@mantine/core', '@mantine/hooks', '@mantine/notifications'],
41
+ name: 'Mantine',
42
+ importPath: '@mantine/core',
43
+ additionalSetup: 'import "@mantine/core/styles.css";'
44
+ },
45
+ chakra: {
46
+ packages: ['@chakra-ui/react', '@emotion/react', '@emotion/styled', 'framer-motion'],
47
+ name: 'Chakra UI',
48
+ importPath: '@chakra-ui/react',
49
+ additionalSetup: 'import { ChakraProvider } from "@chakra-ui/react";'
50
+ }
51
+ };
52
+ async function installDesignSystem(systemKey) {
53
+ const config = DESIGN_SYSTEM_CONFIGS[systemKey];
54
+ const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8'));
55
+ // Check if packages are already installed
56
+ const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
57
+ const missingPackages = config.packages.filter(pkg => !dependencies[pkg]);
58
+ if (missingPackages.length === 0) {
59
+ console.log(chalk.green(`āœ… ${config.name} packages already installed`));
60
+ return true;
61
+ }
62
+ console.log(chalk.blue(`\nšŸ“¦ Installing ${config.name} packages...`));
63
+ console.log(chalk.gray(`Packages: ${missingPackages.join(', ')}`));
64
+ // Detect package manager
65
+ const npmLock = fs.existsSync(path.join(process.cwd(), 'package-lock.json'));
66
+ const yarnLock = fs.existsSync(path.join(process.cwd(), 'yarn.lock'));
67
+ const pnpmLock = fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'));
68
+ let installCommand = `npm install ${missingPackages.join(' ')}`;
69
+ if (yarnLock) {
70
+ installCommand = `yarn add ${missingPackages.join(' ')}`;
71
+ }
72
+ else if (pnpmLock) {
73
+ installCommand = `pnpm add ${missingPackages.join(' ')}`;
74
+ }
75
+ try {
76
+ console.log(chalk.gray(`Running: ${installCommand}`));
77
+ execSync(installCommand, { stdio: 'inherit' });
78
+ console.log(chalk.green(`āœ… ${config.name} installed successfully!`));
79
+ if (config.additionalSetup) {
80
+ console.log(chalk.blue('\nšŸ“‹ Additional setup required:'));
81
+ console.log(chalk.gray(`Add this import to your main CSS/index file:`));
82
+ console.log(chalk.cyan(`${config.additionalSetup}`));
83
+ }
84
+ return true;
85
+ }
86
+ catch (error) {
87
+ console.error(chalk.red(`āŒ Failed to install ${config.name}:`), error);
88
+ console.log(chalk.yellow(`\nšŸ’” You can install manually with: ${installCommand}`));
89
+ return false;
90
+ }
91
+ }
9
92
  export async function setupCommand() {
10
93
  console.log(chalk.blue.bold('\nšŸŽØ Story UI Setup\n'));
11
94
  console.log('This will help you configure Story UI for your design system.\n');
@@ -60,14 +143,25 @@ export async function setupCommand() {
60
143
  message: 'Which design system are you using?',
61
144
  choices: [
62
145
  { name: 'šŸ¤– Auto-detect from package.json', value: 'auto' },
63
- { name: 'šŸŽØ Material-UI (@mui/material)', value: 'mui' },
64
- { name: '⚔ Chakra UI (@chakra-ui/react)', value: 'chakra' },
65
- { name: '🐜 Ant Design (antd)', value: 'antd' },
66
- { name: 'šŸŽÆ Mantine (@mantine/core)', value: 'mantine' },
146
+ { name: '🐜 Ant Design (antd) - Install & Configure', value: 'antd' },
147
+ { name: 'šŸŽÆ Mantine (@mantine/core) - Install & Configure', value: 'mantine' },
148
+ { name: '⚔ Chakra UI (@chakra-ui/react) - Install & Configure', value: 'chakra' },
67
149
  { name: 'šŸ”§ Custom/Other', value: 'custom' }
68
150
  ],
69
151
  default: autoDetected ? 'auto' : 'custom'
70
152
  },
153
+ {
154
+ type: 'confirm',
155
+ name: 'installDesignSystem',
156
+ message: (answers) => {
157
+ const systemName = answers.designSystem === 'antd' ? 'Ant Design' :
158
+ answers.designSystem === 'mantine' ? 'Mantine' :
159
+ answers.designSystem === 'chakra' ? 'Chakra UI' : 'the design system';
160
+ return `Would you like to install ${systemName} and its dependencies now?`;
161
+ },
162
+ when: (answers) => ['antd', 'mantine', 'chakra'].includes(answers.designSystem),
163
+ default: true
164
+ },
71
165
  {
72
166
  type: 'input',
73
167
  name: 'importPath',
@@ -96,6 +190,22 @@ export async function setupCommand() {
96
190
  default: './src/components',
97
191
  when: (answers) => answers.designSystem === 'custom'
98
192
  },
193
+ {
194
+ type: 'input',
195
+ name: 'mcpPort',
196
+ message: 'Port for the Story UI MCP server',
197
+ default: async () => {
198
+ const port = await findAvailablePort(4001);
199
+ return String(port);
200
+ },
201
+ validate: async (input) => {
202
+ const value = parseInt(input, 10);
203
+ if (isNaN(value) || value <= 0)
204
+ return 'Enter a valid port number';
205
+ const available = await isPortAvailable(value);
206
+ return available ? true : `Port ${value} is already in use`;
207
+ }
208
+ },
99
209
  {
100
210
  type: 'confirm',
101
211
  name: 'hasApiKey',
@@ -110,53 +220,53 @@ export async function setupCommand() {
110
220
  validate: (input) => input.trim() ? true : 'API key is required'
111
221
  }
112
222
  ]);
223
+ // Install design system if requested
224
+ if (answers.installDesignSystem && ['antd', 'mantine', 'chakra'].includes(answers.designSystem)) {
225
+ const installSuccess = await installDesignSystem(answers.designSystem);
226
+ if (!installSuccess) {
227
+ console.log(chalk.yellow('āš ļø Installation failed but continuing with configuration...'));
228
+ }
229
+ }
113
230
  // Generate configuration
114
231
  let config = {};
115
232
  if (answers.designSystem === 'auto' && autoDetected) {
116
233
  config = autoDetected;
117
234
  }
118
- else if (answers.designSystem === 'mui') {
235
+ else if (answers.designSystem === 'chakra') {
119
236
  config = {
120
- importPath: '@mui/material',
237
+ importPath: '@chakra-ui/react',
121
238
  componentPrefix: '',
122
239
  layoutRules: {
123
- multiColumnWrapper: 'Grid',
124
- columnComponent: 'Grid',
240
+ multiColumnWrapper: 'SimpleGrid',
241
+ columnComponent: 'Box',
125
242
  containerComponent: 'Container',
126
243
  layoutExamples: {
127
- twoColumn: `<Grid container spacing={2}>
128
- <Grid item xs={6}>
244
+ twoColumn: `<SimpleGrid columns={2} spacing={6}>
245
+ <Box>
129
246
  <Card>
130
- <CardContent>
131
- <Typography variant="h5">Left Card</Typography>
132
- <Typography>Left content</Typography>
133
- </CardContent>
247
+ <CardHeader>
248
+ <Heading size="md">Left Card</Heading>
249
+ </CardHeader>
250
+ <CardBody>
251
+ <Text>Left content goes here</Text>
252
+ </CardBody>
134
253
  </Card>
135
- </Grid>
136
- <Grid item xs={6}>
254
+ </Box>
255
+ <Box>
137
256
  <Card>
138
- <CardContent>
139
- <Typography variant="h5">Right Card</Typography>
140
- <Typography>Right content</Typography>
141
- </CardContent>
257
+ <CardHeader>
258
+ <Heading size="md">Right Card</Heading>
259
+ </CardHeader>
260
+ <CardBody>
261
+ <Text>Right content goes here</Text>
262
+ </CardBody>
142
263
  </Card>
143
- </Grid>
144
- </Grid>`
264
+ </Box>
265
+ </SimpleGrid>`
145
266
  }
146
267
  }
147
268
  };
148
269
  }
149
- else if (answers.designSystem === 'chakra') {
150
- config = {
151
- importPath: '@chakra-ui/react',
152
- componentPrefix: '',
153
- layoutRules: {
154
- multiColumnWrapper: 'SimpleGrid',
155
- columnComponent: 'Box',
156
- containerComponent: 'Container'
157
- }
158
- };
159
- }
160
270
  else if (answers.designSystem === 'antd') {
161
271
  config = {
162
272
  importPath: 'antd',
@@ -164,7 +274,21 @@ export async function setupCommand() {
164
274
  layoutRules: {
165
275
  multiColumnWrapper: 'Row',
166
276
  columnComponent: 'Col',
167
- containerComponent: 'div'
277
+ containerComponent: 'div',
278
+ layoutExamples: {
279
+ twoColumn: `<Row gutter={16}>
280
+ <Col span={12}>
281
+ <Card title="Left Card" bordered={false}>
282
+ <p>Left content goes here</p>
283
+ </Card>
284
+ </Col>
285
+ <Col span={12}>
286
+ <Card title="Right Card" bordered={false}>
287
+ <p>Right content goes here</p>
288
+ </Card>
289
+ </Col>
290
+ </Row>`
291
+ }
168
292
  }
169
293
  };
170
294
  }
@@ -175,7 +299,27 @@ export async function setupCommand() {
175
299
  layoutRules: {
176
300
  multiColumnWrapper: 'SimpleGrid',
177
301
  columnComponent: 'div',
178
- containerComponent: 'Container'
302
+ containerComponent: 'Container',
303
+ layoutExamples: {
304
+ twoColumn: `<SimpleGrid cols={2} spacing="md">
305
+ <div>
306
+ <Card shadow="sm" padding="lg" radius="md" withBorder>
307
+ <Text fw={500} size="lg" mb="xs">Left Card</Text>
308
+ <Text size="sm" c="dimmed">
309
+ Left content goes here
310
+ </Text>
311
+ </Card>
312
+ </div>
313
+ <div>
314
+ <Card shadow="sm" padding="lg" radius="md" withBorder>
315
+ <Text fw={500} size="lg" mb="xs">Right Card</Text>
316
+ <Text size="sm" c="dimmed">
317
+ Right content goes here
318
+ </Text>
319
+ </Card>
320
+ </div>
321
+ </SimpleGrid>`
322
+ }
179
323
  }
180
324
  };
181
325
  }
@@ -236,6 +380,38 @@ export async function setupCommand() {
236
380
  console.warn(chalk.yellow(`āš ļø Template file not found: ${file}`));
237
381
  }
238
382
  }
383
+ // Create considerations file
384
+ const considerationsTemplatePath = path.resolve(__dirname, '../../templates/story-ui-considerations.md');
385
+ const considerationsPath = path.join(process.cwd(), 'story-ui-considerations.md');
386
+ if (!fs.existsSync(considerationsPath) && fs.existsSync(considerationsTemplatePath)) {
387
+ let considerationsContent = fs.readFileSync(considerationsTemplatePath, 'utf-8');
388
+ // Customize based on selected design system
389
+ if (config.importPath) {
390
+ considerationsContent = considerationsContent.replace('[Your Component Library]', config.importPath);
391
+ considerationsContent = considerationsContent.replace('[your-import-path]', config.importPath);
392
+ }
393
+ fs.writeFileSync(considerationsPath, considerationsContent);
394
+ console.log(chalk.green('āœ… Created story-ui-considerations.md for AI customization'));
395
+ }
396
+ // Create documentation directory structure
397
+ const docsDir = path.join(process.cwd(), 'story-ui-docs');
398
+ if (!fs.existsSync(docsDir)) {
399
+ console.log(chalk.blue('\nšŸ“š Creating documentation directory structure...'));
400
+ // Create main directory and subdirectories
401
+ const subdirs = ['guidelines', 'tokens', 'components', 'patterns'];
402
+ fs.mkdirSync(docsDir, { recursive: true });
403
+ for (const subdir of subdirs) {
404
+ fs.mkdirSync(path.join(docsDir, subdir), { recursive: true });
405
+ }
406
+ // Copy README template
407
+ const docsReadmeTemplatePath = path.resolve(__dirname, '../../templates/story-ui-docs-README.md');
408
+ const docsReadmePath = path.join(docsDir, 'README.md');
409
+ if (fs.existsSync(docsReadmeTemplatePath)) {
410
+ fs.writeFileSync(docsReadmePath, fs.readFileSync(docsReadmeTemplatePath, 'utf-8'));
411
+ }
412
+ console.log(chalk.green('āœ… Created story-ui-docs/ directory structure'));
413
+ console.log(chalk.gray(' Add your design system documentation to enhance AI story generation'));
414
+ }
239
415
  // Create .env file from template
240
416
  const envSamplePath = path.resolve(__dirname, '../../.env.sample');
241
417
  const envPath = path.join(process.cwd(), '.env');
@@ -246,6 +422,10 @@ export async function setupCommand() {
246
422
  if (answers.apiKey) {
247
423
  envContent = envContent.replace('your-claude-api-key-here', answers.apiKey);
248
424
  }
425
+ // Update the VITE_STORY_UI_PORT with the chosen port
426
+ if (answers.mcpPort) {
427
+ envContent = envContent.replace('VITE_STORY_UI_PORT=4001', `VITE_STORY_UI_PORT=${answers.mcpPort}`);
428
+ }
249
429
  fs.writeFileSync(envPath, envContent);
250
430
  console.log(chalk.green(`āœ… Created .env file${answers.apiKey ? ' with your API key' : ''}`));
251
431
  }
@@ -277,15 +457,55 @@ export async function setupCommand() {
277
457
  // Update package.json with convenience scripts
278
458
  if (packageJson) {
279
459
  const scripts = packageJson.scripts || {};
460
+ // FIRST_EDIT: include chosen port in script
461
+ const portFlag = `--port ${answers.mcpPort || '4001'}`;
280
462
  if (!scripts['story-ui']) {
281
- scripts['story-ui'] = 'story-ui start';
463
+ scripts['story-ui'] = `story-ui start ${portFlag}`;
464
+ }
465
+ else if (!scripts['story-ui'].includes('--port')) {
466
+ scripts['story-ui'] += ` ${portFlag}`;
282
467
  }
283
468
  if (!scripts['storybook-with-ui'] && scripts['storybook']) {
284
469
  scripts['storybook-with-ui'] = 'concurrently "npm run storybook" "npm run story-ui"';
285
470
  }
286
471
  packageJson.scripts = scripts;
472
+ // Check and add required dependencies
473
+ const dependencies = packageJson.dependencies || {};
474
+ const devDependencies = packageJson.devDependencies || {};
475
+ let needsInstall = false;
476
+ // Check for concurrently (needed for storybook-with-ui script)
477
+ if (!dependencies['concurrently'] && !devDependencies['concurrently']) {
478
+ console.log(chalk.blue('šŸ“¦ Adding concurrently dependency...'));
479
+ devDependencies['concurrently'] = '^8.2.0';
480
+ needsInstall = true;
481
+ }
482
+ packageJson.dependencies = dependencies;
483
+ packageJson.devDependencies = devDependencies;
287
484
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
288
485
  console.log(chalk.green('āœ… Added convenience scripts to package.json'));
486
+ if (needsInstall) {
487
+ console.log(chalk.blue('\nšŸ“¦ Installing required dependencies...'));
488
+ console.log(chalk.gray('This may take a moment...\n'));
489
+ // Detect package manager
490
+ const npmLock = fs.existsSync(path.join(process.cwd(), 'package-lock.json'));
491
+ const yarnLock = fs.existsSync(path.join(process.cwd(), 'yarn.lock'));
492
+ const pnpmLock = fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'));
493
+ let installCommand = 'npm install';
494
+ if (yarnLock) {
495
+ installCommand = 'yarn install';
496
+ }
497
+ else if (pnpmLock) {
498
+ installCommand = 'pnpm install';
499
+ }
500
+ try {
501
+ execSync(installCommand, { stdio: 'inherit' });
502
+ console.log(chalk.green('āœ… Dependencies installed successfully'));
503
+ }
504
+ catch (error) {
505
+ console.log(chalk.yellow('āš ļø Failed to install dependencies automatically.'));
506
+ console.log(chalk.yellow(` Please run "${installCommand}" manually to complete the setup.`));
507
+ }
508
+ }
289
509
  }
290
510
  console.log(chalk.green.bold('\nšŸŽ‰ Setup complete!\n'));
291
511
  console.log(`šŸ“ Configuration saved to: ${chalk.cyan(configPath)}`);
@@ -17,6 +17,7 @@ import { getSyncedStories, deleteSyncedStory, clearAllSyncedStories, syncChatHis
17
17
  import { setupProductionGitignore } from '../story-generator/productionGitignoreManager.js';
18
18
  import { getInMemoryStoryService } from '../story-generator/inMemoryStoryService.js';
19
19
  import { loadUserConfig } from '../story-generator/configLoader.js';
20
+ import fs from 'fs';
20
21
  const app = express();
21
22
  app.use(cors());
22
23
  app.use(express.json());
@@ -40,6 +41,72 @@ app.delete('/mcp/sync/stories/:id', deleteSyncedStory);
40
41
  app.delete('/mcp/sync/stories', clearAllSyncedStories);
41
42
  app.get('/mcp/sync/chat-history', syncChatHistory);
42
43
  app.get('/mcp/sync/validate/:id', validateChatSession);
44
+ // Proxy routes for frontend compatibility (maps /story-ui/ to /mcp/)
45
+ app.get('/story-ui/stories', getStoriesMetadata);
46
+ app.get('/story-ui/stories/:id', getStoryById);
47
+ app.get('/story-ui/stories/:id/content', getStoryContent);
48
+ app.delete('/story-ui/stories/:id', deleteStory);
49
+ app.delete('/story-ui/stories', clearAllStories);
50
+ app.post('/story-ui/generate', generateStoryFromPrompt);
51
+ app.post('/story-ui/claude', claudeProxy);
52
+ app.get('/story-ui/components', getComponents);
53
+ app.get('/story-ui/props', getProps);
54
+ app.get('/story-ui/memory-stats', getMemoryStats);
55
+ // Legacy delete route for backwards compatibility
56
+ app.post('/story-ui/delete', async (req, res) => {
57
+ try {
58
+ const { chatId, storyId } = req.body;
59
+ const id = chatId || storyId; // Support both parameter names
60
+ if (!id) {
61
+ return res.status(400).json({ error: 'chatId or storyId is required' });
62
+ }
63
+ console.log(`šŸ—‘ļø Attempting to delete story: ${id}`);
64
+ // First try in-memory deletion (production mode)
65
+ const storyService = getInMemoryStoryService(config);
66
+ const inMemoryDeleted = storyService.deleteStory(id);
67
+ if (inMemoryDeleted) {
68
+ console.log(`āœ… Deleted story from memory: ${id}`);
69
+ return res.json({
70
+ success: true,
71
+ message: 'Story deleted successfully from memory'
72
+ });
73
+ }
74
+ // If not found in memory, try file-system deletion (development mode)
75
+ if (gitignoreManager && !gitignoreManager.isProductionMode()) {
76
+ const storiesPath = config.generatedStoriesPath;
77
+ console.log(`šŸ” Searching for file-system story in: ${storiesPath}`);
78
+ if (fs.existsSync(storiesPath)) {
79
+ const files = fs.readdirSync(storiesPath);
80
+ const matchingFile = files.find(file => file.includes(id) || file.replace('.stories.tsx', '') === id);
81
+ if (matchingFile) {
82
+ const filePath = path.join(storiesPath, matchingFile);
83
+ fs.unlinkSync(filePath);
84
+ console.log(`āœ… Deleted story file: ${filePath}`);
85
+ return res.json({
86
+ success: true,
87
+ message: 'Story deleted successfully from file system'
88
+ });
89
+ }
90
+ }
91
+ }
92
+ console.log(`āŒ Story not found: ${id}`);
93
+ return res.status(404).json({
94
+ success: false,
95
+ error: 'Story not found'
96
+ });
97
+ }
98
+ catch (error) {
99
+ console.error('Error in legacy delete route:', error);
100
+ res.status(500).json({ error: 'Failed to delete story' });
101
+ }
102
+ });
103
+ // Synchronized story proxy routes
104
+ app.get('/story-ui/sync/stories', getSyncedStories);
105
+ app.get('/story-ui/sync/stories/:id', getSyncedStoryById);
106
+ app.delete('/story-ui/sync/stories/:id', deleteSyncedStory);
107
+ app.delete('/story-ui/sync/stories', clearAllSyncedStories);
108
+ app.get('/story-ui/sync/chat-history', syncChatHistory);
109
+ app.get('/story-ui/sync/validate/:id', validateChatSession);
43
110
  // Set up production-ready gitignore and directory structure on startup
44
111
  const config = loadUserConfig();
45
112
  const gitignoreManager = setupProductionGitignore(config);