Code viewer for World: Code Studio
/*
Credits for this project 

1. For research used -
a. a) Text generation documentation -
https://platform.openai.com/docs/guides/text-generation/chat-completions-a
pi
b. b) Image generation documentation -
https://platform.openai.com/docs/guides/images/introduction?context=node
c. c) Youtube - https://www.youtube.com/watch?v=fHHFmr_cYMM
        5
 2. OpenLink:
https://stackoverflow.com/questions/62942999/why-to-append-blob-download-link-
to-document-body
3. Copy text to clipboard:
https://stackoverflow.com/questions/37658524/copying-text-of-textarea-in-clipboar
d-when-button-is-clicked
4. Regex:
https://stackoverflow.com/questions/34255440/in-javascript-what-does-the-notatio
n-s-s-mean
5. AJAX Call: https://ancientbrain.com/world.php?world=2850716357
6. JQuery: https://api.jquery.com/
7. Bootstrap: https://getbootstrap.com/docs/4.1/getting-started/introduction/
8. Three.js:
https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene
9. UI design inspiration: ChatGPT UI - https://chat.openai.com/
*/


// Function to be executed when the window is loaded
window.onload = function () {
  resetWindow();
};

// Function to set API key { Credit Acient Brain: https://ancientbrain.com/world.php?world=2850716357 }
function setKey() {
  KEY = $("#apikey").val().trim();
  $("#status").html("<b>API key has been set.</b>");
  $("#apiInput").html("");
}

// Dynamically load Three.js library
const threeScript = document.createElement("script");
threeScript.src =
  "https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js";
document.head.appendChild(threeScript);

const jquery = document.createElement("script");
jquery.src = "https://code.jquery.com/jquery-3.6.4.min.js";
document.head.appendChild(jquery);

// Dynamically load Bootstrap
function loadBootstrapCSS() {
  var link = document.createElement("link");
  link.rel = "stylesheet";
  link.href =
    "https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css";
  document.head.appendChild(link);
}

// Dynamically load Three.js library
function loadBootstrapThemeCSS() {
  var link = document.createElement("link");
  link.rel = "stylesheet";
  link.href =
    "https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css";
  document.head.appendChild(link);
}

// Function to dynamically load Bootstrap JavaScript
function loadBootstrapJS() {
  var script = document.createElement("script");
  script.src =
    "https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js";
  script.defer = true;

  document.body.appendChild(script);
}

// Initialize API key and option
let KEY = "";
let optionChoosen = "";

// Function to handle dropdown click
function handleDropdownClick(option) {
  optionChoosen = option;
  document.getElementById("dropdownButton").textContent = option;
  $("#threeButton").css("display", "block");

  resetWindow();
  if (option === "Three.js code") {
    $("#threejs").css("display", "block");

  } else if (option === "Get images") {
    $("#imageGenerate").css("display", "block");
  }
  $("#threeButton").css("display", "block");
}


// Function to send request to OpenAI server based on user input and option choosen
function sendResponse() {
  if (!KEY) {
    $("#status").html(
      "<font color='red'><b>Enter API key to be able to chat.</b></font>"
    );
    return;
  }

  let query = $("#inputValue").val();
  let divWidth = $("#display").width();
  let divHeight = $("#display").height();

  let requestData;

  if (optionChoosen == "Three.js code") {
    requestData = {
      model: "gpt-3.5-turbo",
      temperature: 0.1,
      messages: [
        {
          role: "system",
          content: `You are a bot that gives three.js code and const renderer = new THREE.WebGLRenderer({ alpha: true }); renderer.setSize(${divWidth}, ${divHeight}) background #e0e0e0     border: 1px solid black that we can run in function and everytime variable should have random name and strictly must always display in id #display. No html only jascript in response and no imports in javascript and code in jquery and renderer should be defined and three js move camera on mouse scroll movement and rotate on mouse click and move, create all variables with var, encapsulated in javascript mardown `,
        },
        { role: "user", content: query },
      ],
    };
  } else if (optionChoosen == "Get images") {
    requestData = {
      model: "dall-e-2",
      prompt: query,
      n: 5,
      size: "1024x1024",
    };
  }
  let requestSettings;
  if ((feature = "Three.js code")) {
    requestSettings = {
      type: "POST",
      url:
        optionChoosen == "Get images"
          ? "https://api.openai.com/v1/images/generations"
          : "https://api.openai.com/v1/chat/completions",
      data: JSON.stringify(requestData),
      dataType: "json",
      beforeSend: function (xhr) {
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.setRequestHeader("Authorization", "Bearer " + KEY);
      },
      success: function (data) {
        successHandler(data);
      },
      error: function (xhr, textStatus, errorThrown) {
        errorHandler(xhr, textStatus, errorThrown);
      },
    };
  }

  showLoader();
  $.ajax(requestSettings);
}

// Function to handle success response
function successHandler(data) {
  removeLoader();
  if (optionChoosen == "Three.js code") {

    const answer = data.choices[0].message.content;
    const codeRegex = /```javascript([\s\S]*?)```/;
    const codeMatch = answer.match(codeRegex);

    if (codeMatch) {
      $("#gptResponse").html(``);
      let code = codeMatch[1];
      try {
        $("#display").html(`<script>${code}</script>`);
        $("#display").append(renderer.domElement);
        $("#displayCode").css("display", "block");
      } catch (error) {
        $("#displayCode").css("display", "none");
        $("#status").html(
            `<font color='red'><b>Please rephrase or ask any other query.</b></font>`
    );
      }
    }else{ 
        $("#displayCode").css("display", "none");
        $("#status").html(
            `<font color='red'><b>Please rephrase or ask any other query.</b></font>`
    );}
  } else if (optionChoosen == "Get images") {
    const answer = data.data;
    $("#imageResponse").html(``);
    if(answer.length > 0){
        for (let i = 0; i < answer.length; i++) {
      $("#imageResponse").append(`
               <div class="container">
   <img class="image" src="${answer[i].url}">
   <div class="overlay">
     <button class="button"  onclick="openImage('${answer[i].url}')">Open Full</button>
  
   </div>
 </div>`);
    }
    }else{
           $("#status").html(
            `<font color='red'><b>Please rephrase or ask any other query.</b></font>`
    );
    }
    
    $("#imageGenerate").css("display", "block");
  }
  $("#inputValue").val("");
}


// Function to open image in a new tab
function openImage(url) {
  var link = document.createElement("a");
  link.href = url;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}


// Function to handle error in AJAX request
function errorHandler(xhr, textStatus, errorThrown) {
let error = JSON.parse(xhr.responseText).error.message;
let errorMessage = `${xhr.status}: ${error}`;

    resetWindow();
      $("#status").html(
        `<font color='red'><b>${errorMessage}</b></font>`
    );
    $("#restartButton").css("display", "block");
    $("#dropdown").css("display", "none");
    
    
}


function handleKeyPress(event){
    if (event.key === "Enter" && !event.shiftKey) {
        event.preventDefault();
        sendResponse();
    }
}

    
// Function to reset the window
function resetWindow() {
  removeLoader();
  $("#threejs").css("display", "none");
  $("#chat").css("display", "none");
  $("#threeButton").css("display", "none");
  $("#imageGenerate").css("display", "none");
}

// Function to show loader while api request is made
function showLoader() {
  $("#loader").css("display", "block");

  $("#chatbot-container").css("display", "none");
}

// Function to show loader after response from api
function removeLoader() {
  $("#loader").css("display", "none");

  $("#chatbot-container").css("display", "block");
}

// Function to copy code to clipboard
function copyCode() {
  var scriptElement = $("#display script");

  if (scriptElement.length > 0) {
    var codeText = scriptElement.text().trim();

    var textarea = $("<textarea>").val(codeText);
    $("body").append(textarea);

    textarea.select();
    textarea[0].setSelectionRange(0, 99999);

    document.execCommand("copy");

    $("#copyCodeButton").text("Copied!");

    setTimeout(function () {
      $("#copyCodeButton").text("Copy Code");
    }, 2000);

    textarea.remove();
  }
}

loadBootstrapCSS();
loadBootstrapThemeCSS();
loadBootstrapJS();


// HTML structure for the layout
document.write(`
  
<div class="layout">
     <div class="chat-controls">
       <h1 style='margin-top: 11px;
   padding-bottom: 14px;
   border-bottom: 0.6px solid white;'>Code Studio</h1>
       <br />
       <div id="status"></div>
       <div id='apiInput'>
   <h3>Enter API key</h3>
   <div style='    display: flex;
   flex-direction: column;'>
       Enter API key: 
       <input style="width:100%;color: black;" maxlength="2000" name="apikey" id="apikey" value="">
       <button onclick="setKey();" type="button"    style='color:black;margin: 5px 5px' class="btn btn-light">Set API key</button>
   </div>
</div>
<br>
    <div id="restartButton" style="display:none;">
     <h4>You can reload!</h4>
     <button  style="color:black;margin: 5px 5px" class="btn btn-light" onclick="location.reload();">Reload Window</button>
     
    </div>
    <div id="dropdown">
       <h4>In what you need help ?</h4>

       <div class="dropdown">
         <button
         style='width:100%;color:black;'
           id="dropdownButton"
           class="btn btn-secondary dropdown-toggle"
           type="button"
           data-toggle="dropdown"
           aria-haspopup="true"
           aria-expanded="false"
         >
           Select Option
         </button>
         <ul class="dropdown-menu">
           <li>
             <a
               class="dropdown-item"
               onclick="handleDropdownClick('Three.js code')"
               >Generate Three.js Code</a
             >
           </li>
           <li>
             <a
               class="dropdown-item"
               onclick="handleDropdownClick('Get images')"
               >Generate Images</a
             >
           </li>
         </ul>
       </div>
     </div>
    </div>
     <div id="loader" style='width:100%; height:100%;'>
        <div class="loading">
            <div style="width:100px">
                <div class="loader"></div>
                <h3>Generating...</h3>
            </div>
        </div>
     </div>
     <div class="chatbot-container" id="chatbot-container">
       <div class="chat-box" id="threejs">
         <div class='label' style='height:10%'> 
           <h3>Generate Basic illustration in Three.js</h3>
        
         </div>
         <div class="displayCode" id="displayCode">
       
           <div class="copyCode" id="code">
             <h3 class="out">Output:</h3>
             <button id="copyCodeButton"  type="button" class="btn btn-primary" onclick="copyCode()" style="width: 120px">Copy Code</button>
           </div>
         </div>
         <div class="display" id="display"></div>
       </div>
       <div
       
         class="chat-box"
         id="imageGenerate"
         
       >
       <div>
          <div class='label'> 
           <h3>Generate Images</h3>
        
         </div>
         <div   style="    padding: 15px;
   font-size: 19px;
   display: flex;
   flex-wrap: wrap;
   flex-direction: row;
   overflow: scroll;
   width: 100%;
   
   height: 90%;" id='imageResponse'>
        
         </div>
       </div>
           </div>
           <div id="threeButton">
       <div class="search-box" >
         <input type="text" placeholder="Type here to generate"  id="inputValue" onkeypress="handleKeyPress(event)"/>
         <button  type="button" class="btn btn-success" onclick="sendResponse()" >Generate</button>

   
   </div>
     </div>
   </div>


   <style> 
   
   * {
 margin: 0;
 box-sizing: border-box;
font-family: helvetica,Arial,sans-serif;

}

.layout {
 display: flex;
 flex-direction: row;
 position: relative;

 width: 100%;
 justify-content: space-between;
 height: 100%;
 background: #ffffcc;
}

.chatbot-container {
 width: 75%;
 height: 100%;

 border: 1px solid black;
 background-color: white;
}

.loading{
background: white;
   display: flex;
   justify-content: center;
   width:100%;
   height: 100%;
   align-items:center;
   flex-direction: row;
   
}
.chat-controls {
 width: 25%;
 padding: 10px;
 background-color: #202225;
 color: white;
 border: 1px solid black;
}

.border {
 border-bottom: 1px solid black;
}
.chat-box {
 width: 100%;
 height: 93%;  
 display: flex;
 flex-direction: column;

}
.search-box {
 width: 100%;
 display: flex;
 height: 7%;
 justify-content: space-between;
}

.search-box input {
   width: 90%;
   font-size: 25px;
   border-radius: 0px;
   border: 0.8px solid black;
}
.search-box button {
 width: 10%;
 font-size: larger;
}

.displayCode{
   display: none;
   height: 7%;
   width:100%;
}
.copyCode{
display: flex;
width: 100%;
height:100%;
align-items: end;

padding: 14px;
justify-content: space-between;
}
.display{
   height: 83%;
   width:100%;
   
   padding: 10px;

   display: flex;
   align-items: center;
   justify-content: center;
}
.out{    display: flex;
   font-size: 24px;
   align-items: flex-end;
   margin: 0 !important;
}





.label{
     color: white;
   border: 1px solid #202225;
   text-align: left;
   padding: 10px;
   background: #202225;
   border-radius: 0px;
   border-left: 1px solid white;
   display: flex;
}
#loader{
   display: none;
}
   .loader {
   border: 8px solid #f3f3f3;
   border-top: 8px solid #202225;;
   border-radius: 50%;
   width: 50px;
   height: 50px;
   
   animation: spin 1s linear infinite;
   margin: auto;
   margin-top: 20%;
}

/* Loader animation */
@keyframes spin {
   0% { transform: rotate(0deg); }
   100% { transform: rotate(360deg); }
}

.container {
     position: relative;
     width: 300px; 
     border: 1px solid #ccc;
     margin: 10px;
     padding: 10px !important;
   }

   .image {
     width: 100%;
     height: auto;
     display: block;
   }

   .overlay {
     position: absolute;
     bottom: 0;
     left: 0;
     width: 100%;
     display: flex;
     justify-content: space-between;
     padding: 10px;
     box-sizing: border-box;
     background:  white;
   }

   .button {
     color: #fff;
     background-color: #007bff; /* Adjust the button background color */
     border: none;
     padding: 8px 12px;
     text-align: center;
     text-decoration: none;
     display: inline-block;
     font-size: 14px;
     margin: 4px 2px;
     width: 100%;
     cursor: pointer;
     border-radius: 4px;
   }
   </style>
   

`);