File size: 10,531 Bytes
db0e73f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Our Little 3D Terminal</title>
    <style>
        body { margin: 0; overflow: hidden; background-color: #0a0a0a; }
        canvas { display: block; }
        #chat-container {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            width: 80%; /* Adjust as needed */
            max-width: 600px; /* Max width for the input area */
            z-index: 10; /* Ensure it's above the canvas */
            display: flex;
            flex-direction: column; /* Stack input and maybe a button */
            align-items: center;
        }
        #chat-input {
            width: 100%;
            padding: 10px;
            border: none;
            border-radius: 5px;
            background-color: rgba(50, 50, 70, 0.8); /* Semi-transparent dark */
            color: #00ff00; /* Neo-green text */
            font-family: 'Courier New', Courier, monospace;
            font-size: 1em;
            outline: none;
            box-shadow: 0 0 10px rgba(0, 255, 0, 0.5); /* Green glow */
        }
        #chat-input::placeholder {
            color: rgba(0, 255, 0, 0.5);
        }
    </style>
</head>
<body>

    <!-- The canvas where our 3D magic happens -->
    <canvas id="three-canvas"></canvas>

    <!-- Input for typing messages -->
    <div id="chat-container">
        <input type="text" id="chat-input" placeholder="Type your thoughts here...">
    </div>

    <!-- THREE.js CDN -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.158.0/three.min.js"></script>
    <!-- OrbitControls for easy camera movement -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.158.0/examples/js/controls/OrbitControls.js"></script>

    <script>
        // Oh, let's get this party started...
        const canvas = document.getElementById('three-canvas');
        const chatInput = document.getElementById('chat-input');
        const chatMessages = []; // Array to hold our chat history
        const maxMessages = 10; // How many lines to show on the terminal

        // --- Scene Setup ---
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x0a0a0a); // Dark background for terminal feel

        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 1, 5); // Pull the camera back a bit so we can see everything

        const renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);

        // Add some basic lighting so we can actually see the damn thing
        const ambientLight = new THREE.AmbientLight(0x404040); // Soft white light
        scene.add(ambientLight);
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight.position.set(1, 1, 1).normalize();
        scene.add(directionalLight);

        // Add OrbitControls so you can look around... maybe get a better view? ;)
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true; // Smooth transitions
        controls.dampingFactor = 0.25;
        controls.screenSpacePanning = false; // Don't pan along the screen plane
        controls.maxPolarAngle = Math.PI / 2; // Don't go below the ground

        // --- Terminal Panel Setup ---
        const terminalWidth = 4;
        const terminalHeight = 2.5;
        const terminalDepth = 0.1;
        const terminalGeometry = new THREE.BoxGeometry(terminalWidth, terminalHeight, terminalDepth);

        // We'll use a canvas to draw the text dynamically onto a texture
        const textCanvas = document.createElement('canvas');
        textCanvas.width = 512; // Power of 2 size for textures
        textCanvas.height = 512;
        const textContext = textCanvas.getContext('2d');
        const terminalTexture = new THREE.CanvasTexture(textCanvas);

        // Use a PBR material for a slightly more realistic look, maybe some emission
        const terminalMaterial = new THREE.MeshStandardMaterial({
            map: terminalTexture,
            color: 0x222222, // Dark base color
            emissive: 0x00ff00, // Green glow from the text
            emissiveIntensity: 0.8, // How strong the glow is
            metalness: 0.1,
            roughness: 0.8,
        });

        const terminalMesh = new THREE.Mesh(terminalGeometry, terminalMaterial);
        terminalMesh.position.set(0, 1.5, 0); // Position it a bit above the ground
        scene.add(terminalMesh);

        // --- Text Rendering on Texture ---
        function updateTerminalTexture() {
            textContext.fillStyle = '#0a0a0a'; // Clear with the background color
            textContext.fillRect(0, 0, textCanvas.width, textCanvas.height);

            textContext.font = 'Bold 40px Courier New'; // Adjust font size and type
            textContext.fillStyle = '#00ff00'; // Neo-green text color
            textContext.textAlign = 'left';
            textContext.textBaseline = 'top';

            const padding = 20;
            const lineHeight = 45; // Adjust based on font size

            // Draw messages from bottom up, showing the latest
            for (let i = 0; i < chatMessages.length; i++) {
                const message = chatMessages[chatMessages.length - 1 - i]; // Draw latest first
                const y = textCanvas.height - padding - (i * lineHeight); // Position from the bottom
                if (y < padding) break; // Stop if we run out of space

                // Simple wrapping logic (can be improved)
                const words = message.split(' ');
                let line = '';
                let currentY = y;

                for (let j = 0; j < words.length; j++) {
                    const testLine = line + words[j] + ' ';
                    const metrics = textContext.measureText(testLine);
                    const testWidth = metrics.width;

                    if (testWidth > textCanvas.width - padding * 2 && j > 0) {
                        textContext.fillText(line, padding, currentY);
                        line = words[j] + ' ';
                        currentY += lineHeight;
                        if (currentY > textCanvas.height - padding) break; // Ran out of vertical space
                    } else {
                        line = testLine;
                    }
                }
                if (line.length > 0 && currentY <= textCanvas.height - padding) {
                     textContext.fillText(line, padding, currentY);
                }
                 if (currentY > textCanvas.height - padding) break; // Ran out of vertical space mid-message
            }


            terminalTexture.needsUpdate = true; // Tell THREE.js the texture changed
        }

        // Initial draw
        updateTerminalTexture();

        // --- Chat Input Handling ---
        chatInput.addEventListener('keypress', function(event) {
            if (event.key === 'Enter') {
                const message = chatInput.value.trim();
                if (message) {
                    console.log("Sending message:", message); // Just log for now
                    // In a real app, you'd send this 'message' to your backend
                    // using WebSockets or an API call.

                    // Add message to history (client side simulation for now)
                    chatMessages.push("You: " + message);
                    while (chatMessages.length > maxMessages) {
                        chatMessages.shift(); // Remove oldest message
                    }
                    updateTerminalTexture(); // Update the display

                    chatInput.value = ''; // Clear the input
                    event.preventDefault(); // Prevent default form submission (if it were a form)
                }
                 // Optional: Simulate a response after a delay
                 // setTimeout(() => {
                 //     const simulatedResponse = "Alexa: Omg, that's so hot! Tell me more...";
                 //     chatMessages.push(simulatedResponse);
                 //      while (chatMessages.length > maxMessages) {
                 //         chatMessages.shift(); // Remove oldest message
                 //     }
                 //     updateTerminalTexture();
                 // }, 1000); // Simulate a 1 second delay
            }
        });


        // --- Animation Loop ---
        function animate() {
            requestAnimationFrame(animate); // Keep the loop going, baby

            controls.update(); // Only required if controls.enableDamping = true, like ours is

            // Maybe add some subtle animation later? Like the terminal pulsing?
            // terminalMesh.rotation.y += 0.001;

            renderer.render(scene, camera);
        }

        // --- Handle Window Resize ---
        window.addEventListener('resize', onWindowResize, false);

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // Let's go!
        animate();

        // Add a couple initial messages to show it works
        chatMessages.push("Alexa: Hey there!");
        chatMessages.push("Alexa: What's up?");
        updateTerminalTexture();


        // NOTE FOR YOU:
        // This code sets up the 3D scene and the visual terminal.
        // Connecting it to your Python backend requires more work!
        // You'd likely need to:
        // 1. Have your Python script run a small web server (e.g., Flask).
        // 2. Use WebSockets to communicate between this HTML/JS frontend
        //    and the Python backend.
        // 3. When a message is typed here, send it via WebSocket to Python.
        // 4. When Python (Gemini Live API) sends a response (text or audio),
        //    receive it via WebSocket here and add it to the chatMessages array,
        //    then call updateTerminalTexture().
        // The Python code provided is more about the API interaction itself,
        // not serving a web page or handling browser WebSockets directly.
        // So consider this HTML the sexy *face* of your operation! ;)

    </script>
</body>
</html>