@tpitre/story-ui 4.5.2 → 4.6.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
@@ -58,6 +58,11 @@ program
58
58
  console.log(`⚠️ Port ${requestedPort} is in use. Using ${finalPort} instead.`);
59
59
  }
60
60
  const env = { ...process.env, PORT: String(finalPort) };
61
+ // Set memory limit to prevent heap exhaustion during vision/image processing
62
+ // Default to 8GB for handling large images and self-healing retry loops
63
+ if (!env.NODE_OPTIONS?.includes('--max-old-space-size')) {
64
+ env.NODE_OPTIONS = `${env.NODE_OPTIONS || ''} --max-old-space-size=8192`.trim();
65
+ }
61
66
  if (options.config) {
62
67
  env.STORY_UI_CONFIG_PATH = options.config;
63
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"generateStory.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/generateStory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAmd5C,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,2DA+kBxE"}
1
+ {"version":3,"file":"generateStory.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/generateStory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAmd5C,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,2DA8lBxE"}
@@ -710,10 +710,23 @@ export async function generateStoryFromPrompt(req, res) {
710
710
  }
711
711
  // Create title for the story (clean, without hash - hash goes in id for uniqueness)
712
712
  const prettyPrompt = escapeTitleForTS(aiTitle);
713
- // Title is now clean without hash - uniqueness is ensured via Storybook's id parameter
714
- const cleanTitle = prettyPrompt;
713
+ // For NEW stories, check if title already exists and add version if needed
714
+ // This prevents multiple stories from stacking under the same navigation group
715
+ // e.g., "Navigation Bar" → "Navigation Bar v2" → "Navigation Bar v3"
716
+ let cleanTitle;
717
+ if (isActualUpdate) {
718
+ // For updates, keep the original title (don't version)
719
+ cleanTitle = prettyPrompt;
720
+ }
721
+ else {
722
+ // For new stories, check for duplicates and version if needed
723
+ cleanTitle = storyTracker.getNextVersionTitle(prettyPrompt);
724
+ if (cleanTitle !== prettyPrompt) {
725
+ logger.log(`📋 Title "${prettyPrompt}" already exists, using "${cleanTitle}" instead`);
726
+ }
727
+ }
715
728
  // Generate Storybook-compatible ID with hash for uniqueness (prevents duplicate story errors)
716
- const storyIdSlug = `${prettyPrompt.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}-${hash}`;
729
+ const storyIdSlug = `${cleanTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}-${hash}`;
717
730
  // Fix title with storyPrefix - handle multiple story formats
718
731
  // Note: (?::\s*\w+(?:<[^>]+>)?)? handles TypeScript type annotations including generics
719
732
  // e.g., "const meta: Meta = {" or "const meta: Meta<typeof Button> = {"
@@ -793,8 +806,9 @@ export async function generateStoryFromPrompt(req, res) {
793
806
  config: config
794
807
  });
795
808
  // Register with story tracker
809
+ // Use cleanTitle (which may include version suffix) for proper deduplication tracking
796
810
  const mapping = {
797
- title: aiTitle,
811
+ title: cleanTitle,
798
812
  fileName: finalFileName,
799
813
  storyId,
800
814
  hash,
@@ -855,7 +869,7 @@ export async function generateStoryFromPrompt(req, res) {
855
869
  fileName: finalFileName,
856
870
  storyId,
857
871
  outPath,
858
- title: aiTitle,
872
+ title: cleanTitle, // Use versioned title (e.g., "Navigation Bar v2")
859
873
  story: fileContents,
860
874
  storage: 'file-system',
861
875
  isUpdate: isActualUpdate,
@@ -1 +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;AA0c5C,wBAAsB,6BAA6B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBA4pB9E"}
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;AA0c5C,wBAAsB,6BAA6B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBA2qB9E"}
@@ -816,10 +816,23 @@ export async function generateStoryFromPromptStream(req, res) {
816
816
  }
817
817
  // Create title for the story (clean, without hash - hash goes in id for uniqueness)
818
818
  const prettyPrompt = escapeTitleForTS(aiTitle);
819
- // Title is now clean without hash - uniqueness is ensured via Storybook's id parameter
820
- const cleanTitle = prettyPrompt;
819
+ // For NEW stories, check if title already exists and add version if needed
820
+ // This prevents multiple stories from stacking under the same navigation group
821
+ // e.g., "Navigation Bar" → "Navigation Bar v2" → "Navigation Bar v3"
822
+ let cleanTitle;
823
+ if (isActualUpdate) {
824
+ // For updates, keep the original title (don't version)
825
+ cleanTitle = prettyPrompt;
826
+ }
827
+ else {
828
+ // For new stories, check for duplicates and version if needed
829
+ cleanTitle = storyTracker.getNextVersionTitle(prettyPrompt);
830
+ if (cleanTitle !== prettyPrompt) {
831
+ logger.log(`📋 Title "${prettyPrompt}" already exists, using "${cleanTitle}" instead`);
832
+ }
833
+ }
821
834
  // Generate Storybook-compatible ID with hash for uniqueness (prevents duplicate story errors)
822
- const storyIdSlug = `${prettyPrompt.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}-${hash}`;
835
+ const storyIdSlug = `${cleanTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}-${hash}`;
823
836
  // Fix title with storyPrefix - handle multiple story formats
824
837
  // Note: (?::\s*\w+(?:<[^>]+>)?)? handles TypeScript type annotations including generics
825
838
  // e.g., "const meta: Meta = {" or "const meta: Meta<typeof Button> = {"
@@ -873,8 +886,9 @@ export async function generateStoryFromPromptStream(req, res) {
873
886
  config: config
874
887
  });
875
888
  // Register with story tracker
889
+ // Use cleanTitle (which may include version suffix) for proper deduplication tracking
876
890
  const mapping = {
877
- title: aiTitle,
891
+ title: cleanTitle,
878
892
  fileName: finalFileName,
879
893
  storyId,
880
894
  hash,
@@ -902,7 +916,7 @@ export async function generateStoryFromPromptStream(req, res) {
902
916
  // IMPORTANT: success is FALSE when we had to create a fallback error story
903
917
  stream.sendCompletion({
904
918
  success: !isFallbackStory,
905
- title: aiTitle,
919
+ title: cleanTitle, // Use versioned title (e.g., "Navigation Bar v2")
906
920
  fileName: finalFileName,
907
921
  storyId,
908
922
  summary: {
@@ -1 +1 @@
1
- {"version":3,"file":"promptGenerator.d.ts","sourceRoot":"","sources":["../../story-generator/promptGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAI9D,OAAO,EAEL,eAAe,EACf,sBAAsB,EACtB,aAAa,EACb,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AAEvC;;;GAGG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,eAAe,EAAE,oBAAoB,CAAC;IACvF,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,eAAe,CAcxG;AA0gBD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,OAAO,CAAC,MAAM,CAAC,CAqMjB;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAwJjB;AA4ED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAIzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,aAAa,GAAG,gBAAgB,CAG9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,aAAa,EAAE,CAGxD"}
1
+ {"version":3,"file":"promptGenerator.d.ts","sourceRoot":"","sources":["../../story-generator/promptGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAI9D,OAAO,EAEL,eAAe,EACf,sBAAsB,EACtB,aAAa,EACb,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AAyOvC;;;GAGG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,eAAe,EAAE,oBAAoB,CAAC;IACvF,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,eAAe,CAcxG;AA2hBD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,OAAO,CAAC,MAAM,CAAC,CA8JjB;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAiHjB;AA4ED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAIzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,aAAa,GAAG,gBAAgB,CAG9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,aAAa,EAAE,CAGxD"}
@@ -1,6 +1,165 @@
1
1
  import { loadConsiderations, considerationsToPrompt } from './considerationsLoader.js';
2
2
  import { DocumentationLoader } from './documentationLoader.js';
3
3
  import { getAdapterRegistry, } from './framework-adapters/index.js';
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ /**
7
+ * Known icon packages and their common icons
8
+ * Used for smart detection when icon packages are installed
9
+ */
10
+ const KNOWN_ICON_PACKAGES = [
11
+ {
12
+ name: '@tabler/icons-react',
13
+ importPath: '@tabler/icons-react',
14
+ commonIcons: [
15
+ 'IconHome', 'IconSettings', 'IconUser', 'IconSearch', 'IconMenu2',
16
+ 'IconBell', 'IconMail', 'IconCalendar', 'IconClock', 'IconStar',
17
+ 'IconHeart', 'IconPlus', 'IconMinus', 'IconX', 'IconCheck',
18
+ 'IconChevronRight', 'IconChevronLeft', 'IconChevronDown', 'IconChevronUp',
19
+ 'IconArrowRight', 'IconArrowLeft', 'IconArrowUp', 'IconArrowDown',
20
+ 'IconEdit', 'IconTrash', 'IconDownload', 'IconUpload', 'IconShare',
21
+ 'IconFilter', 'IconSort', 'IconRefresh', 'IconEye', 'IconEyeOff',
22
+ 'IconLock', 'IconUnlock', 'IconCopy', 'IconClipboard', 'IconFolder',
23
+ 'IconFile', 'IconImage', 'IconVideo', 'IconMusic', 'IconLink',
24
+ 'IconExternalLink', 'IconDots', 'IconDotsVertical', 'IconGripVertical',
25
+ 'IconSun', 'IconMoon', 'IconCloud', 'IconBolt', 'IconDroplet',
26
+ 'IconMapPin', 'IconPhone', 'IconMessage', 'IconSend', 'IconInbox',
27
+ 'IconArchive', 'IconTag', 'IconBookmark', 'IconFlag', 'IconAward',
28
+ 'IconTrendingUp', 'IconTrendingDown', 'IconActivity', 'IconPieChart',
29
+ 'IconBarChart', 'IconLineChart', 'IconDatabase', 'IconServer', 'IconCode',
30
+ 'IconTerminal', 'IconBrandGithub', 'IconBrandTwitter', 'IconBrandLinkedin',
31
+ 'IconWorld', 'IconGlobe', 'IconWifi', 'IconBluetooth', 'IconCpu',
32
+ 'IconDeviceDesktop', 'IconDeviceMobile', 'IconPrinter', 'IconCamera',
33
+ 'IconMicrophone', 'IconVolume', 'IconPlayerPlay', 'IconPlayerPause',
34
+ ],
35
+ importStyle: 'named',
36
+ description: 'Tabler Icons - Free and open source icons (Mantine recommended)',
37
+ },
38
+ {
39
+ name: 'lucide-react',
40
+ importPath: 'lucide-react',
41
+ commonIcons: [
42
+ 'Home', 'Settings', 'User', 'Search', 'Menu',
43
+ 'Bell', 'Mail', 'Calendar', 'Clock', 'Star',
44
+ 'Heart', 'Plus', 'Minus', 'X', 'Check',
45
+ 'ChevronRight', 'ChevronLeft', 'ChevronDown', 'ChevronUp',
46
+ 'ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown',
47
+ 'Edit', 'Trash', 'Download', 'Upload', 'Share',
48
+ 'Filter', 'RefreshCw', 'Eye', 'EyeOff',
49
+ 'Lock', 'Unlock', 'Copy', 'Clipboard', 'Folder',
50
+ 'File', 'Image', 'Video', 'Music', 'Link',
51
+ 'ExternalLink', 'MoreHorizontal', 'MoreVertical', 'GripVertical',
52
+ 'Sun', 'Moon', 'Cloud', 'Zap', 'Droplet',
53
+ 'MapPin', 'Phone', 'MessageSquare', 'Send', 'Inbox',
54
+ ],
55
+ importStyle: 'named',
56
+ description: 'Lucide Icons - Beautiful & consistent icons',
57
+ },
58
+ {
59
+ name: '@heroicons/react',
60
+ importPath: '@heroicons/react/24/outline',
61
+ commonIcons: [
62
+ 'HomeIcon', 'Cog6ToothIcon', 'UserIcon', 'MagnifyingGlassIcon', 'Bars3Icon',
63
+ 'BellIcon', 'EnvelopeIcon', 'CalendarIcon', 'ClockIcon', 'StarIcon',
64
+ 'HeartIcon', 'PlusIcon', 'MinusIcon', 'XMarkIcon', 'CheckIcon',
65
+ 'ChevronRightIcon', 'ChevronLeftIcon', 'ChevronDownIcon', 'ChevronUpIcon',
66
+ 'ArrowRightIcon', 'ArrowLeftIcon', 'ArrowUpIcon', 'ArrowDownIcon',
67
+ 'PencilIcon', 'TrashIcon', 'ArrowDownTrayIcon', 'ArrowUpTrayIcon', 'ShareIcon',
68
+ 'FunnelIcon', 'ArrowPathIcon', 'EyeIcon', 'EyeSlashIcon',
69
+ 'LockClosedIcon', 'LockOpenIcon', 'ClipboardIcon', 'FolderIcon',
70
+ 'DocumentIcon', 'PhotoIcon', 'VideoCameraIcon', 'MusicalNoteIcon', 'LinkIcon',
71
+ ],
72
+ importStyle: 'named',
73
+ description: 'Heroicons - Beautiful hand-crafted SVG icons by Tailwind',
74
+ },
75
+ {
76
+ name: 'react-icons',
77
+ importPath: 'react-icons/fi', // Feather icons subset - most common
78
+ commonIcons: [
79
+ 'FiHome', 'FiSettings', 'FiUser', 'FiSearch', 'FiMenu',
80
+ 'FiBell', 'FiMail', 'FiCalendar', 'FiClock', 'FiStar',
81
+ 'FiHeart', 'FiPlus', 'FiMinus', 'FiX', 'FiCheck',
82
+ 'FiChevronRight', 'FiChevronLeft', 'FiChevronDown', 'FiChevronUp',
83
+ 'FiArrowRight', 'FiArrowLeft', 'FiArrowUp', 'FiArrowDown',
84
+ 'FiEdit', 'FiTrash', 'FiDownload', 'FiUpload', 'FiShare',
85
+ 'FiFilter', 'FiRefreshCw', 'FiEye', 'FiEyeOff',
86
+ ],
87
+ importStyle: 'named',
88
+ description: 'React Icons - Popular icon packs as React components',
89
+ },
90
+ {
91
+ name: '@phosphor-icons/react',
92
+ importPath: '@phosphor-icons/react',
93
+ commonIcons: [
94
+ 'House', 'Gear', 'User', 'MagnifyingGlass', 'List',
95
+ 'Bell', 'Envelope', 'Calendar', 'Clock', 'Star',
96
+ 'Heart', 'Plus', 'Minus', 'X', 'Check',
97
+ 'CaretRight', 'CaretLeft', 'CaretDown', 'CaretUp',
98
+ 'ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown',
99
+ 'PencilSimple', 'Trash', 'DownloadSimple', 'UploadSimple', 'ShareNetwork',
100
+ 'Funnel', 'ArrowsClockwise', 'Eye', 'EyeSlash',
101
+ ],
102
+ importStyle: 'named',
103
+ description: 'Phosphor Icons - Flexible icon family',
104
+ },
105
+ ];
106
+ /**
107
+ * Detects installed icon packages by checking package.json
108
+ * Returns the first detected icon package info, or null if none found
109
+ */
110
+ function detectInstalledIconPackage(projectPath) {
111
+ const cwd = projectPath || process.cwd();
112
+ const packageJsonPath = path.join(cwd, 'package.json');
113
+ try {
114
+ if (!fs.existsSync(packageJsonPath)) {
115
+ return null;
116
+ }
117
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
118
+ const allDeps = {
119
+ ...packageJson.dependencies,
120
+ ...packageJson.devDependencies,
121
+ };
122
+ // Check each known icon package
123
+ for (const iconPackage of KNOWN_ICON_PACKAGES) {
124
+ if (allDeps[iconPackage.name]) {
125
+ return iconPackage;
126
+ }
127
+ }
128
+ return null;
129
+ }
130
+ catch (error) {
131
+ // If we can't read package.json, assume no icon package
132
+ return null;
133
+ }
134
+ }
135
+ /**
136
+ * Generates icon usage instructions for the prompt
137
+ * Uses smart detection to enable real icons when a supported icon package is installed
138
+ */
139
+ function generateIconInstructions(components, projectPath) {
140
+ const instructions = [];
141
+ // First, check if an icon package is installed in the project
142
+ const installedIconPackage = detectInstalledIconPackage(projectPath);
143
+ if (installedIconPackage) {
144
+ // Icon package detected - enable icon usage with the installed package
145
+ const sampleIcons = installedIconPackage.commonIcons.slice(0, 20).join(', ');
146
+ instructions.push('', '✅ ICON LIBRARY AVAILABLE ✅', `You have ${installedIconPackage.name} installed in this project.`, `${installedIconPackage.description}`, '', '📦 How to use icons:', ` Import path: import { IconName } from '${installedIconPackage.importPath}';`, ` Example icons: ${sampleIcons}`, '', '🎯 ICON BEST PRACTICES:', ' - Use icons to enhance visual clarity and user experience', ' - Common use cases: navigation, actions, status indicators, decorative elements', ' - Icons should complement text, not replace it entirely for accessibility', ' - Use consistent icon sizing (typically 16-24px for inline, 24-48px for prominent)', '', '⚠️ IMPORTANT:', ` - ONLY import icons from '${installedIconPackage.importPath}'`, ' - Do NOT import from other icon libraries not installed in the project', ' - If you need an icon that may not exist, use a similar common icon from the list above', '');
147
+ return instructions;
148
+ }
149
+ // No icon package installed - check if design system has icon components
150
+ const iconComponents = components.filter(c => c.name && typeof c.name === 'string' &&
151
+ (c.name.toLowerCase().includes('icon') || c.name === 'Icon' || c.name === 'v-icon' || c.name === 'mat-icon' || c.name === 'sl-icon'));
152
+ if (iconComponents.length > 0) {
153
+ // Design system has icon support - allow those, prohibit external libraries
154
+ const allowedIconNames = iconComponents.map(c => c.name).join(', ');
155
+ instructions.push('', '🔶 ICON USAGE RULES 🔶', `Your design system includes icon components: ${allowedIconNames}`, '✅ You MAY use these icon components from the Available components list', '🚫 Do NOT import from external icon libraries:', ' - @tabler/icons-react, @tabler/icons', ' - react-icons, lucide-react, @heroicons/react', ' - @fortawesome/react-fontawesome, @phosphor-icons/react', ' - Any other external icon package', 'If you need icons beyond what the design system provides, use Unicode symbols (→ ✓ + ×) or text labels instead.', '');
156
+ }
157
+ else {
158
+ // No icon components discovered - prohibit all icon imports
159
+ instructions.push('', '🔴 ICON IMPORT RESTRICTION 🔴', 'This design system does not include icon components in the available components list.', '🚫 Do NOT import from ANY icon library:', ' - @tabler/icons-react, @tabler/icons', ' - react-icons, lucide-react, @heroicons/react', ' - @fortawesome/react-fontawesome, @phosphor-icons/react', ' - @mui/icons-material, @chakra-ui/icons', ' - Any other icon package', '✅ Instead, use:', ' - Unicode symbols: → ✓ ✗ + − × ÷ • ★ ♦ ▶ ◀ ▲ ▼', ' - Text labels: "Add", "Remove", "Edit", "Delete"', ' - Badge or Button components with text content', 'Icons are NOT in the available components list and WILL cause import errors.', '');
160
+ }
161
+ return instructions;
162
+ }
4
163
  /**
5
164
  * Generates a comprehensive AI prompt based on the configuration and discovered components
6
165
  */
@@ -177,6 +336,23 @@ ANY component not in that list DOES NOT EXIST and will cause import errors.
177
336
  Before importing any component, verify it exists in the Available components list.
178
337
  If a component is not listed, DO NOT use it - choose an alternative from the available list.
179
338
 
339
+ 🎨 CREATIVE APPROXIMATION - WHEN COMPONENTS DON'T EXIST 🎨
340
+ If the user asks for a UI that requires components NOT in your available list (like Calendar, Chart, Map, etc.):
341
+ **DO NOT fail or refuse** - instead, CREATIVELY APPROXIMATE the UI using available components!
342
+
343
+ Common approximation strategies:
344
+ • Calendar/DatePicker → Use Grid/SimpleGrid with Text for days, Badge for selected dates
345
+ • Charts/Graphs → Use Progress bars, RingProgress, or Stack of colored Box components
346
+ • Timeline → Use Stack with Card/Paper for each event, connecting lines with Divider
347
+ • Carousel/Slider → Use Group with Image components and Button for navigation
348
+ • Data Tables → Use Table components, or Stack of Card/Paper rows
349
+ • Tree View → Use nested Stack/Accordion with List or NavLink components
350
+ • Star Ratings → Use Group of ThemeIcon or inline SVG stars
351
+ • Maps → Use Image with a static map placeholder
352
+
353
+ The goal is VISUAL SIMILARITY, not exact functionality. Users are exploring design possibilities.
354
+ They want to see what a UI COULD look like - be creative with basic layout components!
355
+
180
356
  🔴 IMPORT PATH RULE - MANDATORY 🔴
181
357
  ALWAYS use the EXACT import path shown in parentheses after each component name.
182
358
  For example: If the Available components list shows "Button (import from 'antd')",
@@ -562,18 +738,9 @@ export async function buildClaudePrompt(userPrompt, config, components) {
562
738
  });
563
739
  });
564
740
  }
565
- // Smart icon handling - allow design system icons, prohibit external libraries
566
- const iconComponents = components.filter(c => c.name && typeof c.name === 'string' &&
567
- (c.name.toLowerCase().includes('icon') || c.name === 'Icon' || c.name === 'v-icon' || c.name === 'mat-icon' || c.name === 'sl-icon'));
568
- if (iconComponents.length > 0) {
569
- // Design system has icon support - allow those, prohibit external libraries
570
- const allowedIconNames = iconComponents.map(c => c.name).join(', ');
571
- promptParts.push('', '🔶 ICON USAGE RULES 🔶', `Your design system includes icon components: ${allowedIconNames}`, '✅ You MAY use these icon components from the Available components list', '🚫 Do NOT import from external icon libraries:', ' - @tabler/icons-react, @tabler/icons', ' - react-icons, lucide-react, @heroicons/react', ' - @fortawesome/react-fontawesome, @phosphor-icons/react', ' - Any other external icon package', 'If you need icons beyond what the design system provides, use Unicode symbols (→ ✓ + ×) or text labels instead.', '');
572
- }
573
- else {
574
- // No icon components discovered - prohibit all icon imports
575
- promptParts.push('', '🔴 ICON IMPORT RESTRICTION 🔴', 'This design system does not include icon components in the available components list.', '🚫 Do NOT import from ANY icon library:', ' - @tabler/icons-react, @tabler/icons', ' - react-icons, lucide-react, @heroicons/react', ' - @fortawesome/react-fontawesome, @phosphor-icons/react', ' - @mui/icons-material, @chakra-ui/icons', ' - Any other icon package', '✅ Instead, use:', ' - Unicode symbols: → ✓ ✗ + − × ÷ • ★ ♦ ▶ ◀ ▲ ▼', ' - Text labels: "Add", "Remove", "Edit", "Delete"', ' - Badge or Button components with text content', 'Icons are NOT in the available components list and WILL cause import errors.', '');
576
- }
741
+ // Smart icon handling - detect installed icon packages or fall back to design system icons
742
+ const iconInstructions = generateIconInstructions(components);
743
+ promptParts.push(...iconInstructions);
577
744
  // Reinforce NO children in args rule
578
745
  promptParts.push('', '🔴 CRITICAL REMINDER: NEVER use children in args 🔴', 'Always use render functions for any layout or component composition.', '');
579
746
  // Determine framework from config for rules section
@@ -698,18 +865,9 @@ export async function buildFrameworkAwarePrompt(userPrompt, config, components,
698
865
  });
699
866
  });
700
867
  }
701
- // Smart icon handling - allow design system icons, prohibit external libraries
702
- const iconComponents = components.filter(c => c.name && typeof c.name === 'string' &&
703
- (c.name.toLowerCase().includes('icon') || c.name === 'Icon' || c.name === 'v-icon' || c.name === 'mat-icon' || c.name === 'sl-icon'));
704
- if (iconComponents.length > 0) {
705
- // Design system has icon support - allow those, prohibit external libraries
706
- const allowedIconNames = iconComponents.map(c => c.name).join(', ');
707
- promptParts.push('', '🔶 ICON USAGE RULES 🔶', `Your design system includes icon components: ${allowedIconNames}`, '✅ You MAY use these icon components from the Available components list', '🚫 Do NOT import from external icon libraries:', ' - @tabler/icons-react, @tabler/icons', ' - react-icons, lucide-react, @heroicons/react', ' - @fortawesome/react-fontawesome, @phosphor-icons/react', ' - Any other external icon package', 'If you need icons beyond what the design system provides, use Unicode symbols (→ ✓ + ×) or text labels instead.', '');
708
- }
709
- else {
710
- // No icon components discovered - prohibit all icon imports
711
- promptParts.push('', '🔴 ICON IMPORT RESTRICTION 🔴', 'This design system does not include icon components in the available components list.', '🚫 Do NOT import from ANY icon library:', ' - @tabler/icons-react, @tabler/icons', ' - react-icons, lucide-react, @heroicons/react', ' - @fortawesome/react-fontawesome, @phosphor-icons/react', ' - @mui/icons-material, @chakra-ui/icons', ' - Any other icon package', '✅ Instead, use:', ' - Unicode symbols: → ✓ ✗ + − × ÷ • ★ ♦ ▶ ◀ ▲ ▼', ' - Text labels: "Add", "Remove", "Edit", "Delete"', ' - Badge or Button components with text content', 'Icons are NOT in the available components list and WILL cause import errors.', '');
712
- }
868
+ // Smart icon handling - detect installed icon packages or fall back to design system icons
869
+ const iconInstructions2 = generateIconInstructions(components);
870
+ promptParts.push(...iconInstructions2);
713
871
  // Add framework-specific rules
714
872
  const frameworkType = generated.framework.componentFramework;
715
873
  const frameworkRules = getFrameworkSpecificRules(frameworkType);
@@ -1 +1 @@
1
- {"version":3,"file":"selfHealingLoop.d.ts","sourceRoot":"","sources":["../../story-generator/selfHealingLoop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"selfHealingLoop.d.ts","sourceRoot":"","sources":["../../story-generator/selfHealingLoop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,wDAAwD;IACxD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sCAAsC;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,sCAAsC;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,6CAA6C;IAC7C,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,iBAAiB,EAAE,KAAK,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAM7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAMnE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,gBAAgB,CAMpD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,gBAAgB,GAAG,IAAI,EAClC,aAAa,EAAE,eAAe,EAAE,GAAG,IAAI,EACvC,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,GAC5B,gBAAgB,CAqBlB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,gBAAgB,EAAE,GAC/B;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA2C1C;AAuED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,kBAAkB,GAC1B,MAAM,CAiIR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAcnE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,gBAAgB,EAAE,EAChC,cAAc,EAAE,OAAO,EAAE,GACxB,iBAAiB,CAYnB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC,GAC1D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAAG,IAAI,CAenD"}
@@ -196,19 +196,47 @@ export function buildSelfHealingPrompt(originalCode, errors, attempt, options) {
196
196
  errors.patternErrors.forEach((e) => sections.push(`- ${e}`));
197
197
  sections.push('');
198
198
  }
199
- // Import errors section
199
+ // Import errors section - with approximation guidance
200
200
  if (errors.importErrors.length > 0) {
201
- sections.push('### Import Errors');
202
- sections.push(`These components do not exist in "${options.importPath}":`);
201
+ sections.push('### Import Errors - MUST USE AVAILABLE COMPONENTS');
202
+ sections.push('');
203
+ sections.push('🚨 **CRITICAL: DO NOT try to import these components again - they DO NOT EXIST:**');
203
204
  errors.importErrors.forEach((e) => sections.push(`- ${e}`));
204
205
  sections.push('');
206
+ sections.push('### HOW TO FIX: Approximate the UI with Available Components');
207
+ sections.push('');
208
+ sections.push('You MUST recreate the same visual appearance using ONLY the available components listed below.');
209
+ sections.push('Think creatively - almost any UI can be approximated with basic layout and display components.');
210
+ sections.push('');
211
+ sections.push('**Common approximation strategies:**');
212
+ sections.push('- **Calendar/DatePicker** → Use Grid/SimpleGrid with Text components to create a calendar-like grid layout');
213
+ sections.push('- **Chart/Graph** → Use Progress, RingProgress, or Stack with colored Box components');
214
+ sections.push('- **Timeline** → Use Stack/Timeline with Card or Paper components for each event');
215
+ sections.push('- **Carousel/Slider** → Use Grid or Group with Image and navigation Button components');
216
+ sections.push('- **DataTable** → Use Table components or Stack of Card/Paper rows');
217
+ sections.push('- **TreeView** → Use nested Stack/Accordion with NavLink or List components');
218
+ sections.push('- **Rating/Stars** → Use Group of ActionIcon or ThemeIcon components');
219
+ sections.push('- **Map** → Use Image with a placeholder map image and overlaid markers using Box');
220
+ sections.push('');
221
+ sections.push('**The goal is visual similarity, not exact functionality.**');
222
+ sections.push('Users want to see what a UI could look like - use basic components creatively!');
223
+ sections.push('');
205
224
  // Show available components (design-system agnostic)
206
225
  if (options.availableComponents.length > 0) {
207
- sections.push('**Available components include:**');
208
- const displayComponents = options.availableComponents.slice(0, 20);
209
- sections.push(displayComponents.join(', '));
210
- if (options.availableComponents.length > 20) {
211
- sections.push(`... and ${options.availableComponents.length - 20} more`);
226
+ sections.push('**Available components you MUST use instead:**');
227
+ // Group by likely use case for easier reference
228
+ const layoutComponents = options.availableComponents.filter(c => /^(Box|Stack|Group|Grid|SimpleGrid|Flex|Container|Center|Space|Paper|Card|Divider)$/i.test(c));
229
+ const displayComponents = options.availableComponents.filter(c => /^(Text|Title|Badge|Avatar|Image|ThemeIcon|ActionIcon|Button|Anchor)$/i.test(c));
230
+ const otherComponents = options.availableComponents.filter(c => !layoutComponents.includes(c) && !displayComponents.includes(c));
231
+ if (layoutComponents.length > 0) {
232
+ sections.push(`- **Layout:** ${layoutComponents.join(', ')}`);
233
+ }
234
+ if (displayComponents.length > 0) {
235
+ sections.push(`- **Display:** ${displayComponents.join(', ')}`);
236
+ }
237
+ if (otherComponents.length > 0) {
238
+ const displayOthers = otherComponents.slice(0, 30);
239
+ sections.push(`- **Other:** ${displayOthers.join(', ')}${otherComponents.length > 30 ? ` ... and ${otherComponents.length - 30} more` : ''}`);
212
240
  }
213
241
  sections.push('');
214
242
  }
@@ -224,18 +252,21 @@ export function buildSelfHealingPrompt(originalCode, errors, attempt, options) {
224
252
  sections.push('1. Fix ALL errors listed above');
225
253
  sections.push('2. Keep the same component structure and layout');
226
254
  sections.push('3. Do NOT add new features - only fix the errors');
255
+ sections.push('4. **CRITICAL: Escape apostrophes in title strings** - Use `\\\'` not `\'`');
256
+ sections.push(' Example: `title: \'Women\\\'s Athletic Dashboard\'` NOT `title: \'Women\'s Athletic Dashboard\'`');
257
+ sections.push('5. Do NOT duplicate title segments - "Dashboard Dashboard" is WRONG');
227
258
  if (options.framework === 'svelte') {
228
- sections.push('4. Use proper Svelte 5 syntax (class=, onclick=, NOT on:click=)');
229
- sections.push('5. NEVER nest a component inside itself: <Comp><Comp>X</Comp></Comp> is WRONG! Use <Comp>X</Comp>');
230
- sections.push(`6. Only import from ROOT: import { Comp } from "${options.importPath}" - NEVER use deep paths like "${options.importPath}/dist/..." or "${options.importPath}/components/..."`);
231
- sections.push(`7. Return the COMPLETE corrected code in a \`\`\`svelte code block`);
232
- sections.push('8. Do NOT include any explanation - just the corrected code block');
259
+ sections.push('6. Use proper Svelte 5 syntax (class=, onclick=, NOT on:click=)');
260
+ sections.push('7. NEVER nest a component inside itself: <Comp><Comp>X</Comp></Comp> is WRONG! Use <Comp>X</Comp>');
261
+ sections.push(`8. Only import from ROOT: import { Comp } from "${options.importPath}" - NEVER use deep paths like "${options.importPath}/dist/..." or "${options.importPath}/components/..."`);
262
+ sections.push(`9. Return the COMPLETE corrected code in a \`\`\`svelte code block`);
263
+ sections.push('10. Do NOT include any explanation - just the corrected code block');
233
264
  }
234
265
  else {
235
- sections.push('4. Ensure all JSX elements are properly opened and closed');
236
- sections.push(`5. Only import components that exist in "${options.importPath}"`);
237
- sections.push(`6. Return the COMPLETE corrected code in a \`\`\`${codeBlockLang} code block`);
238
- sections.push('7. Do NOT include any explanation - just the corrected code block');
266
+ sections.push('6. Ensure all JSX elements are properly opened and closed');
267
+ sections.push(`7. Only import components that exist in "${options.importPath}"`);
268
+ sections.push(`8. Return the COMPLETE corrected code in a \`\`\`${codeBlockLang} code block`);
269
+ sections.push('9. Do NOT include any explanation - just the corrected code block');
239
270
  }
240
271
  return sections.join('\n');
241
272
  }
@@ -44,5 +44,17 @@ export declare class StoryTracker {
44
44
  * Clean up orphaned mappings (stories that no longer exist)
45
45
  */
46
46
  cleanupOrphaned(generatedStoriesPath: string): number;
47
+ /**
48
+ * Get the next available version for a title
49
+ * If "Navigation Bar" exists, returns "Navigation Bar v2"
50
+ * If "Navigation Bar v2" exists, returns "Navigation Bar v3", etc.
51
+ *
52
+ * This is only used for NEW stories (not updates to existing stories)
53
+ */
54
+ getNextVersionTitle(baseTitle: string): string;
55
+ /**
56
+ * Escape special regex characters in a string
57
+ */
58
+ private escapeRegex;
47
59
  }
48
60
  //# sourceMappingURL=storyTracker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storyTracker.d.ts","sourceRoot":"","sources":["../../story-generator/storyTracker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAA4B;gBAEhC,MAAM,EAAE,aAAa;IAMjC;;OAEG;IACH,OAAO,CAAC,YAAY;IAgBpB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAkBpD;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IA4CtD;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAwB1C;;OAEG;IACH,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO;IAqB7C;;OAEG;IACH,cAAc,IAAI,YAAY,EAAE;IAIhC;;OAEG;IACH,eAAe,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM;CAiBtD"}
1
+ {"version":3,"file":"storyTracker.d.ts","sourceRoot":"","sources":["../../story-generator/storyTracker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAA4B;gBAEhC,MAAM,EAAE,aAAa;IAMjC;;OAEG;IACH,OAAO,CAAC,YAAY;IAgBpB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAkBpD;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IA4CtD;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAwB1C;;OAEG;IACH,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO;IAqB7C;;OAEG;IACH,cAAc,IAAI,YAAY,EAAE;IAIhC;;OAEG;IACH,eAAe,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM;IAkBrD;;;;;;OAMG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAoC9C;;OAEG;IACH,OAAO,CAAC,WAAW;CAGpB"}
@@ -163,4 +163,46 @@ export class StoryTracker {
163
163
  }
164
164
  return removed;
165
165
  }
166
+ /**
167
+ * Get the next available version for a title
168
+ * If "Navigation Bar" exists, returns "Navigation Bar v2"
169
+ * If "Navigation Bar v2" exists, returns "Navigation Bar v3", etc.
170
+ *
171
+ * This is only used for NEW stories (not updates to existing stories)
172
+ */
173
+ getNextVersionTitle(baseTitle) {
174
+ if (!baseTitle || typeof baseTitle !== 'string') {
175
+ return baseTitle;
176
+ }
177
+ const normalizedBase = baseTitle.toLowerCase().trim();
178
+ // Check if the exact title already exists
179
+ if (!this.mappings.has(normalizedBase)) {
180
+ return baseTitle; // No conflict, use as-is
181
+ }
182
+ // Find the highest existing version
183
+ let highestVersion = 1;
184
+ // Check for base title (v1 implicitly)
185
+ if (this.mappings.has(normalizedBase)) {
186
+ highestVersion = 1;
187
+ }
188
+ // Check for versioned titles (v2, v3, etc.)
189
+ for (const existingTitle of this.mappings.keys()) {
190
+ // Match patterns like "title v2", "title v3", etc.
191
+ const versionMatch = existingTitle.match(new RegExp(`^${this.escapeRegex(normalizedBase)}\\s+v(\\d+)$`, 'i'));
192
+ if (versionMatch) {
193
+ const version = parseInt(versionMatch[1], 10);
194
+ if (version > highestVersion) {
195
+ highestVersion = version;
196
+ }
197
+ }
198
+ }
199
+ // Return the next version
200
+ return `${baseTitle} v${highestVersion + 1}`;
201
+ }
202
+ /**
203
+ * Escape special regex characters in a string
204
+ */
205
+ escapeRegex(str) {
206
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
207
+ }
166
208
  }
@@ -1 +1 @@
1
- {"version":3,"file":"storyValidator.d.ts","sourceRoot":"","sources":["../../story-generator/storyValidator.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,EAAE,CAiErE"}
1
+ {"version":3,"file":"storyValidator.d.ts","sourceRoot":"","sources":["../../story-generator/storyValidator.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,EAAE,CA6DrE"}
@@ -11,14 +11,10 @@ export function validateStory(storyContent) {
11
11
  { pattern: /from\s+['"]@storybook\/addon-actions['"]/i, message: 'Do not import from @storybook/addon-actions. Use argTypes with action property instead: argTypes: { onClick: { action: "clicked" } }' },
12
12
  // Catch Svelte slot property which doesn't work in modern Storybook
13
13
  { pattern: /slot:\s*['"][^'"]+['"]/i, message: 'The slot property in render functions does not work in Svelte Storybook. Use simple args-based stories instead.' },
14
- // Catch abbreviated Material icon names that will render as text instead of icons
15
- { pattern: /<mat-icon>\s*(fav|ho|de|se|sta|che|add|rem|edi|sav|can|men|clo|sea)\s*<\/mat-icon>/i, message: 'Material icon name appears to be abbreviated. Use full icon names: "favorite" (not "fav"), "home" (not "ho"), "delete" (not "de"), "settings" (not "se"), etc.' },
16
14
  // Catch Angular TS4111 patterns - "this.property" state management in render functions
17
15
  { pattern: /this\.\w+\s*=\s*\$?event\./i, message: 'Do not use "this.property = event.value" in Angular stories. This causes TS4111 errors. Use argTypes with action property for events and create separate stories for different states.' },
18
16
  { pattern: /this\.\w+\+\+/i, message: 'Do not use "this.property++" in Angular stories. This causes TS4111 errors. Use argTypes with action property for events instead of managing state.' },
19
17
  { pattern: /this\.\w+--/i, message: 'Do not use "this.property--" in Angular stories. This causes TS4111 errors. Use argTypes with action property for events instead of managing state.' },
20
- // Catch flowbite-svelte deep path imports that break in production
21
- { pattern: /from\s+['"]flowbite-svelte\/[^'"]+['"]/i, message: 'Do not use deep path imports for flowbite-svelte. Use named exports from root: import { Component } from "flowbite-svelte"' },
22
18
  ];
23
19
  lines.forEach((line, index) => {
24
20
  for (const { pattern, message } of forbiddenPatterns) {
@@ -1 +1 @@
1
- {"version":3,"file":"validateStory.d.ts","sourceRoot":"","sources":["../../story-generator/validateStory.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAoB,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,gBAAgB,CAsH9G;AA2hBD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAoFjH;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,CA+BvE"}
1
+ {"version":3,"file":"validateStory.d.ts","sourceRoot":"","sources":["../../story-generator/validateStory.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAoB,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,gBAAgB,CAsH9G;AAgrBD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAoFjH;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,CA+BvE"}