Download client/index.html from M5Stack/SD1.5-LLM8850: direct link, hf CLI and curl.
- Browser
- Download file 28.8 kB
-
https://huggingface.co/M5Stack/SD1.5-LLM8850/resolve/main/client/index.html
- Command line
-
hf download hf://M5Stack/SD1.5-LLM8850/client/index.html
-
curl -L -o index.html https://huggingface.co/M5Stack/SD1.5-LLM8850/resolve/main/client/index.html
28.8 kB
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>M5Stack LLM8850 文生图展示</title> | |
| <style> | |
| :root { | |
| --primary-color: #3498db; | |
| --selected-color: #e74c3c; | |
| --success-color: #27ae60; | |
| --warning-color: #f39c12; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| background: #f0f3f5; | |
| } | |
| .container { | |
| max-width: 100%; | |
| margin: 0 auto; | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
| padding: 2rem; | |
| position: relative; | |
| } | |
| /* 公司图标 */ | |
| .company-logo { | |
| position: absolute; | |
| top: 20px; | |
| right: 20px; | |
| width: 80px; | |
| height: auto; | |
| } | |
| h1 { | |
| text-align: center; | |
| color: #2c3e50; | |
| margin-bottom: 2rem; | |
| font-weight: 600; | |
| margin-right: 100px; | |
| /* 为图标留出空间 */ | |
| } | |
| /* 选中词条区域样式 */ | |
| #selected-terms-box { | |
| position: sticky; | |
| top: 0; | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(5px); | |
| z-index: 10; | |
| padding: 1rem; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08); | |
| margin-bottom: 1.5rem; | |
| border-radius: 8px; | |
| } | |
| .selected-terms-label { | |
| font-weight: 500; | |
| color: #2c3e50; | |
| margin-bottom: 0.5rem; | |
| font-size: 0.9rem; | |
| } | |
| .selected-term { | |
| display: inline-flex; | |
| align-items: center; | |
| padding: 0.4rem 0.8rem; | |
| background: var(--selected-color); | |
| color: white; | |
| border-radius: 20px; | |
| margin: 0.3rem; | |
| font-size: 0.9em; | |
| animation: popIn 0.2s ease; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| } | |
| .selected-term:hover { | |
| background: #c0392b; | |
| transform: scale(1.05); | |
| } | |
| .controls { | |
| display: inline-flex; | |
| gap: 0.5rem; | |
| margin-left: 1rem; | |
| vertical-align: middle; | |
| } | |
| .btn { | |
| padding: 0.6rem 1.2rem; | |
| border: none; | |
| border-radius: 20px; | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| font-weight: 500; | |
| } | |
| #generate-btn { | |
| background: var(--primary-color); | |
| color: white; | |
| padding: 1rem 2rem; | |
| /* 放大生成按钮 */ | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| } | |
| #random-btn { | |
| background: var(--success-color); | |
| color: white; | |
| } | |
| #clear-terms-btn { | |
| background: var(--warning-color); | |
| color: white; | |
| } | |
| #clear-images-btn { | |
| background: #95a5a6; | |
| color: white; | |
| } | |
| .btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); | |
| } | |
| .btn:disabled { | |
| background: #bdc3c7; | |
| cursor: not-allowed; | |
| transform: none; | |
| box-shadow: none; | |
| } | |
| @keyframes popIn { | |
| from { | |
| transform: scale(0.8); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: scale(1); | |
| opacity: 1; | |
| } | |
| } | |
| /* 进度条样式(非弹出式) */ | |
| .progress-container { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| padding: 1rem; | |
| border-radius: 8px; | |
| margin-bottom: 1rem; | |
| color: white; | |
| display: none; | |
| animation: slideDown 0.3s ease; | |
| } | |
| .progress-container.active { | |
| display: block; | |
| } | |
| .progress-bar { | |
| width: 100%; | |
| height: 6px; | |
| background: rgba(255, 255, 255, 0.3); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| margin: 0.5rem 0; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, #fff, #f39c12); | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| border-radius: 3px; | |
| } | |
| .progress-info { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| font-size: 0.9rem; | |
| } | |
| .progress-step { | |
| opacity: 0.9; | |
| } | |
| .fun-fact { | |
| background: rgba(255, 255, 255, 0.1); | |
| padding: 0.5rem; | |
| border-radius: 4px; | |
| margin-top: 0.5rem; | |
| font-style: italic; | |
| font-size: 0.85rem; | |
| animation: fadeIn 0.5s ease; | |
| } | |
| @keyframes slideDown { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| /* 词条分类样式 */ | |
| #term-categories { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); | |
| gap: 1.5rem; | |
| } | |
| .category { | |
| background: #ffffff; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); | |
| overflow: hidden; | |
| } | |
| .category-header { | |
| padding: 1rem; | |
| background: var(--primary-color); | |
| color: white; | |
| font-weight: 500; | |
| position: sticky; | |
| top: 0; | |
| z-index: 1; | |
| } | |
| .scroll-container { | |
| max-height: 240px; | |
| overflow-y: hidden; | |
| padding: 1rem; | |
| position: relative; | |
| } | |
| .term-item { | |
| padding: 0.8rem 1.2rem; | |
| margin: 0.5rem 0; | |
| border-radius: 6px; | |
| background: #f8f9fa; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| position: relative; | |
| overflow: hidden; | |
| display: flex; | |
| align-items: center; | |
| min-height: 48px; | |
| } | |
| .term-content { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.2rem; | |
| } | |
| .chinese { | |
| font-weight: 500; | |
| color: #2c3e50; | |
| } | |
| .term { | |
| font-size: 0.85rem; | |
| color: #7f8c8d; | |
| font-style: italic; | |
| } | |
| .term-item.selected { | |
| background: var(--primary-color); | |
| color: white; | |
| } | |
| .term-item.selected .term { | |
| color: rgba(255, 255, 255, 0.8); | |
| } | |
| .term-item:hover { | |
| transform: translateX(5px); | |
| box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.1); | |
| } | |
| /* 布局调整 - 占满屏幕 */ | |
| .main-content { | |
| display: grid; | |
| grid-template-columns: 1fr 500px; | |
| gap: 2rem; | |
| min-height: 70vh; | |
| } | |
| .left-column { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); | |
| gap: 1.2rem; | |
| align-content: start; | |
| } | |
| .right-column { | |
| background: #f8f9fa; | |
| border-radius: 8px; | |
| padding: 1rem; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .images-header { | |
| text-align: center; | |
| color: #2c3e50; | |
| margin-bottom: 1rem; | |
| font-size: 1.1rem; | |
| font-weight: 500; | |
| flex-shrink: 0; | |
| } | |
| /* 图片容器改为两列 */ | |
| #images-container { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 1rem; | |
| flex: 1; | |
| } | |
| .image-item { | |
| background: white; | |
| border-radius: 8px; | |
| padding: 0.5rem; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| animation: zoomIn 0.3s ease; | |
| align-self: start; | |
| } | |
| .image-item img { | |
| width: 100%; | |
| height: auto; | |
| border-radius: 6px; | |
| display: block; | |
| } | |
| .image-terms { | |
| font-size: 0.75rem; | |
| color: #7f8c8d; | |
| margin-top: 0.5rem; | |
| padding: 0.5rem; | |
| background: #ecf0f1; | |
| border-radius: 4px; | |
| word-wrap: break-word; | |
| line-height: 1.3; | |
| } | |
| .empty-state { | |
| grid-column: 1 / -1; | |
| text-align: center; | |
| color: #7f8c8d; | |
| padding: 2rem; | |
| font-style: italic; | |
| } | |
| @keyframes zoomIn { | |
| from { | |
| opacity: 0; | |
| transform: scale(0.9); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: scale(1); | |
| } | |
| } | |
| /* 响应式设计 */ | |
| @media (max-width: 1400px) { | |
| .main-content { | |
| grid-template-columns: 1fr 450px; | |
| } | |
| } | |
| @media (max-width: 1200px) { | |
| .main-content { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .container { | |
| padding: 1rem; | |
| } | |
| .company-logo { | |
| width: 60px; | |
| top: 10px; | |
| right: 10px; | |
| } | |
| h1 { | |
| margin-right: 70px; | |
| font-size: 1.5rem; | |
| } | |
| #term-categories { | |
| grid-template-columns: 1fr; | |
| } | |
| #images-container { | |
| grid-template-columns: 1fr; | |
| } | |
| .controls { | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| margin-left: 0; | |
| margin-top: 1rem; | |
| } | |
| .btn { | |
| padding: 0.8rem 1.5rem; | |
| } | |
| #generate-btn { | |
| padding: 1rem 1.5rem; | |
| } | |
| } | |
| /* 占满屏幕的调整 */ | |
| @media (min-width: 1600px) { | |
| .container { | |
| max-width: 95%; | |
| } | |
| .main-content { | |
| grid-template-columns: 1fr 600px; | |
| } | |
| .left-column { | |
| grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <!-- 公司图标 --> | |
| <img src="https://docs.m5stack.com/assets/m5logo2022.svg" alt="M5Stack Logo" class="company-logo"> | |
| <h1>M5Stack LLM8850 文生图展示</h1> | |
| <div id="selected-terms-box"> | |
| <div class="selected-terms-label">已选择的提示词 Selected Prompts:</div> | |
| <div id="selected-terms-list"></div> | |
| <div class="controls"> | |
| <button id="generate-btn" class="btn">立即生成 Generate Now</button> | |
| <button id="random-btn" class="btn">随机描述 Random Terms</button> | |
| <button id="clear-terms-btn" class="btn">清除提示词 Clear Terms</button> | |
| <button id="clear-images-btn" class="btn">清除图片 Clear Images</button> | |
| </div> | |
| </div> | |
| <!-- 非弹出式进度条 --> | |
| <div class="progress-container" id="progress-container"> | |
| <div class="progress-info"> | |
| <span>AI 正在创作中... AI is creating...</span> | |
| <span id="progress-percent">0%</span> | |
| </div> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progress-fill"></div> | |
| </div> | |
| <div class="progress-step" id="progress-step">初始化中... Initializing...</div> | |
| <div class="fun-fact" id="fun-fact"></div> | |
| </div> | |
| <div class="main-content"> | |
| <div class="left-column"> | |
| <div id="term-categories"></div> | |
| </div> | |
| <div class="right-column"> | |
| <div class="images-header">生成历史 Generation History</div> | |
| <div id="images-container"> | |
| <div class="empty-state"> | |
| 选择词条并点击生成按钮开始创作<br> | |
| Select terms and click generate to start creating | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let scrollControllers = new Map(); | |
| // 有趣的提示词汇 | |
| const funFacts = [ | |
| "正在分析你的创意想法... Analyzing your creative ideas...", | |
| "AI 大脑正在思考最佳构图... AI brain thinking about optimal composition...", | |
| "调色板准备中,寻找完美色彩... Preparing palette, finding perfect colors...", | |
| "像素精灵们正在排队组合... Pixel sprites lining up for combination...", | |
| "魔法正在将文字转化为视觉... Magic transforming text to visuals...", | |
| "创意工厂全速运转中... Creative factory at full speed...", | |
| "每个细节都在精心雕琢... Every detail carefully crafted...", | |
| "光影大师正在调整明暗... Light master adjusting brightness...", | |
| "笔刷正在画布上飞舞... Brushes dancing on canvas...", | |
| "艺术灵感正在汇聚成形... Artistic inspiration taking shape...", | |
| "创造力引擎火力全开... Creativity engine at full power...", | |
| "美学算法正在优化细节... Aesthetic algorithm optimizing details...", | |
| "视觉交响乐即将奏响... Visual symphony about to play...", | |
| "想象力正在突破边界... Imagination breaking boundaries...", | |
| "奇迹工坊正在施展魔法... Miracle workshop casting magic..." | |
| ]; | |
| // UNet去噪步骤提示 | |
| const unetSteps = [ | |
| "预处理输入图层... Preprocessing input layers...", | |
| "第1层卷积神经网络处理... Layer 1 CNN processing...", | |
| "第2层特征提取中... Layer 2 feature extraction...", | |
| "第3层语义理解... Layer 3 semantic understanding...", | |
| "第4层细节优化... Layer 4 detail optimization...", | |
| "第5层色彩调和... Layer 5 color harmony...", | |
| "第6层纹理生成... Layer 6 texture generation...", | |
| "第7层构图调整... Layer 7 composition adjustment...", | |
| "第8层光影处理... Layer 8 lighting processing...", | |
| "第9层细节增强... Layer 9 detail enhancement...", | |
| "第10层质量检查... Layer 10 quality check...", | |
| "第11层风格融合... Layer 11 style fusion...", | |
| "第12层最终调色... Layer 12 final coloring...", | |
| "第13层锐化处理... Layer 13 sharpening...", | |
| "第14层噪声去除... Layer 14 noise removal...", | |
| "第15层对比度优化... Layer 15 contrast optimization...", | |
| "第16层饱和度调节... Layer 16 saturation adjustment...", | |
| "第17层最终渲染... Layer 17 final rendering...", | |
| "第18层质量验证... Layer 18 quality validation...", | |
| "第19层输出准备... Layer 19 output preparation...", | |
| "第20层完成处理... Layer 20 completion..." | |
| ]; | |
| async function loadAndDisplayTerms() { | |
| try { | |
| const response = await fetch('./person.json'); | |
| const data = await response.json(); | |
| const proprietaryTerms = data.prompt_terms.filter(term => term.type === "专用"); | |
| const categories = proprietaryTerms.reduce((acc, term) => { | |
| const subtype = term.subtype || "通用 General"; | |
| acc[subtype] = acc[subtype] || []; | |
| acc[subtype].push(term); | |
| return acc; | |
| }, {}); | |
| const container = document.getElementById('term-categories'); | |
| Object.entries(categories).forEach(([subtype, terms]) => { | |
| const category = document.createElement('div'); | |
| category.className = 'category'; | |
| const header = document.createElement('div'); | |
| header.className = 'category-header'; | |
| header.textContent = subtype; | |
| const scrollContainer = document.createElement('div'); | |
| scrollContainer.className = 'scroll-container'; | |
| terms.forEach(term => { | |
| const item = document.createElement('div'); | |
| item.className = 'term-item'; | |
| item.dataset.term = term.term; | |
| item.innerHTML = ` | |
| <div class="term-content"> | |
| <div class="chinese">${term.chinese}</div> | |
| <div class="term">${term.term}</div> | |
| </div> | |
| `; | |
| scrollContainer.appendChild(item); | |
| }); | |
| category.append(header, scrollContainer); | |
| container.appendChild(category); | |
| if (scrollContainer.scrollHeight > scrollContainer.clientHeight) { | |
| setupAutoScroll(scrollContainer); | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('数据加载失败 Data loading failed:', error); | |
| } | |
| } | |
| function setupAutoScroll(container) { | |
| let isPaused = false; | |
| let scrollPos = 0; | |
| const scrollSpeed = 0.5; | |
| const scrollHeight = container.scrollHeight - container.clientHeight; | |
| container.addEventListener('mouseenter', () => isPaused = true); | |
| container.addEventListener('mouseleave', () => isPaused = false); | |
| const scroll = () => { | |
| if (!isPaused && scrollHeight > 0) { | |
| scrollPos = (scrollPos + scrollSpeed) % scrollHeight; | |
| const easedPos = scrollPos < scrollHeight / 2 ? scrollPos : scrollHeight - scrollPos; | |
| container.scrollTop = easedPos; | |
| } | |
| requestAnimationFrame(scroll); | |
| }; | |
| scroll(); | |
| } | |
| function updateSelections() { | |
| const selectedItems = Array.from(document.querySelectorAll('.term-item.selected')); | |
| const container = document.getElementById('selected-terms-list'); | |
| container.innerHTML = selectedItems.map(item => { | |
| const chinese = item.querySelector('.chinese').textContent; | |
| const english = item.querySelector('.term').textContent; | |
| return `<span class="selected-term" data-term="${english}">${chinese} ${english}</span>`; | |
| }).join(''); | |
| } | |
| function showProgress() { | |
| const container = document.getElementById('progress-container'); | |
| const progressFill = document.getElementById('progress-fill'); | |
| const progressStep = document.getElementById('progress-step'); | |
| const progressPercent = document.getElementById('progress-percent'); | |
| const funFact = document.getElementById('fun-fact'); | |
| container.classList.add('active'); | |
| let currentStep = 0; | |
| const totalSteps = 20; | |
| const stepDuration = 240; //进度条刷新时间 | |
| return new Promise((resolve) => { | |
| const interval = setInterval(() => { | |
| currentStep++; | |
| const progress = (currentStep / totalSteps) * 100; | |
| progressFill.style.width = progress + '%'; | |
| progressPercent.textContent = Math.round(progress) + '%'; | |
| progressStep.textContent = unetSteps[currentStep - 1] || '处理中... Processing...'; | |
| if (currentStep % 3 === 0) { | |
| const randomFact = funFacts[Math.floor(Math.random() * funFacts.length)]; | |
| funFact.textContent = randomFact; | |
| } | |
| if (currentStep >= totalSteps) { | |
| clearInterval(interval); | |
| progressStep.textContent = '即将完成... Almost done...'; | |
| setTimeout(() => { | |
| container.classList.remove('active'); | |
| resolve(); | |
| }, 500); | |
| } | |
| }, stepDuration); | |
| }); | |
| } | |
| function addImageToHistory(selectedTerms) { | |
| const imagesContainer = document.getElementById('images-container'); | |
| // 清空初始提示 | |
| const emptyState = imagesContainer.querySelector('.empty-state'); | |
| if (emptyState) { | |
| emptyState.remove(); | |
| } | |
| const imgContainer = document.createElement('div'); | |
| imgContainer.className = 'image-item'; | |
| const img = document.createElement('img'); | |
| img.src = 'generated_image.png?' + new Date().getTime(); | |
| img.alt = 'Generated Image'; | |
| const termsDiv = document.createElement('div'); | |
| termsDiv.className = 'image-terms'; | |
| termsDiv.textContent = '关键词 Keywords: ' + selectedTerms.join(', '); | |
| imgContainer.appendChild(img); | |
| imgContainer.appendChild(termsDiv); | |
| // 添加到开头 | |
| imagesContainer.prepend(imgContainer); | |
| // 限制历史记录数量 | |
| while (imagesContainer.children.length > 20) { | |
| imagesContainer.removeChild(imagesContainer.lastChild); | |
| } | |
| } | |
| function clearAllTerms() { | |
| // 清除所有选中状态 | |
| document.querySelectorAll('.term-item.selected').forEach(item => { | |
| item.classList.remove('selected'); | |
| }); | |
| updateSelections(); | |
| } | |
| function clearAllImages() { | |
| const imagesContainer = document.getElementById('images-container'); | |
| imagesContainer.innerHTML = '<div class="empty-state">选择词条并点击生成按钮开始创作<br>Select terms and click generate to start creating</div>'; | |
| } | |
| // 随机选择每个类别中的一个提示词 | |
| function selectRandomTerms() { | |
| // 先清除所有已选择的词条 | |
| clearAllTerms(); | |
| // 获取所有类别 | |
| const categories = document.querySelectorAll('.category'); | |
| const randomTerms = []; | |
| categories.forEach(category => { | |
| const termItems = category.querySelectorAll('.term-item'); | |
| if (termItems.length > 0) { | |
| // 从每个类别中随机选择一个 | |
| const randomIndex = Math.floor(Math.random() * termItems.length); | |
| const selectedItem = termItems[randomIndex]; | |
| selectedItem.classList.add('selected'); | |
| randomTerms.push({ | |
| chinese: selectedItem.querySelector('.chinese').textContent, | |
| english: selectedItem.querySelector('.term').textContent | |
| }); | |
| } | |
| }); | |
| // 更新选中的词条显示 | |
| updateSelections(); | |
| // 可选:显示一个提示告诉用户选择了哪些词条 | |
| if (randomTerms.length > 0) { | |
| console.log('随机选择的词条:', randomTerms); | |
| // 可以添加一个简短的提示 | |
| const notification = document.createElement('div'); | |
| notification.style.cssText = ` | |
| position: fixed; | |
| top: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: var(--success-color); | |
| color: white; | |
| padding: 0.8rem 1.5rem; | |
| border-radius: 20px; | |
| z-index: 1000; | |
| animation: slideDown 0.3s ease; | |
| `; | |
| notification.textContent = `已随机选择 ${randomTerms.length} 个词条 Randomly selected ${randomTerms.length} terms`; | |
| document.body.appendChild(notification); | |
| // 3秒后移除提示 | |
| setTimeout(() => { | |
| notification.remove(); | |
| }, 3000); | |
| } | |
| } | |
| // 事件监听 | |
| document.addEventListener('click', (e) => { | |
| // 点击词条选择/取消选择 | |
| if (e.target.closest('.term-item')) { | |
| const item = e.target.closest('.term-item'); | |
| item.classList.toggle('selected'); | |
| updateSelections(); | |
| } | |
| // 点击已选择的词条气泡取消选择 | |
| if (e.target.closest('.selected-term')) { | |
| const term = e.target.closest('.selected-term').dataset.term; | |
| const termItem = document.querySelector(`.term-item[data-term="${term}"]`); | |
| if (termItem) { | |
| termItem.classList.remove('selected'); | |
| updateSelections(); | |
| } | |
| } | |
| }); | |
| window.addEventListener('load', () => { | |
| loadAndDisplayTerms(); | |
| const generateBtn = document.getElementById('generate-btn'); | |
| const randomBtn = document.getElementById('random-btn'); | |
| const clearTermsBtn = document.getElementById('clear-terms-btn'); | |
| const clearImagesBtn = document.getElementById('clear-images-btn'); | |
| // 生成按钮 | |
| generateBtn.addEventListener('click', async () => { | |
| const selectedTerms = Array.from(document.querySelectorAll('.term-item.selected')) | |
| .map(item => item.dataset.term); | |
| if (selectedTerms.length === 0) { | |
| alert('请至少选择一个词条 Please select at least one term'); | |
| return; | |
| } | |
| generateBtn.disabled = true; | |
| const progressPromise = showProgress(); | |
| try { | |
| const [response] = await Promise.all([ | |
| fetch('/generate', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ terms: selectedTerms }) | |
| }), | |
| progressPromise | |
| ]); | |
| if (response.ok) { | |
| addImageToHistory(selectedTerms); | |
| } else { | |
| const error = await response.json(); | |
| alert(`生成失败 Generation failed: ${error.error}`); | |
| } | |
| } catch (error) { | |
| console.error('生成请求失败 Generation request failed:', error); | |
| alert('生成请求失败,请检查网络连接 Generation request failed, please check network connection'); | |
| } finally { | |
| generateBtn.disabled = false; | |
| } | |
| }); | |
| randomBtn.addEventListener('click', () => { | |
| selectRandomTerms(); | |
| }); | |
| // 清除提示词按钮 | |
| clearTermsBtn.addEventListener('click', clearAllTerms); | |
| // 清除图片按钮 | |
| clearImagesBtn.addEventListener('click', () => { | |
| if (confirm('确定要清除所有生成的图片吗? Are you sure you want to clear all generated images?')) { | |
| clearAllImages(); | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |