in src/gemini_95/index.ts [489:749]
async function navigateToUrl(url: string): Promise<void> {
// Normalize URL format
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
try {
// Parse the domain to use in the prompt
const urlObj = new URL(url);
const domain = urlObj.hostname;
// Show loading screen
loadingEl.innerHTML = `
<style>
.dialup-animation .dot {
animation: dialup-blink 1.4s infinite both;
}
.dialup-animation .dot:nth-child(2) {
animation-delay: 0.2s;
}
.dialup-animation .dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes dialup-blink {
0%, 80%, 100% { opacity: 0; }
40% { opacity: 1; }
}
.browser-loading p { margin: 5px 0; }
.browser-loading .small-text { font-size: 0.8em; color: #aaa; }
</style>
<img src="https://d112y698adiu2z.cloudfront.net/photos/production/software_photos/000/948/341/datas/original.gif"/>
<p>Connecting to ${domain}<span class="dialup-animation"><span class="dot">.</span><span class="dot">.</span><span class="dot">.</span></span></p>
<!-- Sound will play via JS -->
`;
loadingEl.style.display = 'flex'; // Make it visible *after* setting content
// Play dial-up sound
try {
if (!dialUpAudio) { // Create audio element if it doesn't exist
dialUpAudio = new Audio(DIAL_UP_SOUND_URL);
dialUpAudio.loop = true; // Loop the sound while connecting
}
await dialUpAudio.play(); // Play returns a promise, await it
console.log("Playing dial-up sound");
} catch (audioError) {
console.error("Error playing dial-up sound:", audioError);
}
try {
// Try to load or initialize Gemini
if (!geminiInstance) {
if (!await initializeGeminiIfNeeded('initAiBrowser')) {
const errorMsg = "CRITICAL ERROR: Failed to initialize AI. Cannot generate website.";
alert(errorMsg);
iframe.src = 'data:text/plain;charset=utf-8,' + encodeURIComponent(errorMsg);
loadingEl.style.display = 'none';
return;
}
}
console.log("Generating 90s website for domain:", domain);
// Create a comprehensive prompt for a 90s-style website with images
const websitePrompt = `
Create a complete 90s-style website for the domain "${domain}".
You MUST include:
1. One image MUST be in your response - it should be a relevant graphic for "A small graphic logo for a website like ${domain}"
2. Generate HTML with garish 90s styling (neon colors, comic sans, tables for layout)
3. Make the content related specifically to what "${domain}" would be about
4. Include classic 90s web elements:
- Scrolling marquee text
- Retro emoji or ascii art
- Blinking elements
- "Best viewed in Chrome" badge
- Visitor counter (use 9000+)
- "Under Construction" signs
Create a fun, humorous website that feels like it was made in 1996.
The image MUST match the theme of the website.
DO NOT use modern web design principles - embrace the chaos of 90s web!
`;
// Generate website with text and images using the specifically mentioned model
const result = await geminiInstance.models.generateContent({
model: "gemini-2.0-flash-mmgen-rev17",
contents: [{ role: "user", parts: [{ text: websitePrompt }] }],
config: {
temperature: 0.9,
responseModalities: ["TEXT", "IMAGE"]
}
});
console.log("Response received from Gemini API");
console.log("Full Gemini Response:", JSON.stringify(result, null, 2)); // Log the full response object
// Process the response to extract text (HTML) and images
let htmlContent = "";
const images: string[] = [];
// Extract HTML and images from the response
if (result.candidates && result.candidates.length > 0 && result.candidates[0].content) {
for (const part of result.candidates[0].content.parts) {
if (part.text) {
// Extract HTML content (remove any markdown format if present)
const textContent = part.text.replace(/```html|```/g, '').trim();
htmlContent += textContent;
}
else if (part.inlineData && part.inlineData.data) {
// Store image data as data URLs
images.push(`data:${part.inlineData.mimeType || 'image/png'};base64,${part.inlineData.data}`);
console.log("Image extracted successfully");
}
}
}
console.log(`Extracted HTML content and ${images.length} images`);
// If we don't have HTML content, generate a basic framework
if (!htmlContent.includes("<html")) {
// Generate base HTML structure if needed
const baseHTML = `
<!DOCTYPE html>
<html>
<head>
<title>${domain} - 90s Website</title>
<style>
body {
background-color: #FF00FF;
color: black;
font-family: "Comic Sans MS", "Times New Roman", serif;
margin: 10px;
padding: 0;
}
.container {
background-color: #00FFFF;
border: 5px ridge #FFFFFF;
padding: 10px;
margin: 10px auto;
max-width: 800px;
}
h1, h2 {
text-align: center;
color: blue;
text-shadow: 2px 2px yellow;
}
.blink {
animation: blinker 1s linear infinite;
}
@keyframes blinker {
50% { opacity: 0; }
}
img {
border: 3px ridge gray;
display: block;
margin: 10px auto;
max-width: 90%;
}
marquee {
background-color: yellow;
color: red;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<marquee scrollamount="6" behavior="alternate">Welcome to ${domain}!</marquee>
<h1 class="blink">Welcome to ${domain}</h1>
<div id="content">${htmlContent}</div>
</div>
</body>
</html>
`;
htmlContent = baseHTML;
}
// Insert images into HTML - both methods
if (images.length > 0) {
// Method 1: Replace placeholder image references in the HTML
if (images[0]) {
// Replace references to logo or first image
htmlContent = htmlContent.replace(/src=["']logo\.png["']/i, `src="${images[0]}"`);
htmlContent = htmlContent.replace(/src=["'][^"']*logo[^"']*\.(?:png|gif|jpe?g)["']/i, `src="${images[0]}"`);
}
if (images[1]) {
// Replace references to under construction image
htmlContent = htmlContent.replace(/src=["']under_construction\.gif["']/i, `src="${images[1]}"`);
htmlContent = htmlContent.replace(/src=["'][^"']*under[^"']*construction[^"']*\.(?:png|gif|jpe?g)["']/i, `src="${images[1]}"`);
// Replace any other misc image that might be the second image
htmlContent = htmlContent.replace(/src=["'](?!data:)[^"']*\.(?:png|gif|jpe?g)["']/i, `src="${images[1]}"`);
}
// Method 2: If no image tags were found or replaced, insert the images directly
if (!htmlContent.includes(images[0]) && !htmlContent.includes(images[1])) {
// Find appropriate places to insert the images
let updatedHTML = htmlContent;
// If we have at least one image, insert it after the first heading as a logo
if (images[0]) {
updatedHTML = updatedHTML.replace(
/(<h1[^>]*>.*?<\/h1>)/i,
`$1\n<div style="text-align:center;"><img src="${images[0]}" alt="Logo" style="max-width:80%;"></div>`
);
}
// If we have a second image, find a suitable place for the "Under Construction" graphic
if (images[1]) {
// Try to insert before the closing body tag or closing container div
if (updatedHTML.includes('</body>')) {
updatedHTML = updatedHTML.replace(
'</body>',
`<div style="text-align:center;"><img src="${images[1]}" alt="Under Construction" style="max-width:60%;"></div>\n</body>`
);
} else {
// Fallback insertion point
updatedHTML = updatedHTML.replace(
'</div>',
`<div style="text-align:center;"><img src="${images[1]}" alt="Under Construction" style="max-width:60%;"></div>\n</div>`
);
}
}
htmlContent = updatedHTML;
}
}
// Create a data URL from the HTML content
const dataUrl = 'data:text/html;charset=utf-8,' + encodeURIComponent(htmlContent);
// Set the iframe source to our generated content
iframe.src = dataUrl;
// Update address bar to show the URL that was entered
addressBar.value = url;
console.log("Website generated and displayed successfully");
} catch (error: any) {
console.error("Error generating website:", error);
alert(`Error: ${error.message || "Failed to generate website"}`);
} finally {
// Hide loading screen
loadingEl.style.display = 'none';
// Stop dial-up sound
if (dialUpAudio) {
dialUpAudio.pause();
dialUpAudio.currentTime = 0; // Reset playback position
console.log("Stopped dial-up sound");
}
}
} catch (e) {
console.error("Invalid URL:", e);
alert("Please enter a valid URL");
loadingEl.style.display = 'none';
}
}