|
<!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"> </p> |
|
<div class="spinner"></div> |
|
<p class="app-subtitle"> </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'); |
|
} |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
checkFirstRun(); |
|
|
|
|
|
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); |
|
|
|
|
|
const isCpuHigh = cpuUsage > 80; |
|
const isMemoryLow = memoryFree < 3; |
|
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(); |
|
|
|
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) { |
|
|
|
let increment; |
|
|
|
if (progress < 20) { |
|
|
|
increment = Math.random() * 2 + 0.5; |
|
} else if (progress < 50) { |
|
|
|
increment = Math.random() * 1 + 0.2; |
|
} else if (progress < 85) { |
|
|
|
increment = Math.random() * 1.5 + 0.3; |
|
} else { |
|
|
|
increment = Math.random() * 0.8 + 0.1; |
|
|
|
|
|
if (progress > 99) progress = 99; |
|
} |
|
|
|
progress += increment; |
|
progress = Math.min(progress, 99.9); |
|
|
|
|
|
progressBar.style.width = progress + '%'; |
|
percentText.textContent = Math.floor(progress) + '%'; |
|
|
|
|
|
const statusIndex = Math.min(Math.floor(progress / 20), statusMessages.length - 1); |
|
statusText.textContent = statusMessages[statusIndex]; |
|
|
|
|
|
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 { |
|
|
|
progressBar.style.width = '100%'; |
|
percentText.textContent = '100%'; |
|
statusText.textContent = 'Ready'; |
|
|
|
|
|
} |
|
} |
|
|
|
|
|
setTimeout(updateProgress, 600); |
|
</script> |
|
</body> |
|
</html> |
|
|