liumaolin
更新加载页面图标路径,添加新的图标文件至assets/images目录。
fb2e3d6
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voice Dialogue</title>
<style>
:root {
--mac-accent: #0071e3;
--mac-accent-light: #2d8aeb;
--mac-accent-dark: #0060bf;
--mac-bg: #f5f5f7;
--mac-text: #1d1d1f;
--mac-text-secondary: #86868b;
--mac-white: #ffffff;
--mac-border-radius: 10px;
--mac-blur-bg: rgba(255, 255, 255, 0.7);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: var(--mac-bg);
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', Helvetica, Arial, sans-serif;
overflow: hidden;
position: relative;
}
.window-container {
width: 480px;
max-width: 90%;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
background-color: var(--mac-blur-bg);
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
overflow: hidden;
animation: fadeIn 0.5s ease;
}
.window-header {
height: 40px;
width: 100%;
display: flex;
align-items: center;
position: fixed;
top: 4px;
left: 12px;
-webkit-app-region: drag;
cursor: move;
}
.window-controls {
display: flex;
-webkit-app-region: no-drag;
cursor: pointer;
}
.window-button {
width: 24px;
height: 24px;
border: none;
background: transparent;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
padding: 0;
}
.window-button img {
width: 12px;
height: 12px;
transition: opacity 0.2s;
}
.window-button .focus-icon {
display: none;
}
.window-controls:hover .window-button .default-icon,
.window-controls:focus-within .window-button .default-icon {
display: none;
}
.window-controls:hover .window-button .focus-icon,
.window-controls:focus-within .window-button .focus-icon {
display: inline;
}
.window-title {
position: absolute;
left: 0;
right: 0;
text-align: center;
color: var(--mac-text-secondary);
font-size: 13px;
font-weight: 500;
}
.window-content {
padding: 40px 30px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.app-icon {
width: 80px;
height: 80px;
margin-bottom: 24px;
position: relative;
animation: iconPulse 2s ease-in-out infinite;
}
.app-icon-inner {
width: 80px;
height: 80px;
border-radius: 20px;
background: linear-gradient(135deg, #5AC8FA, #007AFF);
box-shadow: 1px 1px 6px 2px rgba(0, 122, 255, 0.3);
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 40px;
font-weight: 600;
}
.app-title {
color: var(--mac-text);
font-size: 22px;
font-weight: 600;
margin-bottom: 8px;
}
.app-subtitle {
color: var(--mac-text-secondary);
font-size: 14px;
margin-bottom: 32px;
text-align: center;
max-width: 340px;
}
.spinner {
width: 22px;
height: 22px;
margin-bottom: 24px;
border: 2px solid rgba(0, 113, 227, 0.2);
border-left-color: var(--mac-accent);
border-radius: 50%;
animation: spin 1s linear infinite;
}
.progress-container {
width: 80%;
height: 6px;
background-color: rgba(0, 113, 227, 0.1);
border-radius: 3px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background-color: var(--mac-accent);
width: 0%;
transition: width 0.4s ease;
border-radius: 3px;
}
.status-container {
display: flex;
justify-content: space-between;
align-items: center;
width: 80%;
margin-bottom: 28px;
}
.status-text {
color: var(--mac-text-secondary);
font-size: 12px;
}
.percent-text {
color: var(--mac-text-secondary);
font-size: 12px;
font-feature-settings: "tnum";
font-variant-numeric: tabular-nums;
}
.notification {
width: 80%;
padding: 14px;
border-radius: 8px;
background-color: rgba(0, 0, 0, 0.03);
display: flex;
align-items: flex-start;
margin-top: 8px;
}
.notification-icon {
margin-right: 12px;
margin-top: 2px;
}
.notification-icon svg {
width: 16px;
height: 16px;
color: var(--mac-accent);
}
.notification-content {
flex: 1;
}
.notification-title {
font-size: 13px;
font-weight: 500;
color: var(--mac-text);
margin-bottom: 4px;
}
.notification-text {
font-size: 12px;
color: var(--mac-text-secondary);
line-height: 1.5;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes iconPulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes fadeIn {
0% { opacity: 0; transform: translateY(10px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes warningPulse {
0% { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); }
50% { box-shadow: 0 8px 32px rgba(255, 193, 7, 0.3); }
100% { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); }
}
.notification.warning-theme {
animation: warningPulse 1s ease-in-out infinite;
background-color: rgba(255, 0, 0, 0.1);
}
</style>
</head>
<body>
<div class="window-header">
<div class="window-controls">
<button class="window-button close" onclick="closeWindow()">
<img class="default-icon"
src="./assets/images/red.png"
alt="close">
<img class="focus-icon"
src="./assets/images/close.png"
alt="close-focus">
</button>
<button class="window-button minimize" onclick="minimizeWindow()">
<img class="default-icon"
src="./assets/images/yellow.png"
alt="minimize">
<img class="focus-icon"
src="./assets/images/min.png"
alt="minimize-focus">
</button>
<button class="window-button maximize" onclick="maximizeWindow()">
<img class="default-icon"
src="./assets/images/green.png"
alt="maximize">
<img class="focus-icon"
src="./assets/images/max.png"
alt="maximize-focus">
</button>
</div>
</div>
<div class="window-content">
<div class="app-icon">
<div class="app-icon-inner"><img width="100" height="100" src="./build/icon.png" alt=""></div>
</div>
<h1 class="app-title">Voice Dialogue</h1>
<!-- <p class="app-subtitle">Please wait while we prepare everything for you</p> -->
<p class="app-subtitle">&nbsp;</p>
<div class="spinner"></div>
<p class="app-subtitle">&nbsp;</p>
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="status-container">
<div class="status-text" id="statusText">Initializing...</div>
<div class="percent-text" id="percentText">0%</div>
</div>
<div class="notification info-theme" id="firstRunNotification" style="display: none;">
<div class="notification-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
</div>
<div class="notification-content">
<h3 class="notification-title">First Launch Notice</h3>
<p class="notification-text">
First launch may take longer as we set up your environment and optimize resources. Subsequent launches will be much faster.
</p>
</div>
</div>
<div class="notification warning-theme" id="resourceWarningNotification" style="display: none;">
<div class="notification-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="color: #ffc107;">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</div>
<div class="notification-content">
<h3 class="notification-title">System Resource Warning</h3>
<p class="notification-text" id="resourceWarningText">
Your system resources are running low. This may affect app performance. Please consider closing other applications or freeing up memory.
</p>
</div>
</div>
</div>
</div>
<script>
// 窗口控制函数
function minimizeWindow ()
{
if (window.electronAPI && window.electronAPI.minimizeWindow) {
window.electronAPI.minimizeWindow();
} else {
console.warn('Electron API not available');
}
}
function maximizeWindow ()
{
if (window.electronAPI && window.electronAPI.maximizeWindow) {
window.electronAPI.maximizeWindow();
} else {
console.warn('Electron API not available');
}
}
function closeWindow ()
{
if (window.electronAPI && window.electronAPI.closeWindow) {
window.electronAPI.closeWindow();
} else {
console.warn('Electron API not available');
}
}
// Check if this is the first run and show notification accordingly
async function checkFirstRun() {
try {
if (window.electronAPI && window.electronAPI.getFirstRunStatus) {
const isFirstRun = await window.electronAPI.getFirstRunStatus();
console.log('isFirstRun', isFirstRun);
if (isFirstRun) {
const notification = document.getElementById('firstRunNotification');
if (notification) {
notification.style.display = 'flex';
}
}
}
} catch (error) {
console.log('Could not check first run status:', error);
}
}
// Call checkFirstRun when the page loads
checkFirstRun();
// Check system resources and show warning if needed
async function checkSystemResources() {
try {
if (window.electronAPI && window.electronAPI.getSystemCpuUsage && window.electronAPI.getSystemFreeMemory) {
const [cpuUsage, memoryFree] = await Promise.all([
window.electronAPI.getSystemCpuUsage(),
window.electronAPI.getSystemFreeMemory()
]);
console.log('CPU Usage:', cpuUsage + '%');
console.log('Memory Info:', memoryFree);
// Check thresholds
const isCpuHigh = cpuUsage > 80;
const isMemoryLow = memoryFree < 3; // Less than 4GB available
console.log('isCpuHigh', isCpuHigh);
console.log('isMemoryLow', isMemoryLow);
if (isCpuHigh || isMemoryLow) {
const notification = document.getElementById('resourceWarningNotification');
const warningText = document.getElementById('resourceWarningText');
let warningMessage = '';
if (isCpuHigh && isMemoryLow) {
warningMessage = `High CPU usage and insufficient memory detected. This may significantly affect app performance. Please close other applications to free up resources.`;
} else if (isCpuHigh) {
warningMessage = `High CPU usage detected. This may affect app performance. Please close CPU-intensive applications.`;
} else if (isMemoryLow) {
warningMessage = `Insufficient memory detected. This may affect app performance. Please close some applications to free up memory.`;
}
warningText.textContent = warningMessage;
notification.style.display = 'flex';
notification.classList.add('warning-notification');
}
}
} catch (error) {
console.log('Could not check system resources:', error);
}
}
checkSystemResources();
// Simulate loading progress
let progress = 0;
const progressBar = document.getElementById('progressBar');
const percentText = document.getElementById('percentText');
const statusText = document.getElementById('statusText');
const statusMessages = [
"Initializing...",
"Checking system requirements...",
"Loading components...",
"Preparing documents...",
"Optimizing performance...",
"Almost ready..."
];
function updateProgress() {
if (progress < 100) {
// Make progress more naturalistic with varying speeds
let increment;
if (progress < 20) {
// Quick start
increment = Math.random() * 2 + 0.5;
} else if (progress < 50) {
// Slow middle (Mac apps typically slow down in the middle)
increment = Math.random() * 1 + 0.2;
} else if (progress < 85) {
// Speed up again
increment = Math.random() * 1.5 + 0.3;
} else {
// Slow finish (typical macOS behavior)
increment = Math.random() * 0.8 + 0.1;
// Hold at 99% for a bit
if (progress > 99) progress = 99;
}
progress += increment;
progress = Math.min(progress, 99.9);
// Update UI
progressBar.style.width = progress + '%';
percentText.textContent = Math.floor(progress) + '%';
// Update status message based on progress
const statusIndex = Math.min(Math.floor(progress / 20), statusMessages.length - 1);
statusText.textContent = statusMessages[statusIndex];
// Typical macOS loading behavior has varying speeds
let delay;
if (progress < 30) {
delay = 100 + Math.random() * 200;
} else if (progress < 60) {
delay = 200 + Math.random() * 300;
} else if (progress < 85) {
delay = 150 + Math.random() * 250;
} else {
delay = 300 + Math.random() * 500;
}
setTimeout(updateProgress, delay);
} else {
// Complete loading
progressBar.style.width = '100%';
percentText.textContent = '100%';
statusText.textContent = 'Ready';
// In a real app, you'd launch the main window here
}
}
// Start with a slight delay like real macOS apps
setTimeout(updateProgress, 600);
</script>
</body>
</html>