summaryrefslogtreecommitdiff
path: root/frontend/src/components/VNInterface.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/VNInterface.tsx')
-rw-r--r--frontend/src/components/VNInterface.tsx208
1 files changed, 208 insertions, 0 deletions
diff --git a/frontend/src/components/VNInterface.tsx b/frontend/src/components/VNInterface.tsx
new file mode 100644
index 0000000..be71d27
--- /dev/null
+++ b/frontend/src/components/VNInterface.tsx
@@ -0,0 +1,208 @@
+import React, { useEffect } from 'react'
+import { useStore } from '@nanostores/react'
+import {
+ isStandbyStore,
+ currentTimeStore,
+ weatherStore,
+ showChoicesStore,
+ showSettingsModalStore,
+ isVisibleStore,
+ yenBalanceStore,
+ toggleStandby,
+ toggleShowChoices,
+ updateTime
+} from '../stores'
+
+interface VNInterfaceProps {
+ onLogout: () => void
+}
+
+export function VNInterface({ onLogout }: VNInterfaceProps) {
+ const isStandby = useStore(isStandbyStore)
+ const currentTime = useStore(currentTimeStore)
+ const weather = useStore(weatherStore)
+ const showChoices = useStore(showChoicesStore)
+ const showSettingsModal = useStore(showSettingsModalStore)
+ const isVisible = useStore(isVisibleStore)
+ const yenBalance = useStore(yenBalanceStore)
+
+ // Fade in effect on mount
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ isVisibleStore.set(true)
+ }, 100)
+ return () => clearTimeout(timer)
+ }, [])
+
+ // Update clock every second (Japan Time)
+ useEffect(() => {
+ const timer = setInterval(() => {
+ const now = new Date()
+ // Convert to Japan Time (UTC+9)
+ const japanTime = new Date(now.getTime() + (now.getTimezoneOffset() * 60000) + (9 * 3600000))
+ updateTime()
+ }, 1000)
+ return () => clearInterval(timer)
+ }, [])
+
+ return (
+ <div className={`vn-interface ${isVisible ? 'fade-in' : 'fade-out'}`}>
+ {/* Background */}
+ <div className="vn-background">
+ <img
+ src="/__gaogao__56242cbde8f18ac64522e410bad04e68_waifu2x_art_noise2.png"
+ alt="Background image"
+ className="background-image"
+ />
+ </div>
+
+ {/* Combined Info Panel (Top Right) */}
+ <div className="floating-info-panel">
+ <div className="info-panel-content">
+ {/* Weather Section */}
+ <div className="weather-section">
+ <div className="weather-icon">🌤️</div>
+ <div className="weather-details">
+ <div className="weather-location">Tokyo</div>
+ <div className="weather-temp">22°C Sunny</div>
+ </div>
+ </div>
+
+ {/* Time Section */}
+ <div className="time-section">
+ <div className="japan-date">{currentTime.toLocaleDateString('ja-JP', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ weekday: 'short'
+ })}</div>
+ <div className="japan-time">{currentTime.toLocaleTimeString('ja-JP', {
+ hour12: false,
+ hour: '2-digit',
+ minute: '2-digit'
+ })}</div>
+ </div>
+
+ {/* Status Section */}
+ <div className="status-section">
+ <div className="status-item">
+ <span className="info-label">Balance:</span>
+ <span className="info-value yen-balance">¥{yenBalance.toLocaleString()}</span>
+ </div>
+ <div className="status-item">
+ <span className="info-label">System:</span>
+ <span
+ className="info-value live-status clickable"
+ onClick={toggleStandby}
+ title="Click to toggle between LIVE and STANDBY"
+ >
+ <span className={`status-dot ${isStandby ? 'standby' : 'live'}`}></span>
+ {isStandby ? 'STDBY' : 'LIVE'}
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ {/* Main VN Viewport */}
+ <div className="vn-viewport">
+ <div className="vn-content">
+ </div>
+ </div>
+
+ {/* Dialogue Panel (Bottom) */}
+ <div className="floating-dialogue-panel">
+ <div className="dialogue-content">
+ <div className="dialogue-speaker">???</div>
+ <div className="dialogue-text">
+ A warm CRT glow fills the room. A figure turns towards you...
+ </div>
+ </div>
+ </div>
+
+ {/* Input/Choice Panel (Bottom) */}
+ <div className="floating-input-panel">
+ <div className="input-content">
+ {!showChoices ? (
+ // Text Input Mode
+ <input
+ type="text"
+ className="vn-text-input"
+ placeholder="Type your response..."
+ onKeyPress={(e) => {
+ if (e.key === 'Enter') {
+ const target = e.target as HTMLInputElement;
+ if (target.value.trim()) {
+ console.log('User input:', target.value);
+ target.value = '';
+ }
+ }
+ }}
+ />
+ ) : (
+ // Choice Options Mode
+ <div className="choice-buttons">
+ <button className="choice-btn" onClick={() => console.log('Choice: Hello?')}>"Hello?"</button>
+ <button className="choice-btn" onClick={() => console.log('Choice: Who are you?')}>"Who are you?"</button>
+ <button className="choice-btn" onClick={() => console.log('Choice: Stay silent')}>(Stay silent)</button>
+ </div>
+ )}
+
+ {/* Toggle Button */}
+ <button
+ className="toggle-input-btn"
+ onClick={toggleShowChoices}
+ title={showChoices ? "Switch to text input" : "Switch to choice options"}
+ >
+ {showChoices ? "⎀" : "≡"}
+ </button>
+ </div>
+ </div>
+
+ {/* Floating Settings Button */}
+ <button className="floating-logout-btn" onClick={() => showSettingsModalStore.set(true)}>
+ <span className="btn-icon">⚙</span>
+ <span className="btn-text">Settings</span>
+ </button>
+
+ {/* Settings Modal */}
+ {showSettingsModal && (
+ <div className="modal-overlay" onClick={() => showSettingsModalStore.set(false)}>
+ <div className="settings-modal" onClick={(e) => e.stopPropagation()}>
+ <div className="modal-header">
+ <h2 className="modal-title">Settings</h2>
+ <button className="modal-close-btn" onClick={() => showSettingsModalStore.set(false)}>
+ ×
+ </button>
+ </div>
+ <div className="modal-content">
+ <div className="settings-section">
+ <h3>Display Options</h3>
+ <div className="setting-item">
+ <label>
+ <input type="checkbox" defaultChecked /> Enable animations
+ </label>
+ </div>
+ </div>
+ <div className="settings-section">
+ <h3>Audio</h3>
+ <div className="setting-item">
+ <label>Master Volume</label>
+ <input type="range" min="0" max="100" defaultValue="75" />
+ </div>
+ </div>
+ </div>
+ <div className="modal-footer">
+ <button className="modal-btn secondary" onClick={() => showSettingsModalStore.set(false)}>
+ Cancel
+ </button>
+ <button className="modal-btn logout" onClick={() => { showSettingsModalStore.set(false); onLogout(); }}>
+ Logout
+ </button>
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ )
+}