@tpitre/story-ui 3.7.0 → 3.9.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 +22 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +28 -2
- package/dist/cli/update.d.ts +29 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +398 -0
- package/dist/mcp-server/index.js +81 -0
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -1
- package/dist/mcp-server/routes/generateStory.js +36 -0
- package/dist/story-generator/runtimeValidator.d.ts +64 -0
- package/dist/story-generator/runtimeValidator.d.ts.map +1 -0
- package/dist/story-generator/runtimeValidator.js +356 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -1
- package/dist/templates/StoryUI/StoryUIPanel.js +183 -11
- package/package.json +1 -1
- package/templates/StoryUI/StoryUIPanel.tsx +239 -13
package/package.json
CHANGED
|
@@ -1656,6 +1656,8 @@ function StoryUIPanel() {
|
|
|
1656
1656
|
const [attachedImages, setAttachedImages] = useState<AttachedImage[]>([]);
|
|
1657
1657
|
const [considerations, setConsiderations] = useState<string>('');
|
|
1658
1658
|
const [orphanStories, setOrphanStories] = useState<OrphanStory[]>([]);
|
|
1659
|
+
const [selectedStoryIds, setSelectedStoryIds] = useState<Set<string>>(new Set());
|
|
1660
|
+
const [isBulkDeleting, setIsBulkDeleting] = useState(false);
|
|
1659
1661
|
const chatEndRef = useRef<HTMLDivElement | null>(null);
|
|
1660
1662
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
1661
1663
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
|
@@ -2462,6 +2464,92 @@ function StoryUIPanel() {
|
|
|
2462
2464
|
}
|
|
2463
2465
|
};
|
|
2464
2466
|
|
|
2467
|
+
// Toggle story selection for bulk operations
|
|
2468
|
+
const toggleStorySelection = (storyId: string) => {
|
|
2469
|
+
setSelectedStoryIds(prev => {
|
|
2470
|
+
const newSet = new Set(prev);
|
|
2471
|
+
if (newSet.has(storyId)) {
|
|
2472
|
+
newSet.delete(storyId);
|
|
2473
|
+
} else {
|
|
2474
|
+
newSet.add(storyId);
|
|
2475
|
+
}
|
|
2476
|
+
return newSet;
|
|
2477
|
+
});
|
|
2478
|
+
};
|
|
2479
|
+
|
|
2480
|
+
// Select/deselect all stories
|
|
2481
|
+
const toggleSelectAll = () => {
|
|
2482
|
+
if (selectedStoryIds.size === orphanStories.length) {
|
|
2483
|
+
setSelectedStoryIds(new Set());
|
|
2484
|
+
} else {
|
|
2485
|
+
setSelectedStoryIds(new Set(orphanStories.map(s => s.id)));
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
|
|
2489
|
+
// Bulk delete selected stories
|
|
2490
|
+
const handleBulkDelete = async () => {
|
|
2491
|
+
if (selectedStoryIds.size === 0) return;
|
|
2492
|
+
|
|
2493
|
+
const count = selectedStoryIds.size;
|
|
2494
|
+
if (!confirm(`Delete ${count} selected ${count === 1 ? 'story' : 'stories'}? This action cannot be undone.`)) {
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
setIsBulkDeleting(true);
|
|
2499
|
+
try {
|
|
2500
|
+
const response = await fetch(`${STORIES_API}/delete-bulk`, {
|
|
2501
|
+
method: 'POST',
|
|
2502
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2503
|
+
body: JSON.stringify({ ids: Array.from(selectedStoryIds) }),
|
|
2504
|
+
});
|
|
2505
|
+
|
|
2506
|
+
if (response.ok) {
|
|
2507
|
+
const result = await response.json();
|
|
2508
|
+
// Remove deleted stories from state
|
|
2509
|
+
setOrphanStories(prev => prev.filter(s => !selectedStoryIds.has(s.id)));
|
|
2510
|
+
setSelectedStoryIds(new Set());
|
|
2511
|
+
console.log(`Deleted ${result.deleted?.length || count} stories`);
|
|
2512
|
+
} else {
|
|
2513
|
+
alert('Failed to delete some stories. Please try again.');
|
|
2514
|
+
}
|
|
2515
|
+
} catch (err) {
|
|
2516
|
+
console.error('Error bulk deleting stories:', err);
|
|
2517
|
+
alert('Failed to delete stories. Please try again.');
|
|
2518
|
+
} finally {
|
|
2519
|
+
setIsBulkDeleting(false);
|
|
2520
|
+
}
|
|
2521
|
+
};
|
|
2522
|
+
|
|
2523
|
+
// Clear all generated stories
|
|
2524
|
+
const handleClearAll = async () => {
|
|
2525
|
+
if (orphanStories.length === 0) return;
|
|
2526
|
+
|
|
2527
|
+
if (!confirm(`Delete ALL ${orphanStories.length} generated stories? This action cannot be undone.`)) {
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
setIsBulkDeleting(true);
|
|
2532
|
+
try {
|
|
2533
|
+
const response = await fetch(STORIES_API, {
|
|
2534
|
+
method: 'DELETE',
|
|
2535
|
+
});
|
|
2536
|
+
|
|
2537
|
+
if (response.ok) {
|
|
2538
|
+
const result = await response.json();
|
|
2539
|
+
setOrphanStories([]);
|
|
2540
|
+
setSelectedStoryIds(new Set());
|
|
2541
|
+
console.log(`Cleared ${result.deleted || 'all'} stories`);
|
|
2542
|
+
} else {
|
|
2543
|
+
alert('Failed to clear stories. Please try again.');
|
|
2544
|
+
}
|
|
2545
|
+
} catch (err) {
|
|
2546
|
+
console.error('Error clearing stories:', err);
|
|
2547
|
+
alert('Failed to clear stories. Please try again.');
|
|
2548
|
+
} finally {
|
|
2549
|
+
setIsBulkDeleting(false);
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
|
|
2465
2553
|
return (
|
|
2466
2554
|
<div className="story-ui-panel" style={STYLES.container}>
|
|
2467
2555
|
{/* Sidebar */}
|
|
@@ -2558,40 +2646,173 @@ function StoryUIPanel() {
|
|
|
2558
2646
|
{/* Generated Files Section - orphan stories without chat history */}
|
|
2559
2647
|
{orphanStories.length > 0 && (
|
|
2560
2648
|
<>
|
|
2649
|
+
{/* Header with Select All and Count */}
|
|
2561
2650
|
<div style={{
|
|
2562
|
-
|
|
2563
|
-
|
|
2651
|
+
display: 'flex',
|
|
2652
|
+
alignItems: 'center',
|
|
2653
|
+
justifyContent: 'space-between',
|
|
2564
2654
|
marginTop: '16px',
|
|
2565
2655
|
marginBottom: '8px',
|
|
2566
|
-
fontWeight: '500',
|
|
2567
|
-
textTransform: 'uppercase',
|
|
2568
|
-
letterSpacing: '0.05em',
|
|
2569
2656
|
}}>
|
|
2570
|
-
|
|
2657
|
+
<div style={{
|
|
2658
|
+
display: 'flex',
|
|
2659
|
+
alignItems: 'center',
|
|
2660
|
+
gap: '8px',
|
|
2661
|
+
}}>
|
|
2662
|
+
<input
|
|
2663
|
+
type="checkbox"
|
|
2664
|
+
checked={selectedStoryIds.size === orphanStories.length && orphanStories.length > 0}
|
|
2665
|
+
onChange={toggleSelectAll}
|
|
2666
|
+
style={{
|
|
2667
|
+
width: '14px',
|
|
2668
|
+
height: '14px',
|
|
2669
|
+
cursor: 'pointer',
|
|
2670
|
+
accentColor: '#3b82f6',
|
|
2671
|
+
}}
|
|
2672
|
+
title={selectedStoryIds.size === orphanStories.length ? 'Deselect all' : 'Select all'}
|
|
2673
|
+
/>
|
|
2674
|
+
<span style={{
|
|
2675
|
+
color: '#64748b',
|
|
2676
|
+
fontSize: '12px',
|
|
2677
|
+
fontWeight: '500',
|
|
2678
|
+
textTransform: 'uppercase',
|
|
2679
|
+
letterSpacing: '0.05em',
|
|
2680
|
+
}}>
|
|
2681
|
+
Generated Files ({orphanStories.length})
|
|
2682
|
+
</span>
|
|
2683
|
+
</div>
|
|
2684
|
+
</div>
|
|
2685
|
+
|
|
2686
|
+
{/* Bulk Action Buttons */}
|
|
2687
|
+
{selectedStoryIds.size > 0 && (
|
|
2688
|
+
<div style={{
|
|
2689
|
+
display: 'flex',
|
|
2690
|
+
gap: '8px',
|
|
2691
|
+
marginBottom: '12px',
|
|
2692
|
+
}}>
|
|
2693
|
+
<button
|
|
2694
|
+
onClick={handleBulkDelete}
|
|
2695
|
+
disabled={isBulkDeleting}
|
|
2696
|
+
style={{
|
|
2697
|
+
flex: 1,
|
|
2698
|
+
padding: '6px 10px',
|
|
2699
|
+
fontSize: '11px',
|
|
2700
|
+
fontWeight: '500',
|
|
2701
|
+
background: 'rgba(239, 68, 68, 0.15)',
|
|
2702
|
+
color: '#f87171',
|
|
2703
|
+
border: '1px solid rgba(239, 68, 68, 0.3)',
|
|
2704
|
+
borderRadius: '6px',
|
|
2705
|
+
cursor: isBulkDeleting ? 'not-allowed' : 'pointer',
|
|
2706
|
+
opacity: isBulkDeleting ? 0.6 : 1,
|
|
2707
|
+
transition: 'all 0.15s ease',
|
|
2708
|
+
}}
|
|
2709
|
+
onMouseEnter={(e) => {
|
|
2710
|
+
if (!isBulkDeleting) {
|
|
2711
|
+
e.currentTarget.style.background = 'rgba(239, 68, 68, 0.25)';
|
|
2712
|
+
}
|
|
2713
|
+
}}
|
|
2714
|
+
onMouseLeave={(e) => {
|
|
2715
|
+
e.currentTarget.style.background = 'rgba(239, 68, 68, 0.15)';
|
|
2716
|
+
}}
|
|
2717
|
+
>
|
|
2718
|
+
{isBulkDeleting ? 'Deleting...' : `Delete Selected (${selectedStoryIds.size})`}
|
|
2719
|
+
</button>
|
|
2720
|
+
</div>
|
|
2721
|
+
)}
|
|
2722
|
+
|
|
2723
|
+
{/* Clear All Button (always visible) */}
|
|
2724
|
+
<div style={{
|
|
2725
|
+
display: 'flex',
|
|
2726
|
+
gap: '8px',
|
|
2727
|
+
marginBottom: '12px',
|
|
2728
|
+
}}>
|
|
2729
|
+
<button
|
|
2730
|
+
onClick={handleClearAll}
|
|
2731
|
+
disabled={isBulkDeleting || orphanStories.length === 0}
|
|
2732
|
+
style={{
|
|
2733
|
+
flex: 1,
|
|
2734
|
+
padding: '6px 10px',
|
|
2735
|
+
fontSize: '11px',
|
|
2736
|
+
fontWeight: '500',
|
|
2737
|
+
background: 'rgba(100, 116, 139, 0.15)',
|
|
2738
|
+
color: '#94a3b8',
|
|
2739
|
+
border: '1px solid rgba(100, 116, 139, 0.3)',
|
|
2740
|
+
borderRadius: '6px',
|
|
2741
|
+
cursor: (isBulkDeleting || orphanStories.length === 0) ? 'not-allowed' : 'pointer',
|
|
2742
|
+
opacity: (isBulkDeleting || orphanStories.length === 0) ? 0.6 : 1,
|
|
2743
|
+
transition: 'all 0.15s ease',
|
|
2744
|
+
}}
|
|
2745
|
+
onMouseEnter={(e) => {
|
|
2746
|
+
if (!isBulkDeleting && orphanStories.length > 0) {
|
|
2747
|
+
e.currentTarget.style.background = 'rgba(239, 68, 68, 0.15)';
|
|
2748
|
+
e.currentTarget.style.color = '#f87171';
|
|
2749
|
+
e.currentTarget.style.borderColor = 'rgba(239, 68, 68, 0.3)';
|
|
2750
|
+
}
|
|
2751
|
+
}}
|
|
2752
|
+
onMouseLeave={(e) => {
|
|
2753
|
+
e.currentTarget.style.background = 'rgba(100, 116, 139, 0.15)';
|
|
2754
|
+
e.currentTarget.style.color = '#94a3b8';
|
|
2755
|
+
e.currentTarget.style.borderColor = 'rgba(100, 116, 139, 0.3)';
|
|
2756
|
+
}}
|
|
2757
|
+
>
|
|
2758
|
+
Clear All Stories
|
|
2759
|
+
</button>
|
|
2571
2760
|
</div>
|
|
2761
|
+
|
|
2762
|
+
{/* Story List */}
|
|
2572
2763
|
{orphanStories.map(story => (
|
|
2573
2764
|
<div
|
|
2574
2765
|
key={story.id}
|
|
2575
2766
|
style={{
|
|
2576
2767
|
...STYLES.chatItem,
|
|
2577
|
-
background:
|
|
2578
|
-
|
|
2768
|
+
background: selectedStoryIds.has(story.id)
|
|
2769
|
+
? 'rgba(59, 130, 246, 0.15)'
|
|
2770
|
+
: 'rgba(251, 191, 36, 0.1)',
|
|
2771
|
+
borderLeft: selectedStoryIds.has(story.id)
|
|
2772
|
+
? '3px solid rgba(59, 130, 246, 0.5)'
|
|
2773
|
+
: '3px solid rgba(251, 191, 36, 0.5)',
|
|
2774
|
+
display: 'flex',
|
|
2775
|
+
alignItems: 'flex-start',
|
|
2776
|
+
gap: '8px',
|
|
2579
2777
|
}}
|
|
2580
2778
|
onMouseEnter={(e) => {
|
|
2581
|
-
|
|
2779
|
+
if (!selectedStoryIds.has(story.id)) {
|
|
2780
|
+
e.currentTarget.style.background = 'rgba(251, 191, 36, 0.15)';
|
|
2781
|
+
}
|
|
2582
2782
|
const deleteBtn = e.currentTarget.querySelector('.delete-orphan-btn') as HTMLElement;
|
|
2583
2783
|
if (deleteBtn) deleteBtn.style.opacity = '1';
|
|
2584
2784
|
}}
|
|
2585
2785
|
onMouseLeave={(e) => {
|
|
2586
|
-
|
|
2786
|
+
if (!selectedStoryIds.has(story.id)) {
|
|
2787
|
+
e.currentTarget.style.background = 'rgba(251, 191, 36, 0.1)';
|
|
2788
|
+
}
|
|
2587
2789
|
const deleteBtn = e.currentTarget.querySelector('.delete-orphan-btn') as HTMLElement;
|
|
2588
2790
|
if (deleteBtn) deleteBtn.style.opacity = '0';
|
|
2589
2791
|
}}
|
|
2590
2792
|
>
|
|
2591
|
-
|
|
2592
|
-
<
|
|
2593
|
-
|
|
2793
|
+
{/* Checkbox */}
|
|
2794
|
+
<input
|
|
2795
|
+
type="checkbox"
|
|
2796
|
+
checked={selectedStoryIds.has(story.id)}
|
|
2797
|
+
onChange={() => toggleStorySelection(story.id)}
|
|
2798
|
+
onClick={(e) => e.stopPropagation()}
|
|
2799
|
+
style={{
|
|
2800
|
+
width: '14px',
|
|
2801
|
+
height: '14px',
|
|
2802
|
+
cursor: 'pointer',
|
|
2803
|
+
accentColor: '#3b82f6',
|
|
2804
|
+
marginTop: '2px',
|
|
2805
|
+
flexShrink: 0,
|
|
2806
|
+
}}
|
|
2807
|
+
/>
|
|
2808
|
+
{/* Story Info */}
|
|
2809
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
2810
|
+
<div style={STYLES.chatItemTitle}>{story.title}</div>
|
|
2811
|
+
<div style={{ ...STYLES.chatItemTime, fontSize: '11px' }}>
|
|
2812
|
+
{story.fileName}
|
|
2813
|
+
</div>
|
|
2594
2814
|
</div>
|
|
2815
|
+
{/* Delete Button */}
|
|
2595
2816
|
<button
|
|
2596
2817
|
className="delete-orphan-btn"
|
|
2597
2818
|
onClick={async (e) => {
|
|
@@ -2602,6 +2823,11 @@ function StoryUIPanel() {
|
|
|
2602
2823
|
});
|
|
2603
2824
|
if (response.ok) {
|
|
2604
2825
|
setOrphanStories(prev => prev.filter(s => s.id !== story.id));
|
|
2826
|
+
setSelectedStoryIds(prev => {
|
|
2827
|
+
const newSet = new Set(prev);
|
|
2828
|
+
newSet.delete(story.id);
|
|
2829
|
+
return newSet;
|
|
2830
|
+
});
|
|
2605
2831
|
} else {
|
|
2606
2832
|
console.error('Failed to delete orphan story');
|
|
2607
2833
|
}
|