@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 +5 -0
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -1
- package/dist/mcp-server/routes/generateStory.js +19 -5
- package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -1
- package/dist/mcp-server/routes/generateStoryStream.js +19 -5
- package/dist/story-generator/promptGenerator.d.ts.map +1 -1
- package/dist/story-generator/promptGenerator.js +182 -24
- package/dist/story-generator/selfHealingLoop.d.ts.map +1 -1
- package/dist/story-generator/selfHealingLoop.js +48 -17
- package/dist/story-generator/storyTracker.d.ts +12 -0
- package/dist/story-generator/storyTracker.d.ts.map +1 -1
- package/dist/story-generator/storyTracker.js +42 -0
- package/dist/story-generator/storyValidator.d.ts.map +1 -1
- package/dist/story-generator/storyValidator.js +0 -4
- package/dist/story-generator/validateStory.d.ts.map +1 -1
- package/dist/story-generator/validateStory.js +128 -0
- package/dist/templates/StoryUI/StoryUIPanel.css +1623 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -1
- package/dist/templates/StoryUI/StoryUIPanel.js +46 -69
- package/dist/templates/StoryUI/StoryUIPanel.mdx +84 -0
- package/package.json +3 -2
- package/templates/StoryUI/StoryUIPanel.css +152 -54
- package/templates/StoryUI/StoryUIPanel.tsx +51 -75
|
@@ -334,8 +334,136 @@ function fixUnexpectedTokens(code) {
|
|
|
334
334
|
for (const fix of fixes) {
|
|
335
335
|
fixedCode = fixedCode.replace(fix.pattern, fix.replacement);
|
|
336
336
|
}
|
|
337
|
+
// CRITICAL: Fix unescaped apostrophes in title strings
|
|
338
|
+
// LLMs often generate titles like: title: 'Women's Athletic' instead of 'Women\'s Athletic'
|
|
339
|
+
fixedCode = fixUnescapedApostrophesInTitles(fixedCode);
|
|
337
340
|
return fixedCode;
|
|
338
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Fixes unescaped apostrophes in title strings
|
|
344
|
+
* LLMs often generate: title: 'Women's Athletic Dashboard'
|
|
345
|
+
* Which should be: title: 'Women\'s Athletic Dashboard'
|
|
346
|
+
*
|
|
347
|
+
* Also handles cases where some apostrophes are escaped and others aren't:
|
|
348
|
+
* title: 'Women\'s Athletic Dashboard's Athletic Dashboard'
|
|
349
|
+
*/
|
|
350
|
+
function fixUnescapedApostrophesInTitles(code) {
|
|
351
|
+
const lines = code.split('\n');
|
|
352
|
+
for (let i = 0; i < lines.length; i++) {
|
|
353
|
+
const line = lines[i];
|
|
354
|
+
// Skip if no title property
|
|
355
|
+
if (!line.includes('title:'))
|
|
356
|
+
continue;
|
|
357
|
+
// Find title: followed by a quote character
|
|
358
|
+
const titleStartMatch = line.match(/title:\s*(['"])/);
|
|
359
|
+
if (!titleStartMatch)
|
|
360
|
+
continue;
|
|
361
|
+
const quoteChar = titleStartMatch[1];
|
|
362
|
+
const titleKeywordIndex = line.indexOf('title:');
|
|
363
|
+
const openQuoteIndex = line.indexOf(quoteChar, titleKeywordIndex);
|
|
364
|
+
// Find the closing quote by looking for quote followed by comma or brace at end of line
|
|
365
|
+
// The pattern is: quote + optional whitespace + (comma or brace) + optional whitespace + end
|
|
366
|
+
const endPattern = new RegExp(`${quoteChar === "'" ? "'" : '"'}\\s*[,}]\\s*$`);
|
|
367
|
+
const endMatch = line.match(endPattern);
|
|
368
|
+
if (!endMatch)
|
|
369
|
+
continue;
|
|
370
|
+
const closeQuoteIndex = line.lastIndexOf(quoteChar);
|
|
371
|
+
if (closeQuoteIndex <= openQuoteIndex)
|
|
372
|
+
continue;
|
|
373
|
+
// Extract content between opening and closing quotes
|
|
374
|
+
const content = line.substring(openQuoteIndex + 1, closeQuoteIndex);
|
|
375
|
+
// Process the content character by character to escape unescaped quotes
|
|
376
|
+
let fixedContent = '';
|
|
377
|
+
for (let j = 0; j < content.length; j++) {
|
|
378
|
+
const char = content[j];
|
|
379
|
+
const prevChar = j > 0 ? content[j - 1] : '';
|
|
380
|
+
if (char === quoteChar && prevChar !== '\\') {
|
|
381
|
+
// This is an unescaped quote inside the string - escape it
|
|
382
|
+
fixedContent += '\\' + char;
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
fixedContent += char;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Only modify if we actually made changes
|
|
389
|
+
if (fixedContent !== content) {
|
|
390
|
+
const beforeQuote = line.substring(0, openQuoteIndex + 1);
|
|
391
|
+
const afterQuote = line.substring(closeQuoteIndex);
|
|
392
|
+
lines[i] = beforeQuote + fixedContent + afterQuote;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
let fixedCode = lines.join('\n');
|
|
396
|
+
// Fix duplicated title segments (LLM sometimes repeats parts of the title)
|
|
397
|
+
// e.g., "Women's Athletic Dashboard's Athletic Dashboard" -> "Women's Athletic Dashboard"
|
|
398
|
+
fixedCode = fixDuplicatedTitleSegments(fixedCode);
|
|
399
|
+
return fixedCode;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Fixes duplicated segments in titles
|
|
403
|
+
* LLMs sometimes generate: "Women's Athletic Dashboard's Athletic Dashboard"
|
|
404
|
+
* Which should be: "Women's Athletic Dashboard"
|
|
405
|
+
*/
|
|
406
|
+
function fixDuplicatedTitleSegments(code) {
|
|
407
|
+
const lines = code.split('\n');
|
|
408
|
+
for (let i = 0; i < lines.length; i++) {
|
|
409
|
+
const line = lines[i];
|
|
410
|
+
if (!line.includes('title:'))
|
|
411
|
+
continue;
|
|
412
|
+
// Find title: followed by a quote
|
|
413
|
+
const titleMatch = line.match(/title:\s*(['"])/);
|
|
414
|
+
if (!titleMatch)
|
|
415
|
+
continue;
|
|
416
|
+
const quoteChar = titleMatch[1];
|
|
417
|
+
const openQuoteIndex = line.indexOf(quoteChar, line.indexOf('title:'));
|
|
418
|
+
const closeQuoteIndex = line.lastIndexOf(quoteChar);
|
|
419
|
+
if (closeQuoteIndex <= openQuoteIndex)
|
|
420
|
+
continue;
|
|
421
|
+
// Extract title content (handling escaped quotes)
|
|
422
|
+
const content = line.substring(openQuoteIndex + 1, closeQuoteIndex);
|
|
423
|
+
// Normalize escaped quotes for word processing
|
|
424
|
+
const normalizedContent = content.replace(/\\'/g, "'");
|
|
425
|
+
// Split into words
|
|
426
|
+
const words = normalizedContent.split(/\s+/);
|
|
427
|
+
// Look for repeated word sequences
|
|
428
|
+
let modified = false;
|
|
429
|
+
let newWords = [...words];
|
|
430
|
+
for (let windowSize = 2; windowSize <= Math.floor(words.length / 2); windowSize++) {
|
|
431
|
+
for (let j = 0; j <= newWords.length - windowSize * 2; j++) {
|
|
432
|
+
const segment1 = newWords.slice(j, j + windowSize).join(' ');
|
|
433
|
+
const segment2 = newWords.slice(j + windowSize, j + windowSize * 2).join(' ');
|
|
434
|
+
if (segment1 === segment2 && segment1.length > 3) {
|
|
435
|
+
newWords = [...newWords.slice(0, j + windowSize), ...newWords.slice(j + windowSize * 2)];
|
|
436
|
+
modified = true;
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (modified)
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
// Also look for possessive duplications: "Dashboard's Athletic" ... "Dashboard"
|
|
444
|
+
// Pattern: something ending with 's + words + that same word
|
|
445
|
+
if (!modified) {
|
|
446
|
+
const possessivePattern = /^(.+?)(['\\]?s\s+)(.+?)\s+\1$/i;
|
|
447
|
+
const match = newWords.join(' ').match(possessivePattern);
|
|
448
|
+
if (match) {
|
|
449
|
+
newWords = (match[1] + match[2] + match[3]).split(/\s+/);
|
|
450
|
+
modified = true;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (modified) {
|
|
454
|
+
// Re-escape apostrophes for the output
|
|
455
|
+
let newContent = newWords.join(' ');
|
|
456
|
+
if (quoteChar === "'") {
|
|
457
|
+
// Re-escape internal single quotes
|
|
458
|
+
newContent = newContent.replace(/(?<!\\)'/g, "\\'");
|
|
459
|
+
}
|
|
460
|
+
const beforeQuote = line.substring(0, openQuoteIndex + 1);
|
|
461
|
+
const afterQuote = line.substring(closeQuoteIndex);
|
|
462
|
+
lines[i] = beforeQuote + newContent + afterQuote;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return lines.join('\n');
|
|
466
|
+
}
|
|
339
467
|
/**
|
|
340
468
|
* Fixes unterminated string literals
|
|
341
469
|
*/
|