/**
 * Scaffo.ai - React Components
 * All reusable UI components.
 * Loaded via Babel standalone - uses var declarations for global scope.
 * 
 * Dependencies: React, ReactDOM, auth.js, api.js, utils.js
 */

// React hooks (var for global scope in Babel standalone)
var { useState, useEffect, useRef, useCallback } = React;

var DarkModeToggle = () => {
            const [isDark, setIsDark] = useState(false);

            useEffect(() => {
                document.documentElement.classList.toggle('dark', isDark);
            }, [isDark]);

            return (
                <button 
                    onClick={() => setIsDark(!isDark)}
                    className="fixed bottom-4 right-4 z-50 p-3 bg-white dark:bg-zinc-800 rounded-full shadow-lg border border-gray-200 dark:border-zinc-700 text-gray-600 dark:text-gray-300 hover:text-primary transition-colors"
                    title="Toggle Dark Mode"
                >
                    <span className="material-symbols-outlined">{isDark ? 'light_mode' : 'dark_mode'}</span>
                </button>
            );
        };

        // Sidebar with embedded Recent Generations
var Sidebar = ({ onNewChat, onOpenLibrary, recentGenerations, onSelectGeneration, onDeleteGeneration, currentGenerationId, currentUser, onLogout }) => {
            const [isRecentCollapsed, setIsRecentCollapsed] = useState(false);
            
            const formatDate = (dateStr) => {
                const date = new Date(dateStr);
                const now = new Date();
                const diff = now - date;
                const mins = Math.floor(diff / (1000 * 60));
                if (mins < 1) return 'Just now';
                if (mins < 60) return mins + 'm ago';
                const hours = Math.floor(mins / 60);
                if (hours < 24) return hours + 'h ago';
                return date.toLocaleDateString();
            };

            return (
                <nav className="w-16 md:w-20 lg:w-72 h-full bg-[#f9fbfb] dark:bg-background-dark border-r border-[#e5e5e0] dark:border-gray-800 flex flex-col py-6 transition-all duration-300 flex-shrink-0 z-10">
                    {/* Header */}
                    <div className="flex flex-col gap-4 px-0 lg:px-4 items-center lg:items-start">
                        <div className="flex items-center gap-3 px-2 lg:px-2 cursor-pointer group" onClick={onNewChat}>
                            <div className="w-10 h-10 rounded-xl bg-primary/10 flex items-center justify-center flex-shrink-0 text-primary group-hover:bg-primary group-hover:text-white transition-colors duration-300">
                                <span className="material-symbols-outlined">school</span>
                            </div>
                            <div className="hidden lg:flex flex-col opacity-0 lg:opacity-100 transition-opacity duration-300 delay-100">
                                <span className="font-bold text-lg tracking-tight leading-none text-[#101718] dark:text-white">Scaffo.ai</span>
                                <span className="text-xs text-primary font-medium tracking-wide">AI Teaching Materials</span>
                            </div>
                        </div>
                        
                        {/* Action Buttons */}
                        <div className="flex flex-col gap-2 w-full">
                            <button onClick={onNewChat} className="group flex items-center gap-3 p-3 rounded-xl bg-white dark:bg-white/5 border border-gray-200 dark:border-white/10 shadow-sm hover:border-primary/50 text-[#101718] dark:text-white transition-all w-full justify-center lg:justify-start">
                                <span className="material-symbols-outlined icon-fill text-primary">add_circle</span>
                                <span className="hidden lg:block text-sm font-semibold">New Chat</span>
                            </button>
                            <button 
                                onClick={onOpenLibrary}
                                className="group flex items-center gap-3 p-3 rounded-xl text-[#5c818a] hover:bg-gray-100 dark:hover:bg-white/5 hover:text-[#101718] dark:hover:text-white transition-all w-full justify-center lg:justify-start"
                            >
                                <span className="material-symbols-outlined group-hover:scale-110 transition-transform">library_books</span>
                                <span className="hidden lg:block text-sm font-medium">Template Library</span>
                            </button>
                        </div>
                    </div>

                    {/* Recent Generations Section - Collapsible */}
                    <div className="flex-1 flex flex-col mt-6 overflow-hidden">
                        <div className="hidden lg:flex items-center justify-between px-4 mb-2">
                            <button 
                                onClick={() => setIsRecentCollapsed(!isRecentCollapsed)}
                                className="flex items-center gap-1 text-xs font-semibold text-gray-400 uppercase tracking-wider hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
                            >
                                <span className="material-symbols-outlined text-[14px] transition-transform" style={{ transform: isRecentCollapsed ? 'rotate(-90deg)' : 'rotate(0deg)' }}>expand_more</span>
                                Recent
                            </button>
                            {recentGenerations.length > 0 && (
                                <span className="text-[10px] bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 px-1.5 py-0.5 rounded">{recentGenerations.length}</span>
                            )}
                        </div>
                        
                        {!isRecentCollapsed && (
                            <div className="flex-1 overflow-y-auto no-scrollbar px-2 lg:px-3">
                                {recentGenerations.length === 0 ? (
                                    <div className="hidden lg:flex flex-col items-center justify-center py-8 text-gray-400">
                                        <span className="material-symbols-outlined text-2xl mb-2">history</span>
                                        <p className="text-xs text-center">Your generated<br/>lessons appear here</p>
                                    </div>
                                ) : (
                                    <div className="flex flex-col gap-1">
                                        {recentGenerations.slice(0, 10).map((gen) => {
                                            const isActive = currentGenerationId === gen.id;
                                            return (
                                            <div
                                                key={gen.id}
                                                onClick={() => onSelectGeneration(gen)}
                                                className={`group flex items-center gap-2 p-2 rounded-lg cursor-pointer transition-all border ${isActive ? 'bg-primary/10 border-primary/30 dark:bg-primary/20' : 'hover:bg-white dark:hover:bg-white/5 border-transparent hover:border-gray-200 dark:hover:border-gray-700'}`}
                                            >
                                                <div 
                                                    className={`w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 ${isActive ? 'bg-primary text-white' : 'bg-amber-50 dark:bg-amber-900/20 text-amber-600 dark:text-amber-400'}`}
                                                >
                                                    <span className="material-symbols-outlined text-[16px]">
                                                        {gen.type === 'package' ? 'folder' : 'article'}
                                                    </span>
                                                </div>
                                                <div className="hidden lg:flex flex-col flex-1 min-w-0">
                                                    <span className="text-sm font-medium text-[#101718] dark:text-white truncate group-hover:text-primary transition-colors" title={gen.title}>
                                                        {gen.title}
                                                        {gen.version > 1 && <span className="ml-1 text-xs text-gray-400">v{gen.version}</span>}
                                                    </span>
                                                    <span className="text-[10px] text-gray-400">
                                                        {formatDate(gen.created_at)}
                                                        {gen.parent_id && <span className="ml-1">• Updated</span>}
                                                    </span>
                                                </div>
                                                {/* Delete button - visible on hover */}
                                                <button
                                                    onClick={(e) => {
                                                        e.stopPropagation();
                                                        if (confirm('Delete this generation?')) {
                                                            onDeleteGeneration(gen.id);
                                                        }
                                                    }}
                                                    className="hidden lg:flex opacity-0 group-hover:opacity-100 p-1 rounded hover:bg-red-100 dark:hover:bg-red-900/30 text-gray-400 hover:text-red-500 transition-all"
                                                    title="Delete"
                                                >
                                                    <span className="material-symbols-outlined text-[16px]">delete</span>
                                                </button>
                                            </div>
                                        );})}
                                    </div>
                                )}
                            </div>
                        )}
                    </div>

                    {/* User Profile */}
                    <div className="flex flex-col gap-2 px-0 lg:px-4 items-center lg:items-start mt-auto">
                        <div className="pt-4 border-t border-gray-200 dark:border-gray-800 w-full">
                            <div className="flex items-center justify-between gap-3 px-2 lg:px-2">
                                <div className="flex items-center gap-3">
                                    <div className="w-8 h-8 rounded-full bg-gradient-to-br from-primary to-teal-600 flex items-center justify-center text-white text-sm font-bold">
                                        {currentUser && currentUser.name ? currentUser.name.charAt(0).toUpperCase() : 'U'}
                                    </div>
                                    <div className="hidden lg:flex flex-col">
                                        <span className="text-sm font-bold text-[#101718] dark:text-white">{currentUser && currentUser.name ? currentUser.name : 'User'}</span>
                                        <span className="text-xs text-[#5c818a]">@{currentUser && currentUser.id ? currentUser.id : 'user'}</span>
                                    </div>
                                </div>
                                <button 
                                    onClick={onLogout}
                                    className="hidden lg:flex items-center justify-center w-8 h-8 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 text-gray-400 hover:text-red-500 transition-colors"
                                    title="Sign Out"
                                >
                                    <span className="material-symbols-outlined text-[18px]">logout</span>
                                </button>
                            </div>
                            {/* Mobile logout button */}
                            <button 
                                onClick={onLogout}
                                className="lg:hidden flex items-center justify-center w-10 h-10 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 text-gray-400 hover:text-red-500 transition-colors mt-2 mx-auto"
                                title="Sign Out"
                            >
                                <span className="material-symbols-outlined text-[20px]">logout</span>
                            </button>
                        </div>
                    </div>
                </nav>
            );
        };

        // Template Library View (Full Page)
var TemplateLibraryView = ({ onBack, onSelectTemplate }) => {
            const [search, setSearch] = useState('');
            const [selectedCategory, setSelectedCategory] = useState('all');

            const categories = ['all', 'Science', 'Math', 'ELA', 'Social Studies', 'Custom'];
            
            const templates = [
                { id: 1, title: '5E Lesson Model', subject: 'Science', category: 'Science', description: 'Engage, Explore, Explain, Elaborate, Evaluate - Perfect for inquiry-based science lessons', icon: 'science', color: 'emerald' },
                { id: 2, title: 'Workshop Model', subject: 'ELA', category: 'ELA', description: 'Mini-lesson, Independent Work, Share - Great for reading and writing workshops', icon: 'menu_book', color: 'blue' },
                { id: 3, title: 'Problem-Based Learning', subject: 'Math', category: 'Math', description: 'Launch, Explore, Discuss - Student-centered math problem solving', icon: 'calculate', color: 'purple' },
                { id: 4, title: 'Socratic Seminar', subject: 'Social Studies', category: 'Social Studies', description: 'Question-driven discussion for critical thinking', icon: 'forum', color: 'amber' },
                { id: 5, title: 'Flipped Classroom', subject: 'General', category: 'Custom', description: 'Pre-learning at home, active learning in class', icon: 'swap_horiz', color: 'rose' },
                { id: 6, title: 'Station Rotation', subject: 'General', category: 'Custom', description: 'Multiple learning stations with timed rotations', icon: 'hub', color: 'cyan' },
            ];

            const filteredTemplates = templates.filter(t => {
                const matchesSearch = !search || t.title.toLowerCase().includes(search.toLowerCase()) || t.description.toLowerCase().includes(search.toLowerCase());
                const matchesCategory = selectedCategory === 'all' || t.category === selectedCategory;
                return matchesSearch && matchesCategory;
            });

            const colorClasses = {
                emerald: 'bg-emerald-50 dark:bg-emerald-900/20 text-emerald-600 dark:text-emerald-400',
                blue: 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400',
                purple: 'bg-purple-50 dark:bg-purple-900/20 text-purple-600 dark:text-purple-400',
                amber: 'bg-amber-50 dark:bg-amber-900/20 text-amber-600 dark:text-amber-400',
                rose: 'bg-rose-50 dark:bg-rose-900/20 text-rose-600 dark:text-rose-400',
                cyan: 'bg-cyan-50 dark:bg-cyan-900/20 text-cyan-600 dark:text-cyan-400',
            };

            return (
                <main className="flex-1 flex flex-col h-full overflow-hidden bg-background-light dark:bg-background-dark">
                    {/* Header */}
                    <header className="h-16 flex items-center justify-between px-6 bg-white dark:bg-surface-dark border-b border-gray-200 dark:border-gray-700 flex-shrink-0">
                        <div className="flex items-center gap-4">
                            <button onClick={onBack} className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500">
                                <span className="material-symbols-outlined">arrow_back</span>
                            </button>
                            <div className="flex items-center gap-3">
                                <div className="w-10 h-10 rounded-xl bg-purple-100 dark:bg-purple-900/20 flex items-center justify-center text-purple-600 dark:text-purple-400">
                                    <span className="material-symbols-outlined">library_books</span>
                                </div>
                                <div>
                                    <h1 className="font-bold text-lg text-[#101718] dark:text-white">Template Library</h1>
                                    <p className="text-xs text-gray-500">Reusable lesson frameworks</p>
                                </div>
                            </div>
                        </div>
                    </header>

                    {/* Content */}
                    <div className="flex-1 overflow-y-auto p-6 md:p-8">
                        <div className="max-w-5xl mx-auto">
                            {/* Search & Filter */}
                            <div className="flex flex-col md:flex-row gap-4 mb-8">
                                <div className="relative flex-1">
                                    <span className="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">search</span>
                                    <input
                                        type="text"
                                        placeholder="Search templates..."
                                        value={search}
                                        onChange={(e) => setSearch(e.target.value)}
                                        className="w-full pl-10 pr-4 py-3 rounded-xl border border-gray-200 dark:border-gray-600 bg-white dark:bg-surface-dark focus:outline-none focus:ring-2 focus:ring-primary/50 dark:text-white"
                                    />
                                </div>
                                <div className="flex gap-2 overflow-x-auto no-scrollbar">
                                    {categories.map(cat => (
                                        <button
                                            key={cat}
                                            onClick={() => setSelectedCategory(cat)}
                                            className={`px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap transition-all ${selectedCategory === cat ? 'bg-primary text-white' : 'bg-white dark:bg-surface-dark border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:border-primary/50'}`}
                                        >
                                            {cat === 'all' ? 'All Templates' : cat}
                                        </button>
                                    ))}
                                </div>
                            </div>

                            {/* Templates Grid */}
                            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
                                {filteredTemplates.map(tpl => (
                                    <div
                                        key={tpl.id}
                                        onClick={() => onSelectTemplate(tpl)}
                                        className="group p-5 rounded-2xl bg-white dark:bg-surface-dark border border-gray-200 dark:border-gray-700 hover:border-primary/50 hover:shadow-lg transition-all cursor-pointer"
                                    >
                                        <div className={`w-12 h-12 rounded-xl ${colorClasses[tpl.color]} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform`}>
                                            <span className="material-symbols-outlined text-2xl">{tpl.icon}</span>
                                        </div>
                                        <h3 className="font-bold text-lg text-[#101718] dark:text-white group-hover:text-primary transition-colors">{tpl.title}</h3>
                                        <p className="text-xs text-primary font-medium mt-1">{tpl.subject}</p>
                                        <p className="text-sm text-gray-500 dark:text-gray-400 mt-2 leading-relaxed">{tpl.description}</p>
                                        <div className="flex items-center gap-2 mt-4 text-primary opacity-0 group-hover:opacity-100 transition-opacity">
                                            <span className="text-sm font-medium">Use Template</span>
                                            <span className="material-symbols-outlined text-[18px]">arrow_forward</span>
                                        </div>
                                    </div>
                                ))}
                            </div>

                            {/* Create Custom Template */}
                            <div className="mt-8 p-6 rounded-2xl bg-gray-50 dark:bg-gray-800/30 border-2 border-dashed border-gray-300 dark:border-gray-600 text-center hover:border-primary/50 transition-colors cursor-pointer group">
                                <span className="material-symbols-outlined text-gray-400 text-4xl mb-3 group-hover:text-primary transition-colors">add_circle</span>
                                <h3 className="font-bold text-gray-600 dark:text-gray-300 group-hover:text-primary transition-colors">Create Custom Template</h3>
                                <p className="text-sm text-gray-400 mt-1">Save your lesson blueprint as a reusable template</p>
                            </div>
                        </div>
                    </div>
                </main>
            );
        };

        // Chat Message

// tryParseStreamingBlueprint - defined in js/utils.js
// blueprintToMarkdown - defined in js/utils.js


        // ContentEditable component that doesn't cause cursor jump
var ContentEditableDiv = ({ contentRef, initialHtml, onInput, isEditing, resetKey, isReadOnly }) => {
            var localRef = useRef(null);
            var hasInitialized = useRef(false);
            var lastInitialHtml = useRef(initialHtml);
            
            // Assign ref
            React.useEffect(function() {
                if (localRef.current && contentRef) {
                    contentRef.current = localRef.current;
                }
            }, [contentRef]);
            
            // Set initial content only once
            React.useEffect(function() {
                if (localRef.current && !hasInitialized.current) {
                    localRef.current.innerHTML = initialHtml;
                    hasInitialized.current = true;
                    lastInitialHtml.current = initialHtml;
                }
            }, []);
            
            // Reset only when initialHtml actually changes (e.g., loading history version)
            // Not when isEditing changes
            React.useEffect(function() {
                if (localRef.current && initialHtml !== lastInitialHtml.current) {
                    localRef.current.innerHTML = initialHtml;
                    lastInitialHtml.current = initialHtml;
                }
            }, [initialHtml]);
            
            // Force reset when resetKey changes (for explicit resets like loading history)
            React.useEffect(function() {
                if (localRef.current && resetKey) {
                    localRef.current.innerHTML = initialHtml;
                    lastInitialHtml.current = initialHtml;
                }
            }, [resetKey]);
            
            return (
                <div 
                    ref={localRef}
                    contentEditable={!isReadOnly}
                    onInput={isReadOnly ? undefined : onInput}
                    suppressContentEditableWarning={true}
                    className={'prose prose-lg dark:prose-invert max-w-none outline-none focus:outline-none ' +
                    (isReadOnly ? 'cursor-default select-text ' : '') +
                    '[&_h1]:text-[2.25rem] [&_h1]:font-bold [&_h1]:text-gray-900 dark:[&_h1]:text-white [&_h1]:leading-tight [&_h1]:mb-6 [&_h1]:mt-0 [&_h1]:font-serif ' +
                    '[&_h2]:text-[1.5rem] [&_h2]:font-bold [&_h2]:text-gray-800 dark:[&_h2]:text-gray-100 [&_h2]:mt-10 [&_h2]:mb-4 [&_h2]:pb-3 [&_h2]:border-b [&_h2]:border-gray-200 dark:[&_h2]:border-gray-700 ' +
                    '[&_h3]:text-[1.25rem] [&_h3]:font-semibold [&_h3]:text-gray-800 dark:[&_h3]:text-gray-200 [&_h3]:mt-8 [&_h3]:mb-3 ' +
                    '[&_h4]:text-[1.1rem] [&_h4]:font-semibold [&_h4]:text-gray-700 dark:[&_h4]:text-gray-300 [&_h4]:mt-6 [&_h4]:mb-2 ' +
                    '[&_p]:text-[1rem] [&_p]:text-gray-600 dark:[&_p]:text-gray-400 [&_p]:leading-[1.8] [&_p]:my-4 ' +
                    '[&_strong]:text-gray-900 dark:[&_strong]:text-white [&_strong]:font-semibold ' +
                    '[&_ul]:my-4 [&_ul]:pl-6 [&_ol]:my-4 [&_ol]:pl-6 ' +
                    '[&_li]:text-[1rem] [&_li]:text-gray-600 dark:[&_li]:text-gray-400 [&_li]:my-2 [&_li]:leading-[1.7] ' +
                    '[&_*]:outline-none [&_*]:focus:outline-none'}
                />
            );
        };

        // Blueprint Message - Manus Style (Compact Preview + Modal)
var BlueprintMessage = ({ blueprint, onSave, onGenerate, generationId, blueprintHistory, version, updatedAt, isGeneratingMaterials, materialsGenerated, blueprintModifiedSinceGeneration, onBlueprintModified, isGenerationContext, isReadOnly, isHistoryVersion, historyVersion, historyTimestamp, streamingContent, isStreaming, thoughtText }) => {
            // Full Screen Modal - Manus Style
            const [showModal, setShowModal] = useState(false);
            // Use raw_content if available (for persisted edits), otherwise generate from structured blueprint
            var initialMarkdown = blueprint.raw_content || blueprintToMarkdown(blueprint);
            const [markdown, setMarkdown] = useState(initialMarkdown);
            const [hasChanges, setHasChanges] = useState(false);
            const [activeOutlineIdx, setActiveOutlineIdx] = useState(0);
            const [showDownloadMenu, setShowDownloadMenu] = useState(false);
            const [isDownloading, setIsDownloading] = useState(false);
            const [isFullscreen, setIsFullscreen] = useState(false);
            const [showHistoryPanel, setShowHistoryPanel] = useState(false);
            const [isSaving, setIsSaving] = useState(false);
            const [isEditing, setIsEditing] = useState(false);
            const [localHistory, setLocalHistory] = useState(blueprintHistory || []);
            const [contentResetKey, setContentResetKey] = useState(0);
            var contentRef = useRef(null);
            var lastSavedMarkdown = useRef(initialMarkdown);
            
            // Sync local history when prop changes
            React.useEffect(function() {
                setLocalHistory(blueprintHistory || []);
            }, [blueprintHistory]);
            
            // History data - use local state
            var historyCount = localHistory.length;
            var currentVersion = version || 1;
            
            // Extract outline from markdown for the right sidebar (h2, h3, h4)
            var extractOutline = function(md) {
                var items = [];
                var lines = md.split('\n');
                for (var i = 0; i < lines.length; i++) {
                    var line = lines[i];
                    if (line.match(/^####\s+(.+)$/)) {
                        items.push({ level: 4, text: line.replace(/^####\s+/, ''), index: items.length });
                    } else if (line.match(/^###\s+(.+)$/)) {
                        items.push({ level: 3, text: line.replace(/^###\s+/, ''), index: items.length });
                    } else if (line.match(/^##\s+(.+)$/)) {
                        items.push({ level: 2, text: line.replace(/^##\s+/, ''), index: items.length });
                    }
                }
                return items;
            };
            // Use last saved markdown for stable outline during editing
            var outline = extractOutline(lastSavedMarkdown.current);
            
            // Download format options
            var downloadFormats = [
                { id: 'pdf', label: 'PDF', icon: 'picture_as_pdf', color: 'text-red-500' },
                { id: 'docx', label: 'Word (DOCX)', icon: 'description', color: 'text-blue-500' },
                { id: 'md', label: 'Markdown', icon: 'code', color: 'text-gray-600' }
            ];
            
            // Handle blueprint download
            var handleDownload = function(format) {
                setShowDownloadMenu(false);
                setIsDownloading(true);
                
                fetch('/api/v1/download-blueprint', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        markdown: markdown,
                        title: blueprint.title || 'Lesson Blueprint',
                        format: format
                    })
                })
                .then(function(response) {
                    if (!response.ok) throw new Error('Download failed');
                    return response.blob();
                })
                .then(function(blob) {
                    var url = window.URL.createObjectURL(blob);
                    var a = document.createElement('a');
                    a.href = url;
                    a.download = 'lesson-blueprint.' + format;
                    document.body.appendChild(a);
                    a.click();
                    window.URL.revokeObjectURL(url);
                    a.remove();
                    setIsDownloading(false);
                })
                .catch(function(err) {
                    console.error('Download error:', err);
                    alert('Download failed. Please try again.');
                    setIsDownloading(false);
                });
            };
            
            // Close download menu when clicking outside
            React.useEffect(function() {
                var handleClickOutside = function(e) {
                    if (!e.target.closest('.blueprint-download-dropdown')) {
                        setShowDownloadMenu(false);
                    }
                };
                document.addEventListener('click', handleClickOutside);
                return function() {
                    document.removeEventListener('click', handleClickOutside);
                };
            }, []);

            // Handle content edit (directly in the rendered view)
            var handleContentChange = function() {
                if (contentRef.current) {
                    // Just mark as having changes - don't update state to avoid cursor jump
                    if (!isEditing) {
                        setIsEditing(true);
                    }
                    setHasChanges(true);
                }
            };
            
            // Get current content from editor (call this when saving)
            var getCurrentContent = function() {
                if (contentRef.current) {
                    return contentRef.current.innerHTML;
                }
                return markdown;
            };
            
            // Convert HTML back to markdown-like text
            var htmlToMarkdown = function(html) {
                var temp = document.createElement('div');
                temp.innerHTML = html;
                
                var md = '';
                var processNode = function(node, inList) {
                    if (node.nodeType === Node.TEXT_NODE) {
                        return node.textContent;
                    }
                    if (node.nodeType !== Node.ELEMENT_NODE) return '';
                    
                    var tag = node.tagName.toLowerCase();
                    var text = '';
                    
                    // Process children first
                    for (var i = 0; i < node.childNodes.length; i++) {
                        text += processNode(node.childNodes[i], tag === 'ul' || tag === 'ol');
                    }
                    
                    switch(tag) {
                        case 'h1': return '# ' + text.trim() + '\n\n';
                        case 'h2': return '## ' + text.trim() + '\n\n';
                        case 'h3': return '### ' + text.trim() + '\n\n';
                        case 'h4': return '#### ' + text.trim() + '\n\n';
                        case 'p': return text.trim() + '\n\n';
                        case 'strong': case 'b': return '**' + text + '**';
                        case 'em': case 'i': return '*' + text + '*';
                        case 'li': return '- ' + text.trim() + '\n';
                        case 'ul': case 'ol': return text + '\n';
                        case 'br': return '\n';
                        default: return text;
                    }
                };
                
                for (var i = 0; i < temp.childNodes.length; i++) {
                    md += processNode(temp.childNodes[i], false);
                }
                
                return md.replace(/\n{3,}/g, '\n\n').trim();
            };
            
            var handleSaveChanges = async function() {
                // Get current content from the editor
                var currentHtml = getCurrentContent();
                
                // Convert HTML to markdown for storage
                var editedMarkdown = htmlToMarkdown(currentHtml);
                
                // Create updated blueprint with raw_content field to persist edits
                var updatedBlueprint = Object.assign({}, blueprint, {
                    raw_content: editedMarkdown,
                    _edited_at: new Date().toISOString()
                });
                
                if (!generationId) {
                    // No generation ID, just call local save with markdown (not HTML)
                    onSave(editedMarkdown);
                    setHasChanges(false);
                    setIsEditing(false);
                    // Notify that blueprint was modified
                    if (onBlueprintModified) onBlueprintModified();
                    return;
                }
                
                setIsSaving(true);
                try {
                    // Save updated blueprint with raw_content to server
                    await api.saveBlueprint(generationId, updatedBlueprint, 'Blueprint content updated', false);
                    onSave(editedMarkdown);
                    // Update lastSavedMarkdown with edited content
                    lastSavedMarkdown.current = editedMarkdown;
                    setMarkdown(editedMarkdown);
                    setHasChanges(false);
                    setIsEditing(false);
                    // Notify that blueprint was modified (allows regenerating materials)
                    if (onBlueprintModified) onBlueprintModified();
                } catch (err) {
                    console.error('Failed to save blueprint:', err);
                    alert('Failed to save changes. Please try again.');
                } finally {
                    setIsSaving(false);
                }
            };
            
            // Delete a history entry
            var handleDeleteHistory = async function(version) {
                if (!generationId) return;
                
                try {
                    await api.deleteHistoryEntry(generationId, version);
                    // Update local state to remove the deleted entry (no page reload)
                    setLocalHistory(function(prev) {
                        return prev.filter(function(entry) {
                            return entry.version !== version;
                        });
                    });
                } catch (err) {
                    console.error('Failed to delete history:', err);
                    alert('Failed to delete history entry. Please try again.');
                }
            };
            
            // Scroll to section when clicking outline
            var scrollToSection = function(idx) {
                setActiveOutlineIdx(idx);
                if (contentRef.current) {
                    // Get all h2, h3, h4 headers in order
                    var headers = contentRef.current.querySelectorAll('h2, h3, h4');
                    if (headers[idx]) {
                        // Add highlight effect
                        headers[idx].style.transition = 'background-color 0.3s';
                        headers[idx].style.backgroundColor = 'rgba(59, 130, 246, 0.1)';
                        headers[idx].style.borderRadius = '4px';
                        headers[idx].style.padding = '4px 8px';
                        headers[idx].style.marginLeft = '-8px';
                        
                        headers[idx].scrollIntoView({ behavior: 'smooth', block: 'center' });
                        
                        // Remove highlight after animation
                        setTimeout(function() {
                            headers[idx].style.backgroundColor = 'transparent';
                        }, 1500);
                    }
                }
            };

            var renderMarkdown = function(md) {
                if (typeof marked !== 'undefined') {
                    return marked.parse(md);
                }
                return md;
            };

            // Compact Preview Card in Chat
            return (
                <div className="w-full">
                    <div className="flex flex-col gap-2 items-start w-full animate-fade-in text-left">
                        <div className="flex gap-2 items-center mb-1">
                            <div className={`size-6 rounded-full flex items-center justify-center ${isStreaming ? 'bg-green-100 dark:bg-green-900/20' : isHistoryVersion ? 'bg-amber-100' : isGenerationContext ? 'bg-primary/20' : 'bg-amber-100 dark:bg-amber-900/20'}`}>
                                <span className={`material-symbols-outlined text-sm icon-fill ${isStreaming ? 'text-green-600 dark:text-green-400 animate-pulse' : isHistoryVersion ? 'text-amber-600' : isGenerationContext ? 'text-primary' : 'text-amber-600 dark:text-amber-400'}`}>{isStreaming ? 'edit_note' : isHistoryVersion ? 'history' : isGenerationContext ? 'task' : 'auto_awesome'}</span>
                            </div>
                            <span className={`text-xs font-semibold uppercase tracking-wider ${isStreaming ? 'text-green-600 dark:text-green-400' : isHistoryVersion ? 'text-amber-600 dark:text-amber-400' : isGenerationContext ? 'text-primary' : 'text-amber-600 dark:text-amber-400'}`}>
                                {isStreaming ? (isChinese(blueprint.title || '') ? '正在生成 Blueprint...' : 'Generating Blueprint...') : isHistoryVersion ? 'Blueprint v' + historyVersion : isGenerationContext ? 'Generating from Blueprint' : 'Blueprint Generated'}
                            </span>
                            {isStreaming && streamingContent && (
                                <span className="text-xs text-green-500 dark:text-green-400 tabular-nums">{streamingContent.length} chars</span>
                            )}
                            {historyTimestamp && (
                                <span className="text-xs text-slate-400">{historyTimestamp}</span>
                            )}
                        </div>
                        
                        {/* Show thinking collapsible for blueprint */}
                        {thoughtText && !isStreaming && (
                            <ThinkingCollapse 
                                thoughtText={thoughtText} 
                                isChinese={blueprint.title && /[\u4e00-\u9fff]/.test(blueprint.title)}
                                isStreaming={false}
                            />
                        )}
                        
                        {/* Preview Card with Document Content - Manus Style */}
                        <button
                            data-blueprint-preview="true"
                            onClick={() => !isGenerationContext && setShowModal(true)}
                            className={`w-full max-w-2xl group text-left ${isGenerationContext && !isStreaming ? 'cursor-default' : 'cursor-pointer'}`}
                        >
                            <div className={`bg-white dark:bg-gray-900 rounded-xl border shadow-sm transition-all duration-300 overflow-hidden ${isGenerationContext ? 'border-primary/50 ring-2 ring-primary/20' : 'border-gray-200 dark:border-gray-700 hover:shadow-lg hover:border-gray-300 dark:hover:border-gray-600 cursor-pointer'}`}>
                                {/* Header with icon and title */}
                                <div className="flex items-center gap-3 px-5 py-4 border-b border-gray-100 dark:border-gray-800">
                                    <div className={'w-9 h-9 rounded-lg flex items-center justify-center flex-shrink-0 ' + (isReadOnly ? 'bg-gray-400' : 'bg-blue-600')}>
                                        <span className="material-symbols-outlined text-white text-lg icon-fill">{isReadOnly ? 'lock' : 'description'}</span>
                                    </div>
                                    <div className="flex-1 text-left min-w-0">
                                        <div className="text-[15px] font-semibold text-gray-900 dark:text-white truncate">
                                            {blueprint.title}
                                        </div>
                                        {isReadOnly && (
                                            <div className="text-[11px] text-gray-400 dark:text-gray-500 mt-0.5">
                                                {isChinese(blueprint.title || '') ? '历史版本 • 只读' : 'Previous version • Read only'}
                                            </div>
                                        )}
                                    </div>
                                    {!isReadOnly && (
                                        <div className="flex items-center text-gray-400 dark:text-gray-500 opacity-0 group-hover:opacity-100 transition-opacity">
                                            <span className="material-symbols-outlined text-[20px]">more_horiz</span>
                                        </div>
                                    )}
                                </div>
                                
                                {/* Document Preview - Clean Structured Layout like Manus */}
                                <div className="relative px-5 py-4 overflow-hidden bg-gray-50/50 dark:bg-gray-950/50 pointer-events-none" style={{ maxHeight: '280px' }}>
                                    {/* Title */}
                                    <h3 className="text-base font-semibold text-gray-900 dark:text-white leading-snug mb-1">
                                        {blueprint.title}
                                    </h3>
                                    
                                    {/* Summary/Description */}
                                    <p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed mb-3 line-clamp-2">
                                        {blueprint.summary || 'An interactive lesson designed to engage students and achieve learning objectives.'}
                                    </p>
                                    
                                    {/* Metadata Line */}
                                    <p className="text-xs text-gray-500 dark:text-gray-500 mb-3">
                                        <span className="font-medium text-gray-700 dark:text-gray-300">{isChinese(blueprint.title) ? '年级' : 'Grade'}:</span> {(blueprint.metadata && blueprint.metadata.grade_level) || 'N/A'} | 
                                        <span className="font-medium text-gray-700 dark:text-gray-300 ml-1">{isChinese(blueprint.title) ? '学科' : 'Subject'}:</span> {(blueprint.metadata && blueprint.metadata.subject) || 'N/A'} | 
                                        <span className="font-medium text-gray-700 dark:text-gray-300 ml-1">{isChinese(blueprint.title) ? '时长' : 'Duration'}:</span> {(blueprint.metadata && blueprint.metadata.duration_minutes) || 45} {isChinese(blueprint.title) ? '分钟' : 'minutes'}
                                    </p>
                                    
                                    {/* Standards Section */}
                                    {blueprint.standards_alignment && blueprint.standards_alignment.primary_standards && blueprint.standards_alignment.primary_standards.length > 0 && (
                                        <div className="mb-3">
                                            <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1">{isChinese(blueprint.title) ? '教学标准' : 'Standards'}</h4>
                                            <p className="text-xs text-gray-500 dark:text-gray-400 line-clamp-1">
                                                {blueprint.standards_alignment.primary_standards[0]}
                                            </p>
                                        </div>
                                    )}
                                    
                                    {/* Learning Objectives Section */}
                                    {blueprint.learning_objectives && blueprint.learning_objectives.length > 0 && (
                                        <div className="mb-3">
                                            <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1">{isChinese(blueprint.title) ? '学习目标' : 'Learning Objectives'}</h4>
                                            <div className="space-y-0.5">
                                                {blueprint.learning_objectives.slice(0, 3).map(function(obj, idx) {
                                                    var text = obj.text || obj;
                                                    return (
                                                        <p key={idx} className="text-xs text-gray-500 dark:text-gray-400 line-clamp-1">
                                                            {text}
                                                        </p>
                                                    );
                                                })}
                                            </div>
                                        </div>
                                    )}
                                    
                                    {/* Lesson Flow Preview */}
                                    {blueprint.lesson_flow && blueprint.lesson_flow.length > 0 && (
                                        <div className="mb-3">
                                            <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1">{isChinese(blueprint.title) ? '课程流程' : 'Lesson Flow'}</h4>
                                            <p className="text-xs text-gray-500 dark:text-gray-400">
                                                {blueprint.lesson_flow[0] && blueprint.lesson_flow[0].title && ('1. ' + blueprint.lesson_flow[0].title.substring(0, 50))}
                                                {blueprint.lesson_flow[0] && blueprint.lesson_flow[0].title && blueprint.lesson_flow[0].title.length > 50 && '...'}
                                            </p>
                                        </div>
                                    )}
                                    
                                    {/* Materials to Create - Package Types */}
                                    {blueprint.materials_to_create && blueprint.materials_to_create.length > 0 && (
                                        <div className="mb-3">
                                            <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1.5">{isChinese(blueprint.title) ? '📦 将生成材料' : '📦 Materials to Create'}</h4>
                                            <div className="flex flex-wrap gap-1">
                                                {blueprint.materials_to_create.map(function(mat, idx) {
                                                    var typeIcons = {
                                                        'slide': 'slideshow',
                                                        'lesson_plan': 'description',
                                                        'worksheet': 'assignment',
                                                        'quiz': 'quiz',
                                                        'exit_ticket': 'receipt_long',
                                                        'group_activity': 'groups',
                                                        'rubric': 'format_list_numbered',
                                                        'vocabulary_cards': 'style'
                                                    };
                                                    var typeLabels = {
                                                        'slide': isChinese(blueprint.title) ? '课件' : 'Slides',
                                                        'lesson_plan': isChinese(blueprint.title) ? '教案' : 'Lesson Plan',
                                                        'worksheet': isChinese(blueprint.title) ? '练习' : 'Worksheet',
                                                        'quiz': isChinese(blueprint.title) ? '测验' : 'Quiz',
                                                        'exit_ticket': isChinese(blueprint.title) ? '出门检测' : 'Exit Ticket',
                                                        'group_activity': isChinese(blueprint.title) ? '小组活动' : 'Group Activity',
                                                        'rubric': isChinese(blueprint.title) ? '评分标准' : 'Rubric',
                                                        'vocabulary_cards': isChinese(blueprint.title) ? '词汇卡' : 'Vocab Cards'
                                                    };
                                                    var icon = typeIcons[mat.type] || 'folder';
                                                    var label = typeLabels[mat.type] || mat.type;
                                                    return (
                                                        <span key={idx} className="inline-flex items-center gap-1 px-2 py-0.5 bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 text-xs rounded-full" title={mat.title}>
                                                            <span className="material-symbols-outlined text-xs">{icon}</span>
                                                            {label}
                                                        </span>
                                                    );
                                                })}
                                            </div>
                                        </div>
                                    )}
                                    
                                    {/* Soft Gradient Fade */}
                                    <div className="absolute left-0 right-0 bottom-0 h-20 bg-gradient-to-t from-gray-50 dark:from-gray-950 via-gray-50/90 dark:via-gray-950/90 to-transparent pointer-events-none"></div>
                                </div>
                            </div>
                        </button>

                        {/* Note: Create Teaching Materials button moved to blueprint_ready card to avoid duplication */}
                        
                        {/* Regenerate button - shown when materials exist but blueprint was modified (not for read-only versions) */}
                        {!isReadOnly && materialsGenerated && blueprintModifiedSinceGeneration && (function() {
                            // Detect language from blueprint title
                            var isZh = isChinese(blueprint.title || '');
                            return (
                                <div className="mt-3 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-xl">
                                    <div className="flex items-center gap-2 mb-2">
                                        <span className="material-symbols-outlined text-amber-600 dark:text-amber-400 text-sm">edit_note</span>
                                        <span className="text-xs font-semibold text-amber-700 dark:text-amber-300">
                                            {isZh ? 'Blueprint 已修改' : 'Blueprint Modified'}
                                        </span>
                                    </div>
                                    <p className="text-xs text-amber-600 dark:text-amber-400 mb-3">
                                        {isZh 
                                            ? '您的课程蓝图已更新。点击重新生成以应用更改到教学材料。'
                                            : 'Your lesson blueprint has been updated. Click regenerate to apply changes to teaching materials.'}
                                    </p>
                                    <button
                                        onClick={onGenerate}
                                        disabled={isGeneratingMaterials}
                                        className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all ${isGeneratingMaterials ? 'bg-gray-300 dark:bg-gray-600 text-gray-500 cursor-not-allowed' : 'bg-amber-500 text-white hover:bg-amber-600 shadow-md hover:shadow-lg'}`}
                                    >
                                        {isGeneratingMaterials ? (
                                            <React.Fragment>
                                                <span className="material-symbols-outlined text-base animate-spin">progress_activity</span>
                                                {isZh ? '生成中...' : 'Generating...'}
                                            </React.Fragment>
                                        ) : (
                                            <React.Fragment>
                                                <span className="material-symbols-outlined text-base">refresh</span>
                                                {isZh ? '重新生成教学材料' : 'Regenerate Materials'}
                                            </React.Fragment>
                                        )}
                                    </button>
                                </div>
                            );
                        })()}
                    </div>

                    {showModal && (
                        <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-slate-900/60 backdrop-blur-sm animate-fade-in" onClick={function(e) { if (e.target === e.currentTarget) setShowModal(false); }}>
                            <div className={`relative bg-white dark:bg-gray-900 rounded-2xl shadow-2xl flex flex-col overflow-hidden animate-modal-scale-in transition-all duration-300 ${isFullscreen ? 'w-full h-full rounded-none' : 'w-[95vw] max-w-6xl h-[90vh]'}`}>
                            {/* Modal Header - Manus Style */}
                            <div className="flex items-center justify-between px-4 py-2.5 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 z-10 flex-shrink-0 rounded-t-2xl">
                                <div className="flex items-center gap-3 flex-1 min-w-0">
                                    <div className="w-8 h-8 rounded-lg bg-blue-600 flex items-center justify-center flex-shrink-0">
                                        <span className="material-symbols-outlined text-white text-base icon-fill">description</span>
                                    </div>
                                    <div className="flex flex-col min-w-0">
                                        <div className="flex items-center gap-2">
                                            <h2 className="text-sm font-bold text-gray-900 dark:text-white truncate leading-tight">
                                                {blueprint.title}
                                            </h2>
                                            {historyCount > 0 && (
                                                <span className="text-[10px] px-1.5 py-0.5 bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded font-medium">
                                                    v{currentVersion}
                                                </span>
                                            )}
                                        </div>
                                        <p className="text-[10px] text-gray-400 uppercase tracking-wider font-medium">
                                            {(function() {
                                                var isZh = isChinese(blueprint.title || '');
                                                if (isReadOnly) {
                                                    return <span className="text-gray-400">{isZh ? '只读 • 历史版本' : 'Read Only • Previous Version'}</span>;
                                                } else {
                                                    return <span>{isZh ? '点击文本编辑' : 'Click text to edit'} • {hasChanges ? (isZh ? '未保存' : 'Unsaved changes') : (updatedAt ? (isZh ? '更新于: ' : 'Updated: ') + new Date(updatedAt).toLocaleString() : (isZh ? '最后修改: ' : 'Last modified: ') + new Date().toLocaleDateString())}</span>;
                                                }
                                            })()}
                                        </p>
                                    </div>
                                </div>
                                
                                <div className="flex items-center gap-2">
                                    {!isReadOnly && hasChanges && (
                                        <span className="text-xs text-amber-600 dark:text-amber-400 font-medium flex items-center gap-1 mr-2">
                                            <span className="w-1.5 h-1.5 bg-amber-500 rounded-full animate-pulse"></span>
                                            Unsaved
                                        </span>
                                    )}
                                    
                                    {/* History Button - hidden in read-only mode */}
                                    {!isReadOnly && historyCount > 0 && (
                                        <button 
                                            onClick={function() { setShowHistoryPanel(!showHistoryPanel); }}
                                            className={'flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg transition-colors text-sm ' + (showHistoryPanel ? 'bg-amber-100 dark:bg-amber-900/30 text-amber-600' : 'text-gray-400 hover:text-amber-600 hover:bg-amber-50 dark:hover:bg-amber-900/20')}
                                            title="Restore to original version"
                                        >
                                            <span className="material-symbols-outlined text-base">restore</span>
                                            <span className="hidden sm:inline font-medium">Restore</span>
                                        </button>
                                    )}
                                    
                                    {/* Fullscreen Toggle */}
                                    <button 
                                        onClick={function() { setIsFullscreen(!isFullscreen); }}
                                        className="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
                                        title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
                                    >
                                        <span className="material-symbols-outlined text-lg">{isFullscreen ? 'fullscreen_exit' : 'fullscreen'}</span>
                                    </button>
                                    
                                    {/* Download Button with Dropdown */}
                                    <div className="relative blueprint-download-dropdown">
                                        <button 
                                            onClick={function(e) { e.stopPropagation(); setShowDownloadMenu(!showDownloadMenu); }}
                                            disabled={isDownloading}
                                            className="flex items-center gap-1 px-2.5 py-1.5 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors disabled:opacity-50"
                                        >
                                            <span className="material-symbols-outlined text-lg">{isDownloading ? 'hourglass_empty' : 'download'}</span>
                                            <span className="material-symbols-outlined text-xs">expand_more</span>
                                        </button>
                                        {showDownloadMenu && (
                                            <div className="absolute right-0 top-full mt-1 bg-white dark:bg-slate-800 rounded-lg shadow-xl border border-slate-200 dark:border-slate-700 py-1 z-50 min-w-[150px]">
                                                {downloadFormats.map(function(fmt) {
                                                    return (
                                                        <button
                                                            key={fmt.id}
                                                            onClick={function() { handleDownload(fmt.id); }}
                                                            className="w-full flex items-center gap-2 px-3 py-2.5 text-left text-sm hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
                                                        >
                                                            <span className={'material-symbols-outlined text-lg ' + fmt.color}>{fmt.icon}</span>
                                                            <span className="text-slate-700 dark:text-slate-200">{fmt.label}</span>
                                                        </button>
                                                    );
                                                })}
                                            </div>
                                        )}
                                    </div>
                                    
                                    {!isReadOnly && (
                                        <React.Fragment>
                                            <div className="h-5 w-[1px] bg-gray-200 dark:bg-gray-700"></div>
                                            
                                            {/* Save Button - Right side of header */}
                                            <button 
                                                onClick={handleSaveChanges}
                                                disabled={!hasChanges || isSaving}
                                                className={'flex items-center gap-1.5 px-3 py-1.5 text-sm font-semibold rounded-lg transition-all ' + (
                                                    hasChanges 
                                                        ? 'bg-primary text-white hover:bg-primary-hover shadow-sm' 
                                                        : 'text-gray-400 dark:text-gray-500 cursor-not-allowed'
                                                )}
                                            >
                                                <span className="material-symbols-outlined text-sm">
                                                    {isSaving ? 'hourglass_empty' : (hasChanges ? 'save' : 'check')}
                                                </span>
                                                {isSaving ? 'Saving...' : (hasChanges ? 'Save' : 'Saved')}
                                            </button>
                                        </React.Fragment>
                                    )}
                                    
                                    {isReadOnly && (
                                        <div className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-gray-400 bg-gray-100 dark:bg-gray-800 rounded-lg">
                                            <span className="material-symbols-outlined text-sm">lock</span>
                                            {isChinese(blueprint.title || '') ? '只读模式' : 'Read Only'}
                                        </div>
                                    )}
                                    
                                    <div className="h-5 w-[1px] bg-gray-200 dark:bg-gray-700"></div>
                                    <button 
                                        onClick={function() { setShowModal(false); }}
                                        className="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
                                    >
                                        <span className="material-symbols-outlined text-lg">close</span>
                                    </button>
                                </div>
                            </div>

                            {/* Modal Content - Manus Layout with Dot Outline on Right */}
                            <div className="flex-1 flex overflow-hidden relative">
                                {/* Restore Panel - Simple floating panel with original version only */}
                                {showHistoryPanel && historyCount > 0 && (
                                    <div className="absolute left-4 top-4 z-20 w-72 bg-white dark:bg-gray-900 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-800 overflow-hidden animate-fade-in">
                                        <div className="p-3 border-b border-gray-200 dark:border-gray-800 flex items-center justify-between bg-amber-50/50 dark:bg-amber-950/20">
                                            <h3 className="text-xs font-bold text-amber-600 dark:text-amber-400 uppercase tracking-wider flex items-center gap-1.5">
                                                <span className="material-symbols-outlined text-sm">restore</span>
                                                Restore Original
                                            </h3>
                                            <button onClick={function() { setShowHistoryPanel(false); }} className="p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded">
                                                <span className="material-symbols-outlined text-sm text-gray-500">close</span>
                                            </button>
                                        </div>
                                        <div className="p-4">
                                            {/* Show original version (first in history) */}
                                            {localHistory && localHistory.length > 0 && (function() {
                                                var originalEntry = localHistory[0];
                                                var snapshotBlueprint = originalEntry.blueprint_snapshot || originalEntry.blueprint;
                                                return (
                                                    <div className="space-y-3">
                                                        <div className="flex items-start gap-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
                                                            <div className="w-10 h-10 rounded-lg bg-amber-100 dark:bg-amber-900/30 flex items-center justify-center flex-shrink-0">
                                                                <span className="material-symbols-outlined text-amber-600 dark:text-amber-400">description</span>
                                                            </div>
                                                            <div className="flex-1 min-w-0">
                                                                <p className="text-sm font-medium text-gray-900 dark:text-white">Original Version</p>
                                                                <p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
                                                                    Created: {new Date(originalEntry.timestamp).toLocaleDateString()}
                                                                </p>
                                                            </div>
                                                        </div>
                                                        <p className="text-xs text-gray-500 dark:text-gray-400 text-center">
                                                            This will restore the blueprint to its original generated state.
                                                        </p>
                                                        <button
                                                            onClick={function() {
                                                                if (snapshotBlueprint) {
                                                                    var historyMarkdown = blueprintToMarkdown(snapshotBlueprint);
                                                                    setMarkdown(historyMarkdown);
                                                                    setIsEditing(false);
                                                                    setHasChanges(true);
                                                                    lastSavedMarkdown.current = historyMarkdown;
                                                                    setContentResetKey(function(k) { return k + 1; });
                                                                    setShowHistoryPanel(false);
                                                                }
                                                            }}
                                                            className="w-full flex items-center justify-center gap-2 px-4 py-2.5 bg-amber-500 hover:bg-amber-600 text-white rounded-lg text-sm font-semibold transition-colors shadow-sm"
                                                        >
                                                            <span className="material-symbols-outlined text-base">restore</span>
                                                            Restore to Original
                                                        </button>
                                                        <p className="text-[10px] text-gray-400 text-center">
                                                            After restoring, click Save to confirm changes.
                                                        </p>
                                                    </div>
                                                );
                                            })()}
                                            {(!localHistory || localHistory.length === 0) && (
                                                <p className="text-sm text-gray-400 text-center py-4">No original version available</p>
                                            )}
                                        </div>
                                    </div>
                                )}

                                {/* Main Document Area - Full Width, scales up in fullscreen */}
                                <div className="flex-1 overflow-y-auto bg-gray-100/80 dark:bg-gray-950 scroll-smooth">
                                    <div className={`mx-auto py-8 px-6 sm:px-12 transition-all duration-300 ${isFullscreen ? 'max-w-6xl' : 'max-w-4xl'}`}>
                                        <div className="bg-white dark:bg-gray-900 shadow-lg rounded-xl border border-gray-200/60 dark:border-gray-800 overflow-hidden">
                                            {/* Streaming indicator when blueprint is being generated */}
                                            {isStreaming && streamingContent && (
                                                <div className="px-6 py-3 bg-gradient-to-r from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 border-b border-green-200 dark:border-green-800/40 flex items-center gap-3">
                                                    <span className="material-symbols-outlined text-green-600 dark:text-green-400 text-lg animate-pulse">edit_note</span>
                                                    <span className="text-sm font-medium text-green-700 dark:text-green-300">
                                                        {isChinese(blueprint.title || '') ? '正在实时编写 Blueprint...' : 'Writing Blueprint in real-time...'}
                                                    </span>
                                                    <span className="text-xs text-green-500 dark:text-green-400 ml-auto tabular-nums">{streamingContent.length} chars</span>
                                                </div>
                                            )}
                                            
                                            {/* Document Content - Wider padding, larger text in fullscreen */}
                                            <div className={`manus-document transition-all duration-300 ${isFullscreen ? 'p-12 sm:p-20 text-lg [&_h1]:text-5xl [&_h2]:text-3xl [&_h3]:text-2xl [&_h4]:text-xl [&_p]:text-lg [&_li]:text-lg' : 'p-10 sm:p-16'}`}>
                                                {/* Show streaming content if available, otherwise show editable content */}
                                                {isStreaming && streamingContent ? (
                                                    (() => {
                                                        const parsed = tryParseStreamingBlueprint(streamingContent);
                                                        if (parsed.success && parsed.markdown) {
                                                            // Successfully parsed - render as markdown with same style as final blueprint
                                                            return (
                                                                <div 
                                                                    className="prose prose-slate dark:prose-invert max-w-none prose-headings:font-semibold prose-h1:text-3xl prose-h1:mb-4 prose-h2:text-xl prose-h2:mt-8 prose-h2:mb-3 prose-h3:text-lg prose-h3:mt-6 prose-p:text-base prose-p:leading-relaxed prose-li:text-base"
                                                                    dangerouslySetInnerHTML={{ __html: renderMarkdown(parsed.markdown) }}
                                                                />
                                                            );
                                                        } else {
                                                            // Failed to parse - show raw JSON with nice formatting
                                                            return (
                                                                <div className="prose dark:prose-invert max-w-none">
                                                                    <div className="text-sm text-gray-500 dark:text-gray-400 mb-3 flex items-center gap-2">
                                                                        <span className="material-symbols-outlined text-base animate-pulse">hourglass_top</span>
                                                                        {isChinese(blueprint.title || '') ? '正在接收数据，等待完整内容...' : 'Receiving data, waiting for complete content...'}
                                                                    </div>
                                                                    <pre className="text-xs text-gray-600 dark:text-gray-400 font-mono whitespace-pre-wrap break-words bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg border border-gray-200 dark:border-gray-700">{streamingContent}</pre>
                                                                </div>
                                                            );
                                                        }
                                                    })()
                                                ) : (
                                                    <ContentEditableDiv
                                                        contentRef={contentRef}
                                                        initialHtml={renderMarkdown(markdown)}
                                                        onInput={handleContentChange}
                                                        isEditing={isEditing}
                                                        resetKey={contentResetKey}
                                                        isReadOnly={isReadOnly}
                                                    />
                                                )}
                                            </div>
                                            
                                            {/* Status indicator at bottom - hidden in read-only mode */}
                                            {!isReadOnly && hasChanges && !isStreaming && (
                                                <div className={`py-3 bg-amber-50 dark:bg-amber-900/20 border-t border-amber-200 dark:border-amber-800/40 flex items-center gap-2 ${isFullscreen ? 'px-12 sm:px-20' : 'px-10 sm:px-16'}`}>
                                                    <span className="material-symbols-outlined text-amber-500 text-sm">edit_note</span>
                                                    <span className="text-xs text-amber-600 dark:text-amber-400">You have unsaved changes - click Save in the top right to save</span>
                                                </div>
                                            )}
                                        </div>
                                    </div>
                                </div>
                                
                                {/* Right Dot Navigation - Manus Style */}
                                {outline.length > 0 && (
                                    <div className="hidden md:flex flex-col items-center justify-center w-8 py-8 bg-transparent absolute right-4 top-1/2 -translate-y-1/2 z-10">
                                        <div className="flex flex-col items-center gap-2">
                                            {outline.filter(function(item) { return item.level === 2; }).map(function(item, idx) {
                                                var actualIdx = outline.findIndex(function(o) { return o === item; });
                                                var isActive = actualIdx === activeOutlineIdx;
                                                return (
                                                    <div key={idx} className="group relative">
                                                        <button
                                                            onClick={function() { scrollToSection(actualIdx); }}
                                                            className={'w-2.5 h-2.5 rounded-full transition-all duration-200 ' + (
                                                                isActive 
                                                                    ? 'bg-primary scale-125 shadow-lg shadow-primary/30' 
                                                                    : 'bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 hover:scale-110'
                                                            )}
                                                            title={item.text}
                                                        />
                                                        {/* Tooltip on hover */}
                                                        <div className="absolute right-full mr-3 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity duration-200">
                                                            <div className="bg-gray-900 dark:bg-gray-700 text-white text-xs px-2 py-1 rounded whitespace-nowrap shadow-lg">
                                                                {item.text}
                                                            </div>
                                                        </div>
                                                    </div>
                                                );
                                            })}
                                        </div>
                                    </div>
                                )}
                            </div>
                            </div>
                        </div>
                    )}
                </div>
            );
        };

        // Generating Message - Shows progress in chat
        // Enhanced with multi-step tracking and slide preview
var GeneratingMessage = ({ phase, progress, detailLog, activityLogs, selectedPackages, completedSteps, currentStep, slidePreview }) => {
            // Package ID to display config mapping
            const packageConfig = {
                'slide': { label: 'Interactive Lesson', labelZh: '互动课件', icon: 'slideshow' },
                'slides': { label: 'Interactive Lesson', labelZh: '互动课件', icon: 'slideshow' },
                'worksheet': { label: 'Student Worksheet', labelZh: '学生练习', icon: 'assignment' },
                'quiz': { label: 'Assessment Quiz', labelZh: '测验', icon: 'quiz' },
                'lesson_plan': { label: 'Lesson Plan', labelZh: '教案', icon: 'description' },
                'exit_ticket': { label: 'Exit Ticket', labelZh: '出门检测', icon: 'receipt_long' },
                'rubric': { label: 'Grading Rubric', labelZh: '评分标准', icon: 'grading' },
                'vocabulary_cards': { label: 'Vocabulary Cards', labelZh: '词汇卡片', icon: 'style' },
                'group_activity': { label: 'Group Activity', labelZh: '小组活动', icon: 'groups' }
            };
            
            // Detect if phase is Chinese for localized labels
            var isZhPhase = phase && /[\u4e00-\u9fff]/.test(phase);
            
            // Build steps from selected packages or use default
            const defaultPackages = ['slide', 'worksheet', 'quiz', 'lesson_plan', 'exit_ticket'];
            const packagesToShow = (selectedPackages && selectedPackages.length > 0) ? selectedPackages : defaultPackages;
            
            const steps = packagesToShow.map(function(pkgId) {
                var config = packageConfig[pkgId] || { label: pkgId, labelZh: pkgId, icon: 'description' };
                return {
                    id: pkgId,
                    label: isZhPhase ? config.labelZh : config.label,
                    icon: config.icon
                };
            });
            
            // Use explicit step tracking if available (more reliable than pattern matching)
            var completedStepsSet = new Set(completedSteps || []);
            var currentStepId = currentStep || null;
            
            // Helper function to check if any pattern matches
            var matchesAny = function(patterns, text) {
                if (!patterns) return false;
                return patterns.some(function(p) { return text.includes(p); });
            };
            
            var getStepStatus = function(stepId) {
                // Normalize stepId for slide/slides
                var normalizedStepId = stepId === 'slides' ? 'slide' : stepId;
                
                // PRIORITY 1: Use explicit step tracking from SSE events (most reliable)
                // Check both original and normalized IDs
                if (completedStepsSet.has(stepId) || completedStepsSet.has(normalizedStepId)) return 'completed';
                if (currentStepId === stepId || currentStepId === normalizedStepId) return 'current';
                
                // PRIORITY 2: For parallel generation, check if this step is in the "in_progress" set
                // When multiple steps run in parallel, they're all "current"
                var isSlide = stepId === 'slide' || stepId === 'slides';
                var slideCompleted = completedStepsSet.has('slide') || completedStepsSet.has('slides');
                if (currentStepId === 'parallel' && !completedStepsSet.has(stepId) && !completedStepsSet.has(normalizedStepId) && !isSlide) {
                    // During parallel generation, all non-slide steps are "current"
                    return 'current';
                }
                
                // PRIORITY 3: Fall back to pattern matching for compatibility
                if (!phase) return 'pending';
                var phaseLower = phase.toLowerCase();
                
                // Patterns to detect COMPLETED steps
                var completedPatterns = {
                    'slide': ['interactive lesson completed', '✓ interactive lesson', 'slide_complete', 'slides_complete', '课件完成', 'slide complete', 'slides saved', 'interactive lesson complete', 'slides saved to'],
                    'slides': ['interactive lesson completed', '✓ interactive lesson', 'slide_complete', 'slides_complete', '课件完成', 'slide complete', 'slides saved', 'interactive lesson complete', 'slides saved to'],
                    'worksheet': ['worksheet completed', '✓ student worksheet', '✓ worksheet', 'worksheet_complete', '练习完成', 'worksheet saved'],
                    'quiz': ['quiz completed', '✓ assessment quiz', '✓ quiz', 'quiz_complete', '测验完成', 'quiz saved'],
                    'exit_ticket': ['exit ticket completed', '✓ exit ticket', 'exit_ticket_complete', '出门检测完成', 'exit ticket saved'],
                    'lesson_plan': ['lesson plan completed', '✓ lesson plan', 'lesson_plan_complete', '教案完成', 'lesson plan saved'],
                    'rubric': ['rubric completed', '✓ grading rubric', '✓ rubric', 'rubric_complete', '评分标准完成', 'rubric saved'],
                    'vocabulary_cards': ['vocabulary cards completed', '✓ vocabulary cards', '✓ vocabulary', 'vocabulary_cards_complete', '词汇卡片完成', 'vocabulary saved'],
                    'group_activity': ['group activity completed', '✓ group activity', 'group_activity_complete', '小组活动完成', 'group activity saved']
                };
                
                // Patterns to detect IN-PROGRESS steps
                var inProgressPatterns = {
                    'slide': ['generating interactive', 'interactive presentation', '生成课件', '互动课件', 'generating image', 'image 1/', 'image 2/', 'image 3/', 'image 4/', 'image 5/', 'background image', 'generating background', 'interactive for slide', 'background template', 'parallel image', 'preparing to generate'],
                    'slides': ['generating interactive', 'interactive presentation', '生成课件', '互动课件', 'generating image', 'image 1/', 'image 2/', 'image 3/', 'image 4/', 'image 5/', 'background image', 'generating background', 'interactive for slide', 'background template', 'parallel image', 'preparing to generate'],
                    'worksheet': ['generating student worksheet', 'generating worksheet', '生成练习'],
                    'quiz': ['generating assessment quiz', 'generating quiz', '生成测验'],
                    'exit_ticket': ['generating exit ticket', '生成出门检测'],
                    'lesson_plan': ['generating lesson plan', '生成教案'],
                    'rubric': ['generating grading rubric', 'generating rubric', '评分标准'],
                    'vocabulary_cards': ['generating vocabulary cards', 'generating vocabulary', '词汇卡片'],
                    'group_activity': ['generating group activity', 'generating group', '小组活动']
                };
                
                // Use the order from steps (which comes from selectedPackages)
                var stepOrder = steps.map(function(s) { return s.id; });
                var stepIdx = stepOrder.indexOf(stepId);
                
                // Check if this step is completed via pattern
                if (matchesAny(completedPatterns[stepId], phaseLower)) return 'completed';
                
                // Check if this step is in progress
                var isThisStepInProgress = matchesAny(inProgressPatterns[stepId], phaseLower);
                
                // Check if any LATER step is in progress or completed
                var laterStepActive = false;
                for (var i = stepIdx + 1; i < stepOrder.length; i++) {
                    var laterStep = stepOrder[i];
                    if (matchesAny(completedPatterns[laterStep], phaseLower) ||
                        matchesAny(inProgressPatterns[laterStep], phaseLower)) {
                        laterStepActive = true;
                        break;
                    }
                }
                
                if (laterStepActive) return 'completed';
                if (isThisStepInProgress) return 'current';
                
                // Check if parallel generation message indicates multiple steps running
                if (phaseLower.includes('parallel generation') || phaseLower.includes('materials in parallel')) {
                    // During parallel phase, mark non-slide steps as current if slide is done
                    if (stepId !== 'slide' && completedStepsSet.has('slide')) {
                        return 'current';
                    }
                }
                
                return 'pending';
            };
            
            // Calculate estimated time remaining based on progress
            // Full generation takes ~7-8 minutes based on logs
            var getTimeEstimate = function() {
                var progressNum = progress || 5;
                if (progressNum >= 95) return 'Almost done...';
                if (progressNum >= 90) return '~30 seconds remaining';
                if (progressNum >= 85) return '~1 minute remaining';
                if (progressNum >= 75) return '~1-2 minutes remaining';
                if (progressNum >= 60) return '~2-3 minutes remaining';
                if (progressNum >= 40) return '~3-4 minutes remaining';
                if (progressNum >= 20) return '~5-6 minutes remaining';
                if (progressNum >= 10) return '~6-7 minutes remaining';
                return '~7-8 minutes remaining';
            };
            
            return (
                <div className="flex flex-col gap-2 items-start w-full">
                    <div className="flex gap-2 items-center mb-1">
                        <div className="size-6 rounded-full bg-primary/10 flex items-center justify-center">
                            <span className="material-symbols-outlined text-primary text-sm animate-spin">sync</span>
                        </div>
                        <span className="text-xs font-bold text-primary">Tess</span>
                    </div>
                    <div className="w-full max-w-2xl bg-white dark:bg-slate-800 rounded-2xl shadow-lg border border-slate-200 dark:border-slate-700 p-6">
                        <div className="flex items-center gap-3 mb-5">
                            <span className="material-symbols-outlined text-primary text-2xl animate-pulse">auto_awesome</span>
                            <span className="font-bold text-lg text-slate-900 dark:text-white">Creating Teaching Materials...</span>
                        </div>
                        <div className="space-y-3">
                            {steps.map(function(step) {
                                var status = getStepStatus(step.id);
                                return (
                                    <div key={step.id} className="flex items-center gap-4">
                                        <div className={'w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 transition-all ' + (
                                            status === 'completed' ? 'bg-green-100 dark:bg-green-900/30 text-green-600' :
                                            status === 'current' ? 'bg-primary/10 text-primary' :
                                            'bg-slate-100 dark:bg-slate-700 text-slate-400'
                                        )}>
                                            {status === 'completed' ? (
                                                <span className="material-symbols-outlined text-xl icon-fill">check_circle</span>
                                            ) : status === 'current' ? (
                                                <span className="material-symbols-outlined text-xl animate-spin">progress_activity</span>
                                            ) : (
                                                <span className="material-symbols-outlined text-xl">{step.icon}</span>
                                            )}
                                        </div>
                                        <span className={'text-sm font-medium ' + (
                                            status === 'completed' ? 'text-green-600 dark:text-green-400' :
                                            status === 'current' ? 'text-slate-900 dark:text-white' :
                                            'text-slate-400'
                                        )}>
                                            {step.label}
                                            {status === 'completed' && ' ✓'}
                                        </span>
                                    </div>
                                );
                            })}
                        </div>
                        
                        {/* Slide Preview - Shows real-time slide structure */}
                        {slidePreview && slidePreview.slides_preview && slidePreview.slides_preview.length > 0 && (
                            <div className="mt-5 pt-4 border-t border-slate-100 dark:border-slate-700">
                                <div className="flex items-center gap-2 mb-3">
                                    <span className="material-symbols-outlined text-primary text-sm">slideshow</span>
                                    <span className="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
                                        Slide Preview ({slidePreview.slide_count} slides)
                                    </span>
                                    {/* Generating hint */}
                                    <span className="text-[10px] text-amber-500 dark:text-amber-400 ml-auto animate-pulse">
                                        生成完成后可预览
                                    </span>
                                </div>
                                <div className="grid grid-cols-6 gap-2">
                                    {slidePreview.slides_preview.map(function(slide, idx) {
                                        var bgColor = slidePreview.theme && slidePreview.theme.palette && slidePreview.theme.palette.primary 
                                            ? slidePreview.theme.palette.primary 
                                            : '#6366f1';
                                        return (
                                            React.createElement('div', {
                                                key: idx,
                                                className: 'slide-preview-item aspect-video rounded-lg border border-slate-200 dark:border-slate-600 overflow-hidden relative group transition-all hover:scale-105 hover:shadow-lg cursor-pointer',
                                                style: { background: 'linear-gradient(135deg, ' + bgColor + '22, ' + bgColor + '44)' },
                                                onClick: function(e) {
                                                    // Visual feedback: pulse animation on click
                                                    console.log('Slide preview clicked:', slide.index);
                                                    var target = e.currentTarget;
                                                    target.classList.add('ring-2', 'ring-primary', 'ring-offset-1', 'scale-110');
                                                    setTimeout(function() {
                                                        target.classList.remove('ring-2', 'ring-primary', 'ring-offset-1', 'scale-110');
                                                    }, 300);
                                                },
                                                title: (slide.title || ('Slide ' + slide.index)) + ' - 正在生成中...'
                                            },
                                                React.createElement('div', { className: 'absolute inset-0 flex flex-col items-center justify-center p-1' },
                                                    React.createElement('span', { className: 'text-xs font-bold text-slate-700 dark:text-slate-300 truncate w-full text-center' }, 
                                                        slide.index
                                                    ),
                                                    slide.has_interactive && React.createElement('span', { 
                                                        className: 'material-symbols-outlined text-xs text-amber-500 mt-0.5',
                                                        title: 'Interactive'
                                                    }, 'touch_app'),
                                                    slide.has_image && React.createElement('span', { 
                                                        className: 'material-symbols-outlined text-xs text-blue-500 mt-0.5',
                                                        title: 'Has Image'
                                                    }, 'image')
                                                ),
                                                React.createElement('div', { 
                                                    className: 'absolute bottom-0 left-0 right-0 bg-black/50 px-1 py-0.5 opacity-0 group-hover:opacity-100 transition-opacity'
                                                },
                                                    React.createElement('p', { className: 'text-[8px] text-white truncate text-center' }, 
                                                        slide.title || ('Slide ' + slide.index)
                                                    )
                                                )
                                            )
                                        );
                                    })}
                                </div>
                            </div>
                        )}
                        
                        {/* Activity Log - Shows detailed progress */}
                        {(detailLog || (activityLogs && activityLogs.length > 0)) && (
                            <div className="mt-5 pt-4 border-t border-slate-100 dark:border-slate-700">
                                <div className="flex items-center gap-2 mb-2">
                                    <span className="material-symbols-outlined text-slate-400 text-sm">terminal</span>
                                    <span className="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">Progress Log</span>
                                </div>
                                <div className="bg-slate-900 dark:bg-slate-950 rounded-lg p-4 max-h-48 overflow-y-auto font-mono">
                                    {activityLogs && activityLogs.slice(-10).map(function(log, idx) {
                                        return (
                                            React.createElement('p', { key: idx, className: 'text-xs text-slate-400 leading-relaxed py-0.5' },
                                                React.createElement('span', { className: 'text-slate-600 mr-2' }, '▸'),
                                                log
                                            )
                                        );
                                    })}
                                    {detailLog && (
                                        React.createElement('p', { className: 'text-xs text-emerald-400 leading-relaxed py-0.5 animate-pulse' },
                                            React.createElement('span', { className: 'text-emerald-600 mr-2' }, '→'),
                                            detailLog
                                        )
                                    )}
                                </div>
                            </div>
                        )}
                        
                        <div className="mt-5 pt-4 border-t border-slate-100 dark:border-slate-700">
                            <div className="w-full h-3 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden">
                                <div className="h-full bg-gradient-to-r from-primary to-emerald-500 rounded-full transition-all duration-500" style={{ width: (progress || 10) + '%' }}></div>
                            </div>
                            <p className="text-sm text-slate-500 mt-2 text-center font-medium">{getTimeEstimate()}</p>
                        </div>
                    </div>
                </div>
            );
        };
        
        // Package Message - Shows completed package in chat
var PackageMessage = ({ pkg, isHistoryVersion, historyVersion, historyTimestamp }) => {
            var files = (pkg && pkg.files) || [];
            var slidesFile = files.find(function(f) { return f.type === 'slide'; });
            var downloadAllUrl = slidesFile && slidesFile.download_url;
            
            // Detect if package title contains Chinese characters to determine language
            var isChinese = pkg && pkg.title && /[\u4e00-\u9fff]/.test(pkg.title);
            
            // State for preview modal
            var [previewFile, setPreviewFile] = React.useState(null);
            var [showDownloadMenu, setShowDownloadMenu] = React.useState(false);
            var [showDownloadAllMenu, setShowDownloadAllMenu] = React.useState(false);
            var [isPreviewFullscreen, setIsPreviewFullscreen] = React.useState(false);
            var [currentSlideIndex, setCurrentSlideIndex] = React.useState(0);
            var [slideNotes, setSlideNotes] = React.useState({}); // { slideIndex: noteText }
            var [isDragEditMode, setIsDragEditMode] = React.useState(false);
            var [slideLayouts, setSlideLayouts] = React.useState({}); // { slideIndex: { elementId: {x, y, width, height} } }
            var [currentPreviewGenId, setCurrentPreviewGenId] = React.useState(null);
            var [pendingSlideEdits, setPendingSlideEdits] = React.useState({}); // { slideIndex: { field: value } }
            var [isSavingSlides, setIsSavingSlides] = React.useState(false);
            var [slidesSaveStatus, setSlidesSaveStatus] = React.useState(null); // 'success' | 'error' | null
            
            // Extract generation ID from preview URL
            var getGenerationIdFromPreview = function() {
                if (!previewFile || !previewFile.preview_url) return null;
                // Support both URL formats:
                // - /output/user/genId/v1/... (with user_id)
                // - /view/genId/v1/... (anonymous, no user_id)
                var matchOutput = previewFile.preview_url.match(/\/output\/[^\/]+\/([^\/]+)/);
                if (matchOutput) return matchOutput[1];
                var matchView = previewFile.preview_url.match(/\/view\/([^\/]+)/);
                return matchView ? matchView[1] : null;
            };
            
            // Load notes and layouts from server when preview file changes
            React.useEffect(function() {
                if (previewFile && previewFile.type === 'slide') {
                    var genId = getGenerationIdFromPreview();
                    if (genId && genId !== currentPreviewGenId) {
                        setCurrentPreviewGenId(genId);
                        
                        // Load from server (slide-metadata endpoint)
                        fetch(API_BASE + '/api/v1/slide-metadata/' + genId, {
                            headers: getAuthHeaders()
                        })
                        .then(function(res) { return res.ok ? res.json() : { slide_notes: {}, slide_layouts: {} }; })
                        .then(function(data) {
                            var notes = data.slide_notes || {};
                            var layouts = data.slide_layouts || {};
                            
                            // If no notes found, try loading from slides_data.json
                            if (Object.keys(notes).length === 0) {
                                console.log('📝 No saved notes found, loading from slides_data.json...');
                                fetch(API_BASE + '/api/v1/generations/' + genId + '/slides-data', {
                                    headers: getAuthHeaders()
                                })
                                .then(function(res) { return res.ok ? res.json() : null; })
                                .then(function(slidesData) {
                                    if (slidesData && slidesData.slides) {
                                        var loadedNotes = {};
                                        slidesData.slides.forEach(function(slide) {
                                            var idx = (slide.slide_index || 1) - 1; // Convert to 0-based
                                            if (slide.speaker_notes) {
                                                loadedNotes[idx] = slide.speaker_notes;
                                            }
                                        });
                                        if (Object.keys(loadedNotes).length > 0) {
                                            console.log('✅ Loaded ' + Object.keys(loadedNotes).length + ' speaker notes from slides_data.json');
                                            setSlideNotes(loadedNotes);
                                        } else {
                                            setSlideNotes({});
                                        }
                                    } else {
                                        setSlideNotes({});
                                    }
                                })
                                .catch(function() {
                                    setSlideNotes({});
                                });
                            } else {
                                setSlideNotes(notes);
                            }
                            setSlideLayouts(layouts);
                        })
                        .catch(function() {
                            setSlideNotes({});
                            setSlideLayouts({});
                        });
                    }
                }
            }, [previewFile]);
            
            // Debounced save to server
            var saveTimeoutRef = React.useRef(null);
            var saveSlideMetadata = function(notes, layouts) {
                var genId = getGenerationIdFromPreview();
                if (!genId) return;
                
                // Clear previous timeout
                if (saveTimeoutRef.current) {
                    clearTimeout(saveTimeoutRef.current);
                }
                
                // Debounce: save after 1 second of no changes
                saveTimeoutRef.current = setTimeout(function() {
                    fetch(API_BASE + '/api/v1/save-slide-metadata', {
                        method: 'POST',
                        headers: Object.assign({ 'Content-Type': 'application/json' }, getAuthHeaders()),
                        body: JSON.stringify({
                            generation_id: genId,
                            slide_notes: notes,
                            slide_layouts: layouts
                        })
                    }).catch(function(e) {
                        console.error('Failed to save slide metadata:', e);
                    });
                }, 1000);
            };
            
            // Save notes when they change
            React.useEffect(function() {
                if (currentPreviewGenId && Object.keys(slideNotes).length > 0) {
                    saveSlideMetadata(slideNotes, slideLayouts);
                }
            }, [slideNotes]);
            
            // Save layouts when they change
            React.useEffect(function() {
                if (currentPreviewGenId && Object.keys(slideLayouts).length > 0) {
                    saveSlideMetadata(slideNotes, slideLayouts);
                }
            }, [slideLayouts]);
            
            // State for image editing
            var [editImageModal, setEditImageModal] = React.useState(null); // { slideIndex, currentPrompt }
            var [newImagePrompt, setNewImagePrompt] = React.useState('');
            var [isGeneratingImage, setIsGeneratingImage] = React.useState(false);
            
            // State for document editing (worksheet, quiz, exit_ticket, lesson_plan)
            var [isDocEditMode, setIsDocEditMode] = React.useState(false);
            var [isSavingDoc, setIsSavingDoc] = React.useState(false);
            var [docHasChanges, setDocHasChanges] = React.useState(false);
            var docIframeRef = React.useRef(null);
            var originalDocContent = React.useRef('');
            
            // Editable document types (note: lesson_plan is stored as 'document' type)
            var editableTypes = ['worksheet', 'quiz', 'exit_ticket', 'lesson_plan', 'document'];
            
            // Enable/disable edit mode in iframe
            var toggleIframeEditMode = function(enable) {
                var iframe = docIframeRef.current;
                if (!iframe || !iframe.contentDocument) return;
                
                try {
                    var doc = iframe.contentDocument;
                    if (enable) {
                        // Store original content
                        originalDocContent.current = doc.body.innerHTML;
                        // Enable design mode
                        doc.designMode = 'on';
                        // Add edit mode styles
                        var style = doc.createElement('style');
                        style.id = 'edit-mode-styles';
                        style.textContent = `
                            body { 
                                cursor: text !important; 
                                outline: none !important;
                            }
                            body:focus { outline: none !important; }
                            *:hover { 
                                outline: 2px dashed rgba(59, 130, 246, 0.5) !important; 
                                outline-offset: 2px !important;
                            }
                            *:focus { 
                                outline: 2px solid rgba(59, 130, 246, 0.8) !important; 
                                outline-offset: 2px !important;
                            }
                        `;
                        doc.head.appendChild(style);
                        // Listen for changes
                        doc.body.addEventListener('input', function() {
                            setDocHasChanges(doc.body.innerHTML !== originalDocContent.current);
                        });
                    } else {
                        // Disable design mode
                        doc.designMode = 'off';
                        // Remove edit mode styles
                        var editStyle = doc.getElementById('edit-mode-styles');
                        if (editStyle) editStyle.remove();
                    }
                } catch (e) {
                    console.error('Failed to toggle edit mode:', e);
                }
            };
            
            // Save slide content edits
            var saveSlideEdits = function() {
                var genId = getGenerationIdFromPreview();
                if (!genId || Object.keys(pendingSlideEdits).length === 0) return;
                
                setIsSavingSlides(true);
                setSlidesSaveStatus(null);
                
                // Collect all pending edits
                var editsArray = [];
                Object.keys(pendingSlideEdits).forEach(function(slideIdx) {
                    var fields = pendingSlideEdits[slideIdx];
                    Object.keys(fields).forEach(function(field) {
                        editsArray.push({
                            slide_index: parseInt(slideIdx),
                            field: field,
                            value: fields[field]
                        });
                    });
                });
                
                // Save all edits sequentially
                var saveNext = function(index) {
                    if (index >= editsArray.length) {
                        // All saves complete
                        setPendingSlideEdits({});
                        setIsSavingSlides(false);
                        setSlidesSaveStatus('success');
                        console.log('✅ All slide edits saved');
                        
                        // Force refresh the current slide iframe to show changes
                        setTimeout(function() {
                            var slideIframe = document.querySelector('iframe[title*="Slide ' + (currentSlideIndex + 1) + '"]');
                            if (slideIframe && slideIframe.src) {
                                var baseUrl = slideIframe.src.split('?')[0];
                                slideIframe.src = baseUrl + '?t=' + Date.now();
                            }
                            setSlidesSaveStatus(null);
                        }, 2000);
                        return;
                    }
                    
                    var edit = editsArray[index];
                    fetch(API_BASE + '/api/v1/save-slide-content', {
                        method: 'POST',
                        headers: Object.assign({ 'Content-Type': 'application/json' }, getAuthHeaders()),
                        credentials: 'include',
                        body: JSON.stringify({
                            generation_id: genId,
                            slide_index: edit.slide_index,
                            field: edit.field,
                            value: edit.value
                        })
                    })
                    .then(function(res) {
                        if (!res.ok) throw new Error('Save failed: ' + res.status);
                        return res.json();
                    })
                    .then(function(data) {
                        console.log('Saved slide ' + edit.slide_index + ' ' + edit.field);
                        saveNext(index + 1);
                    })
                    .catch(function(err) {
                        console.error('Failed to save slide edit:', err);
                        setIsSavingSlides(false);
                        setSlidesSaveStatus('error');
                    });
                };
                
                saveNext(0);
            };
            
            // Save edited document directly from iframe
            var saveDocContent = function() {
                var iframe = docIframeRef.current;
                if (!iframe || !iframe.contentDocument || !previewFile) return;
                
                setIsSavingDoc(true);
                var genId = getGenerationIdFromPreview();
                if (!genId) {
                    setIsSavingDoc(false);
                    return;
                }
                
                var newContent = iframe.contentDocument.body.innerHTML;
                
                fetch(API_BASE + '/api/v1/save-document', {
                    method: 'POST',
                    headers: Object.assign({ 'Content-Type': 'application/json' }, getAuthHeaders()),
                    body: JSON.stringify({
                        generation_id: genId,
                        doc_type: previewFile.type,
                        content: newContent
                    })
                })
                .then(function(res) { return res.json(); })
                .then(function(data) {
                    if (data.success) {
                        originalDocContent.current = newContent;
                        setDocHasChanges(false);
                        // Update the preview URL with cache-busting timestamp
                        // This ensures the saved content is loaded when reopening
                        if (previewFile.preview_url) {
                            var baseUrl = previewFile.preview_url.split('?')[0];
                            previewFile.preview_url = baseUrl + '?t=' + Date.now();
                        }
                    }
                    setIsSavingDoc(false);
                })
                .catch(function(e) {
                    console.error('Failed to save document:', e);
                    setIsSavingDoc(false);
                });
            };
            
            // Reset edit mode when closing modal or changing file
            React.useEffect(function() {
                if (!previewFile) {
                    setIsDocEditMode(false);
                    setDocHasChanges(false);
                }
            }, [previewFile]);
            
            // Apply edit mode when toggled
            React.useEffect(function() {
                if (previewFile && editableTypes.includes(previewFile.type)) {
                    // Small delay to ensure iframe is loaded
                    var timer = setTimeout(function() {
                        toggleIframeEditMode(isDocEditMode);
                    }, 500);
                    return function() { clearTimeout(timer); };
                }
            }, [isDocEditMode, previewFile]);
            
            // Listen for messages from slide iframes (edit image, generate image)
            React.useEffect(function() {
                function handleMessage(event) {
                    var data = event.data;
                    if (!data || !data.type) return;
                    
                    if (data.type === 'EDIT_IMAGE') {
                        // Don't open edit modal in drag mode
                        if (isDragEditMode) return;
                        
                        // Get the image prompt from the slide data if available
                        var slideIndex = data.slideIndex;
                        var prompt = data.prompt || 'A friendly educational illustration';
                        setEditImageModal({ slideIndex: slideIndex, currentPrompt: prompt });
                        setNewImagePrompt(prompt);
                    } else if (data.type === 'SLIDE_EDIT') {
                        // Store edit in pending state (will be saved when user clicks Save)
                        var slideIdx = data.slideIndex;
                        var field = data.field;
                        var value = data.value;
                        
                        if (field && value !== undefined) {
                            setPendingSlideEdits(function(prev) {
                                var updated = Object.assign({}, prev);
                                if (!updated[slideIdx]) updated[slideIdx] = {};
                                updated[slideIdx][field] = value;
                                return updated;
                            });
                            setSlidesSaveStatus(null); // Clear any previous status
                            console.log('Slide edit pending:', slideIdx, field, value.substring(0, 50) + '...');
                        }
                    } else if (data.type === 'GENERATE_IMAGE') {
                        // Auto-generate with existing prompt
                        var slideIdx = data.slideIndex;
                        alert('Image generation for slide ' + (slideIdx + 1) + ' will be added soon!');
                    } else if (data.type === 'ELEMENT_MOVED') {
                        // Save element position when dragged
                        var slideIdx = data.slideIndex;
                        var elementId = data.elementId;
                        var position = data.position;
                        setSlideLayouts(function(prev) {
                            var updated = Object.assign({}, prev);
                            if (!updated[slideIdx]) updated[slideIdx] = {};
                            updated[slideIdx][elementId] = position;
                            return updated;
                        });
                    }
                }
                
                window.addEventListener('message', handleMessage);
                return function() {
                    window.removeEventListener('message', handleMessage);
                };
            }, [isDragEditMode, previewFile]);
            
            // Extract folder info from any file's preview_url (including version if present)
            // Returns { folderName, isViewFormat } where:
            // - folderName: user_id/gen_id/version (for /output/) or gen_id/version (for /view/)
            // - isViewFormat: true if URL uses /view/ format (no user_id)
            var getFolderInfo = function() {
                for (var i = 0; i < files.length; i++) {
                    var url = files[i].preview_url || files[i].download_url;
                    if (url) {
                        // Try /output/ format first: /output/USER_ID/GENERATION_ID/VERSION/file.html
                        var matchOutput = url.match(/\/output\/([^\/]+\/[^\/]+\/v\d+)\//);
                        if (matchOutput) return { folderName: matchOutput[1], isViewFormat: false };
                        // Try /output/ without version
                        var matchOutputNoVer = url.match(/\/output\/([^\/]+\/[^\/]+)\//);
                        if (matchOutputNoVer) return { folderName: matchOutputNoVer[1], isViewFormat: false };
                        
                        // Try /view/ format: /view/GENERATION_ID/VERSION/file.html
                        var matchView = url.match(/\/view\/([^\/]+\/v\d+)\//);
                        if (matchView) return { folderName: matchView[1], isViewFormat: true };
                        // Try /view/ without version
                        var matchViewNoVer = url.match(/\/view\/([^\/]+)\//);
                        if (matchViewNoVer) return { folderName: matchViewNoVer[1], isViewFormat: true };
                    }
                }
                return null;
            };
            var folderInfo = getFolderInfo();
            
            // Download in specified format (pdf, docx, md)
            var handleDownload = function(file, format) {
                setShowDownloadMenu(false);
                if (!folderInfo) {
                    alert('Download not available');
                    return;
                }
                
                // Map file type to API endpoint type
                var typeMap = {
                    'worksheet': 'worksheet',
                    'quiz': 'quiz',
                    'exit_ticket': 'exit_ticket',
                    'document': 'lesson_plan',
                    'slide': 'presentation',
                    'group_activity': 'group_activity',
                    'rubric': 'rubric',
                    'vocabulary_cards': 'vocabulary_cards'
                };
                var apiType = typeMap[file.type];
                
                if (apiType) {
                    // Use view-download API for /view/ format URLs, regular download for /output/
                    var apiPath = folderInfo.isViewFormat ? '/api/v1/view-download/' : '/api/v1/download/';
                    var downloadUrl = apiPath + folderInfo.folderName + '/' + apiType + '/' + format;
                    window.open(downloadUrl, '_blank');
                }
            };
            
            // Download all materials in a specific format
            var handleDownloadAll = function(format) {
                setShowDownloadAllMenu(false);
                if (!folderInfo) {
                    alert('Download not available');
                    return;
                }
                // Use view-download-all API for /view/ format URLs, regular download-all for /output/
                var apiPath = folderInfo.isViewFormat ? '/api/v1/view-download-all/' : '/api/v1/download-all/';
                var url = apiPath + folderInfo.folderName + '/' + format;
                window.open(url, '_blank');
            };
            
            // Close menus when clicking outside
            React.useEffect(function() {
                var handleClickOutside = function(e) {
                    if (!e.target.closest('.download-dropdown')) {
                        setShowDownloadMenu(false);
                    }
                    if (!e.target.closest('.download-all-dropdown')) {
                        setShowDownloadAllMenu(false);
                    }
                };
                document.addEventListener('click', handleClickOutside);
                return function() {
                    document.removeEventListener('click', handleClickOutside);
                };
            }, []);
            
            // Handle browser back button to close preview modal
            React.useEffect(function() {
                if (previewFile) {
                    // Push a state when opening preview
                    window.history.pushState({ preview: true }, '');
                    
                    var handlePopState = function() {
                        setPreviewFile(null);
                        setIsPreviewFullscreen(false);
                        setCurrentSlideIndex(0);
                    };
                    
                    window.addEventListener('popstate', handlePopState);
                    return function() {
                        window.removeEventListener('popstate', handlePopState);
                    };
                }
            }, [previewFile]);
            
            // Keyboard navigation for fullscreen slide preview
            React.useEffect(function() {
                if (isPreviewFullscreen && previewFile && previewFile.type === 'slide' && previewFile.slide_urls) {
                    var handleKeyDown = function(e) {
                        var totalSlides = previewFile.slide_urls.length;
                        if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
                            setCurrentSlideIndex(function(prev) { return Math.min(prev + 1, totalSlides - 1); });
                        } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
                            setCurrentSlideIndex(function(prev) { return Math.max(prev - 1, 0); });
                        } else if (e.key === 'Home') {
                            setCurrentSlideIndex(0);
                        } else if (e.key === 'End') {
                            setCurrentSlideIndex(totalSlides - 1);
                        }
                    };
                    window.addEventListener('keydown', handleKeyDown);
                    return function() {
                        window.removeEventListener('keydown', handleKeyDown);
                    };
                }
            }, [isPreviewFullscreen, previewFile]);
            
            // Apply saved layout when slide changes or edit mode is toggled
            React.useEffect(function() {
                if (isPreviewFullscreen && previewFile && previewFile.type === 'slide' && isDragEditMode) {
                    // Small delay to let iframe load
                    var timer = setTimeout(function() {
                        var layout = slideLayouts[currentSlideIndex] || {};
                        var iframes = document.querySelectorAll('iframe');
                        iframes.forEach(function(iframe) {
                            try {
                                iframe.contentWindow.postMessage({
                                    type: 'APPLY_LAYOUT',
                                    layout: layout
                                }, '*');
                            } catch(e) {}
                        });
                    }, 300);
                    return function() { clearTimeout(timer); };
                }
            }, [currentSlideIndex, isDragEditMode, slideLayouts, isPreviewFullscreen, previewFile]);
            
            // Reset edit mode when closing preview or exiting fullscreen
            React.useEffect(function() {
                if (!isPreviewFullscreen || !previewFile) {
                    setIsDragEditMode(false);
                }
            }, [isPreviewFullscreen, previewFile]);
            
            // Download format options
            var downloadFormats = [
                { id: 'pdf', label: 'PDF', icon: 'picture_as_pdf', color: 'text-red-500' },
                { id: 'docx', label: 'Word (DOCX)', icon: 'description', color: 'text-blue-500' },
                { id: 'md', label: 'Markdown', icon: 'code', color: 'text-gray-600' }
            ];
            
            // Material type configurations with labels (English and Chinese)
            var typeConfig = {
                slide: { 
                    icon: 'slideshow', 
                    label: 'Interactive Slides',
                    labelZh: '互动课件',
                    labelShort: 'INTERACTIVE SLIDES',
                    labelShortZh: '互动课件',
                    descZh: '包含互动元素的课程幻灯片',
                    bg: 'bg-amber-50 dark:bg-amber-900/20', 
                    color: 'text-amber-600 dark:text-amber-400',
                    hoverBorder: 'hover:border-amber-300 dark:hover:border-amber-700',
                    canDownload: false
                },
                worksheet: { 
                    icon: 'assignment', 
                    label: 'Student Worksheet',
                    labelZh: '学生练习',
                    labelShort: 'STUDENT WORKSHEET',
                    labelShortZh: '学生练习',
                    descZh: '配套课堂练习题',
                    bg: 'bg-blue-50 dark:bg-blue-900/20', 
                    color: 'text-blue-600 dark:text-blue-400',
                    hoverBorder: 'hover:border-blue-300 dark:hover:border-blue-700',
                    canDownload: true
                },
                quiz: { 
                    icon: 'quiz', 
                    label: 'Assessment Quiz',
                    labelZh: '课堂测验',
                    labelShort: 'ASSESSMENT QUIZ',
                    labelShortZh: '课堂测验',
                    descZh: '课堂知识检测',
                    bg: 'bg-emerald-50 dark:bg-emerald-900/20', 
                    color: 'text-emerald-600 dark:text-emerald-400',
                    hoverBorder: 'hover:border-emerald-300 dark:hover:border-emerald-700',
                    canDownload: true
                },
                exit_ticket: { 
                    icon: 'receipt_long', 
                    label: 'Exit Ticket',
                    labelZh: '出门检测',
                    labelShort: 'EXIT TICKET',
                    labelShortZh: '出门检测',
                    descZh: '课程结束快速评估',
                    bg: 'bg-teal-50 dark:bg-teal-900/20', 
                    color: 'text-teal-600 dark:text-teal-400',
                    hoverBorder: 'hover:border-teal-300 dark:hover:border-teal-700',
                    canDownload: true
                },
                lesson_plan: { 
                    icon: 'description', 
                    label: 'Lesson Plan',
                    labelZh: '教案',
                    labelShort: 'LESSON PLAN',
                    labelShortZh: '教案',
                    descZh: '包含详细教学步骤的综合教案',
                    bg: 'bg-rose-50 dark:bg-rose-900/20', 
                    color: 'text-rose-600 dark:text-rose-400',
                    hoverBorder: 'hover:border-rose-300 dark:hover:border-rose-700',
                    canDownload: true
                },
                document: { 
                    icon: 'description', 
                    label: 'Lesson Plan',
                    labelZh: '教案',
                    labelShort: 'LESSON PLAN',
                    labelShortZh: '教案',
                    descZh: '包含详细教学步骤的综合教案',
                    bg: 'bg-rose-50 dark:bg-rose-900/20', 
                    color: 'text-rose-600 dark:text-rose-400',
                    hoverBorder: 'hover:border-rose-300 dark:hover:border-rose-700',
                    canDownload: true
                },
                rubric: { 
                    icon: 'grading', 
                    label: 'Grading Rubric',
                    labelZh: '评分标准',
                    labelShort: 'GRADING RUBRIC',
                    labelShortZh: '评分标准',
                    descZh: '作业评分参考标准',
                    bg: 'bg-purple-50 dark:bg-purple-900/20', 
                    color: 'text-purple-600 dark:text-purple-400',
                    hoverBorder: 'hover:border-purple-300 dark:hover:border-purple-700',
                    canDownload: true
                },
                vocabulary_cards: { 
                    icon: 'style', 
                    label: 'Vocabulary Cards',
                    labelZh: '词汇卡片',
                    labelShort: 'VOCABULARY CARDS',
                    labelShortZh: '词汇卡片',
                    descZh: '核心词汇学习卡片',
                    bg: 'bg-indigo-50 dark:bg-indigo-900/20', 
                    color: 'text-indigo-600 dark:text-indigo-400',
                    hoverBorder: 'hover:border-indigo-300 dark:hover:border-indigo-700',
                    canDownload: true
                },
                group_activity: { 
                    icon: 'groups', 
                    label: 'Group Activity',
                    labelZh: '小组活动',
                    labelShort: 'GROUP ACTIVITY',
                    labelShortZh: '小组活动',
                    descZh: '小组协作活动指南',
                    bg: 'bg-orange-50 dark:bg-orange-900/20', 
                    color: 'text-orange-600 dark:text-orange-400',
                    hoverBorder: 'hover:border-orange-300 dark:hover:border-orange-700',
                    canDownload: true
                }
            };
            
            return (
                <div className="flex flex-col gap-2 items-start w-full">
                    <div className="flex gap-2 items-center mb-1">
                        <div className={'size-6 rounded-full flex items-center justify-center ' + (isHistoryVersion ? 'bg-amber-100' : 'bg-green-100')}>
                            <span className={'material-symbols-outlined text-sm ' + (isHistoryVersion ? 'text-amber-600' : 'text-green-600')}>{isHistoryVersion ? 'history' : 'check_circle'}</span>
                        </div>
                        <span className="text-xs font-bold text-primary">Tess</span>
                        {isHistoryVersion && (
                            <span className="text-xs text-amber-600 dark:text-amber-400 font-semibold">v{historyVersion}</span>
                        )}
                    </div>
                    <div className={'w-full max-w-xl rounded-xl shadow-sm overflow-hidden ' + (isHistoryVersion ? 'bg-amber-50/50 dark:bg-amber-900/10 border-2 border-amber-200 dark:border-amber-800/50' : 'bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700')}>
                        {/* Header */}
                        <div className={'p-4 border-b ' + (isHistoryVersion ? 'bg-gradient-to-r from-amber-50 to-orange-50 dark:from-amber-900/20 dark:to-orange-900/20 border-amber-200 dark:border-amber-800/50' : 'bg-gradient-to-r from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 border-slate-200 dark:border-slate-700')}>
                            <div className="flex items-center gap-2 mb-2">
                                <span className={'material-symbols-outlined icon-fill ' + (isHistoryVersion ? 'text-amber-600' : 'text-green-600')}>{isHistoryVersion ? 'history' : 'check_circle'}</span>
                                <span className={'text-xs font-bold uppercase tracking-wide ' + (isHistoryVersion ? 'text-amber-700 dark:text-amber-300' : 'text-green-700 dark:text-green-300')}>
                                    {isHistoryVersion 
                                        ? (isChinese ? '历史版本' : 'Previous Version') 
                                        : (isChinese ? '材料已就绪' : 'Materials Ready')}
                                </span>
                                {historyTimestamp && (
                                    <span className="text-xs text-slate-400 ml-auto">{historyTimestamp}</span>
                                )}
                            </div>
                            <h3 className="font-bold text-slate-900 dark:text-white">{(pkg && pkg.title) || (isChinese ? '教学资源包' : 'Teaching Package')}</h3>
                            <p className="text-xs text-slate-500 dark:text-slate-400 mt-1">
                                {isChinese ? ('已生成 ' + files.length + ' 份材料') : (files.length + ' materials generated')}
                            </p>
                        </div>
                        
                        {/* Files List - Single clickable blocks */}
                        <div className="p-3 space-y-2">
                            {files.map(function(file, i) {
                                var config = typeConfig[file.type] || typeConfig.document;
                                var hasPreview = !!file.preview_url;
                                var isSlide = file.type === 'slide';
                                
                                return (
                                    <div 
                                        key={i} 
                                        onClick={function() { 
                                            if (hasPreview) {
                                                setPreviewFile(file);
                                                // Auto-fullscreen for slides
                                                if (file.type === 'slide') {
                                                    setIsPreviewFullscreen(true);
                                                }
                                            }
                                        }}
                                        className={'group rounded-xl p-4 border-2 border-transparent transition-all cursor-pointer ' + config.bg + ' ' + config.hoverBorder + (hasPreview ? '' : ' opacity-60 cursor-not-allowed')}
                                    >
                                        <div className="flex items-center gap-3">
                                            <div className={'w-11 h-11 rounded-xl flex items-center justify-center flex-shrink-0 bg-white dark:bg-slate-800 shadow-sm ' + config.color}>
                                                <span className="material-symbols-outlined text-2xl icon-fill">{config.icon}</span>
                                            </div>
                                            <div className="flex-1 min-w-0">
                                                <div className="flex items-center gap-2 mb-1">
                                                    <span className={'text-[10px] font-bold uppercase tracking-wider ' + config.color}>
                                                        {isChinese ? (config.labelShortZh || config.labelShort) : config.labelShort}
                                                    </span>
                                                    {isSlide && <span className="text-[9px] text-gray-400 italic">{isChinese ? '仅支持在线预览' : 'Interactive only'}</span>}
                                                </div>
                                                <p className="font-semibold text-sm text-slate-900 dark:text-white truncate">{isChinese ? (config.labelZh || file.title) : file.title}</p>
                                                <p className="text-[11px] text-slate-500 dark:text-slate-400 mt-0.5">
                                                    {file.slide_count 
                                                        ? (isChinese 
                                                            ? (file.slide_count + ' ' + (file.type === 'slide' ? '张幻灯片' : file.type === 'quiz' ? '道题目' : '个部分'))
                                                            : (file.slide_count + ' ' + (file.type === 'slide' ? 'slides' : file.type === 'quiz' ? 'questions' : 'sections')))
                                                        : (isChinese ? (config.descZh || file.description || '包含详细说明的综合教学指南') : (file.description || 'Comprehensive teaching guide with detailed instructions'))}
                                                </p>
                                            </div>
                                            <div className="flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity">
                                                <span className="material-symbols-outlined text-gray-400">chevron_right</span>
                                            </div>
                                        </div>
                                    </div>
                                );
                            })}
                        </div>
                        
                        {/* Download All Button with Format Selection */}
                        <div className="p-3 pt-1 border-t border-slate-100 dark:border-slate-700">
                            <div className="relative download-all-dropdown">
                                <button 
                                    onClick={function(e) { e.stopPropagation(); setShowDownloadAllMenu(!showDownloadAllMenu); }}
                                    disabled={!folderInfo}
                                    className="w-full flex items-center justify-center gap-2 bg-primary hover:bg-primary-hover text-white py-3 rounded-xl font-bold text-sm shadow-md transition-all disabled:opacity-50"
                                >
                                    <span className="material-symbols-outlined text-lg">folder_zip</span>
                                    {isChinese ? '下载文档' : 'Download Docs'}
                                    <span className="material-symbols-outlined text-sm">expand_more</span>
                                </button>
                                {showDownloadAllMenu && (
                                    <div className="absolute left-0 right-0 bottom-full mb-2 bg-white dark:bg-slate-800 rounded-lg shadow-xl border border-slate-200 dark:border-slate-700 py-2 z-50">
                                        <p className="px-3 py-1 text-[10px] text-slate-400 uppercase tracking-wide">Select Format</p>
                                        {downloadFormats.map(function(fmt) {
                                            return (
                                                <button
                                                    key={fmt.id}
                                                    onClick={function() { handleDownloadAll(fmt.id); }}
                                                    className="w-full flex items-center gap-3 px-3 py-2.5 text-left text-sm hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
                                                >
                                                    <span className={'material-symbols-outlined text-lg ' + fmt.color}>{fmt.icon}</span>
                                                    <div>
                                                        <span className="text-slate-700 dark:text-slate-200 font-medium">{fmt.label}</span>
                                                        <p className="text-[10px] text-slate-400">Download all as {fmt.label} files</p>
                                                    </div>
                                                </button>
                                            );
                                        })}
                                    </div>
                                )}
                            </div>
                            <p className="text-[10px] text-slate-400 text-center mt-2">{isChinese ? '下载教案、练习等文档' : 'Download lesson plan, worksheets & more'}</p>
                            
                            {/* Version Badge - show current version number */}
                            {pkg && pkg.version !== undefined && (
                                <div className="flex items-center justify-center gap-1.5 mt-2 py-1.5 text-xs text-slate-400">
                                    <span className="material-symbols-outlined text-sm">verified</span>
                                    <span>Version {pkg.version}</span>
                                </div>
                            )}
                        </div>
                    </div>
                    
                    {/* Preview Modal - Same page iframe preview */}
                    {previewFile && (
                        <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-slate-900/60 backdrop-blur-sm animate-fade-in" onClick={function(e) { if (e.target === e.currentTarget && !isPreviewFullscreen) setPreviewFile(null); }}>
                            <div className={`relative flex flex-col bg-white dark:bg-gray-900 shadow-2xl overflow-hidden animate-modal-scale-in transition-all duration-300 ${isPreviewFullscreen ? 'w-full h-full rounded-none' : 'w-[95vw] max-w-5xl h-[90vh] rounded-2xl'}`}>
                                {/* Preview Modal Header */}
                                <div className="flex items-center justify-between px-4 py-3 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 flex-shrink-0">
                                    <div className="flex items-center gap-3 flex-1 min-w-0">
                                        <div className={'w-9 h-9 rounded-lg flex items-center justify-center ' + (typeConfig[previewFile.type] || typeConfig.document).bg + ' ' + (typeConfig[previewFile.type] || typeConfig.document).color}>
                                            <span className="material-symbols-outlined text-lg icon-fill">{(typeConfig[previewFile.type] || typeConfig.document).icon}</span>
                                        </div>
                                        <div className="flex flex-col min-w-0">
                                            <h2 className="text-sm font-bold text-gray-900 dark:text-white truncate leading-tight">
                                                {previewFile.title}
                                            </h2>
                                            <p className="text-[10px] text-gray-400 uppercase tracking-wider font-medium">
                                                {(typeConfig[previewFile.type] || typeConfig.document).label}
                                            </p>
                                        </div>
                                    </div>
                                    
                                    <div className="flex items-center gap-2">
                                        {/* Download Button with Dropdown - Only for downloadable types */}
                                        {(typeConfig[previewFile.type] || typeConfig.document).canDownload && (
                                            <div className="relative download-dropdown">
                                                <button 
                                                    onClick={function(e) { e.stopPropagation(); setShowDownloadMenu(!showDownloadMenu); }}
                                                    className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
                                                >
                                                    <span className="material-symbols-outlined text-lg">download</span>
                                                    <span className="hidden sm:inline">Download</span>
                                                    <span className="material-symbols-outlined text-xs">expand_more</span>
                                                </button>
                                                {showDownloadMenu && (
                                                    <div className="absolute right-0 top-full mt-1 bg-white dark:bg-slate-800 rounded-lg shadow-xl border border-slate-200 dark:border-slate-700 py-1 z-50 min-w-[180px]">
                                                        {downloadFormats.map(function(fmt) {
                                                            return (
                                                                <button
                                                                    key={fmt.id}
                                                                    onClick={function() { handleDownload(previewFile, fmt.id); }}
                                                                    className="w-full flex items-center gap-2 px-3 py-2.5 text-left text-sm hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
                                                                >
                                                                    <span className={'material-symbols-outlined text-lg ' + fmt.color}>{fmt.icon}</span>
                                                                    <div className="flex flex-col">
                                                                        <span className="text-slate-700 dark:text-slate-200">{fmt.label}</span>
                                                                        {fmt.hint && <span className="text-[10px] text-slate-400">{fmt.hint}</span>}
                                                                    </div>
                                                                </button>
                                                            );
                                                        })}
                                                    </div>
                                                )}
                                            </div>
                                        )}
                                        
                                        {/* Edit Layout Mode - only for slides */}
                                        {previewFile.type === 'slide' && isPreviewFullscreen && (
                                            <button 
                                                onClick={function() { 
                                                    var newMode = !isDragEditMode;
                                                    setIsDragEditMode(newMode);
                                                    // Send message to all slide iframes
                                                    var iframes = document.querySelectorAll('iframe');
                                                    iframes.forEach(function(iframe) {
                                                        try {
                                                            iframe.contentWindow.postMessage({
                                                                type: 'TOGGLE_DRAG_MODE',
                                                                enabled: newMode
                                                            }, '*');
                                                        } catch(e) {}
                                                    });
                                                }}
                                                className={'p-1.5 rounded-lg transition-colors ' + (isDragEditMode ? 'bg-primary text-white' : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800')}
                                                title={isDragEditMode ? 'Exit edit mode' : 'Edit layout (drag elements)'}
                                            >
                                                <span className="material-symbols-outlined text-lg">{isDragEditMode ? 'check' : 'drag_pan'}</span>
                                            </button>
                                        )}
                                        
                                        {/* Edit Document Mode - for worksheet, quiz, exit_ticket, lesson_plan */}
                                        {editableTypes.includes(previewFile.type) && (
                                            <button 
                                                onClick={function() { 
                                                    setIsDocEditMode(!isDocEditMode);
                                                }}
                                                className={'flex items-center gap-1.5 px-3 py-1.5 rounded-lg transition-colors text-sm font-medium ' + (isDocEditMode ? 'bg-blue-600 text-white' : 'text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800')}
                                                title={isDocEditMode ? 'Exit edit mode' : 'Edit document'}
                                            >
                                                <span className="material-symbols-outlined text-lg">{isDocEditMode ? 'edit_off' : 'edit'}</span>
                                                <span className="hidden sm:inline">{isDocEditMode ? 'Editing' : 'Edit'}</span>
                                            </button>
                                        )}
                                        
                                        {/* Save Button - only when editing and has changes */}
                                        {isDocEditMode && docHasChanges && (
                                            <button 
                                                onClick={saveDocContent}
                                                disabled={isSavingDoc}
                                                className="flex items-center gap-1.5 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded-lg transition-colors text-sm font-medium disabled:opacity-50 animate-pulse"
                                            >
                                                {isSavingDoc ? (
                                                    React.createElement(React.Fragment, null,
                                                        React.createElement('span', { className: 'material-symbols-outlined text-lg animate-spin' }, 'progress_activity'),
                                                        React.createElement('span', { className: 'hidden sm:inline' }, 'Saving...')
                                                    )
                                                ) : (
                                                    React.createElement(React.Fragment, null,
                                                        React.createElement('span', { className: 'material-symbols-outlined text-lg' }, 'save'),
                                                        React.createElement('span', { className: 'hidden sm:inline' }, 'Save')
                                                    )
                                                )}
                                            </button>
                                        )}
                                        
                                        {/* Slide Edit Save Button - shows when there are pending slide edits */}
                                        {previewFile && previewFile.type === 'slide' && Object.keys(pendingSlideEdits).length > 0 && (
                                            <button 
                                                onClick={saveSlideEdits}
                                                disabled={isSavingSlides}
                                                className={'flex items-center gap-1.5 px-3 py-1.5 rounded-lg transition-colors text-sm font-medium ' + 
                                                    (slidesSaveStatus === 'success' ? 'bg-green-500 text-white' : 
                                                     slidesSaveStatus === 'error' ? 'bg-red-500 text-white' :
                                                     'bg-amber-500 hover:bg-amber-600 text-white animate-pulse')}
                                                title="Save slide edits"
                                            >
                                                {isSavingSlides ? (
                                                    React.createElement(React.Fragment, null,
                                                        React.createElement('span', { className: 'material-symbols-outlined text-lg animate-spin' }, 'progress_activity'),
                                                        React.createElement('span', { className: 'hidden sm:inline' }, 'Saving...')
                                                    )
                                                ) : slidesSaveStatus === 'success' ? (
                                                    React.createElement(React.Fragment, null,
                                                        React.createElement('span', { className: 'material-symbols-outlined text-lg' }, 'check_circle'),
                                                        React.createElement('span', { className: 'hidden sm:inline' }, 'Saved!')
                                                    )
                                                ) : slidesSaveStatus === 'error' ? (
                                                    React.createElement(React.Fragment, null,
                                                        React.createElement('span', { className: 'material-symbols-outlined text-lg' }, 'error'),
                                                        React.createElement('span', { className: 'hidden sm:inline' }, 'Error')
                                                    )
                                                ) : (
                                                    React.createElement(React.Fragment, null,
                                                        React.createElement('span', { className: 'material-symbols-outlined text-lg' }, 'save'),
                                                        React.createElement('span', { className: 'hidden sm:inline' }, 'Save Changes (' + Object.keys(pendingSlideEdits).length + ')')
                                                    )
                                                )}
                                            </button>
                                        )}
                                        
                                        {/* Fullscreen Toggle */}
                                        <button 
                                            onClick={function() { setIsPreviewFullscreen(!isPreviewFullscreen); }}
                                            className="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
                                            title={isPreviewFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
                                        >
                                            <span className="material-symbols-outlined text-lg">{isPreviewFullscreen ? 'fullscreen_exit' : 'fullscreen'}</span>
                                        </button>
                                        
                                        {/* Open in new tab */}
                                        <button 
                                            onClick={function() { window.open(previewFile.preview_url, '_blank'); }}
                                            className="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
                                            title="Open in new tab"
                                        >
                                            <span className="material-symbols-outlined text-lg">open_in_new</span>
                                        </button>
                                        
                                        <div className="h-5 w-[1px] bg-gray-200 dark:bg-gray-700"></div>
                                        <button 
                                            onClick={function() { 
                                                // Warn if there are unsaved slide edits
                                                if (Object.keys(pendingSlideEdits).length > 0) {
                                                    if (!confirm('You have unsaved slide edits. Are you sure you want to close?')) {
                                                        return;
                                                    }
                                                }
                                                setPendingSlideEdits({});
                                                setSlidesSaveStatus(null);
                                                setPreviewFile(null); 
                                                setIsPreviewFullscreen(false); 
                                                setCurrentSlideIndex(0); 
                                            }}
                                            className="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
                                        >
                                            <span className="material-symbols-outlined text-lg">close</span>
                                        </button>
                                    </div>
                                </div>
                                
                                {/* Preview Content - Iframe or Slide Layout */}
                                {isPreviewFullscreen && previewFile.type === 'slide' && previewFile.slide_urls && previewFile.slide_urls.length > 0 ? (
                                    /* Fullscreen Slide Layout - Manus Style */
                                    <div className="flex-1 flex overflow-hidden bg-white dark:bg-gray-900">
                                        {/* Left Sidebar - Slide Thumbnails */}
                                        <div className="w-56 bg-gray-50 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 flex flex-col overflow-hidden flex-shrink-0">
                                            <div className="flex-1 overflow-y-auto py-3 px-3 space-y-3">
                                                {previewFile.slide_urls.map(function(slideUrl, idx) {
                                                    var isActive = idx === currentSlideIndex;
                                                    return (
                                                        <div 
                                                            key={idx}
                                                            onClick={function() { setCurrentSlideIndex(idx); }}
                                                            className="cursor-pointer group"
                                                        >
                                                            {/* Thumbnail with border highlight */}
                                                            <div className={'rounded-lg overflow-hidden border-2 transition-all shadow-sm ' + (isActive ? 'border-blue-500 shadow-md' : 'border-gray-200 dark:border-gray-600 hover:border-blue-300')}>
                                                                <div className="aspect-[16/10] bg-white overflow-hidden pointer-events-none relative">
                                                                    <iframe 
                                                                        src={slideUrl}
                                                                        className="absolute top-0 left-0 border-0 origin-top-left"
                                                                        style={{ 
                                                                            width: '1200px', 
                                                                            height: '750px', 
                                                                            transform: 'scale(0.165)',
                                                                            transformOrigin: 'top left'
                                                                        }}
                                                                        title={'Slide ' + (idx + 1)}
                                                                        loading="eager"
                                                                    />
                                                                </div>
                                                            </div>
                                                            {/* Slide number below thumbnail */}
                                                            <div className={'text-center mt-1.5 text-sm font-medium ' + (isActive ? 'text-blue-600' : 'text-gray-500')}>
                                                                {idx + 1}
                                                            </div>
                                                        </div>
                                                    );
                                                })}
                                            </div>
                                        </div>
                                        {/* Right Side - Main slide + notes area */}
                                        <div className="flex-1 flex flex-col overflow-hidden">
                                            {/* Main Slide Area - fills available space with scrolling */}
                                            <div className="flex-1 bg-gray-100 dark:bg-gray-800 p-4 min-h-0 overflow-auto">
                                                <div 
                                                    className="bg-white rounded shadow-xl mx-auto"
                                                    style={{ 
                                                        width: '100%',
                                                        maxWidth: '1200px',
                                                        minHeight: '100%'
                                                    }}
                                                >
                                                    <iframe 
                                                        src={previewFile.slide_urls[currentSlideIndex]}
                                                        className="w-full border-0"
                                                        style={{ minHeight: 'calc(100vh - 180px)', height: '100%' }}
                                                        title={previewFile.title + ' - Slide ' + (currentSlideIndex + 1)}
                                                    />
                                                </div>
                                            </div>
                                            {/* Speaker Notes Area - Enhanced with Regenerate */}
                                            <div className="h-44 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 px-4 py-2 flex-shrink-0 flex flex-col">
                                                <div className="flex items-center justify-between mb-2">
                                                    <div className="flex items-center gap-2">
                                                        <span className="material-symbols-outlined text-sm text-gray-500 dark:text-gray-400">speaker_notes</span>
                                                        <span className="text-xs font-medium text-gray-500 dark:text-gray-400">Speaker Notes</span>
                                                        {slideNotes[currentSlideIndex] && (
                                                            <span className="text-[10px] px-1.5 py-0.5 bg-green-100 dark:bg-green-900/30 text-green-600 dark:text-green-400 rounded">
                                                                已保存
                                                            </span>
                                                        )}
                                                    </div>
                                                    <div className="flex items-center gap-2">
                                                        <span className="text-xs text-gray-400 dark:text-gray-500">Slide {currentSlideIndex + 1} / {previewFile.slide_urls.length}</span>
                                                        {/* Regenerate Slide Button */}
                                                        <button
                                                            onClick={function() {
                                                                var genId = getGenerationIdFromPreview();
                                                                if (!genId) {
                                                                    alert('无法获取生成ID');
                                                                    return;
                                                                }
                                                                // 检查当前 slide 是否是交互组件 slide
                                                                var isInteractiveSlide = false;
                                                                var currentSlideNotes = slideNotes[currentSlideIndex] || '';
                                                                if (currentSlideNotes.includes('交互组件:') || currentSlideNotes.includes('Interactive:') || currentSlideNotes.includes('🎮')) {
                                                                    isInteractiveSlide = true;
                                                                }
                                                                
                                                                var confirmMsg = '确定要重新生成 Slide ' + (currentSlideIndex + 1) + ' 吗？\n\n将使用当前的 Speaker Notes 作为参考重新生成此幻灯片的图片和内容。';
                                                                
                                                                // 如果是交互组件 slide 且不是第一个 slide，询问是否联动更新前一个 slide
                                                                var updatePreviousSlide = false;
                                                                if (isInteractiveSlide && currentSlideIndex > 0) {
                                                                    confirmMsg += '\n\n⚠️ 检测到这是一个交互组件 Slide。\n前一个 Slide 可能包含相关的使用说明。';
                                                                }
                                                                
                                                                if (confirm(confirmMsg)) {
                                                                    // 如果是交互组件，询问是否联动更新
                                                                    if (isInteractiveSlide && currentSlideIndex > 0) {
                                                                        updatePreviousSlide = confirm('是否同时更新 Slide ' + currentSlideIndex + ' 的说明内容？\n\n点击 "确定" 将同时重新生成前一个 slide 的说明，\n使其与新的交互组件保持一致。\n\n点击 "取消" 仅重新生成当前交互组件。');
                                                                    }
                                                                    
                                                                    // Call regenerate API
                                                                    var currentNotes = slideNotes[currentSlideIndex] || '';
                                                                    fetch(API_BASE + '/api/v1/generations/' + genId + '/regenerate-slide', {
                                                                        method: 'POST',
                                                                        headers: Object.assign({}, getAuthHeaders(), { 'Content-Type': 'application/json' }),
                                                                        body: JSON.stringify({
                                                                            slide_index: currentSlideIndex + 1,
                                                                            speaker_notes: currentNotes,
                                                                            regenerate_image: true,
                                                                            regenerate_interactive: true,
                                                                            update_previous_slide: updatePreviousSlide
                                                                        })
                                                                    })
                                                                    .then(function(res) { return res.json(); })
                                                                    .then(function(data) {
                                                                        if (data.status === 'success') {
                                                                            alert('✅ Slide ' + (currentSlideIndex + 1) + ' 重新生成成功！\n\n耗时: ' + data.duration_seconds.toFixed(1) + 's\n重新生成: ' + (data.regenerated || []).join(', ') + '\n\n页面将自动刷新以显示更新内容。');
                                                                            // Force refresh all iframes with cache-busting timestamp
                                                                            var timestamp = Date.now();
                                                                            var allIframes = document.querySelectorAll('iframe');
                                                                            allIframes.forEach(function(iframe) {
                                                                                var src = iframe.src;
                                                                                if (src) {
                                                                                    // Remove existing timestamp and add new one
                                                                                    var baseUrl = src.split('?')[0];
                                                                                    iframe.src = baseUrl + '?_nocache=' + timestamp;
                                                                                }
                                                                            });
                                                                            // Also update previewFile slide_urls to force re-render
                                                                            if (previewFile && previewFile.slide_urls) {
                                                                                var updatedUrls = previewFile.slide_urls.map(function(url) {
                                                                                    var baseUrl = url.split('?')[0];
                                                                                    return baseUrl + '?_nocache=' + timestamp;
                                                                                });
                                                                                setPreviewFile(Object.assign({}, previewFile, { slide_urls: updatedUrls }));
                                                                            }
                                                                        } else {
                                                                            alert('❌ 重新生成失败: ' + (data.error || data.detail || '未知错误'));
                                                                        }
                                                                    })
                                                                    .catch(function(e) {
                                                                        alert('❌ 请求失败: ' + e.message);
                                                                    });
                                                                }
                                                            }}
                                                            className="flex items-center gap-1 px-2 py-1 text-xs bg-amber-50 dark:bg-amber-900/20 text-amber-600 dark:text-amber-400 rounded-md hover:bg-amber-100 dark:hover:bg-amber-900/40 transition-colors"
                                                            title="使用当前 Speaker Notes 重新生成此 Slide"
                                                        >
                                                            <span className="material-symbols-outlined text-sm">refresh</span>
                                                            重新生成
                                                        </button>
                                                    </div>
                                                </div>
                                                <textarea
                                                    value={slideNotes[currentSlideIndex] || ''}
                                                    onChange={function(e) {
                                                        setSlideNotes(function(prev) {
                                                            var updated = Object.assign({}, prev);
                                                            updated[currentSlideIndex] = e.target.value;
                                                            return updated;
                                                        });
                                                    }}
                                                    placeholder={slideNotes[currentSlideIndex] ? '' : '输入演讲笔记... 修改后可用于重新生成此 Slide'}
                                                    className="flex-1 w-full resize-none text-sm text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary placeholder-gray-400 dark:placeholder-gray-500"
                                                />
                                                <div className="flex items-center justify-between mt-1.5 text-[10px] text-gray-400 dark:text-gray-500">
                                                    <span>💡 编辑笔记后点击"重新生成"可根据修改内容更新 Slide</span>
                                                    <span className="flex items-center gap-1">
                                                        <span className="material-symbols-outlined text-xs">auto_mode</span>
                                                        自动保存
                                                    </span>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                ) : (
                                    /* Default iframe preview - centered document style with zoom in fullscreen */
                                    <div className={`flex-1 overflow-auto ${isPreviewFullscreen ? 'bg-gray-200 dark:bg-gray-900 py-6 px-4 flex justify-center' : 'bg-gray-100 dark:bg-gray-950'}`}>
                                        {/* Edit mode indicator bar */}
                                        {isDocEditMode && editableTypes.includes(previewFile.type) && (
                                            <div className="sticky top-0 z-10 bg-blue-600 text-white px-4 py-2 text-sm flex items-center justify-between">
                                                <span className="flex items-center gap-2">
                                                    <span className="material-symbols-outlined text-lg">edit_note</span>
                                                    Edit Mode - Click directly on text to edit
                                                </span>
                                                {docHasChanges && (
                                                    <span className="flex items-center gap-1 text-yellow-200">
                                                        <span className="material-symbols-outlined text-sm animate-pulse">circle</span>
                                                        Unsaved changes
                                                    </span>
                                                )}
                                            </div>
                                        )}
                                        <div 
                                            className={`transition-all duration-300 ${isPreviewFullscreen ? 'max-w-5xl w-full bg-white dark:bg-gray-800 rounded-xl shadow-2xl overflow-hidden' : 'w-full h-full'} ${isDocEditMode ? 'ring-2 ring-blue-500 ring-offset-2' : ''}`}
                                            style={isPreviewFullscreen ? { height: 'fit-content', minHeight: '100%' } : {}}
                                        >
                                            <iframe 
                                                ref={editableTypes.includes(previewFile.type) ? docIframeRef : null}
                                                src={previewFile.preview_url + (previewFile.preview_url.includes('?') ? '&' : '?') + '_t=' + Date.now()}
                                                className="w-full border-0 origin-top doc-preview-iframe"
                                                style={isPreviewFullscreen ? { 
                                                    height: 'calc(100vh - 140px)',
                                                    zoom: '1.15'
                                                } : { height: '100%' }}
                                                title={previewFile.title}
                                                onLoad={function() {
                                                    // Re-apply edit mode if active when iframe loads
                                                    if (isDocEditMode && editableTypes.includes(previewFile.type)) {
                                                        setTimeout(function() {
                                                            toggleIframeEditMode(true);
                                                        }, 100);
                                                    }
                                                }}
                                            />
                                        </div>
                                    </div>
                                )}
                            </div>
                        </div>
                    )}
                    
                    {/* Image Edit Modal */}
                    {editImageModal && (
                        <div className="fixed inset-0 z-[10000] flex items-center justify-center bg-slate-900/60 backdrop-blur-sm animate-fade-in" onClick={function(e) { if (e.target === e.currentTarget) setEditImageModal(null); }}>
                            <div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-[90vw] max-w-lg overflow-hidden animate-modal-scale-in">
                                <div className="bg-gradient-to-r from-amber-500 to-orange-500 px-6 py-4">
                                    <div className="flex items-center gap-3">
                                        <span className="text-2xl">🎨</span>
                                        <div>
                                            <h3 className="text-lg font-bold text-white">Edit Image Prompt</h3>
                                            <p className="text-sm text-white/80">Slide {editImageModal.slideIndex + 1}</p>
                                        </div>
                                    </div>
                                </div>
                                <div className="p-6 space-y-4">
                                    <div>
                                        <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
                                            Image Description
                                        </label>
                                        <textarea
                                            value={newImagePrompt}
                                            onChange={function(e) { setNewImagePrompt(e.target.value); }}
                                            className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-xl bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-amber-500 focus:border-transparent resize-none"
                                            rows={4}
                                            placeholder="Describe the image you want to generate..."
                                        />
                                    </div>
                                    <p className="text-xs text-gray-500 dark:text-gray-400">
                                        💡 Tip: Be specific about style, colors, and content. For educational slides, describe the concept visually.
                                    </p>
                                    <div className="flex gap-3 pt-2">
                                        <button
                                            onClick={function() { setEditImageModal(null); }}
                                            className="flex-1 px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-xl text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
                                        >
                                            Cancel
                                        </button>
                                        <button
                                            onClick={function() {
                                                alert('🚧 Image regeneration feature coming soon!\n\nPrompt: ' + newImagePrompt + '\n\nThis will regenerate the image for slide ' + (editImageModal.slideIndex + 1));
                                                setEditImageModal(null);
                                            }}
                                            disabled={isGeneratingImage || !newImagePrompt.trim()}
                                            className="flex-1 px-4 py-2.5 bg-gradient-to-r from-amber-500 to-orange-500 text-white rounded-xl hover:from-amber-600 hover:to-orange-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed font-medium flex items-center justify-center gap-2"
                                        >
                                            {isGeneratingImage ? (
                                                React.createElement(React.Fragment, null,
                                                    React.createElement('svg', { className: 'animate-spin h-4 w-4', viewBox: '0 0 24 24' },
                                                        React.createElement('circle', { className: 'opacity-25', cx: '12', cy: '12', r: '10', stroke: 'currentColor', strokeWidth: '4', fill: 'none' }),
                                                        React.createElement('path', { className: 'opacity-75', fill: 'currentColor', d: 'M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z' })
                                                    ),
                                                    ' Generating...'
                                                )
                                            ) : (
                                                React.createElement(React.Fragment, null,
                                                    React.createElement('span', { className: 'material-symbols-outlined text-lg' }, 'auto_awesome'),
                                                    ' Generate Image'
                                                )
                                            )}
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                    )}
                </div>
            );
        };

        // Confirmed Lesson Summary - Read-only display for history (same style as ConfirmationCard) - with multilingual support
var ConfirmedLessonSummary = ({ lessonInfo }) => {
            // Detect language from topic
            var isZh = lessonInfo.topic && /[\u4e00-\u9fa5]/.test(lessonInfo.topic);
            
            // Localized labels
            var labels = {
                title: isZh ? '课程详情确认' : 'Confirm Lesson Details',
                subtitle: isZh ? '蓝图生成前的确认' : 'Review and edit before generating blueprint',
                topic: isZh ? '主题' : 'Topic',
                grade: isZh ? '年级' : 'Grade Level',
                subject: isZh ? '学科' : 'Subject',
                duration: isZh ? '时长（分钟）' : 'Duration (min)',
                teachingStyle: isZh ? '教学风格' : 'Teaching Style',
                assessment: isZh ? '评估方式' : 'Assessment',
                objectives: isZh ? '学习目标' : 'Learning Objectives',
                activities: isZh ? '活动' : 'Activities',
                packages: isZh ? '生成材料' : 'Materials',
                notSpecified: isZh ? '未指定' : 'Not specified',
                confirmed: isZh ? '已确认并生成蓝图' : 'Confirmed and blueprint generated'
            };
            
            // Package type labels
            var pkgLabels = {
                slide: isZh ? '互动课件' : 'Slides',
                lesson_plan: isZh ? '教案' : 'Lesson Plan',
                worksheet: isZh ? '学生练习' : 'Worksheet',
                quiz: isZh ? '测验' : 'Quiz',
                exit_ticket: isZh ? '出门检测' : 'Exit Ticket',
                group_activity: isZh ? '小组活动' : 'Group Activity',
                rubric: isZh ? '评分标准' : 'Rubric',
                vocabulary_cards: isZh ? '词汇卡片' : 'Vocabulary Cards'
            };
            
            var infoFields = [
                { key: 'topic', label: labels.topic, icon: 'topic', required: true },
                { key: 'grade', label: labels.grade, icon: 'school', required: true },
                { key: 'subject', label: labels.subject, icon: 'menu_book' },
                { key: 'duration', label: labels.duration, icon: 'schedule' },
                { key: 'teaching_style', label: labels.teachingStyle, icon: 'style' },
                { key: 'assessment_type', label: labels.assessment, icon: 'assignment' }
            ];
            
            return (
                <div className="w-full max-w-2xl bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
                    <div className="bg-gradient-to-r from-primary/10 to-primary/5 dark:from-primary/20 dark:to-primary/10 px-5 py-4 border-b border-gray-200 dark:border-gray-700">
                        <div className="flex items-center gap-3">
                            <div className="w-10 h-10 rounded-xl bg-primary/20 flex items-center justify-center">
                                <span className="material-symbols-outlined text-primary text-xl">checklist</span>
                            </div>
                            <div>
                                <h3 className="text-base font-bold text-gray-900 dark:text-white">{labels.title}</h3>
                                <p className="text-xs text-gray-500 dark:text-gray-400">{labels.subtitle}</p>
                            </div>
                        </div>
                    </div>
                    
                    <div className="p-5 space-y-4">
                        {infoFields.map(function(field) {
                            var value = lessonInfo[field.key];
                            if (!value) return null;
                            var displayValue = Array.isArray(value) ? value.join(', ') : value;
                            
                            return (
                                <div key={field.key} className="flex items-start gap-3">
                                    <div className="w-8 h-8 rounded-lg bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0 mt-0.5">
                                        <span className="material-symbols-outlined text-gray-500 dark:text-gray-400 text-sm">{field.icon}</span>
                                    </div>
                                    <div className="flex-1 min-w-0">
                                        <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">
                                            {field.label} {field.required && <span className="text-red-500">*</span>}
                                        </label>
                                        <p className="text-sm text-gray-900 dark:text-white">
                                            {displayValue || <span className="text-gray-400 italic">{labels.notSpecified}</span>}
                                        </p>
                                    </div>
                                </div>
                            );
                        })}
                        
                        {/* Learning Objectives - same as ConfirmationCard */}
                        {(lessonInfo.objectives && lessonInfo.objectives.length > 0) && (
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0 mt-0.5">
                                    <span className="material-symbols-outlined text-gray-500 dark:text-gray-400 text-sm">flag</span>
                                </div>
                                <div className="flex-1">
                                    <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">{labels.objectives}</label>
                                    <ul className="text-sm text-gray-900 dark:text-white list-disc list-inside space-y-1">
                                        {lessonInfo.objectives.map(function(obj, idx) {
                                            return <li key={idx}>{obj}</li>;
                                        })}
                                    </ul>
                                </div>
                            </div>
                        )}
                        
                        {/* Activities - same as ConfirmationCard */}
                        {(lessonInfo.activities && lessonInfo.activities.length > 0) && (
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0 mt-0.5">
                                    <span className="material-symbols-outlined text-gray-500 dark:text-gray-400 text-sm">sports_esports</span>
                                </div>
                                <div className="flex-1">
                                    <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">{labels.activities}</label>
                                    <div className="flex flex-wrap gap-1.5">
                                        {lessonInfo.activities.map(function(activity, idx) {
                                            return (
                                                <span key={idx} className="px-2 py-1 text-xs rounded-full bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300">
                                                    {activity}
                                                </span>
                                            );
                                        })}
                                    </div>
                                </div>
                            </div>
                        )}
                        
                        {/* Selected Packages */}
                        {(lessonInfo.selectedPackages && lessonInfo.selectedPackages.length > 0) && (
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5">
                                    <span className="material-symbols-outlined text-primary text-sm">folder_zip</span>
                                </div>
                                <div className="flex-1">
                                    <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">{labels.packages}</label>
                                    <div className="flex flex-wrap gap-1.5">
                                        {lessonInfo.selectedPackages.map(function(pkg, idx) {
                                            return (
                                                <span key={idx} className="px-2 py-1 text-xs rounded-full bg-primary/10 text-primary">
                                                    {pkgLabels[pkg] || pkg}
                                                </span>
                                            );
                                        })}
                                    </div>
                                </div>
                            </div>
                        )}
                    </div>
                    
                    {/* Confirmed status footer */}
                    <div className="px-5 py-4 bg-green-50 dark:bg-green-900/20 border-t border-gray-200 dark:border-gray-700 flex items-center gap-2">
                        <span className="material-symbols-outlined text-green-600 dark:text-green-400 text-lg">check_circle</span>
                        <span className="text-sm font-medium text-green-700 dark:text-green-300">{labels.confirmed}</span>
                    </div>
                </div>
            );
        };

        // Confirmation Card for blueprint generation - with multilingual support and package selection
var ConfirmationCard = ({ extractedInfo, onConfirm, onEdit, messages, isReadOnly, isGenerating }) => {
            var [editMode, setEditMode] = React.useState(false);
            var [editedInfo, setEditedInfo] = React.useState(extractedInfo || {});
            
            // If materials already generated, show read-only mode
            var readOnlyMode = isReadOnly === true;
            
            // Detect language from topic or last user message
            var detectLanguage = function() {
                // Check topic first
                if (editedInfo.topic && /[\u4e00-\u9fa5]/.test(editedInfo.topic)) return 'zh';
                // Check last user message
                if (messages && messages.length > 0) {
                    var userMsgs = messages.filter(function(m) { return m.role === 'user'; });
                    if (userMsgs.length > 0) {
                        var lastContent = userMsgs[userMsgs.length - 1].content || '';
                        if (/[\u4e00-\u9fa5]/.test(lastContent)) return 'zh';
                    }
                }
                return 'en';
            };
            var lang = detectLanguage();
            var isZh = lang === 'zh';
            
            // Package types configuration with required/optional
            var allPackageTypes = [
                { id: 'slide', label: isZh ? '互动课件' : 'Interactive Slides', icon: 'slideshow', required: true },
                { id: 'lesson_plan', label: isZh ? '教案' : 'Lesson Plan', icon: 'description', required: true },
                { id: 'worksheet', label: isZh ? '学生练习' : 'Student Worksheet', icon: 'assignment', required: false },
                { id: 'quiz', label: isZh ? '测验' : 'Assessment Quiz', icon: 'quiz', required: false },
                { id: 'exit_ticket', label: isZh ? '出门检测' : 'Exit Ticket', icon: 'receipt_long', required: false },
                { id: 'group_activity', label: isZh ? '小组活动' : 'Group Activity', icon: 'groups', required: false },
                { id: 'rubric', label: isZh ? '评分标准' : 'Grading Rubric', icon: 'format_list_numbered', required: false },
                { id: 'vocabulary_cards', label: isZh ? '词汇卡片' : 'Vocabulary Cards', icon: 'style', required: false }
            ];
            
            // Initialize selected packages (required ones always selected, plus LLM suggested packages)
            var getInitialPackages = function() {
                var initial = {};
                // Start with required packages selected
                allPackageTypes.forEach(function(pkg) {
                    initial[pkg.id] = pkg.required;
                });
                
                // Use LLM's suggested_packages if available (intelligent detection)
                var suggestedPackages = editedInfo.suggested_packages || [];
                if (suggestedPackages && suggestedPackages.length > 0) {
                    suggestedPackages.forEach(function(pkgId) {
                        if (initial.hasOwnProperty(pkgId)) {
                            initial[pkgId] = true;
                        }
                    });
                    console.log('📦 LLM suggested packages:', suggestedPackages);
                }
                
                // Auto-map activities to corresponding packages
                // 活动 → 生成材料的映射
                var activities = editedInfo.activities || [];
                var activityToPackageMap = {
                    '小组活动': 'group_activity',
                    '小组': 'group_activity',
                    'group work': 'group_activity',
                    'group activity': 'group_activity',
                    'collaborative': 'group_activity',
                    '练习册': 'worksheet',
                    '练习': 'worksheet',
                    'worksheet': 'worksheet',
                    'practice': 'worksheet',
                    '测验': 'quiz',
                    'quiz': 'quiz',
                    '词汇': 'vocabulary_cards',
                    'vocabulary': 'vocabulary_cards'
                };
                
                activities.forEach(function(activity) {
                    var activityLower = (activity || '').toLowerCase();
                    Object.keys(activityToPackageMap).forEach(function(key) {
                        if (activityLower.includes(key.toLowerCase())) {
                            var pkgId = activityToPackageMap[key];
                            if (initial.hasOwnProperty(pkgId)) {
                                initial[pkgId] = true;
                                console.log('📦 Auto-selected package from activity:', activity, '→', pkgId);
                            }
                        }
                    });
                });
                
                return initial;
            };
            var [selectedPackages, setSelectedPackages] = React.useState(getInitialPackages);
            
            var handleFieldChange = function(field, value) {
                setEditedInfo(function(prev) {
                    var updated = Object.assign({}, prev);
                    updated[field] = value;
                    return updated;
                });
            };
            
            var handlePackageToggle = function(pkgId, isRequired) {
                if (isRequired) return; // Cannot toggle required packages
                setSelectedPackages(function(prev) {
                    var updated = Object.assign({}, prev);
                    updated[pkgId] = !updated[pkgId];
                    return updated;
                });
            };
            
            var handleConfirm = function() {
                // Include selected packages in the confirmed info
                var confirmedData = Object.assign({}, editedInfo, {
                    selectedPackages: Object.keys(selectedPackages).filter(function(k) { return selectedPackages[k]; })
                });
                onConfirm(confirmedData);
            };
            
            // Localized labels
            var labels = {
                title: isZh ? '确认课程详情' : 'Confirm Lesson Details',
                subtitle: isZh ? '生成前请检查并编辑' : 'Review and edit before generating blueprint',
                topic: isZh ? '主题' : 'Topic',
                grade: isZh ? '年级' : 'Grade Level',
                subject: isZh ? '学科' : 'Subject',
                duration: isZh ? '时长（分钟）' : 'Duration (min)',
                teachingStyle: isZh ? '教学风格' : 'Teaching Style',
                assessment: isZh ? '评估方式' : 'Assessment',
                objectives: isZh ? '学习目标' : 'Learning Objectives',
                activities: isZh ? '活动' : 'Activities',
                packages: isZh ? '生成材料' : 'Materials to Generate',
                packagesHint: isZh ? '必选项已锁定，可额外选择其他材料' : 'Required items are locked. Select additional materials as needed.',
                notSpecified: isZh ? '未指定' : 'Not specified',
                edit: isZh ? '编辑' : 'Edit',
                preview: isZh ? '预览' : 'Preview',
                generate: isZh ? '生成蓝图' : 'Generate Blueprint',
                enterPlaceholder: isZh ? '请输入' : 'Enter '
            };
            
            var infoFields = [
                { key: 'topic', label: labels.topic, icon: 'topic', required: true },
                { key: 'grade', label: labels.grade, icon: 'school', required: true },
                { key: 'subject', label: labels.subject, icon: 'menu_book' },
                { key: 'duration', label: labels.duration, icon: 'schedule', type: 'number' },
                { key: 'teaching_style', label: labels.teachingStyle, icon: 'style' },
                { key: 'assessment_type', label: labels.assessment, icon: 'assignment' }
            ];
            
            return (
                <div className="w-full max-w-2xl bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-700 overflow-hidden animate-fade-in-up">
                    <div className="bg-gradient-to-r from-primary/10 to-primary/5 dark:from-primary/20 dark:to-primary/10 px-5 py-4 border-b border-gray-200 dark:border-gray-700">
                        <div className="flex items-center gap-3">
                            <div className="w-10 h-10 rounded-xl bg-primary/20 flex items-center justify-center">
                                <span className="material-symbols-outlined text-primary text-xl">checklist</span>
                            </div>
                            <div>
                                <h3 className="text-base font-bold text-gray-900 dark:text-white">{labels.title}</h3>
                                <p className="text-xs text-gray-500 dark:text-gray-400">{labels.subtitle}</p>
                            </div>
                        </div>
                    </div>
                    
                    <div className="p-5 space-y-4">
                        {infoFields.map(function(field) {
                            var value = editedInfo[field.key] || '';
                            var displayValue = Array.isArray(value) ? value.join(', ') : value;
                            
                            return (
                                <div key={field.key} className="flex items-start gap-3">
                                    <div className="w-8 h-8 rounded-lg bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0 mt-0.5">
                                        <span className="material-symbols-outlined text-gray-500 dark:text-gray-400 text-sm">{field.icon}</span>
                                    </div>
                                    <div className="flex-1 min-w-0">
                                        <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">
                                            {field.label} {field.required && <span className="text-red-500">*</span>}
                                        </label>
                                        {editMode ? (
                                            <input
                                                type={field.type || 'text'}
                                                value={displayValue}
                                                onChange={function(e) { handleFieldChange(field.key, e.target.value); }}
                                                className="w-full px-3 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-primary/50 focus:border-primary"
                                                placeholder={labels.enterPlaceholder + field.label.toLowerCase()}
                                            />
                                        ) : (
                                            <p className="text-sm text-gray-900 dark:text-white">
                                                {displayValue || <span className="text-gray-400 italic">{labels.notSpecified}</span>}
                                            </p>
                                        )}
                                    </div>
                                </div>
                            );
                        })}
                        
                        {/* Objectives */}
                        {(editedInfo.objectives && editedInfo.objectives.length > 0) && (
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0 mt-0.5">
                                    <span className="material-symbols-outlined text-gray-500 dark:text-gray-400 text-sm">flag</span>
                                </div>
                                <div className="flex-1">
                                    <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">{labels.objectives}</label>
                                    <ul className="text-sm text-gray-900 dark:text-white list-disc list-inside space-y-1">
                                        {editedInfo.objectives.map(function(obj, idx) {
                                            return <li key={idx}>{obj}</li>;
                                        })}
                                    </ul>
                                </div>
                            </div>
                        )}
                        
                        {/* Activities */}
                        {(editedInfo.activities && editedInfo.activities.length > 0) && (
                            <div className="flex items-start gap-3">
                                <div className="w-8 h-8 rounded-lg bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0 mt-0.5">
                                    <span className="material-symbols-outlined text-gray-500 dark:text-gray-400 text-sm">sports_esports</span>
                                </div>
                                <div className="flex-1">
                                    <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">{labels.activities}</label>
                                    <div className="flex flex-wrap gap-1.5">
                                        {editedInfo.activities.map(function(activity, idx) {
                                            return (
                                                <span key={idx} className="px-2 py-1 text-xs rounded-full bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300">
                                                    {activity}
                                                </span>
                                            );
                                        })}
                                    </div>
                                </div>
                            </div>
                        )}
                        
                        {/* Package Selection */}
                        <div className="pt-3 border-t border-gray-200 dark:border-gray-700">
                            <div className="flex items-start gap-3 mb-3">
                                <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5">
                                    <span className="material-symbols-outlined text-primary text-sm">folder_zip</span>
                                </div>
                                <div className="flex-1">
                                    <label className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1 block">{labels.packages}</label>
                                    <p className="text-[10px] text-gray-400 dark:text-gray-500 mb-2">{labels.packagesHint}</p>
                                </div>
                            </div>
                            <div className="grid grid-cols-2 gap-2 ml-11">
                                {allPackageTypes.map(function(pkg) {
                                    var isSelected = selectedPackages[pkg.id];
                                    var isRequired = pkg.required;
                                    var isDisabled = isRequired || readOnlyMode;
                                    return (
                                        <button
                                            key={pkg.id}
                                            onClick={function() { if (!readOnlyMode) handlePackageToggle(pkg.id, isRequired); }}
                                            disabled={isDisabled}
                                            className={
                                                "flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium transition-all " +
                                                (isSelected
                                                    ? (isDisabled
                                                        ? "bg-primary/20 text-primary border border-primary/30 cursor-not-allowed"
                                                        : "bg-primary/10 text-primary border border-primary/20 hover:bg-primary/20")
                                                    : (readOnlyMode
                                                        ? "bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 border border-transparent cursor-not-allowed"
                                                        : "bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 border border-transparent hover:border-gray-300 dark:hover:border-gray-600"))
                                            }
                                        >
                                            <span className="material-symbols-outlined text-sm">{pkg.icon}</span>
                                            <span className="flex-1 text-left">{pkg.label}</span>
                                            {isRequired && (
                                                <span className="material-symbols-outlined text-[10px] text-primary/60">lock</span>
                                            )}
                                            {!isRequired && isSelected && (
                                                <span className="material-symbols-outlined text-sm text-primary">check_circle</span>
                                            )}
                                        </button>
                                    );
                                })}
                            </div>
                        </div>
                    </div>
                    
                    <div className="px-5 py-4 bg-gray-50 dark:bg-gray-800/50 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between gap-3">
                        {readOnlyMode ? (
                            <div className="flex items-center gap-2 text-green-600 dark:text-green-400">
                                <span className="material-symbols-outlined text-lg icon-fill">check_circle</span>
                                <span className="text-sm font-medium">{isZh ? '已确认并生成' : 'Confirmed & Generated'}</span>
                            </div>
                        ) : isGenerating ? (
                            <div className="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg text-gray-400 dark:text-gray-500 cursor-not-allowed">
                                <span className="material-symbols-outlined text-lg opacity-50">{editMode ? 'visibility' : 'edit'}</span>
                                {editMode ? labels.preview : labels.edit}
                            </div>
                        ) : (
                            <button
                                onClick={function() { setEditMode(!editMode); }}
                                className="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
                            >
                                <span className="material-symbols-outlined text-lg">{editMode ? 'visibility' : 'edit'}</span>
                                {editMode ? labels.preview : labels.edit}
                            </button>
                        )}
                        {readOnlyMode ? (
                            <div className="flex items-center gap-2 px-5 py-2.5 text-sm font-bold rounded-xl bg-gray-200 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed">
                                <span className="material-symbols-outlined text-lg">done_all</span>
                                {isZh ? '蓝图已生成' : 'Blueprint Generated'}
                            </div>
                        ) : isGenerating ? (
                            <div className="flex items-center gap-2 px-5 py-2.5 text-sm font-bold rounded-xl bg-primary/70 text-white cursor-not-allowed">
                                <span className="material-symbols-outlined text-lg animate-spin">progress_activity</span>
                                {isZh ? '正在生成...' : 'Generating...'}
                            </div>
                        ) : (
                            <button
                                onClick={handleConfirm}
                                className="flex items-center gap-2 px-5 py-2.5 text-sm font-bold rounded-xl bg-primary text-white hover:bg-primary-hover transition-all shadow-md hover:shadow-lg active:scale-[0.98]"
                            >
                                <span className="material-symbols-outlined text-lg">auto_awesome</span>
                                {labels.generate}
                            </button>
                        )}
                    </div>
                </div>
            );
        };

        // Get icon for file type (defined globally for use in ChatMessage)

// getFileIcon, isChinese, getLocalizedMsg - defined in js/utils.js

var AttachmentPreview = ({ attachment, onClose }) => {
            var [zoom, setZoom] = React.useState(1);
            var [position, setPosition] = React.useState({ x: 0, y: 0 });
            var [isDragging, setIsDragging] = React.useState(false);
            var [dragStart, setDragStart] = React.useState({ x: 0, y: 0 });
            
            if (!attachment) return null;
            
            var isImage = attachment.analysis_type === 'image' || 
                          (attachment.filename && /\.(jpg|jpeg|png|gif|webp|svg|bmp)$/i.test(attachment.filename));
            
            var handleZoomIn = function(e) {
                e.stopPropagation();
                setZoom(function(z) { return Math.min(z + 0.25, 3); });
            };
            
            var handleZoomOut = function(e) {
                e.stopPropagation();
                setZoom(function(z) { return Math.max(z - 0.25, 0.5); });
            };
            
            var handleReset = function(e) {
                e.stopPropagation();
                setZoom(1);
                setPosition({ x: 0, y: 0 });
            };
            
            var handleWheel = function(e) {
                e.preventDefault();
                e.stopPropagation();
                if (e.deltaY < 0) {
                    setZoom(function(z) { return Math.min(z + 0.1, 3); });
                } else {
                    setZoom(function(z) { return Math.max(z - 0.1, 0.5); });
                }
            };
            
            var handleMouseDown = function(e) {
                if (zoom > 1) {
                    e.stopPropagation();
                    setIsDragging(true);
                    setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y });
                }
            };
            
            var handleMouseMove = function(e) {
                if (isDragging && zoom > 1) {
                    e.stopPropagation();
                    setPosition({
                        x: e.clientX - dragStart.x,
                        y: e.clientY - dragStart.y
                    });
                }
            };
            
            var handleMouseUp = function() {
                setIsDragging(false);
            };
            
            return (
                <div 
                    className="fixed inset-0 z-[10001] flex items-center justify-center bg-black/80 backdrop-blur-sm animate-fade-in"
                    onClick={onClose}
                    onMouseMove={handleMouseMove}
                    onMouseUp={handleMouseUp}
                    onMouseLeave={handleMouseUp}
                >
                    {/* Close button */}
                    <button 
                        onClick={onClose}
                        className="absolute top-4 right-4 text-white/80 hover:text-white transition-colors z-10"
                    >
                        <span className="material-symbols-outlined text-3xl">close</span>
                    </button>
                    
                    {isImage ? (
                        <React.Fragment>
                            {/* Zoom controls */}
                            <div className="absolute bottom-6 left-1/2 -translate-x-1/2 flex items-center gap-2 bg-black/60 rounded-full px-4 py-2 z-10">
                                <button
                                    onClick={handleZoomOut}
                                    className="text-white/80 hover:text-white transition-colors p-1"
                                    title="Zoom out"
                                >
                                    <span className="material-symbols-outlined">remove</span>
                                </button>
                                <span className="text-white text-sm font-medium min-w-[50px] text-center">
                                    {Math.round(zoom * 100)}%
                                </span>
                                <button
                                    onClick={handleZoomIn}
                                    className="text-white/80 hover:text-white transition-colors p-1"
                                    title="Zoom in"
                                >
                                    <span className="material-symbols-outlined">add</span>
                                </button>
                                <div className="w-px h-5 bg-white/30 mx-1"></div>
                                <button
                                    onClick={handleReset}
                                    className="text-white/80 hover:text-white transition-colors p-1"
                                    title="Reset zoom"
                                >
                                    <span className="material-symbols-outlined text-[20px]">fit_screen</span>
                                </button>
                            </div>
                            
                            {/* Image with zoom */}
                            <div 
                                className="overflow-hidden"
                                style={{ maxWidth: '90vw', maxHeight: '85vh' }}
                                onWheel={handleWheel}
                            >
                                <img 
                                    src={attachment.file_url}
                                    alt={attachment.filename}
                                    className="rounded-lg shadow-2xl transition-transform duration-100"
                                    style={{
                                        transform: 'scale(' + zoom + ') translate(' + (position.x / zoom) + 'px, ' + (position.y / zoom) + 'px)',
                                        cursor: zoom > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default',
                                        maxWidth: '90vw',
                                        maxHeight: '85vh',
                                        objectFit: 'contain'
                                    }}
                                    onClick={function(e) { e.stopPropagation(); }}
                                    onMouseDown={handleMouseDown}
                                    draggable={false}
                                />
                            </div>
                            
                            {/* Filename */}
                            <div className="absolute top-4 left-4 bg-black/60 rounded-lg px-3 py-1.5 text-white text-sm max-w-[60%] truncate">
                                {attachment.filename}
                            </div>
                        </React.Fragment>
                    ) : (
                        <div 
                            className="bg-white dark:bg-gray-800 rounded-2xl p-8 max-w-md animate-modal-scale-in"
                            onClick={function(e) { e.stopPropagation(); }}
                        >
                            <div className="text-center">
                                <span className="text-6xl mb-4 block">{getFileIcon(attachment.analysis_type)}</span>
                                <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{attachment.filename}</h3>
                                <p className="text-sm text-gray-500 dark:text-gray-400 mb-4">
                                    {attachment.analysis_type || 'File'}
                                </p>
                                <a
                                    href={attachment.file_url}
                                    target="_blank"
                                    rel="noopener noreferrer"
                                    className="inline-flex items-center gap-2 px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors"
                                >
                                    <span className="material-symbols-outlined text-lg">open_in_new</span>
                                    Open File
                                </a>
                            </div>
                        </div>
                    )}
                </div>
            );
        };

        // Gemini-style "Show thinking" collapsible component
var ThinkingCollapse = ({ thoughtText, isChinese, isStreaming }) => {
            // Default to expanded (open by default)
            var [isExpanded, setIsExpanded] = React.useState(true);
            var contentRef = React.useRef(null);
            
            if (!thoughtText) return null;
            
            // Auto-scroll to bottom when streaming new content
            React.useEffect(function() {
                if (isStreaming && isExpanded && contentRef.current) {
                    contentRef.current.scrollTop = contentRef.current.scrollHeight;
                }
            }, [thoughtText, isStreaming, isExpanded]);
            
            // Format thought text: convert **title** to styled headers
            var formatThought = function(text) {
                // Normalize multiple newlines to double newlines before splitting
                var normalizedText = text.replace(/\n{3,}/g, '\n\n');
                var paragraphs = normalizedText.split('\n\n');
                var elements = [];
                for (var i = 0; i < paragraphs.length; i++) {
                    // Trim each paragraph to remove leading/trailing whitespace
                    var paragraph = paragraphs[i].trim();
                    if (!paragraph) continue;
                    
                    var titleMatch = paragraph.match(/^\*\*(.+?)\*\*/);
                    if (titleMatch) {
                        var title = titleMatch[1];
                        var rest = paragraph.replace(/^\*\*.+?\*\*\s*/, '').trim();
                        elements.push(
                            React.createElement('div', { key: i, className: "mb-3" },
                                React.createElement('div', { className: "font-semibold text-gray-700 dark:text-gray-200 text-sm mb-1" }, title),
                                rest && React.createElement('div', { className: "text-gray-500 dark:text-gray-400 text-sm leading-relaxed" }, rest)
                            )
                        );
                    } else {
                        elements.push(
                            React.createElement('p', { key: i, className: "text-gray-500 dark:text-gray-400 text-sm leading-relaxed mb-2" }, paragraph)
                        );
                    }
                }
                return elements;
            };
            
            return (
                React.createElement('div', { className: "mb-2" },
                    React.createElement('button', {
                        onClick: function() { setIsExpanded(!isExpanded); },
                        className: "flex items-center gap-2 px-3 py-1.5 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-sm text-gray-600 dark:text-gray-300 font-medium"
                    },
                        React.createElement('span', { className: "text-primary" }, "✦"),
                        React.createElement('span', null, isExpanded ? (isChinese ? '隐藏思考' : 'Hide thinking') : (isChinese ? '显示思考' : 'Show thinking')),
                        isStreaming && React.createElement('span', { className: "text-xs text-gray-400" }, "..."),
                        React.createElement('span', {
                            className: 'material-symbols-outlined text-base transition-transform ' + (isExpanded ? 'rotate-180' : ''),
                            style: {fontSize: '18px'}
                        }, "expand_more")
                    ),
                    isExpanded && React.createElement('div', { 
                        ref: contentRef,
                        className: "mt-3 pl-4 border-l-2 border-primary/20 max-h-96 overflow-y-auto scroll-smooth" 
                    },
                        formatThought(thoughtText)
                    )
                )
            );
        };

        // Global cache for streaming blueprint - persists across component remounts
        // This prevents flickering when StreamingBlueprintCard is unmounted/remounted during rapid state updates

// streamingBlueprintCache - defined in js/utils.js

        
        // Streaming Blueprint Card - with stable caching to prevent flicker
var StreamingBlueprintCard = ({ partialContent, isChinese }) => {
            // State for preview modal
            const [showPreviewModal, setShowPreviewModal] = React.useState(false);
            const [isFullscreen, setIsFullscreen] = React.useState(false);
            const previewContentRef = React.useRef(null);
            
            // Use global cache instead of useRef to persist across remounts
            const parsed = tryParseStreamingBlueprint(partialContent);
            
            // Only update cache if:
            // 1. Parse is successful
            // 2. Content is longer than last time (streaming is progressing, not reverting)
            // 3. We have meaningful data (title or other fields)
            if (parsed.success && parsed.partialData && partialContent.length >= streamingBlueprintCache.lastContentLength) {
                const newBp = parsed.partialData;
                const oldBp = streamingBlueprintCache.bp;
                
                // Merge new data with existing cache - never lose data
                const mergedBp = { ...oldBp };
                
                // Only update fields if new value exists and is not empty
                if (newBp.title) mergedBp.title = newBp.title;
                if (newBp.summary && newBp.summary.length > (mergedBp.summary || '').length) {
                    mergedBp.summary = newBp.summary;
                }
                if (newBp.metadata) {
                    mergedBp.metadata = { ...(mergedBp.metadata || {}), ...newBp.metadata };
                }
                if (newBp.standards_alignment) {
                    mergedBp.standards_alignment = newBp.standards_alignment;
                }
                if (newBp.learning_objectives && newBp.learning_objectives.length >= (mergedBp.learning_objectives || []).length) {
                    mergedBp.learning_objectives = newBp.learning_objectives;
                }
                if (newBp.lesson_flow && newBp.lesson_flow.length >= (mergedBp.lesson_flow || []).length) {
                    mergedBp.lesson_flow = newBp.lesson_flow;
                }
                if (newBp.materials_to_create && newBp.materials_to_create.length >= (mergedBp.materials_to_create || []).length) {
                    mergedBp.materials_to_create = newBp.materials_to_create;
                }
                
                streamingBlueprintCache.bp = mergedBp;
                streamingBlueprintCache.displayTitle = mergedBp.title || streamingBlueprintCache.displayTitle;
                streamingBlueprintCache.lastContentLength = partialContent.length;
            }
            
            // Get current cached values (create a copy to ensure React detects changes)
            const bp = { ...streamingBlueprintCache.bp };
            const displayTitle = streamingBlueprintCache.displayTitle || (isChinese ? '正在生成课程蓝图...' : 'Generating lesson blueprint...');
            
            // Track content length with state to force re-renders during streaming
            // Use a key trick: always update state to force React to re-render
            const [renderTrigger, setRenderTrigger] = React.useState(0);
            const lastLengthRef = React.useRef(0);
            
            // Force re-render whenever partialContent changes length
            React.useEffect(() => {
                if (partialContent.length !== lastLengthRef.current) {
                    lastLengthRef.current = partialContent.length;
                    setRenderTrigger(prev => prev + 1);
                }
            }, [partialContent]);
            
            // Auto-scroll to bottom when content updates in preview modal
            React.useEffect(() => {
                if (showPreviewModal && previewContentRef.current) {
                    previewContentRef.current.scrollTop = previewContentRef.current.scrollHeight;
                }
            }, [partialContent, showPreviewModal, renderTrigger]);
            
            // Generate markdown from cached blueprint data for preview
            // Recompute whenever renderTrigger changes (which happens on every content update)
            const streamingMarkdown = React.useMemo(() => {
                // renderTrigger ensures this runs on every content update
                const _ = renderTrigger;
                const currentBp = streamingBlueprintCache.bp;
                if (!currentBp || Object.keys(currentBp).length === 0) return '';
                return blueprintToMarkdown(currentBp);
            }, [renderTrigger]);
            
            return (
                <div className="w-full max-w-2xl">
                    {/* Blueprint Generating header - matches final style */}
                    <div className="flex gap-2 items-center mb-2">
                        <div className="size-6 rounded-full bg-green-100 dark:bg-green-900/20 flex items-center justify-center">
                            <span className="material-symbols-outlined text-green-600 dark:text-green-400 text-sm icon-fill animate-pulse">edit_note</span>
                        </div>
                        <span className="text-xs font-semibold uppercase tracking-wider text-green-600 dark:text-green-400">
                            {isChinese ? '正在生成 Blueprint...' : 'Generating Blueprint...'}
                        </span>
                        <span className="text-xs text-green-500 dark:text-green-400 ml-auto tabular-nums">{partialContent.length} chars</span>
                    </div>
                    
                    {/* Card styled exactly like BlueprintMessage preview - CLICKABLE */}
                    <div 
                        className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 shadow-sm overflow-hidden hover:shadow-lg transition-shadow cursor-pointer"
                        onClick={() => setShowPreviewModal(true)}
                    >
                        {/* Header with icon and title */}
                        <div className="flex items-center gap-3 px-5 py-4 border-b border-gray-100 dark:border-gray-800">
                            <div className="w-9 h-9 rounded-lg flex items-center justify-center flex-shrink-0 bg-blue-600">
                                <span className="material-symbols-outlined text-white text-lg icon-fill">description</span>
                            </div>
                            <div className="flex-1 text-left min-w-0">
                                <div className="text-[15px] font-semibold text-gray-900 dark:text-white truncate">
                                    {displayTitle}
                                </div>
                            </div>
                            {/* Click to preview hint */}
                            <div className="flex items-center gap-1 text-xs text-gray-400">
                                <span className="material-symbols-outlined text-sm">visibility</span>
                                {isChinese ? '点击预览' : 'Click to preview'}
                            </div>
                        </div>
                        
                        {/* Document Preview - Same structure as final BlueprintMessage */}
                        <div className="relative px-5 py-4 overflow-hidden bg-gray-50/50 dark:bg-gray-950/50 pointer-events-none" style={{ maxHeight: '280px' }}>
                            {/* Title */}
                            <h3 className="text-base font-semibold text-gray-900 dark:text-white leading-snug mb-1">
                                {displayTitle}
                            </h3>
                            
                            {/* Summary/Description */}
                            <p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed mb-3 line-clamp-2">
                                {bp.summary || (isChinese ? '正在生成课程摘要...' : 'Generating lesson summary...')}
                            </p>
                            
                            {/* Metadata Line */}
                            <p className="text-xs text-gray-500 dark:text-gray-500 mb-3">
                                <span className="font-medium text-gray-700 dark:text-gray-300">{isChinese ? '年级' : 'Grade'}:</span> {(bp.metadata && bp.metadata.grade_level) || '...'} | 
                                <span className="font-medium text-gray-700 dark:text-gray-300 ml-1">{isChinese ? '学科' : 'Subject'}:</span> {(bp.metadata && bp.metadata.subject) || '...'} | 
                                <span className="font-medium text-gray-700 dark:text-gray-300 ml-1">{isChinese ? '时长' : 'Duration'}:</span> {(bp.metadata && bp.metadata.duration_minutes) || '...'} {isChinese ? '分钟' : 'minutes'}
                            </p>
                            
                            {/* Standards Section */}
                            {bp.standards_alignment && bp.standards_alignment.primary_standards && bp.standards_alignment.primary_standards.length > 0 && (
                                <div className="mb-3">
                                    <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1">{isChinese ? '教学标准' : 'Standards'}</h4>
                                    <p className="text-xs text-gray-500 dark:text-gray-400 line-clamp-1">
                                        {bp.standards_alignment.primary_standards[0]}
                                    </p>
                                </div>
                            )}
                            
                            {/* Learning Objectives Section */}
                            {bp.learning_objectives && bp.learning_objectives.length > 0 && (
                                <div className="mb-3">
                                    <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1">{isChinese ? '学习目标' : 'Learning Objectives'}</h4>
                                    <div className="space-y-0.5">
                                        {bp.learning_objectives.slice(0, 3).map(function(obj, idx) {
                                            var text = obj.text || obj;
                                            return (
                                                <p key={idx} className="text-xs text-gray-500 dark:text-gray-400 line-clamp-1">
                                                    {text}
                                                </p>
                                            );
                                        })}
                                    </div>
                                </div>
                            )}
                            
                            {/* Lesson Flow Preview - Show all items as they stream in */}
                            {bp.lesson_flow && bp.lesson_flow.length > 0 && (
                                <div className="mb-3">
                                    <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1">
                                        {isChinese ? '课程流程' : 'Lesson Flow'} ({bp.lesson_flow.length})
                                    </h4>
                                    <div className="space-y-0.5">
                                        {bp.lesson_flow.map(function(flow, idx) {
                                            const duration = flow.duration_minutes ? ` (${flow.duration_minutes} ${isChinese ? '分钟' : 'min'})` : '';
                                            return (
                                                <p key={idx} className="text-xs text-gray-500 dark:text-gray-400 line-clamp-1">
                                                    {(idx + 1) + '. ' + (flow.title || '').substring(0, 40)}{duration}
                                                </p>
                                            );
                                        })}
                                    </div>
                                </div>
                            )}
                            
                            {/* Materials to Create - Package Types (Streaming) */}
                            {bp.materials_to_create && bp.materials_to_create.length > 0 && (
                                <div className="mb-3">
                                    <h4 className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1.5">{isChinese ? '📦 将生成材料' : '📦 Materials'}</h4>
                                    <div className="flex flex-wrap gap-1">
                                        {bp.materials_to_create.map(function(mat, idx) {
                                            var typeIcons = {
                                                'slide': 'slideshow',
                                                'lesson_plan': 'description',
                                                'worksheet': 'assignment',
                                                'quiz': 'quiz',
                                                'exit_ticket': 'receipt_long',
                                                'group_activity': 'groups',
                                                'rubric': 'format_list_numbered',
                                                'vocabulary_cards': 'style'
                                            };
                                            var typeLabels = {
                                                'slide': isChinese ? '课件' : 'Slides',
                                                'lesson_plan': isChinese ? '教案' : 'Plan',
                                                'worksheet': isChinese ? '练习' : 'Sheet',
                                                'quiz': isChinese ? '测验' : 'Quiz',
                                                'exit_ticket': isChinese ? '出门检测' : 'Exit',
                                                'group_activity': isChinese ? '小组' : 'Group',
                                                'rubric': isChinese ? '评分' : 'Rubric',
                                                'vocabulary_cards': isChinese ? '词汇' : 'Vocab'
                                            };
                                            var icon = typeIcons[mat.type] || 'folder';
                                            var label = typeLabels[mat.type] || mat.type;
                                            return (
                                                <span key={idx} className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 text-[10px] rounded-full">
                                                    <span className="material-symbols-outlined" style={{fontSize: '10px'}}>{icon}</span>
                                                    {label}
                                                </span>
                                            );
                                        })}
                                    </div>
                                </div>
                            )}
                            
                            {/* Loading indicator when no content yet */}
                            {!bp.learning_objectives && !bp.lesson_flow && (
                                <div className="flex items-center gap-2 text-xs text-gray-400">
                                    <span className="material-symbols-outlined text-sm animate-spin">progress_activity</span>
                                    {isChinese ? '正在生成更多内容...' : 'Generating more content...'}
                                </div>
                            )}
                            
                            {/* Soft Gradient Fade */}
                            <div className="absolute left-0 right-0 bottom-0 h-20 bg-gradient-to-t from-gray-50 dark:from-gray-950 via-gray-50/90 dark:via-gray-950/90 to-transparent pointer-events-none"></div>
                        </div>
                    </div>
                    
                    {/* Preview Modal - Read-only during streaming */}
                    {showPreviewModal && (
                        <div 
                            className="fixed inset-0 z-[9999] flex items-center justify-center bg-slate-900/60 backdrop-blur-sm animate-fade-in" 
                            onClick={(e) => { if (e.target === e.currentTarget) setShowPreviewModal(false); }}
                        >
                            <div className={`relative bg-white dark:bg-gray-900 rounded-2xl shadow-2xl flex flex-col overflow-hidden animate-modal-scale-in transition-all duration-300 ${isFullscreen ? 'w-full h-full rounded-none' : 'w-[95vw] max-w-6xl h-[90vh]'}`}>
                                {/* Modal Header */}
                                <div className="flex items-center justify-between px-4 py-2.5 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 z-10 flex-shrink-0 rounded-t-2xl">
                                    <div className="flex items-center gap-3 min-w-0">
                                        <div className="w-8 h-8 rounded-lg bg-blue-600 flex items-center justify-center flex-shrink-0">
                                            <span className="material-symbols-outlined text-white text-base icon-fill">description</span>
                                        </div>
                                        <div className="flex flex-col min-w-0">
                                            <div className="flex items-center gap-2">
                                                <h2 className="text-sm font-bold text-gray-900 dark:text-white truncate leading-tight">
                                                    {displayTitle}
                                                </h2>
                                                {/* Streaming indicator */}
                                                <span className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-green-100 dark:bg-green-900/30 text-green-600 dark:text-green-400 text-[10px] font-semibold animate-pulse">
                                                    <span className="material-symbols-outlined text-xs">edit_note</span>
                                                    {isChinese ? '生成中...' : 'Generating...'}
                                                </span>
                                            </div>
                                            <span className="text-[10px] text-gray-400 uppercase tracking-wide">
                                                {isChinese ? '实时预览 • 只读模式' : 'LIVE PREVIEW • READ-ONLY'}
                                            </span>
                                        </div>
                                    </div>
                                    
                                    {/* Header Actions - Only fullscreen and close during streaming */}
                                    <div className="flex items-center gap-1">
                                        {/* Fullscreen toggle */}
                                        <button
                                            onClick={() => setIsFullscreen(!isFullscreen)}
                                            className="p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-500 dark:text-gray-400 transition-colors"
                                            title={isFullscreen ? (isChinese ? '退出全屏' : 'Exit fullscreen') : (isChinese ? '全屏' : 'Fullscreen')}
                                        >
                                            <span className="material-symbols-outlined text-lg">{isFullscreen ? 'fullscreen_exit' : 'fullscreen'}</span>
                                        </button>
                                        {/* Download disabled during streaming */}
                                        <button
                                            disabled
                                            className="p-1.5 rounded-lg text-gray-300 dark:text-gray-600 cursor-not-allowed"
                                            title={isChinese ? '生成完成后可下载' : 'Download available after generation'}
                                        >
                                            <span className="material-symbols-outlined text-lg">download</span>
                                        </button>
                                        {/* Close button */}
                                        <button
                                            onClick={() => setShowPreviewModal(false)}
                                            className="p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-500 dark:text-gray-400 transition-colors"
                                        >
                                            <span className="material-symbols-outlined text-lg">close</span>
                                        </button>
                                    </div>
                                </div>
                                
                                {/* Modal Content - Streaming Markdown Preview */}
                                <div 
                                    ref={previewContentRef}
                                    className="flex-1 overflow-y-auto bg-gray-100/80 dark:bg-gray-950 scroll-smooth"
                                >
                                    <div className={`mx-auto py-8 px-6 sm:px-12 transition-all duration-300 ${isFullscreen ? 'max-w-6xl' : 'max-w-4xl'}`}>
                                        <div className="bg-white dark:bg-gray-900 shadow-lg rounded-xl border border-gray-200/60 dark:border-gray-800 overflow-hidden">
                                            {/* Streaming indicator bar */}
                                            <div className="h-1 bg-gradient-to-r from-green-400 via-green-500 to-green-400 animate-pulse"></div>
                                            
                                            {/* Document Content - Same style as final BlueprintMessage */}
                                            <div className={`manus-document transition-all duration-300 ${isFullscreen ? 'p-12 sm:p-20 text-lg [&_h1]:text-5xl [&_h2]:text-3xl [&_h3]:text-2xl [&_h4]:text-xl [&_p]:text-lg [&_li]:text-lg' : 'p-10 sm:p-16'}`}>
                                                {streamingMarkdown ? (
                                                    <div 
                                                        className="prose prose-slate dark:prose-invert max-w-none prose-headings:font-semibold prose-h1:text-3xl prose-h1:mb-4 prose-h2:text-xl prose-h2:mt-8 prose-h2:mb-3 prose-h3:text-lg prose-h3:mt-6 prose-p:text-base prose-p:leading-relaxed prose-li:text-base"
                                                        dangerouslySetInnerHTML={{ __html: typeof marked !== 'undefined' ? marked.parse(streamingMarkdown) : streamingMarkdown }}
                                                    />
                                                ) : (
                                                    <div className="flex flex-col items-center justify-center py-20 text-gray-400">
                                                        <span className="material-symbols-outlined text-5xl mb-4 animate-spin">progress_activity</span>
                                                        <p className="text-lg">{isChinese ? '等待内容生成...' : 'Waiting for content...'}</p>
                                                    </div>
                                                )}
                                            </div>
                                            
                                            {/* Bottom streaming indicator */}
                                            <div className="px-6 py-3 bg-green-50 dark:bg-green-900/20 border-t border-green-100 dark:border-green-900/30 flex items-center justify-between">
                                                <div className="flex items-center gap-2 text-sm text-green-600 dark:text-green-400">
                                                    <span className="material-symbols-outlined text-base animate-pulse">edit_note</span>
                                                    {isChinese ? '正在实时生成内容...' : 'Generating content in real-time...'}
                                                </div>
                                                <span className="text-xs text-green-500 dark:text-green-400 tabular-nums font-mono">
                                                    {partialContent.length} chars
                                                </span>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    )}
                </div>
            );
        };

var ChatMessage = ({ message, isUser, type, blueprint, pkg, phase, progress, detailLog, activityLogs, onBlueprintSave, onBlueprintGenerate, generationId, blueprintHistory, version, updatedAt, quickOptions, onQuickOptionClick, isLastMessage, extractedInfo, lessonInfo, onConfirmGenerate, isGeneratingMaterials, isGeneratingBlueprint, onCreateMaterials, materialsGenerated, blueprintModifiedSinceGeneration, onBlueprintModified, isGenerationContext, attachments, isReadOnly, isHistoryVersion, historyVersion, historyTimestamp, thoughtText, streamingContent, isStreaming, conversationMessages, selectedPackages, completedSteps, currentStep, slidePreview }) => {
            // State for attachment preview
            var [previewAttachment, setPreviewAttachment] = React.useState(null);
            
            // Confirmed summary - read-only display for history
            if (type === 'confirmed_summary' && lessonInfo) {
                return (
                    <div className="flex flex-col gap-2 items-start">
                        <div className="flex gap-2 items-center mb-1">
                            <div className="size-6 rounded-full bg-primary/10 flex items-center justify-center">
                                <span className="material-symbols-outlined text-primary text-sm">dashboard_customize</span>
                            </div>
                            <span className="text-xs font-bold text-primary">Tess</span>
                        </div>
                        <ConfirmedLessonSummary lessonInfo={lessonInfo} />
                    </div>
                );
            }
            
            if (type === 'confirmation' && extractedInfo) {
                return (
                    <div className="flex flex-col gap-2 items-start">
                        <div className="flex gap-2 items-center mb-1">
                            <div className="size-6 rounded-full bg-primary/10 flex items-center justify-center">
                                <span className="material-symbols-outlined text-primary text-sm">dashboard_customize</span>
                            </div>
                            <span className="text-xs font-bold text-primary">Tess</span>
                        </div>
                        <div className="font-chat text-[15px] p-3 max-w-[90%] text-[#34322D] dark:text-gray-200 leading-relaxed mb-2">
                            {message}
                        </div>
                        <ConfirmationCard 
                            extractedInfo={extractedInfo} 
                            onConfirm={onConfirmGenerate}
                            messages={conversationMessages}
                            isReadOnly={materialsGenerated}
                            isGenerating={isGeneratingBlueprint}
                        />
                    </div>
                );
            }
            
            if (type === 'blueprint' && blueprint) {
                return (
                    <BlueprintMessage 
                        blueprint={blueprint} 
                        onSave={onBlueprintSave} 
                        onGenerate={onBlueprintGenerate}
                        generationId={generationId}
                        blueprintHistory={blueprintHistory}
                        version={version}
                        updatedAt={updatedAt}
                        isGeneratingMaterials={isGeneratingMaterials}
                        materialsGenerated={materialsGenerated}
                        blueprintModifiedSinceGeneration={blueprintModifiedSinceGeneration}
                        onBlueprintModified={onBlueprintModified}
                        isGenerationContext={isGenerationContext}
                        isReadOnly={isReadOnly || isHistoryVersion}
                        isHistoryVersion={isHistoryVersion}
                        historyVersion={historyVersion}
                        historyTimestamp={historyTimestamp}
                        streamingContent={streamingContent}
                        isStreaming={isStreaming}
                        thoughtText={thoughtText}
                    />
                );
            }
            
            // Blueprint ready message with action buttons
            // Hide buttons when materials are already generated
            if (type === 'blueprint_ready') {
                return (
                    <div className="flex flex-col gap-2 items-start">
                        <div className="flex gap-2 items-center mb-1">
                            <div className="size-6 rounded-full bg-emerald-100 dark:bg-emerald-900/30 flex items-center justify-center">
                                <span className="material-symbols-outlined text-emerald-600 dark:text-emerald-400 text-sm">check_circle</span>
                            </div>
                            <span className="text-xs font-bold text-emerald-600 dark:text-emerald-400">BLUEPRINT READY</span>
                        </div>
                        <div className="bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-200 dark:border-emerald-800 rounded-xl p-4 max-w-[90%]">
                            <p className="font-chat text-[14px] text-emerald-800 dark:text-emerald-200 leading-relaxed whitespace-pre-line">{message}</p>
                            {/* Only show action buttons if materials not yet generated */}
                            {!materialsGenerated && (
                                <div className="flex flex-wrap gap-2 mt-4">
                                    <button
                                        onClick={onCreateMaterials}
                                        disabled={isGeneratingMaterials}
                                        className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all ${isGeneratingMaterials ? 'bg-gray-300 dark:bg-gray-600 text-gray-500 cursor-not-allowed' : 'bg-primary text-white hover:bg-primary-hover shadow-md hover:shadow-lg'}`}
                                    >
                                        {isGeneratingMaterials ? (
                                            <React.Fragment>
                                                <span className="material-symbols-outlined text-base animate-spin">progress_activity</span>
                                                Generating...
                                            </React.Fragment>
                                        ) : (
                                            <React.Fragment>
                                                <span className="material-symbols-outlined text-base">auto_awesome</span>
                                                Create Materials Now
                                            </React.Fragment>
                                        )}
                                    </button>
                                    <button
                                        onClick={function() { 
                                            var blueprintBtn = document.querySelector('[data-blueprint-preview]');
                                            if (blueprintBtn) blueprintBtn.click();
                                        }}
                                        className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
                                    >
                                        <span className="material-symbols-outlined text-base">edit</span>
                                        Edit Blueprint First
                                    </button>
                                </div>
                            )}
                        </div>
                    </div>
                );
            }
            
            if (type === 'generating') {
                return React.createElement(GeneratingMessage, {
                    phase: phase,
                    progress: progress,
                    detailLog: detailLog,
                    activityLogs: activityLogs,
                    selectedPackages: selectedPackages,
                    completedSteps: completedSteps,
                    currentStep: currentStep,
                    slidePreview: slidePreview
                });
            }
            
            if (type === 'package' && pkg) {
                return <PackageMessage pkg={pkg} isHistoryVersion={isHistoryVersion} historyVersion={historyVersion} historyTimestamp={historyTimestamp} />;
            }
            
            // Check if we should show quick options (only for last assistant message with options)
            var showQuickOptions = !isUser && isLastMessage && quickOptions && quickOptions.length > 0;
            
            // Simple markdown rendering for chat messages (bold, italic)
            // Also removes the "📎 Attached: ..." line since we'll render attachments separately
            var renderChatMarkdown = function(text, hasAttachments) {
                if (!text) return '';
                var processedText = text;
                // Remove the "📎 Attached: ..." line if we have structured attachments to render
                if (hasAttachments) {
                    processedText = processedText.replace(/\n\n📎 Attached:.*$/s, '');
                    processedText = processedText.replace(/^📎 Attached:.*$/m, '');
                }
                // Escape HTML first to prevent XSS
                var html = processedText.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                // Replace **text** with <strong>text</strong> (bold)
                html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
                // Replace _text_ with <em>text</em> (italic)
                html = html.replace(/_([^_]+)_/g, '<em>$1</em>');
                // Preserve newlines
                html = html.replace(/\n/g, '<br/>');
                return html.trim();
            };
            
            // Check if message has attachments
            var hasAttachments = attachments && attachments.length > 0;
            var renderedMessage = renderChatMarkdown(message, hasAttachments);
            
            return (
                <div className={`flex flex-col gap-2 ${isUser ? 'items-end' : 'items-start'}`}>
                    {!isUser && (
                        <div className="flex gap-2 items-center mb-1">
                            <div className="size-6 rounded-full bg-primary/10 flex items-center justify-center">
                                <span className="material-symbols-outlined text-primary text-sm">dashboard_customize</span>
                            </div>
                            <span className="text-xs font-bold text-primary">Tess</span>
                        </div>
                    )}
                    {/* Show thinking collapsible - Gemini style (before message) */}
                    {!isUser && thoughtText && (
                        <ThinkingCollapse 
                            thoughtText={thoughtText} 
                            isChinese={message && /[\u4e00-\u9fff]/.test(message)}
                            isStreaming={false}
                        />
                    )}
                    {/* Text content (if any) */}
                    {renderedMessage && (
                        <div 
                            className={`${isUser ? 'bg-[#eaeff1] dark:bg-zinc-800 rounded-2xl rounded-tr-none' : ''} font-chat text-[15px] p-3 max-w-[90%] text-[#34322D] dark:text-gray-200 leading-relaxed [&_strong]:font-semibold [&_em]:italic`}
                            dangerouslySetInnerHTML={{ __html: renderedMessage }}
                        />
                    )}
                    {/* Attachments rendered as clickable cards/thumbnails */}
                    {hasAttachments && (
                        <div className={`flex flex-wrap gap-2 ${isUser ? 'justify-end' : 'justify-start'} max-w-[90%] mt-1`}>
                            {attachments.map(function(att, idx) {
                                var isImage = att.analysis_type === 'image' || 
                                              (att.filename && /\.(jpg|jpeg|png|gif|webp|svg|bmp)$/i.test(att.filename));
                                
                                // For images with file_url, show thumbnail; otherwise fallback to card
                                if (isImage && att.file_url) {
                                    // Image attachment - show thumbnail
                                    return (
                                        <div
                                            key={idx}
                                            onClick={function() { setPreviewAttachment(att); }}
                                            className="relative group cursor-pointer rounded-lg overflow-hidden border border-gray-200 dark:border-gray-600 hover:border-primary/50 transition-all shadow-sm hover:shadow-md"
                                            style={{ maxWidth: '200px', maxHeight: '150px' }}
                                        >
                                            <img 
                                                src={att.file_url}
                                                alt={att.filename}
                                                className="w-full h-full object-cover"
                                                style={{ maxWidth: '200px', maxHeight: '150px' }}
                                            />
                                            <div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors flex items-center justify-center">
                                                <span className="material-symbols-outlined text-white opacity-0 group-hover:opacity-100 transition-opacity text-2xl drop-shadow-lg">
                                                    zoom_in
                                                </span>
                                            </div>
                                            <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/60 to-transparent p-2 opacity-0 group-hover:opacity-100 transition-opacity">
                                                <span className="text-xs text-white truncate block">{att.filename}</span>
                                            </div>
                                        </div>
                                    );
                                }
                                
                                // Non-image attachment OR image without file_url (legacy) - show card
                                return (
                                    <div
                                        key={idx}
                                        onClick={att.file_url ? function() { setPreviewAttachment(att); } : undefined}
                                        className={'group flex items-center gap-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-sm ' + (att.file_url ? 'hover:border-primary/50 hover:bg-primary/5 transition-all shadow-sm hover:shadow cursor-pointer' : 'opacity-75')}
                                    >
                                        <span className="text-lg">{isImage ? '🖼️' : getFileIcon(att.analysis_type)}</span>
                                        <span className="text-gray-700 dark:text-gray-300 max-w-[200px] truncate font-medium">
                                            {att.filename}
                                        </span>
                                        {att.file_url && (
                                            <span className="material-symbols-outlined text-[16px] text-gray-400 group-hover:text-primary transition-colors">
                                                open_in_new
                                            </span>
                                        )}
                                    </div>
                                );
                            })}
                        </div>
                    )}
                    {/* Attachment Preview Modal */}
                    {previewAttachment && (
                        <AttachmentPreview 
                            attachment={previewAttachment} 
                            onClose={function() { setPreviewAttachment(null); }} 
                        />
                    )}
                    {showQuickOptions && (
                        <div className="mt-2 max-w-[90%]">
                            <div className="flex flex-wrap gap-2">
                                {quickOptions.map(function(option, idx) {
                                    return (
                                        <button
                                            key={idx}
                                            onClick={function() { onQuickOptionClick && onQuickOptionClick(option); }}
                                            className="px-3 py-1.5 text-sm font-medium rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-primary/10 hover:border-primary/30 hover:text-primary transition-all shadow-sm hover:shadow"
                                        >
                                            {option.label}
                                        </button>
                                    );
                                })}
                            </div>
                            {/* Manual input hint */}
                            <p className="text-[11px] text-gray-400 dark:text-gray-500 mt-2 flex items-center gap-1">
                                <span className="material-symbols-outlined text-[12px]">keyboard</span>
                                {/[\u4e00-\u9fa5]/.test(message || '') 
                                    ? '以上选项不合适？可以直接在下方输入框输入您的答案' 
                                    : 'None of these? You can type your answer in the input box below'}
                            </p>
                        </div>
                    )}
                </div>
            );
        };

        // Blueprint View
var BlueprintView = ({ blueprint, onAdjust, onCreateMaterials, isGeneratingMaterials, materialsGenerated }) => {
            const metadata = blueprint.metadata || {};
            const lessonFlow = blueprint.lesson_flow || [];
            const objectives = blueprint.learning_objectives || [];
            const differentiation = blueprint.differentiation_strategies || {};
            const standards = blueprint.standards_alignment || {};
            
            // Detect language from blueprint title
            const isZh = isChinese(blueprint.title || '');
            
            return (
                <div className="w-full bg-white dark:bg-paper-dark shadow-sm border border-[#e5e5e0] dark:border-zinc-700 rounded-lg overflow-hidden animate-fade-in-up">
                    {/* Header */}
                    <div className="p-4 pb-3 border-b border-[#f0f0f0] dark:border-zinc-700">
                        <div className="flex items-start justify-between mb-3">
                            <div className="flex flex-wrap gap-1.5">
                                <div className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-[#f0f4f5] dark:bg-zinc-700 text-[10px] font-semibold text-[#3b4d50] dark:text-gray-300">
                                    <span className="material-symbols-outlined text-[12px]">school</span>
                                    {metadata.grade_level || '?'}
                                </div>
                                <div className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-[#f0f4f5] dark:bg-zinc-700 text-[10px] font-semibold text-[#3b4d50] dark:text-gray-300">
                                    <span className="material-symbols-outlined text-[12px]">science</span>
                                    {metadata.subject || (isZh ? '通用' : 'General')}
                                </div>
                                <div className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-[#fff4e5] dark:bg-orange-900/30 text-[10px] font-semibold text-[#b56e26] dark:text-orange-200">
                                    <span className="material-symbols-outlined text-[12px]">schedule</span>
                                    {metadata.duration_minutes || 45}{isZh ? '分钟' : 'm'}
                                </div>
                            </div>
                            <div className="flex items-center gap-2">
                                <button className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300 transition-colors text-[11px] font-medium">
                                    <span className="material-symbols-outlined text-[14px]">history</span>
                                    {isZh ? '历史' : 'History'}
                                </button>
                                <button className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-300 transition-colors text-[11px] font-medium">
                                    <span className="material-symbols-outlined text-[14px]">share</span>
                                    {isZh ? '分享' : 'Share'}
                                </button>
                            </div>
                        </div>
                        <h1 className="text-lg font-bold text-[#101718] dark:text-white leading-tight mb-1">{blueprint.title}</h1>
                        <p className="text-gray-500 dark:text-gray-400 text-xs leading-relaxed">{blueprint.summary}</p>
                    </div>

                    {/* Learning Objectives */}
                    <div className="p-4 border-b border-[#f0f0f0] dark:border-zinc-700 group">
                        <div className="flex items-center justify-between mb-2">
                            <h3 className="text-sm font-bold text-[#101718] dark:text-gray-100 flex items-center gap-1.5">
                                <span className="w-0.5 h-4 bg-primary rounded-full"></span>
                                {isZh ? '学习目标' : 'Objectives'}
                            </h3>
                            <button onClick={() => onAdjust('objectives')} className="opacity-0 group-hover:opacity-100 transition-opacity text-primary hover:bg-primary/5 px-2 py-0.5 rounded text-[10px] font-medium flex items-center gap-0.5">
                                <span className="material-symbols-outlined text-[12px]">edit</span> {isZh ? '调整' : 'Adjust'}
                            </button>
                        </div>
                        <ul className="space-y-2">
                            {objectives.map((obj, i) => (
                                <li key={i} className="flex items-start gap-2">
                                    <span className="material-symbols-outlined text-green-600 mt-0.5 text-sm">check_circle</span>
                                    <span className="text-gray-700 dark:text-gray-300 text-xs leading-relaxed">{obj.text || obj}</span>
                                </li>
                            ))}
                        </ul>
                    </div>

                    {/* Standards */}
                    {standards.primary_standards && standards.primary_standards.length > 0 && (
                        <div className="p-3 border-b border-[#f0f0f0] dark:border-zinc-700 bg-[#fafafa] dark:bg-zinc-800/30">
                            <div className="flex items-start gap-2">
                                <span className="bg-[#eaeff1] dark:bg-zinc-700 text-gray-700 dark:text-gray-200 font-bold text-[10px] px-1.5 py-0.5 rounded border border-gray-300 dark:border-zinc-600 whitespace-nowrap">{isZh ? '教学标准' : 'Standards'}</span>
                                <span className="text-[#101718] dark:text-gray-100 font-medium text-[10px] leading-relaxed">{standards.primary_standards.join(', ')}</span>
                            </div>
                        </div>
                    )}

                    {/* Lesson Flow */}
                    <div className="p-4 border-b border-[#f0f0f0] dark:border-zinc-700">
                        <div className="flex items-center justify-between mb-4">
                            <h3 className="text-sm font-bold text-[#101718] dark:text-gray-100 flex items-center gap-1.5">
                                <span className="w-0.5 h-4 bg-primary rounded-full"></span>
                                {isZh ? '课程流程' : 'Lesson Flow'}
                            </h3>
                            <button onClick={() => onAdjust('flow')} className="text-gray-400 hover:text-primary transition-colors">
                                <span className="material-symbols-outlined text-[16px]">tune</span>
                            </button>
                        </div>
                        <div className="relative flex flex-col gap-0">
                            {lessonFlow.map((phase, i) => (
                                <div key={i} className={`relative flex gap-4 group ${i !== lessonFlow.length - 1 ? 'pb-6' : 'pb-2'}`}>
                                    {/* Timeline vertical line */}
                                    {i !== lessonFlow.length - 1 && (
                                        <div className="absolute left-[52px] top-[24px] bottom-0 w-px bg-gray-200 dark:bg-gray-700"></div>
                                    )}
                                    
                                    {/* Time and Phase Label */}
                                    <div className="flex flex-col items-center w-[88px] shrink-0 pt-1">
                                        <span className="text-sm font-bold text-gray-900 dark:text-white">{phase.duration_minutes || 5} min</span>
                                        <span className="text-[10px] text-gray-500 dark:text-gray-400 mt-0.5">{phase.phase || 'Activity'}</span>
                                    </div>
                                    
                                    {/* Timeline dot with icon */}
                                    <div className={`relative z-10 mt-1.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full transition-all ${
                                        i === 0 
                                            ? 'bg-primary text-white' 
                                            : 'bg-white dark:bg-zinc-700 border-2 border-gray-300 dark:border-gray-600 text-gray-400 dark:text-gray-500 group-hover:border-primary group-hover:text-primary'
                                    }`}>
                                        <span className="material-symbols-outlined text-[14px]">
                                            {phase.activity_type === 'Discussion' ? 'forum' : 
                                             phase.activity_type === 'Lab' || phase.activity_type === 'Activity' ? 'science' :
                                             phase.activity_type === 'Presentation' ? 'slideshow' :
                                             phase.activity_type === 'Assessment' ? 'assignment_turned_in' :
                                             'radio_button_checked'}
                                        </span>
                                    </div>
                                    
                                    {/* Content */}
                                    <div onClick={() => onAdjust(phase.id)} className="flex-1 pt-0.5 group-hover:bg-gray-50 dark:group-hover:bg-zinc-800 p-3 rounded-lg cursor-pointer transition-all border border-transparent group-hover:border-gray-200 dark:group-hover:border-zinc-600 group-hover:shadow-sm">
                                        <h4 className="text-sm font-bold text-[#101718] dark:text-white mb-1">{phase.title}</h4>
                                        <p className="text-xs text-gray-600 dark:text-gray-400 leading-relaxed mb-2">{phase.description}</p>
                                        {phase.activity_type && (
                                            <span className="inline-flex items-center rounded-full bg-blue-50 dark:bg-blue-900/30 px-2 py-1 text-[10px] font-medium text-blue-700 dark:text-blue-300">
                                                {phase.activity_type}
                                            </span>
                                        )}
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>

                    {/* Differentiation */}
                    <div className="p-4 bg-gray-50/50 dark:bg-zinc-800/20">
                        <div className="flex items-center justify-between mb-3">
                            <h3 className="text-sm font-bold text-[#101718] dark:text-gray-100 flex items-center gap-1.5">
                                <span className="w-0.5 h-4 bg-primary rounded-full"></span>
                                Differentiation
                            </h3>
                        </div>
                        <div className="flex flex-col gap-2">
                            {differentiation.struggling_learners && (
                                <div className="bg-white dark:bg-zinc-700 p-2.5 rounded-lg border border-gray-100 dark:border-zinc-600 shadow-sm relative overflow-hidden">
                                    <div className="absolute top-0 left-0 w-0.5 h-full bg-orange-400"></div>
                                    <div className="flex items-center gap-1.5 mb-1">
                                        <span className="material-symbols-outlined text-orange-400 text-xs">support</span>
                                        <h4 className="text-[10px] font-bold text-gray-900 dark:text-white">Support</h4>
                                    </div>
                                    <p className="text-[10px] text-gray-600 dark:text-gray-300 leading-relaxed">{differentiation.struggling_learners}</p>
                                </div>
                            )}
                            {differentiation.advanced_learners && (
                                <div className="bg-white dark:bg-zinc-700 p-2.5 rounded-lg border border-gray-100 dark:border-zinc-600 shadow-sm relative overflow-hidden">
                                    <div className="absolute top-0 left-0 w-0.5 h-full bg-purple-500"></div>
                                    <div className="flex items-center gap-1.5 mb-1">
                                        <span className="material-symbols-outlined text-purple-500 text-xs">rocket_launch</span>
                                        <h4 className="text-[10px] font-bold text-gray-900 dark:text-white">Extension</h4>
                                    </div>
                                    <p className="text-[10px] text-gray-600 dark:text-gray-300 leading-relaxed">{differentiation.advanced_learners}</p>
                                </div>
                            )}
                        </div>
                    </div>

                    {/* Create Materials CTA - only show if materials not yet generated */}
                    {!materialsGenerated ? (
                        <div className="p-4 bg-gradient-to-r from-primary/5 to-primary/10 border-t border-primary/20">
                            <button 
                                onClick={onCreateMaterials} 
                                disabled={isGeneratingMaterials}
                                className={`w-full flex items-center justify-center gap-2 text-white px-5 py-3 rounded-lg font-bold text-sm shadow-lg transition-all ${isGeneratingMaterials ? 'bg-gray-400 cursor-not-allowed' : 'bg-primary hover:bg-primary-hover transform hover:scale-[1.02] active:scale-95'}`}
                            >
                                {isGeneratingMaterials ? (
                                    <React.Fragment>
                                        <span className="material-symbols-outlined text-[18px] animate-spin">progress_activity</span>
                                        Generating materials...
                                    </React.Fragment>
                                ) : (
                                    <React.Fragment>
                                        <span className="material-symbols-outlined text-[18px] icon-fill">auto_awesome</span>
                                        Looks great! Create my materials
                                    </React.Fragment>
                                )}
                            </button>
                        </div>
                    ) : (
                        <div className="p-4 bg-gray-50 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700">
                            <div className="flex items-center justify-center gap-2 text-sm text-gray-600 dark:text-gray-400">
                                <span className="material-symbols-outlined text-green-500">check_circle</span>
                                Materials have been created. View them in the Materials tab.
                            </div>
                        </div>
                    )}
                </div>
            );
        };

        // Package View
var PackageView = ({ package: pkg, onBack }) => {
            const files = (pkg && pkg.files) || [];
            
            // Find the slides file for main download
            const slidesFile = files.find(f => f.type === 'slide');
            const downloadUrl = slidesFile && slidesFile.download_url;
            
            const handlePreview = (file) => {
                if (file.preview_url) {
                    window.open(file.preview_url, '_blank');
                } else {
                    alert('Preview not available for this item');
                }
            };
            
            const handleDownloadFile = (file) => {
                if (file.download_url) {
                    window.location.href = file.download_url;
                } else {
                    alert('Download not available for this item');
                }
            };
            
            const handleDownloadPackage = () => {
                if (downloadUrl) {
                    window.location.href = downloadUrl;
                } else {
                    alert('Package download not available');
                }
            };
            
            return (
                <div className="w-full animate-fade-in-up">
                    <div className="flex flex-col gap-3 mb-4 pb-3 border-b border-gray-200 dark:border-gray-700">
                        <div className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 w-fit">
                            <span className="material-symbols-outlined text-[12px] icon-fill">check_circle</span>
                            <span className="text-[10px] font-bold uppercase tracking-wider">Ready</span>
                        </div>
                        <h1 className="text-base font-bold text-slate-900 dark:text-white tracking-tight leading-tight">{(pkg && pkg.title) || 'Teaching Package'}</h1>
                        <button 
                            onClick={handleDownloadPackage} 
                            disabled={!downloadUrl}
                            className="w-full flex items-center justify-center gap-1.5 bg-primary hover:bg-primary-hover text-white px-3 py-2 rounded-lg font-bold text-xs shadow-md transform active:scale-95 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
                        >
                            <span className="material-symbols-outlined text-[14px]">folder_zip</span>
                            <span>{isChinese ? '下载文档' : 'Download Docs'}</span>
                        </button>
                    </div>

                    <div className="flex flex-col gap-3">
                        {files.map((file, i) => {
                            const icons = {
                                slide: { icon: 'slideshow', bg: 'bg-amber-50 dark:bg-amber-900/20', color: 'text-amber-600 dark:text-amber-400' },
                                worksheet: { icon: 'article', bg: 'bg-blue-50 dark:bg-blue-900/20', color: 'text-blue-600 dark:text-blue-400' },
                                quiz: { icon: 'assignment_turned_in', bg: 'bg-emerald-50 dark:bg-emerald-900/20', color: 'text-emerald-600 dark:text-emerald-400' },
                                exit_ticket: { icon: 'receipt_long', bg: 'bg-teal-50 dark:bg-teal-900/20', color: 'text-teal-600 dark:text-teal-400' },
                                document: { icon: 'description', bg: 'bg-rose-50 dark:bg-rose-900/20', color: 'text-rose-600 dark:text-rose-400' }
                            };
                            const iconConfig = icons[file.type] || icons.document;
                            const hasPreview = !!file.preview_url;
                            const hasDownload = !!file.download_url;
                            
                            return (
                                <div key={i} className="group bg-surface-light dark:bg-surface-dark border border-slate-200 dark:border-slate-700 rounded-lg p-3 hover:shadow-md hover:border-primary/30 transition-all">
                                    <div className="flex items-start gap-2.5 mb-2">
                                        <div className={`size-9 rounded-lg ${iconConfig.bg} ${iconConfig.color} flex items-center justify-center flex-shrink-0 group-hover:scale-105 transition-transform`}>
                                            <span className="material-symbols-outlined text-lg icon-fill">{iconConfig.icon}</span>
                                        </div>
                                        <div className="flex-1 min-w-0">
                                            <h3 className="font-bold text-xs text-slate-900 dark:text-white group-hover:text-primary transition-colors truncate">{file.title}</h3>
                                            <p className="text-[10px] text-slate-500 dark:text-slate-400 mt-0.5 line-clamp-1">{file.description}</p>
                                            {file.slide_count && (
                                                <p className="text-[9px] text-primary mt-0.5">{file.slide_count} slides</p>
                                            )}
                                        </div>
                                    </div>
                                    <div className="flex items-center gap-1.5">
                                        <button 
                                            onClick={() => handlePreview(file)} 
                                            disabled={!hasPreview}
                                            className="flex-1 flex items-center justify-center gap-1 h-7 rounded text-[10px] font-semibold text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
                                        >
                                            <span className="material-symbols-outlined text-[14px]">visibility</span>
                                            {hasPreview ? 'Preview' : 'N/A'}
                                        </button>
                                        <div className="w-px h-4 bg-slate-200 dark:bg-slate-700"></div>
                                        <button 
                                            onClick={() => handleDownloadFile(file)}
                                            disabled={!hasDownload}
                                            className="flex-1 flex items-center justify-center gap-1 h-7 rounded text-[10px] font-semibold text-primary hover:bg-primary/5 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
                                        >
                                            <span className="material-symbols-outlined text-[14px]">download</span>
                                            {hasDownload ? 'Get' : 'N/A'}
                                        </button>
                                    </div>
                                </div>
                            );
                        })}
                    </div>
                    
                    {/* Back button */}
                    <div className="flex justify-center mt-6 pt-3 border-t border-gray-200 dark:border-gray-700">
                        <button onClick={onBack} className="flex items-center gap-1.5 text-slate-500 hover:text-primary transition-colors text-xs">
                            <span className="material-symbols-outlined text-[14px]">arrow_back</span>
                            <span>New Lesson</span>
                        </button>
                    </div>
                </div>
            );
        };

        // Generation View - Show full generation record
var GenerationView = ({ generation, blueprint, teachingPackage, messages, onBack, onCreateMaterials, onAdjust, isGeneratingMaterials }) => {
            const [activeTab, setActiveTab] = useState(teachingPackage ? 'package' : 'blueprint');
            
            const metadata = (blueprint && blueprint.metadata) || {};
            
            return (
                <main className="flex-1 flex flex-col h-full overflow-hidden bg-background-light dark:bg-background-dark">
                    {/* Header */}
                    <header className="h-16 flex items-center justify-between px-6 bg-white dark:bg-surface-dark border-b border-gray-200 dark:border-gray-700 flex-shrink-0">
                        <div className="flex items-center gap-4">
                            <button onClick={onBack} className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500">
                                <span className="material-symbols-outlined">arrow_back</span>
                            </button>
                            <div className="flex items-center gap-3">
                                <div className="w-10 h-10 rounded-xl bg-amber-100 dark:bg-amber-900/20 flex items-center justify-center text-amber-600 dark:text-amber-400">
                                    <span className="material-symbols-outlined">folder</span>
                                </div>
                                <div>
                                    <h1 className="font-bold text-lg text-[#101718] dark:text-white line-clamp-2 leading-tight">{(blueprint && blueprint.title) || 'Teaching Package'}</h1>
                                    <div className="flex items-center gap-2 text-xs text-gray-500">
                                        {metadata.subject && <span>{metadata.subject}</span>}
                                        {metadata.grade_level && <span>• Grade {metadata.grade_level}</span>}
                                        {metadata.duration_minutes && <span>• {metadata.duration_minutes} min</span>}
                                    </div>
                                </div>
                            </div>
                        </div>
                        
                        {/* Tab Switcher */}
                        <div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 p-1 rounded-lg">
                            <button
                                onClick={() => setActiveTab('conversation')}
                                className={`px-4 py-1.5 rounded-md text-sm font-medium transition-all ${activeTab === 'conversation' ? 'bg-white dark:bg-gray-700 shadow-sm text-primary' : 'text-gray-500 hover:text-gray-700'}`}
                            >
                                <span className="material-symbols-outlined text-[16px] mr-1 align-middle">chat</span>
                                Conversation
                            </button>
                            <button
                                onClick={() => setActiveTab('blueprint')}
                                className={`px-4 py-1.5 rounded-md text-sm font-medium transition-all ${activeTab === 'blueprint' ? 'bg-white dark:bg-gray-700 shadow-sm text-primary' : 'text-gray-500 hover:text-gray-700'}`}
                            >
                                <span className="material-symbols-outlined text-[16px] mr-1 align-middle">article</span>
                                Blueprint
                            </button>
                            {teachingPackage && (
                                <button
                                    onClick={() => setActiveTab('package')}
                                    className={`px-4 py-1.5 rounded-md text-sm font-medium transition-all ${activeTab === 'package' ? 'bg-white dark:bg-gray-700 shadow-sm text-primary' : 'text-gray-500 hover:text-gray-700'}`}
                                >
                                    <span className="material-symbols-outlined text-[16px] mr-1 align-middle">folder_zip</span>
                                    Materials
                                </button>
                            )}
                        </div>
                    </header>

                    {/* Content */}
                    <div className="flex-1 overflow-y-auto overflow-x-hidden p-6 md:p-8">
                        {activeTab === 'conversation' && (
                            <div className="max-w-3xl mx-auto animate-fade-in-up w-full">
                                <div className="mb-6 pb-4 border-b border-gray-200 dark:border-gray-700">
                                    <h2 className="text-xl font-bold text-[#101718] dark:text-white mb-1">Conversation History</h2>
                                    <p className="text-sm text-gray-500">Original conversation that generated this lesson</p>
                                </div>
                                <div className="space-y-6 w-full">
                                    {messages.map((msg, i) => (
                                        <ChatMessage key={i} message={msg.content} isUser={msg.role === 'user'} attachments={msg.attachments} thoughtText={msg.thoughtText} isReadOnly={true} />
                                    ))}
                                    {messages.length === 0 && (
                                        <div className="text-center py-12 text-gray-400">
                                            <span className="material-symbols-outlined text-4xl mb-2">history</span>
                                            <p>No conversation history available</p>
                                        </div>
                                    )}
                                </div>
                            </div>
                        )}
                        
                        {activeTab === 'blueprint' && blueprint && (
                            <BlueprintView
                                blueprint={blueprint}
                                onAdjust={onAdjust}
                                onCreateMaterials={onCreateMaterials}
                                isGeneratingMaterials={isGeneratingMaterials}
                                materialsGenerated={teachingPackage !== null}
                            />
                        )}
                        
                        {activeTab === 'package' && teachingPackage && (
                            <PackageView
                                package={teachingPackage}
                                onBack={onBack}
                            />
                        )}
                    </div>
                </main>
            );
        };

        // Generating Animation with detailed progress
var GeneratingView = ({ phase, progress, progressSteps = [], stepTracking = {} }) => {
            // Default steps if none provided
            const defaultSteps = [
                { id: 'slide', label: 'Interactive Lesson', icon: 'slideshow', status: 'pending' },
                { id: 'lesson_plan', label: 'Lesson Plan', icon: 'description', status: 'pending' },
                { id: 'worksheet', label: 'Student Worksheet', icon: 'assignment', status: 'pending' },
                { id: 'quiz', label: 'Assessment Quiz', icon: 'quiz', status: 'pending' },
                { id: 'group_activity', label: 'Group Activity', icon: 'groups', status: 'pending' },
                { id: 'vocabulary_cards', label: 'Vocabulary Cards', icon: 'style', status: 'pending' }
            ];
            
            const steps = progressSteps.length > 0 ? progressSteps : defaultSteps;
            
            // Use explicit step tracking from SSE events
            const completedStepsSet = new Set(stepTracking.completedSteps || []);
            const currentStepId = stepTracking.currentStep || null;
            
            // Determine current step based on explicit tracking + fallback to phase message
            const getStepStatus = (stepId) => {
                // Normalize stepId for slide/slides
                const normalizedStepId = stepId === 'slides' ? 'slide' : stepId;
                
                // PRIORITY 1: Use explicit step tracking from SSE events (most reliable)
                if (completedStepsSet.has(stepId) || completedStepsSet.has(normalizedStepId)) {
                    return 'completed';
                }
                if (currentStepId === stepId || currentStepId === normalizedStepId) {
                    return 'current';
                }
                
                // PRIORITY 2: For parallel generation, non-slide steps are all "current"
                const isSlide = stepId === 'slide' || stepId === 'slides';
                const slideCompleted = completedStepsSet.has('slide') || completedStepsSet.has('slides');
                if (currentStepId === 'parallel' && !completedStepsSet.has(stepId) && !completedStepsSet.has(normalizedStepId) && !isSlide) {
                    return 'current';
                }
                
                // PRIORITY 3: Fall back to pattern matching for compatibility
                if (!phase) return 'pending';
                const phaseLower = phase.toLowerCase();
                
                // Helper function to check if any pattern matches
                const matchesAny = (patterns, text) => {
                    if (!patterns) return false;
                    return patterns.some(function(p) { return text.includes(p); });
                };
                
                // Check for completion markers
                const completedPatterns = {
                    'slide': ['interactive lesson completed', '✓ interactive lesson', 'slides saved', 'slide complete'],
                    'slides': ['interactive lesson completed', '✓ interactive lesson', 'slides saved', 'slide complete'],
                    'worksheet': ['worksheet completed', '✓ student worksheet', '✓ worksheet', 'worksheet saved'],
                    'quiz': ['quiz completed', '✓ assessment quiz', '✓ quiz', 'quiz saved'],
                    'lesson_plan': ['lesson plan completed', '✓ lesson plan', 'lesson plan saved'],
                    'group_activity': ['group activity completed', '✓ group activity', 'group activity saved'],
                    'vocabulary_cards': ['vocabulary cards completed', '✓ vocabulary', 'vocabulary saved']
                };
                
                // Check for in-progress patterns
                const inProgressPatterns = {
                    'slide': ['generating interactive', 'interactive presentation', 'generating image', 'background image', 'parallel image'],
                    'slides': ['generating interactive', 'interactive presentation', 'generating image', 'background image', 'parallel image'],
                    'worksheet': ['generating student worksheet', 'generating worksheet'],
                    'quiz': ['generating assessment quiz', 'generating quiz'],
                    'lesson_plan': ['generating lesson plan'],
                    'group_activity': ['generating group activity', 'generating group'],
                    'vocabulary_cards': ['generating vocabulary cards', 'generating vocabulary']
                };
                
                // Check if this step is completed via pattern
                if (matchesAny(completedPatterns[stepId], phaseLower) || matchesAny(completedPatterns[normalizedStepId], phaseLower)) {
                    return 'completed';
                }
                
                // Check if this step is in progress via pattern
                if (matchesAny(inProgressPatterns[stepId], phaseLower) || matchesAny(inProgressPatterns[normalizedStepId], phaseLower)) {
                    return 'current';
                }
                
                return 'pending';
            };
            
            return (
                <div className="flex flex-col items-center justify-center h-full animate-fade-in-up">
                    <div className="relative mb-8">
                        <div className="w-20 h-20 rounded-2xl bg-primary/10 flex items-center justify-center">
                            <span className="material-symbols-outlined text-primary text-4xl animate-spin">sync</span>
                        </div>
                    </div>
                    <h2 className="text-2xl font-bold text-slate-900 dark:text-white mb-2">Creating Teaching Materials</h2>
                    <p className="text-slate-500 dark:text-slate-400 mb-6">Your materials will be ready in ~2 minutes</p>
                    
                    {/* Progress Steps */}
                    <div className="w-full max-w-md bg-white dark:bg-slate-800 rounded-2xl shadow-lg border border-slate-200 dark:border-slate-700 p-6 mb-6">
                        <div className="space-y-4">
                            {steps.map((step, idx) => {
                                const status = getStepStatus(step.id);
                                return (
                                    <div key={step.id} className="flex items-center gap-4">
                                        <div className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 transition-all ${
                                            status === 'completed' ? 'bg-green-100 dark:bg-green-900/30 text-green-600' :
                                            status === 'current' ? 'bg-primary/10 text-primary' :
                                            'bg-slate-100 dark:bg-slate-700 text-slate-400'
                                        }`}>
                                            {status === 'completed' ? (
                                                <span className="material-symbols-outlined text-xl">check_circle</span>
                                            ) : status === 'current' ? (
                                                <span className="material-symbols-outlined text-xl animate-spin">progress_activity</span>
                                            ) : (
                                                <span className="material-symbols-outlined text-xl">{step.icon}</span>
                                            )}
                                        </div>
                                        <div className="flex-1 min-w-0">
                                            <p className={`font-medium truncate ${
                                                status === 'completed' ? 'text-green-600 dark:text-green-400' :
                                                status === 'current' ? 'text-slate-900 dark:text-white' :
                                                'text-slate-400'
                                            }`}>
                                                {status === 'completed' ? '✓ ' : status === 'current' ? '⏳ ' : ''}{step.label}
                                            </p>
                                            {status === 'current' && phase && (
                                                <p className="text-xs text-slate-500 dark:text-slate-400 truncate">{phase}</p>
                                            )}
                                        </div>
                                    </div>
                                );
                            })}
                        </div>
                    </div>
                    
                    {/* Progress Bar */}
                    <div className="w-64 h-2 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden">
                        <div className="h-full bg-primary rounded-full transition-all duration-500" style={{ width: `${progress || 10}%` }}></div>
                    </div>
                </div>
            );
        };

        // Adjust Blueprint Modal
var AdjustModal = ({ isOpen, onClose, onSubmit, sectionId }) => {
            const [instructions, setInstructions] = useState('');
            
            if (!isOpen) return null;
            
            return (
                <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/30 backdrop-blur-sm">
                    <div className="relative w-full max-w-2xl bg-zinc-50 dark:bg-[#1a2023] border border-white/50 dark:border-slate-700 shadow-2xl rounded-2xl flex flex-col max-h-[90vh]">
                        <div className="flex items-center justify-between px-6 py-5 border-b border-slate-200/60 dark:border-slate-700/60 bg-white/50 dark:bg-slate-800/20">
                            <div className="flex items-center gap-3">
                                <div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
                                    <span className="material-symbols-outlined">edit_note</span>
                                </div>
                                <div>
                                    <h3 className="text-slate-900 dark:text-white text-xl font-bold">Adjust Section</h3>
                                    <p className="text-slate-500 text-sm">Refine content with AI</p>
                                </div>
                            </div>
                            <button onClick={onClose} className="rounded-full p-2 hover:bg-slate-100 dark:hover:bg-slate-700">
                                <span className="material-symbols-outlined">close</span>
                            </button>
                        </div>
                        <div className="flex-1 overflow-y-auto px-6 py-6">
                            <label className="flex items-center gap-2 text-slate-700 dark:text-slate-200 text-sm font-semibold pb-2.5">
                                <span className="material-symbols-outlined text-[18px] text-primary">auto_awesome</span>
                                Modification Instructions
                            </label>
                            <textarea
                                className="w-full rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 p-4 min-h-[160px] dark:text-white focus:ring-2 focus:ring-primary/50 focus:border-primary"
                                placeholder="Describe the changes you want to make..."
                                value={instructions}
                                onChange={(e) => setInstructions(e.target.value)}
                            />
                        </div>
                        <div className="flex items-center justify-end gap-3 px-6 py-5 bg-slate-50 dark:bg-slate-800/50 border-t border-slate-200 dark:border-slate-700 rounded-b-2xl">
                            <button onClick={onClose} className="px-5 h-11 rounded-lg text-slate-600 dark:text-slate-300 font-bold text-sm hover:bg-slate-200 transition-colors">
                                Cancel
                            </button>
                            <button onClick={() => { onSubmit(sectionId, instructions); setInstructions(''); }} className="px-6 h-11 rounded-lg bg-primary text-white font-bold text-sm shadow-md hover:bg-primary-hover transition-colors">
                                Update Blueprint
                            </button>
                        </div>
                    </div>
                </div>
            );
        };

        // ============================================================================
        // Login Page Component
        // ============================================================================
        
var LoginPage = ({ onLogin }) => {
            const [isRegister, setIsRegister] = useState(false);
            const [username, setUsername] = useState('');
            const [password, setPassword] = useState('');
            const [displayName, setDisplayName] = useState('');
            const [inviteCode, setInviteCode] = useState('');
            const [error, setError] = useState('');
            const [isLoading, setIsLoading] = useState(false);
            
            const handleSubmit = async (e) => {
                e.preventDefault();
                setError('');
                setIsLoading(true);
                
                try {
                    if (isRegister) {
                        // Register
                        const response = await fetch('/api/v1/register', {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ 
                                username: username.trim(), 
                                password, 
                                name: displayName.trim() || username.trim(),
                                invite_code: inviteCode.trim()
                            })
                        });
                        const result = await response.json();
                        if (!response.ok) {
                            throw new Error(result.detail || 'Registration failed');
                        }
                        setAuthToken(result.token);
                        setStoredUser(result.user);
                        onLogin(result.user);
                    } else {
                        // Login
                        const result = await api.login(username, password);
                        setAuthToken(result.token);
                        setStoredUser(result.user);
                        onLogin(result.user);
                    }
                } catch (err) {
                    setError(err.message || (isRegister ? 'Registration failed.' : 'Login failed. Please check your credentials.'));
                } finally {
                    setIsLoading(false);
                }
            };
            
            const toggleMode = () => {
                setIsRegister(!isRegister);
                setError('');
            };
            
            return (
                <div className="min-h-screen bg-gradient-to-br from-teal-50 via-white to-cyan-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 flex items-center justify-center p-4">
                    <div className="w-full max-w-md">
                        {/* Logo & Title */}
                        <div className="text-center mb-8">
                            <div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-gradient-to-br from-primary to-teal-600 text-white mb-4 shadow-lg">
                                <span className="material-symbols-outlined text-4xl">auto_awesome</span>
                            </div>
                            <h1 className="text-3xl font-bold text-gray-800 dark:text-white font-display">
                                Scaffo.ai
                            </h1>
                            <p className="text-gray-500 dark:text-gray-400 mt-2">
                                AI Teaching Materials Generator
                            </p>
                        </div>
                        
                        {/* Login/Register Card */}
                        <div className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl p-8 border border-gray-100 dark:border-gray-700">
                            <h2 className="text-xl font-semibold text-gray-800 dark:text-white mb-6 text-center">
                                {isRegister ? 'Create Account' : 'Sign In'}
                            </h2>
                            
                            {/* Form */}
                            <form onSubmit={handleSubmit} className="space-y-4">
                                <div>
                                    <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5">
                                        Username
                                    </label>
                                    <input
                                        type="text"
                                        value={username}
                                        onChange={(e) => setUsername(e.target.value)}
                                        placeholder="Enter username"
                                        className="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-800 dark:text-white focus:ring-2 focus:ring-primary focus:border-transparent transition-all"
                                        required
                                        minLength={3}
                                        maxLength={20}
                                    />
                                </div>
                                
                                {isRegister && (
                                    <div>
                                        <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5">
                                            Display Name
                                        </label>
                                        <input
                                            type="text"
                                            value={displayName}
                                            onChange={(e) => setDisplayName(e.target.value)}
                                            placeholder="How should we call you?"
                                            className="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-800 dark:text-white focus:ring-2 focus:ring-primary focus:border-transparent transition-all"
                                            maxLength={50}
                                        />
                                    </div>
                                )}
                                
                                {isRegister && (
                                    <div>
                                        <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5">
                                            Invite Code <span className="text-red-500">*</span>
                                        </label>
                                        <input
                                            type="text"
                                            value={inviteCode}
                                            onChange={(e) => setInviteCode(e.target.value.toUpperCase())}
                                            placeholder="SCAFFO-XXXX-XXXX"
                                            className="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-800 dark:text-white focus:ring-2 focus:ring-primary focus:border-transparent transition-all font-mono tracking-wider"
                                            required
                                            maxLength={16}
                                        />
                                        <p className="text-xs text-gray-400 mt-1.5">Ask your administrator for an invite code</p>
                                    </div>
                                )}
                                
                                <div>
                                    <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5">
                                        Password
                                    </label>
                                    <input
                                        type="password"
                                        value={password}
                                        onChange={(e) => setPassword(e.target.value)}
                                        placeholder={isRegister ? "At least 6 characters" : "Enter password"}
                                        className="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-800 dark:text-white focus:ring-2 focus:ring-primary focus:border-transparent transition-all"
                                        required
                                        minLength={isRegister ? 6 : 1}
                                    />
                                </div>
                                
                                {error && (
                                    <div className="p-3 rounded-lg bg-red-50 dark:bg-red-900/30 text-red-600 dark:text-red-400 text-sm">
                                        {error}
                                    </div>
                                )}
                                
                                <button
                                    type="submit"
                                    disabled={isLoading}
                                    className="w-full py-3 px-4 rounded-xl bg-primary hover:bg-primary-hover text-white font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
                                >
                                    {isLoading ? (
                                        <React.Fragment>
                                            <span className="material-symbols-outlined animate-spin text-lg">progress_activity</span>
                                            {isRegister ? 'Creating account...' : 'Signing in...'}
                                        </React.Fragment>
                                    ) : (
                                        isRegister ? 'Create Account' : 'Sign In'
                                    )}
                                </button>
                            </form>
                            
                            {/* Toggle Login/Register */}
                            <div className="mt-6 text-center">
                                <span className="text-gray-500 dark:text-gray-400 text-sm">
                                    {isRegister ? 'Already have an account?' : "Don't have an account?"}
                                </span>
                                <button
                                    type="button"
                                    onClick={toggleMode}
                                    className="ml-2 text-primary hover:text-primary-hover font-medium text-sm"
                                >
                                    {isRegister ? 'Sign In' : 'Sign Up'}
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
            );
        };

        // ============================================================================
        // Main App
        // ============================================================================
