Files
OpenRouter_Image/newindex.html

398 lines
20 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenRouter Image Generator</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
<link href=".superdesign/design_iterations/clean_theme.css" rel="stylesheet">
</head>
<body>
<div class="app-container">
<!-- Header -->
<header class="app-header">
<div class="header-title">
<i data-lucide="image" style="width: 24px; height: 24px;"></i>
OpenRouter Image Generator
</div>
<div class="header-actions">
<button class="btn btn-secondary btn-sm" id="settingsBtn" title="设置">
<i data-lucide="settings" style="width: 16px; height: 16px;"></i>
</button>
<div class="status-indicator">
<span class="status-dot status-disconnected" id="connectionStatus"></span>
<span id="connectionText">未连接</span>
</div>
</div>
</header>
<!-- Sidebar -->
<aside class="app-sidebar" id="sidebar">
<!-- Prompt Section -->
<div class="sidebar-section">
<div class="sidebar-section-header">
<i data-lucide="edit-3" style="width: 16px; height: 16px;"></i>
生成提示
</div>
<div class="sidebar-section-content">
<div class="form-group">
<label class="form-label" for="messageInput">描述您想要生成的图像</label>
<textarea class="form-input form-textarea" id="messageInput" placeholder="例如:一只可爱的橙色小猫坐在阳光洒满的窗台上..."></textarea>
</div>
</div>
</div>
<!-- Reference Images -->
<div class="sidebar-section">
<div class="sidebar-section-header">
<i data-lucide="image-plus" style="width: 16px; height: 16px;"></i>
参考图像
</div>
<div class="sidebar-section-content">
<div class="upload-zone" id="dropZone" onclick="document.getElementById('imageInput').click()">
<i data-lucide="upload" class="upload-zone-icon"></i>
<p class="upload-zone-text">点击上传或拖拽图像到此处</p>
<p class="upload-zone-text" style="font-size: 0.75rem; color: var(--text-muted);">支持 JPG, PNG, WebP (最多10张)</p>
<input type="file" style="display: none;" id="imageInput" accept="image/*" multiple>
</div>
<div class="image-preview" id="imagePreview" style="margin-top: var(--space-md); display: none;">
<!-- Preview items will be added here -->
</div>
</div>
</div>
<!-- Generation Controls -->
<div class="sidebar-section">
<div class="sidebar-section-header">
<i data-lucide="sliders-horizontal" style="width: 16px; height: 16px;"></i>
生成控制
</div>
<div class="sidebar-section-content">
<div class="form-group">
<label class="form-label" for="batchCount">生成数量</label>
<input type="number" class="form-input" id="batchCount" min="1" max="20" value="6">
</div>
<div style="display: flex; flex-direction: column; gap: var(--space-sm);">
<button class="btn btn-primary btn-full" onclick="sendMessage()">
<i data-lucide="zap" style="width: 16px; height: 16px;"></i>
生成图像
</button>
<button class="btn btn-success btn-full" onclick="sendBatchMessage()">
<i data-lucide="layers" style="width: 16px; height: 16px;"></i>
批量生成
</button>
</div>
</div>
</div>
<!-- Settings Panel (Hidden by default) -->
<div class="sidebar-section" id="settingsPanel" style="display: none;">
<div class="sidebar-section-header">
<i data-lucide="settings" style="width: 16px; height: 16px;"></i>
API设置
</div>
<div class="sidebar-section-content">
<div class="form-group">
<label class="form-label" for="apiKey">API Key</label>
<input type="password" class="form-input" id="apiKey" placeholder="输入您的OpenRouter API Key">
</div>
<div class="form-group">
<label class="form-label" for="baseUrl">Base URL</label>
<input type="url" class="form-input" id="baseUrl" value="https://openrouter.ai/api/v1">
</div>
<div class="form-group">
<label class="form-label" for="model">模型</label>
<select class="form-input" id="model">
<option value="google/gemini-2.5-flash-image-preview:free">Google Gemini 2.5 Flash Image Preview (Free)</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="timeout">超时时间 (秒)</label>
<input type="number" class="form-input" id="timeout" value="600">
</div>
<div class="form-group">
<label class="form-label" for="proxy">代理 (可选)</label>
<input type="url" class="form-input" id="proxy" placeholder="http://proxy:port">
</div>
<div style="display: flex; gap: var(--space-sm);">
<button class="btn btn-secondary" onclick="testConnection()">
<i data-lucide="wifi" style="width: 16px; height: 16px;"></i>
测试连接
</button>
<button class="btn btn-secondary" onclick="app.clearStorage()">
<i data-lucide="trash-2" style="width: 16px; height: 16px;"></i>
清除数据
</button>
</div>
</div>
</div>
<!-- Chat History -->
<div class="sidebar-section">
<div class="sidebar-section-header">
<i data-lucide="message-circle" style="width: 16px; height: 16px;"></i>
对话历史
</div>
<div class="sidebar-section-content">
<div id="chatHistory" style="max-height: 200px; overflow-y: auto; font-size: 0.8125rem; color: var(--text-secondary);">
<p style="text-align: center; color: var(--text-muted); padding: var(--space-lg) 0;">暂无对话记录</p>
</div>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="app-main">
<div class="main-content">
<!-- Generation Preview Area -->
<section class="main-section">
<div class="generation-preview" id="generationPreview">
<div class="generation-preview-empty" id="previewEmpty">
<i data-lucide="image" style="width: 64px; height: 64px; color: var(--text-muted); margin-bottom: var(--space-lg);"></i>
<h3 style="color: var(--text-secondary); font-weight: 500; margin-bottom: var(--space-md);">准备生成图像</h3>
<p style="color: var(--text-muted); font-size: 0.875rem;">在左侧输入提示词,然后点击生成按钮开始创作</p>
</div>
<div class="generation-preview-loading" id="previewLoading" style="display: none;">
<div class="spinner"></div>
<p style="color: var(--text-secondary);">正在生成图像,请稍候...</p>
</div>
</div>
</section>
<!-- Generated Images Gallery -->
<section class="main-section">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-lg);">
<h2 class="main-section-title" style="margin: 0;">
<i data-lucide="images" style="width: 20px; height: 20px;"></i>
生成的图像
</h2>
<button class="btn btn-success btn-sm" id="downloadAllImagesBtn" style="display: none;">
<i data-lucide="download" style="width: 16px; height: 16px;"></i>
全部下载
</button>
</div>
<div class="image-gallery" id="imageGallery">
<!-- Images will be added here -->
</div>
</section>
</div>
</main>
</div>
<!-- Mobile Sidebar Overlay -->
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeMobileSidebar()"></div>
<!-- Image Viewer Modal -->
<div id="imageViewerModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); z-index: 1000; backdrop-filter: blur(8px);">
<div style="position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; padding: var(--space-lg);">
<!-- Close Button -->
<button onclick="closeImageViewer()" style="position: absolute; top: var(--space-lg); right: var(--space-lg); background: rgba(255, 255, 255, 0.1); border: none; border-radius: 50%; width: 48px; height: 48px; display: flex; align-items: center; justify-content: center; cursor: pointer; backdrop-filter: blur(8px);">
<i data-lucide="x" style="width: 24px; height: 24px; color: white;"></i>
</button>
<!-- Navigation -->
<button id="prevImageBtn" style="position: absolute; left: var(--space-lg); top: 50%; transform: translateY(-50%); background: rgba(255, 255, 255, 0.1); border: none; border-radius: 50%; width: 48px; height: 48px; display: flex; align-items: center; justify-content: center; cursor: pointer; backdrop-filter: blur(8px);">
<i data-lucide="chevron-left" style="width: 24px; height: 24px; color: white;"></i>
</button>
<button id="nextImageBtn" style="position: absolute; right: var(--space-lg); top: 50%; transform: translateY(-50%); background: rgba(255, 255, 255, 0.1); border: none; border-radius: 50%; width: 48px; height: 48px; display: flex; align-items: center; justify-content: center; cursor: pointer; backdrop-filter: blur(8px);">
<i data-lucide="chevron-right" style="width: 24px; height: 24px; color: white;"></i>
</button>
<!-- Image -->
<img id="viewerModalImage" src="" alt="查看图像" style="max-width: 90%; max-height: 90%; object-fit: contain; border-radius: var(--border-radius);">
<!-- Actions -->
<div style="position: absolute; bottom: var(--space-lg); left: 50%; transform: translateX(-50%); display: flex; gap: var(--space-sm);">
<button class="btn btn-secondary" id="viewerDownloadImage">
<i data-lucide="download" style="width: 16px; height: 16px;"></i>
下载
</button>
<button class="btn btn-secondary" id="viewerDeleteImage" style="background: var(--accent-danger);">
<i data-lucide="trash-2" style="width: 16px; height: 16px;"></i>
删除
</button>
</div>
</div>
</div>
<script>
// Initialize Lucide icons
lucide.createIcons();
// UI State Management
let settingsVisible = false;
let currentImages = [];
let currentImageIndex = 0;
// Settings toggle
document.getElementById('settingsBtn').addEventListener('click', function() {
const panel = document.getElementById('settingsPanel');
settingsVisible = !settingsVisible;
panel.style.display = settingsVisible ? 'block' : 'none';
});
// File upload handling
const dropZone = document.getElementById('dropZone');
const imageInput = document.getElementById('imageInput');
const imagePreview = document.getElementById('imagePreview');
dropZone.addEventListener('dragover', function(e) {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', function(e) {
e.preventDefault();
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', function(e) {
e.preventDefault();
dropZone.classList.remove('drag-over');
const files = e.dataTransfer.files;
handleFiles(files);
});
imageInput.addEventListener('change', function(e) {
handleFiles(e.target.files);
});
function handleFiles(files) {
if (files.length === 0) return;
imagePreview.style.display = 'block';
imagePreview.innerHTML = '';
Array.from(files).forEach((file, index) => {
if (file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function(e) {
const item = document.createElement('div');
item.className = 'image-preview-item';
item.innerHTML = `
<img src="${e.target.result}" alt="${file.name}" class="image-preview-thumb">
<div class="image-preview-info">
<div class="image-preview-name">${file.name}</div>
<div class="image-preview-size">${(file.size / 1024).toFixed(1)} KB</div>
</div>
<button onclick="removeImage(${index})" style="background: none; border: none; color: var(--accent-danger); cursor: pointer;">
<i data-lucide="x" style="width: 16px; height: 16px;"></i>
</button>
`;
imagePreview.appendChild(item);
lucide.createIcons();
};
reader.readAsDataURL(file);
}
});
}
function removeImage(index) {
// Implementation for removing images
console.log('Remove image:', index);
}
// Loading state management
function showLoading() {
document.getElementById('previewEmpty').style.display = 'none';
document.getElementById('previewLoading').style.display = 'flex';
}
function hideLoading() {
document.getElementById('previewEmpty').style.display = 'block';
document.getElementById('previewLoading').style.display = 'none';
}
// Image viewer modal
function openImageViewer(imageSrc, index) {
currentImageIndex = index;
document.getElementById('viewerModalImage').src = imageSrc;
document.getElementById('imageViewerModal').style.display = 'block';
document.body.style.overflow = 'hidden';
}
function closeImageViewer() {
document.getElementById('imageViewerModal').style.display = 'none';
document.body.style.overflow = 'auto';
}
// Mobile sidebar
function openMobileSidebar() {
document.getElementById('sidebar').classList.add('open');
document.getElementById('sidebarOverlay').classList.add('show');
}
function closeMobileSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebarOverlay').classList.remove('show');
}
// Placeholder functions for existing functionality
function testConnection() {
console.log('Testing connection...');
// Update status indicator
const statusDot = document.querySelector('#connectionStatus');
const statusText = document.querySelector('#connectionText');
statusDot.className = 'status-dot status-connecting';
statusText.textContent = '连接中...';
// Simulate connection test
setTimeout(() => {
statusDot.className = 'status-dot status-connected';
statusText.textContent = '已连接';
}, 2000);
}
function sendMessage() {
const message = document.getElementById('messageInput').value.trim();
if (!message) {
alert('请输入生成提示词');
return;
}
showLoading();
console.log('Sending single message:', message);
}
function sendBatchMessage() {
const message = document.getElementById('messageInput').value.trim();
const count = document.getElementById('batchCount').value;
if (!message) {
alert('请输入生成提示词');
return;
}
showLoading();
console.log('Sending batch message:', message, 'Count:', count);
}
// Initialize app object for compatibility
const app = {
clearStorage: function() {
if (confirm('确定要清除所有存储数据吗?')) {
localStorage.clear();
sessionStorage.clear();
console.log('Storage cleared');
}
}
};
// Mobile menu toggle
if (window.innerWidth <= 768) {
const headerTitle = document.querySelector('.header-title');
headerTitle.style.cursor = 'pointer';
headerTitle.addEventListener('click', openMobileSidebar);
}
// Update mobile layout on resize
window.addEventListener('resize', function() {
if (window.innerWidth > 768) {
closeMobileSidebar();
}
});
</script>
<script src="script.js"></script>
</body>
</html>