×
Your notification message here. this is a longer message for testing purposes how
does it look?
Results will appear here after you search for a package.
`;
// Append the dependenciesContainer to the provided container
const parentContainer = document.getElementById(containerPrefix);
if (parentContainer) {
parentContainer.appendChild(dependenciesContainer);
}
// Add event listener for the Enter key on the search input
const searchInput = dependenciesContainer.querySelector('#search-query');
searchInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
searchPackages(); // Trigger search when Enter key is pressed
}
});
}
function createGitHub(file, containerId, containerPrefix = 'editorContainer') {
// Create and set up the preview container
const previewContainer = document.createElement('div');
previewContainer.id = containerId;
previewContainer.className = 'github-container';
// Create and append the iframe
const iframe = document.createElement('iframe');
iframe.className = 'github-iframe';
// Append the default color as a query parameter
// iframe.src = `color_picker.html?defaultColor=${encodeURIComponent(currentSettings.colorPicker)}`;
iframe.src = `git.html`
iframe.onload = () => {
// iframe.contentWindow.postMessage({
// type: 'getData',
// categories: categories,
// }, '*');
};
previewContainer.appendChild(iframe);
// Append the preview container to the specified container prefix
document.getElementById(containerPrefix).appendChild(previewContainer);
return previewContainer;
}
window.addEventListener('message', function (event) {
if (event.data.type === 'updateData') {
categories = event.data.categories;
// console.log('Categories updated:', categories);
}
if (event.data.type === 'saveData') {
categories = event.data.categories;
saveCategoriesToFirestore();
// console.log('Categories updated:', categories);
}
});
let graphInstances = {};
function createGraph(file, containerId, containerPrefix = 'editorContainer') {
loadSettingsFromCache()
// Create and set up the preview container
const previewContainer = document.createElement('div');
previewContainer.id = containerId;
previewContainer.className = 'graph-container';
// Create and append the iframe
const iframe = document.createElement('iframe');
iframe.className = 'graph-iframe';
// Append the default color as a query parameter
// iframe.src = `color_picker.html?defaultColor=${encodeURIComponent(currentSettings.colorPicker)}`;
iframe.src = `graph.html`
previewContainer.appendChild(iframe);
// Append the preview container to the specified container prefix
document.getElementById(containerPrefix).appendChild(previewContainer);
// Store the preview container instance for future reference
graphInstances[containerId] = previewContainer;
return previewContainer;
}
let assistantInstances = {};
function createAssistant(file, containerId, containerPrefix = 'editorContainer') {
// Find the parent container using the containerPrefix
const parentContainer = document.getElementById(containerPrefix);
if (!parentContainer) {
console.error(`Parent container with id '${containerPrefix}' not found.`);
return;
}
parentContainer.classList.add('assistant-container');
// Check for an existing chat interface and remove it
const existingChat = parentContainer.querySelector(`#${containerId}`);
if (existingChat) {
existingChat.remove();
}
// Create the chat container div
const chatContainer = document.createElement('div');
chatContainer.id = containerId;
chatContainer.classList.add('chat-container');
// Create the chat messages div
const chatMessagesDiv = document.createElement('div');
chatMessagesDiv.id = 'chat';
chatContainer.appendChild(chatMessagesDiv);
// Create the user input div
const userInputDiv = document.createElement('div');
userInputDiv.id = 'user-input';
// Create the textarea for input
const textarea = document.createElement('textarea');
textarea.classList.add('user-input');
textarea.id = 'input-box';
textarea.placeholder = 'Send a message...';
// Event listener for textarea
textarea.addEventListener('keydown', function (event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault(); // Prevent default behavior (new line)
const message = this.value.trim();
if (message) {
this.value = '';
displayUserMessage(message);
callOpenAI(message);
document.getElementById('input-box').style.height = '74px'; // Reset before adjusting to get accurate scrollHeight
document.getElementById('chat').style.minHeight = `calc(100% - 168px`;
document.getElementById('chat').style.maxHeight = `calc(100% - 168px`;
}
}
});
userInputDiv.appendChild(textarea);
// Create the send button
const sendButton = document.createElement('button');
sendButton.id = 'send-button';
sendButton.innerHTML = '';
sendButton.addEventListener('click', function () {
const inputBox = document.getElementById('input-box');
const message = inputBox.value.trim(); // remove leading and trailing whitespace
if (!inputBox.disabled && message.length > 0) { // Only send the message if the input box is not disabled and the message is not empty
inputBox.value = '';
displayUserMessage(message);
callOpenAI(message);
document.getElementById('input-box').style.height = '74px'; // Reset before adjusting to get accurate scrollHeight
document.getElementById('chat').style.minHeight = `calc(100% - 168px`;
document.getElementById('chat').style.maxHeight = `calc(100% - 168px`;
}
});
userInputDiv.appendChild(sendButton);
// Append the user input div to the chat container
chatContainer.appendChild(userInputDiv);
// Create the controls container div
const controlsContainer = document.createElement('div');
controlsContainer.id = 'controls-container';
controlsContainer.classList.add('controls-container');
// Wrapper for context checkbox
const contextCheckboxWrapper = document.createElement('div');
contextCheckboxWrapper.classList.add('control-wrapper');
// Create the checkbox
const contextCheckbox = document.createElement('input');
contextCheckbox.type = 'checkbox';
contextCheckbox.id = 'context-checkbox';
contextCheckbox.classList.add('custom-checkbox');
contextCheckboxWrapper.appendChild(contextCheckbox);
contextCheckboxWrapper.title = 'Enable context for the AI near your cursor position. This increases API token usage.';
const checkboxLabelCustom = document.createElement('label');
checkboxLabelCustom.htmlFor = 'context-checkbox';
checkboxLabelCustom.textContent = ':';
checkboxLabelCustom.classList.add('checkboxLabelCustom');
contextCheckboxWrapper.appendChild(checkboxLabelCustom);
// Create the label for the checkbox
const checkboxLabel = document.createElement('span');
checkboxLabel.textContent = 'context: ';
translateElement(checkboxLabel, startingLanguage, newLanguage);
contextCheckboxWrapper.appendChild(checkboxLabel);
// Append contextCheckboxWrapper to controlsContainer
controlsContainer.appendChild(contextCheckboxWrapper);
// Wrapper for file name
const fileNameWrapper = document.createElement('div');
fileNameWrapper.classList.add('control-wrapper');
// Create the span for the file name
const fileNameSpan = document.createElement('span');
fileNameSpan.id = 'file-name-span';
fileNameSpan.textContent = file ? file.name : 'No file selected';
fileNameWrapper.appendChild(fileNameSpan);
// Append fileNameWrapper to controlsContainer
controlsContainer.appendChild(fileNameWrapper);
// Placeholder for new dropdown select
const dropdownWrapper = document.createElement('div');
dropdownWrapper.classList.add('control-wrapper');
dropdownWrapper.classList.add('gpt-model-wrapper');
let dropdownIcon = document.createElement('div');
dropdownIcon.classList.add('dropdown-icon');
dropdownIcon.classList.add('gpt-35-color');
const dropdownSelect = document.createElement('select');
dropdownSelect.id = 'gpt-model-select'; // Replace with a suitable ID
// Add options to dropdownSelect as needed
createNotification(true);
updateModalMessage("Assistant set to gpt-3.5, project context disabled.");
setTimeout(() => startCloseCountdown(3000), 10);
// Option for GPT-3.5
const optionGPT35 = document.createElement('option');
optionGPT35.value = 'gpt-3.5';
optionGPT35.textContent = 'GPT-3.5';
dropdownSelect.appendChild(optionGPT35);
// if(defaultApiKey==false){
const optionGPT4 = document.createElement('option');
optionGPT4.id = 'gpt-4-select';
optionGPT4.value = 'gpt-4';
optionGPT4.textContent = 'GPT-4';
dropdownSelect.appendChild(optionGPT4);
// }
const optionGemini = document.createElement('option');
optionGemini.id = 'Gemini-select';
optionGemini.value = 'Gemini';
optionGemini.textContent = 'Gemini';
dropdownSelect.appendChild(optionGemini);
const optionClaude = document.createElement('option');
optionClaude.id = 'Claude-select';
optionClaude.value = 'Claude';
optionClaude.textContent = 'Claude';
dropdownSelect.appendChild(optionClaude);
// Event listener for changes in dropdown selection
dropdownSelect.addEventListener('change', (event) => {
const selectedModel = event.target.value;
const gptIcons = document.querySelectorAll('.icon-label-container i.fa-robot'); // Select all GPT icons
gptIcons.forEach(gptIcon => {
if (selectedModel === 'gpt-4') {
if (defaultApiKey == false) {
dropdownIcon.classList.add('gpt-4-color');
dropdownIcon.classList.remove('gpt-35-color');
dropdownIcon.classList.remove('gemini-color');
dropdownIcon.classList.remove('claude-color');
} else {
event.target.value = 'gpt-3.5'
createNotification(true);
updateModalMessage("Assistant set to gpt-3.5, to use gpt-4 go to settings and upload your api key.");
setTimeout(() => startCloseCountdown(5000), 10);
}
} else if (selectedModel === 'Gemini') {
if (userApiKeyGemini != "") {
dropdownIcon.classList.add('gemini-color');
dropdownIcon.classList.remove('gpt-35-color');
dropdownIcon.classList.remove('gpt-4-color');
dropdownIcon.classList.remove('claude-color');
} else {
event.target.value = 'gpt-3.5'
createNotification(true);
updateModalMessage("Assistant set to gpt-3.5, to use Gemini go to settings and upload your api key.");
setTimeout(() => startCloseCountdown(5000), 10);
}
} else if (selectedModel === 'Claude') {
if (userApiKeyClaude != "") {
dropdownIcon.classList.remove('gemini-color');
dropdownIcon.classList.remove('gpt-35-color');
dropdownIcon.classList.remove('gpt-4-color');
dropdownIcon.classList.add('claude-color');
} else {
event.target.value = 'gpt-3.5'
createNotification(true);
updateModalMessage("Assistant set to gpt-3.5, to use Claude go to settings and upload your api key.");
setTimeout(() => startCloseCountdown(5000), 10);
}
} else if (selectedModel === 'gpt-3.5') {
dropdownIcon.classList.add('gpt-35-color');
dropdownIcon.classList.remove('gpt-4-color');
dropdownIcon.classList.remove('gemini-color');
dropdownIcon.classList.remove('claude-color');
}
});
});
dropdownWrapper.appendChild(dropdownIcon);
var dropdownButton = document.createElement('button');
dropdownButton.id = 'dropdown-button-gpt';
dropdownButton.textContent = 'GPT-3.5';
dropdownWrapper.appendChild(dropdownButton);
dropdownWrapper.appendChild(dropdownSelect);
var fakeDropdown = document.createElement('div');
fakeDropdown.id = 'fake-dropdown';
fakeDropdown.className = 'fake-dropdown';
// Create and append the fake-option divs
var options = [
{ text: 'GPT-3.5', value: 'gpt-3.5' },
{ text: 'GPT-4', value: 'gpt-4' },
{ text: 'Gemini', value: 'Gemini' },
{ text: 'Claude', value: 'Claude' }
];
options.forEach(function (option) {
var fakeOption = document.createElement('div');
fakeOption.className = 'fake-option';
fakeOption.setAttribute('data-value', option.value);
fakeOption.textContent = option.text;
fakeDropdown.appendChild(fakeOption);
});
dropdownWrapper.appendChild(fakeDropdown);
controlsContainer.appendChild(dropdownWrapper);
// Append the controls container div to the chat container, before the user input
chatContainer.insertBefore(controlsContainer, userInputDiv);
// Append the chat container to the parent container
parentContainer.appendChild(chatContainer);
// Optionally, store the chat interface instance for future reference
// chatInterfaceInstances[containerId] = chatContainer;
// Load and display stored messages if any
loadAndDisplayStoredMessages();
translatePage(startingLanguage, newLanguage);
// Update button text when a fake option is selected
dropdownButton.addEventListener('click', function (event) {
event.stopPropagation();
fakeDropdown.style.display = fakeDropdown.style.display === 'none' ? 'block' : 'none';
});
document.querySelectorAll('.fake-option').forEach(option => {
option.addEventListener('click', (event) => {
const selectedModel = event.target.dataset.value;
const selectedModelText = event.target.textContent;
// Update the select value
document.getElementById('gpt-model-select').value = selectedModel;
document.getElementById('gpt-model-select').dispatchEvent(new Event('change'));
// Update the button text to match the selected model
// document.getElementById('dropdown-button-gpt').textContent = selectedModelText;
// Hide the dropdown after selection
document.getElementById('fake-dropdown').style.display = 'none';
});
});
// Optionally, listen for changes to the real select element, if it's interacted with elsewhere in your application
document.getElementById('gpt-model-select').addEventListener('change', (event) => {
const selectedOptionText = event.target.options[event.target.selectedIndex].text;
document.getElementById('dropdown-button-gpt').textContent = selectedOptionText;
});
// Hide the dropdown when clicking anywhere off screen
document.addEventListener('click', () => {
let fakeDropdownMenu = document.getElementById('fake-dropdown')
if (fakeDropdownMenu) {
fakeDropdownMenu.style.display = 'none';
}
});
// document.getElementById('input-box').addEventListener('keydown', function (event) {
// // Check if Enter is pressed without the Shift key
// if (event.key === 'Enter' && !event.shiftKey) {
// event.preventDefault(); // Prevent default behavior (new line)
// const message = this.value.trim();
// if (message) {
// this.value = '';
// displayUserMessage(message);
// callOpenAI(message);
// document.getElementById('input-box').style.height = '74px'; // Reset before adjusting to get accurate scrollHeight
// document.getElementById('chat').style.minHeight = `calc(100% - 168px`;
// document.getElementById('chat').style.maxHeight = `calc(100% - 168px`;
// }
// }
// });
let textArea = document.getElementById('input-box')
let chat = document.getElementById('chat')
// window.addEventListener("resize", function () {
// adjustTextareaHeight(textArea, chat);
// });
textArea.addEventListener("input", function () {
adjustTextareaHeight(textArea, chat);
});
function adjustTextareaHeight(textarea, chat) {
if (textarea.scrollHeight > 54) {
textarea.style.height = '54px'; // Reset before adjusting to get accurate scrollHeight
textarea.style.height = `${textarea.scrollHeight - 0}px`;
if (textarea.scrollHeight < 500) {
chat.style.minHeight = `calc(100% - ${74 + textarea.scrollHeight}px`;
chat.style.maxHeight = `calc(100% - ${74 + textarea.scrollHeight}px`;
} else {
textarea.style.height = '54px'; // Reset before adjusting to get accurate scrollHeight
textarea.style.height = `${470 - 0}px`;
chat.style.minHeight = `calc(100% - ${74 + 490}px`;
chat.style.maxHeight = `calc(100% - ${74 + 490}px`;
}
} else {
textarea.style.height = '54px'; // Reset before adjusting to get accurate scrollHeight
textarea.style.height = `${textarea.scrollHeight - 0}px`;
chat.style.minHeight = `calc(100% - 174px`;
chat.style.maxHeight = `calc(100% - 174px`;
}
}
return chatContainer;
}
function createConsole(file, containerId, containerPrefix = 'editorContainer') {
// Find the parent container using the containerPrefix
const parentContainer = document.getElementById(containerPrefix);
if (!parentContainer) {
console.error(`Parent container with id '${containerPrefix}' not found.`);
return;
}
// Check for an existing console and remove it
const existingConsole = parentContainer.querySelector(`#${containerId}`);
if (existingConsole) {
existingConsole.remove();
}
// Create the console div
const consoleDiv = document.createElement('div');
consoleDiv.classList.add('console');
consoleDiv.id = containerId; // Use the containerId for the console's ID
// Append the new console div to the parent container
parentContainer.appendChild(consoleDiv);
// Optionally, store the console instance for future reference
// consoleInstances[containerId] = consoleDiv;
return consoleDiv;
}
function createNewTabList(containerId, targetContainer) {
if (mode == 'editor') {
return
}
targetContainer = targetContainer.replace('container', 'editorContainer');
let destinationContainer = targetContainer.replace('editorContainer', 'container');
// Create and set up the new tab list container
const newTabListContainer = document.createElement('div');
newTabListContainer.id = containerId;
newTabListContainer.classList.add('new-tab-list-container');
// Container for the square buttons
const buttonContainer = document.createElement('div');
buttonContainer.classList.add('button-container');
newTabListContainer.appendChild(buttonContainer);
// Button 1: New Tab Preview
const newTabPreviewButton = document.createElement('div');
newTabPreviewButton.classList.add('square-button', 'new-tab-preview-button');
newTabPreviewButton.textContent = 'Preview';
buttonContainer.appendChild(newTabPreviewButton);
newTabPreviewButton.addEventListener('click', () => {
openPreview(containerTypes.preview, destinationContainer);
});
// Button 1: New Tab Preview
const galleryPreviewButton = document.createElement('div');
galleryPreviewButton.classList.add('square-button', 'new-tab-gallery-button');
galleryPreviewButton.textContent = 'Gallery';
buttonContainer.appendChild(galleryPreviewButton);
galleryPreviewButton.addEventListener('click', () => {
openGallery(containerTypes.gallery, destinationContainer);
});
// Button 2: New Tab Console
const newTabConsoleButton = document.createElement('div');
newTabConsoleButton.classList.add('square-button', 'new-tab-console-button');
newTabConsoleButton.textContent = 'Console';
buttonContainer.appendChild(newTabConsoleButton);
newTabConsoleButton.addEventListener('click', () => {
openConsole(containerTypes.console, destinationContainer);
});
// Button 3: New Tab AI
const newTabAIButton = document.createElement('div');
newTabAIButton.classList.add('square-button', 'new-tab-AI-button');
newTabAIButton.textContent = 'AI';
buttonContainer.appendChild(newTabAIButton);
newTabAIButton.addEventListener('click', () => {
openAssistant(containerTypes.AI, destinationContainer);
});
// Button 4: New Tab Color Picker
const newTabColorPickerButton = document.createElement('div');
newTabColorPickerButton.classList.add('square-button', 'new-tab-color-picker-button');
newTabColorPickerButton.textContent = 'Color Picker';
buttonContainer.appendChild(newTabColorPickerButton);
newTabColorPickerButton.addEventListener('click', () => {
openColorPicker(containerTypes.Color_Picker, destinationContainer);
});
// Button 5: New Tab graph
const newTabGraphButton = document.createElement('div');
newTabGraphButton.classList.add('square-button', 'new-tab-graph-button');
newTabGraphButton.textContent = 'Graph';
buttonContainer.appendChild(newTabGraphButton);
newTabGraphButton.addEventListener('click', () => {
openGraph(containerTypes.graph, destinationContainer);
});
// Button 5: New Tab notes
const newTabnotesButton = document.createElement('div');
newTabnotesButton.classList.add('square-button', 'new-tab-notes-button');
newTabnotesButton.textContent = 'Notes';
buttonContainer.appendChild(newTabnotesButton);
newTabnotesButton.addEventListener('click', () => {
openNotes(containerTypes.notes, destinationContainer);
});
// Button 5: New Tab notes
const newTabGitButton = document.createElement('div');
newTabGitButton.classList.add('square-button', 'new-tab-github-button');
newTabGitButton.textContent = 'GitHub';
buttonContainer.appendChild(newTabGitButton);
newTabGitButton.addEventListener('click', () => {
openGitHub(containerTypes.github, destinationContainer);
});
const newTabDependenciesButton = document.createElement('div');
newTabDependenciesButton.classList.add('square-button', 'new-tab-dependencies-button');
newTabDependenciesButton.textContent = 'Dependencies';
buttonContainer.appendChild(newTabDependenciesButton);
newTabDependenciesButton.addEventListener('click', () => {
openDependencies(containerTypes.dependencies, destinationContainer);
});
// Create the 'New File' button at the top
const newFileButton = document.createElement('div');
newFileButton.textContent = 'New file';
newFileButton.classList.add('new-file-button');
newFileButton.addEventListener('click', () => {
addFile('root', destinationContainer);
//closeAllNewTabs();
});
newTabListContainer.appendChild(newFileButton);
const openFileButton = document.createElement('div');
openFileButton.textContent = 'Open File';
openFileButton.classList.add('open-file-button');
newTabListContainer.appendChild(openFileButton);
// Create a scrollable list for files
const fileList = document.createElement('ul');
fileList.classList.add('file-list');
fileList.id = 'fileList-' + containerId; // Assign a unique ID
newTabListContainer.appendChild(fileList);
// Append the new tab list container to the specified container prefix
document.getElementById(targetContainer).appendChild(newTabListContainer);
// Populate the file list for the first time
updateFileList(containerId);
translatePage(startingLanguage, newLanguage);
}
function updateFileList(containerId) {
const fileList = document.getElementById('fileList-' + containerId);
if (!fileList) return;
// Clear existing file items
fileList.innerHTML = '';
// Collect all files in alphabetical order
const allFiles = [];
const collectFiles = (directory) => {
Object.values(directory.children).forEach(child => {
if (child.type === 'file') {
allFiles.push(child);
} else if (child.type === 'directory') {
collectFiles(child);
}
});
};
// Collect files from fileSystem
collectFiles(fileSystem.myProject);
// Sort and append file items
allFiles.sort((a, b) => a.name.localeCompare(b.name)).forEach(file => {
const fileItem = document.createElement('li');
fileItem.classList.add('new-file-item');
const extension = getFileExtension(file.name);
fileItem.setAttribute('data-type', extension);
fileItem.textContent = file.name;
fileList.appendChild(fileItem);
});
// Attach a click event listener to the fileList for event delegation
fileList.onclick = function (event) {
let target = event.target;
while (target != this) {
if (target.classList.contains('new-file-item')) {
const fileName = target.textContent;
const file = allFiles.find(f => f.name === fileName);
if (file) {
// Find the current container of the tab
let currentContainer = target;
while (currentContainer && !currentContainer.classList.contains('editor-container')) {
currentContainer = currentContainer.parentNode;
}
if (currentContainer) {
// Extract the container key or ID from the currentContainer
let currentContainerKey = currentContainer.id.replace('editor-', '');
currentContainerKey = currentContainerKey.replace('editorContainer', 'container');
openFile(file, currentContainerKey);
//closeAllNewTabs();
// const fileItem = document.querySelector(`.file-item[data-id="${file.Id}"]`);
// if (fileItem && !fileItem.classList.contains('active-file')) {
// fileItem.click();
// }
}
}
return;
}
target = target.parentNode;
}
};
}
function closeAllNewTabs() {
Object.keys(openTabs).forEach(containerKey => {
Object.keys(openTabs[containerKey]).forEach(fileId => {
const tabInfo = openTabs[containerKey][fileId];
if (tabInfo.file.name === 'New Tab') {
closeTab(fileId, false); // Close the tab named 'New Tab'
}
});
});
}
function setTheme(themeName) {
//console.log("themechanged")
// Update the setting when a theme is selected
if (editorMode != 'fullscreen') {
updateSetting('theme', themeName);
}
// Load the theme from the JSON file
fetch(`./themes/${themeName}.json`)
.then(response => response.json())
.then(themeData => {
// Define the custom theme
monaco.editor.defineTheme('custom-theme', themeData);
// Set the custom theme
monaco.editor.setTheme('custom-theme');
// Extract colors and set as root CSS variables
setRootCssVariables(themeData.colors);
setTokenColorsAsVariables(themeData.rules);
// Refresh color picker if it's active
// refreshColorPickerIfActive();
refreshGraphActive();
refreshNotesActive();
})
.catch(error => {
console.error('Error loading custom theme:', error);
});
}
function refreshNotesActive() {
loadSettingsFromCache()
// Check if any color picker iframes are present
const colorPickers = document.querySelectorAll('.notes-iframe');
colorPickers.forEach(iframe => {
// Refresh the iframe to update its content
iframe.src = `notes.html`;
iframe.onload = () => {
iframe.contentWindow.postMessage({
type: 'getData',
categories: categories,
}, '*');
};
});
}
function refreshGraphActive() {
loadSettingsFromCache()
// Check if any color picker iframes are present
const colorPickers = document.querySelectorAll('.graph-iframe');
colorPickers.forEach(iframe => {
// Refresh the iframe to update its content
iframe.src = `graph.html`;
});
}
function refreshColorPickerIfActive() {
// loadSettingsFromCache()
// // Check if any color picker iframes are present
// const colorPickers = document.querySelectorAll('.color-picker-iframe');
// colorPickers.forEach(iframe => {
// // Refresh the iframe to update its content
// iframe.src = `color_picker.html?defaultColor=${encodeURIComponent(currentSettings.colorPicker)}`;
// });
}
function setFontSize(fontSize) {
// Update the fontSize setting
updateSetting('fontSize', fontSize + 'px');
// Iterate over all editor instances and set the font size
for (let id in editorInstances) {
const editor = editorInstances[id];
if (editor) {
editor.updateOptions({ fontSize: parseInt(fontSize) });
}
}
}
function setWordWrap(key){
updateSetting('wordWrap', key);
for (let id in editorInstances) {
const editor = editorInstances[id];
if (editor) {
editor.updateOptions({ wordWrap: key });
}
}
}
function setFontWeight(fontWeight) {
//console.log(fontWeight)
document.documentElement.style.setProperty('--codeFontWeight', fontWeight);
if (fontWeight == '400') {
updateSetting('fontWeight', false);
} else {
updateSetting('fontWeight', true);
}
// Iterate over all editor instances and set the font size
for (let id in editorInstances) {
const editor = editorInstances[id];
if (editor) {
editor.updateOptions({ fontWeight: fontWeight });
}
}
}
async function getProjectData(projectName, userId) {
const projectRef = db.collection('Users').doc(userId).collection('Projects').doc(projectName);
const doc = await projectRef.get();
if (doc.exists) {
return doc.data();
} else {
console.log("No such document!");
return null; // or appropriate default data
}
}
async function createPublishMenu(file, containerId, containerPrefix = 'editorContainer') {
const parentContainer = document.getElementById(containerPrefix);
if (!parentContainer) {
console.error(`Parent container with id '${containerPrefix}' not found.`);
return;
}
const existingPublishMenu = parentContainer.querySelector(`#${containerId}`);
if (existingPublishMenu) {
existingPublishMenu.remove();
}
const publishMenuDiv = document.createElement('div');
publishMenuDiv.classList.add('publish-menu');
publishMenuDiv.id = containerId;
// Creating the project type wrapper
const typeProjectWrapper = document.createElement('div');
typeProjectWrapper.classList.add('type-project-wrapper');
const projectButton = document.createElement('button');
projectButton.textContent = 'Project';
projectButton.classList.add('type-project-button', 'default-project', 'active-project-type');
const codeShortButton = document.createElement('button');
codeShortButton.textContent = 'Code Short';
codeShortButton.classList.add('type-project-button', 'code-short-project');
// Toggle functionality
const toggleActiveType = (button) => {
document.querySelectorAll('.type-project-button').forEach(btn => {
btn.classList.remove('active-project-type');
});
button.classList.add('active-project-type');
};
// Event listeners for buttons
projectButton.addEventListener('click', () => toggleActiveType(projectButton));
codeShortButton.addEventListener('click', () => toggleActiveType(codeShortButton));
// Adding the "?" button linking to documentation
const helpButton = document.createElement('a');
helpButton.href = '/docs#code-shorts-';
helpButton.target = '_blank';
helpButton.classList.add('help-button');
helpButton.textContent = '?';
helpButton.title="What are Code Shorts?"
// Info container with reworded message
const typeDescriptionContainer = document.createElement('div');
typeDescriptionContainer.classList.add('setting-container');
const typeSpan = document.createElement('span');
typeSpan.textContent = 'Project Type';
typeSpan.classList.add('publish-setting-title')
const typeInfoText = document.createElement('span');
typeInfoText.className = 'setting-info';
typeInfoText.classList.add('project-type-info')
typeInfoText.innerHTML = `Choose 'Code Short' for projects based on the Code Shorts template. For all other projects, select 'Project'. If unsure, use 'Project'.`;
// Building the component structure
typeDescriptionContainer.appendChild(typeSpan);
typeDescriptionContainer.appendChild(typeInfoText);
typeDescriptionContainer.appendChild(typeProjectWrapper);
typeProjectWrapper.appendChild(projectButton);
typeProjectWrapper.appendChild(codeShortButton);
typeProjectWrapper.appendChild(helpButton);
publishMenuDiv.appendChild(typeDescriptionContainer);
// Function to update the image source
function updateCoverImage() {
const coverImageFile = findFileByName(fileSystem, 'cover.png');
if (coverImageFile !== null && coverImageFile.content) {
imagePlaceholder.src = coverImageFile.content;
imagePlaceholder.alt = 'Cover Image';
infoText.textContent = 'Cover Image must be in your root folder.';
} else {
imagePlaceholder.src = 'path/to/default/image'; // Placeholder image source
imagePlaceholder.alt = 'No Cover Image';
infoText.textContent = "To include an image in your project, add a file named cover.png to your root folder. If you do not have one, you can create it by running your project and clicking the 'Add to project files' button in the preview tab.";
}
}
// Image setting container
const imageContainer = document.createElement('div');
imageContainer.classList.add('setting-container');
const imagePlaceholder = document.createElement('img');
const infoText = document.createElement('span');
infoText.className = 'setting-info';
infoText.classList.add('cover-image-info');
updateCoverImage(); // Call this function to set initial image
// Refresh button
const refreshButton = document.createElement('button');
refreshButton.textContent = 'Reload Image';
refreshButton.classList.add('publish-button');
refreshButton.addEventListener('click', updateCoverImage);
imageContainer.appendChild(imagePlaceholder);
imageContainer.appendChild(infoText);
imageContainer.appendChild(refreshButton); // Add the refresh button to the container
publishMenuDiv.appendChild(imageContainer);
// Description setting container
const descriptionContainer = document.createElement('div');
descriptionContainer.classList.add('setting-container');
const descriptionSpan = document.createElement('span');
descriptionSpan.textContent = 'Description';
descriptionSpan.classList.add('publish-setting-title')
const descriptionTextarea = document.createElement('textarea');
descriptionTextarea.id = 'descriptionTextarea'
descriptionContainer.appendChild(descriptionSpan);
descriptionContainer.appendChild(descriptionTextarea);
publishMenuDiv.appendChild(descriptionContainer);
// Categories setting container
const categoriesContainer = document.createElement('div');
categoriesContainer.classList.add('setting-container');
const categoriesSpan = document.createElement('span');
categoriesSpan.textContent = 'Categories';
categoriesSpan.classList.add('publish-setting-title')
categoriesContainer.appendChild(categoriesSpan);
const categories = ['Artwork', 'Games', 'Interactive', 'Animated', 'Templates', 'Math', 'CSS Only'];
categories.forEach(category => {
const checkboxId = category.toLowerCase().replace(/ /g, '-'); // e.g. "artwork"
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = checkboxId;
checkbox.classList.add('custom-checkbox');
const infoText = document.createElement('span');
infoText.className = 'setting-info';
infoText.textContent = category;
const label = document.createElement('label');
label.htmlFor = checkboxId;
label.appendChild(infoText);
categoriesContainer.appendChild(checkbox);
categoriesContainer.appendChild(label);
});
publishMenuDiv.appendChild(categoriesContainer);
// Tags setting container
const tagsContainer = document.createElement('div');
tagsContainer.classList.add('setting-container');
const tagsSpan = document.createElement('span');
tagsSpan.textContent = 'Tags (comma-separated)';
tagsSpan.classList.add('publish-setting-title')
const tagsTextarea = document.createElement('textarea');
tagsTextarea.id = 'tags'
tagsContainer.appendChild(tagsSpan);
tagsContainer.appendChild(tagsTextarea);
publishMenuDiv.appendChild(tagsContainer);
const serverModeContainer = document.createElement('div');
serverModeContainer.classList.add('setting-container');
const serverModeSpan = document.createElement('span');
serverModeSpan.textContent = 'Server Mode';
serverModeSpan.classList.add('publish-setting-title')
serverModeContainer.appendChild(serverModeSpan);
const serverModeCheckbox = document.createElement('input');
serverModeCheckbox.type = 'checkbox';
serverModeCheckbox.id = 'server-mode';
serverModeCheckbox.classList.add('custom-checkbox');
const serverModeLabel = document.createElement('label');
serverModeLabel.htmlFor = 'server-mode';
const serverModeInfoText = document.createElement('span');
serverModeInfoText.className = 'setting-info';
serverModeInfoText.textContent = 'Enable Server Mode for users who open your project.';
serverModeLabel.appendChild(serverModeInfoText);
const serverHelpButton = document.createElement('a');
serverHelpButton.href = '/docs#code-editor--server-mode-';
serverHelpButton.target = '_blank';
serverHelpButton.classList.add('help-button');
serverHelpButton.textContent = '?';
serverHelpButton.title="What is Server Mode?"
serverModeContainer.appendChild(serverModeCheckbox);
serverModeContainer.appendChild(serverModeLabel);
serverModeContainer.appendChild(serverHelpButton);
publishMenuDiv.appendChild(serverModeContainer);
const forkedInfoContainer = document.createElement('div');
forkedInfoContainer.classList.add('setting-container');
const forkedSpan = document.createElement('span');
forkedSpan.textContent = 'Fork Information';
forkedSpan.classList.add('publish-setting-title');
const forkInfoText = document.createElement('span');
forkInfoText.className = 'setting-info';
forkInfoText.classList.add('project-type-info');
// Enhanced text content
forkInfoText.innerHTML = `If this project is derived from another user's work, we recommend creating a fork using the menu: Fork Project button from within that users project.
If you have instead just made a copy of another user's project, please include proper references in the description or remake the project as an official fork to automatically credit the original author.`;
// Append the elements to the container
forkedInfoContainer.appendChild(forkedSpan);
forkedInfoContainer.appendChild(forkInfoText);
// Add conditional global indicator for fork status
const forkStatusIndicator = document.createElement('div');
forkStatusIndicator.classList.add('fork-status-indicator');
if (typeof isFork !== 'undefined') {
if (isFork) {
forkStatusIndicator.textContent = 'This project is a fork';
forkStatusIndicator.title="Fork details will be added automatically"
forkStatusIndicator.classList.add('fork-true');
} else {
forkStatusIndicator.textContent = 'This project is not a fork';
forkStatusIndicator.title="This project will NOT appear as a fork!"
forkStatusIndicator.classList.add('fork-false');
}
} else {
console.warn('Global variable isFork is not defined. Defaulting to unknown status.');
forkStatusIndicator.textContent = 'Fork status unknown.';
forkStatusIndicator.classList.add('fork-unknown');
}
const forkInfoText2 = document.createElement('span');
forkInfoText2.className = 'setting-info';
forkInfoText2.classList.add('project-type-info');
// Enhanced text content
forkInfoText2.innerHTML = `If your project is based on or inspired by a project from another website, it is essential to link to the original source in your project description.`;
forkedInfoContainer.appendChild(forkInfoText2);
forkedInfoContainer.appendChild(forkStatusIndicator);
const forkHelpButton = document.createElement('a');
forkHelpButton.href = '/docs#forking-projects-';
forkHelpButton.target = '_blank';
forkHelpButton.classList.add('help-button');
forkHelpButton.textContent = '?';
forkHelpButton.title="Learn more about Forks"
forkedInfoContainer.appendChild(forkHelpButton);
// Append the final container to the publish menu div
publishMenuDiv.appendChild(forkedInfoContainer);
// Publish button
const publishButton = document.createElement('button');
publishButton.textContent = 'Publish Project';
publishButton.classList.add('publish-button');
publishButton.classList.add('publish-publish-button');
publishButton.addEventListener('click', async () => {
let project = findItemById(fileSystem.myProject, 'root');
await saveProject();
if (project.name == openedProjectName) {
createNotification(false);
updateModalMessage("Publishing project...");
const publishData = getPublishData();
updateFirestore(publishData);
} else {
customAlert('Save your project before publishing.');
}
});
// Unpublish button
const unpublishButton = document.createElement('button');
unpublishButton.textContent = 'Unpublish Project';
unpublishButton.classList.add('unpublish-button');
unpublishButton.classList.add('publish-button');
unpublishButton.addEventListener('click', async () => {
createNotification(false);
updateModalMessage("Unpublishing project...");
await unpublishProject();
});
// Create a wrapper for the publish info text
const publishInfoWrapper = document.createElement('div');
publishInfoWrapper.classList.add('publish-info-wrapper'); // Wrapper class for padding and styling
// Create the publish info text element
const publishInfoText = document.createElement('span');
publishInfoText.className = 'setting-info';
publishInfoText.classList.add('project-type-info', 'project-publish-info');
publishInfoText.textContent = `You can update your published project settings at any time. Just make your changes and click 'Publish Project' again.`;
// Append publish info text to the wrapper
publishInfoWrapper.appendChild(publishInfoText);
// Create publish button container
const publishButtonContainer = document.createElement('div');
publishButtonContainer.classList.add('publish-container');
publishButtonContainer.appendChild(publishButton);
publishButtonContainer.appendChild(unpublishButton);
// Append wrapper and button container to the main menu div
publishMenuDiv.appendChild(publishInfoWrapper);
publishMenuDiv.appendChild(publishButtonContainer);
// Add the main menu div to the parent container
parentContainer.appendChild(publishMenuDiv);
const currentUser = firebase.auth().currentUser;
if (!currentUser) {
console.error("User not logged in");
return;
}
const userId = currentUser.uid;
let project = findItemById(fileSystem.myProject, 'root');
if (!project) {
console.error("Project not found");
return;
}
let projectData;
// Retrieve existing project data from Firebase
if (project.name == openedProjectName) {
projectData = await getProjectData(project.name, userId);
}
// Use projectData to pre-populate fields
if (projectData) {
if (projectData.description) {
descriptionTextarea.value = projectData.description;
}
if (projectData.categories && projectData.categories.length > 0) {
projectData.categories.forEach(category => {
const checkboxId = category.toLowerCase().replace(/ /g, '-');
const checkbox = document.getElementById(checkboxId);
if (checkbox) checkbox.checked = true;
});
}
if (projectData.tags && projectData.tags.length > 0) {
tagsTextarea.value = projectData.tags.join(', ');
}
if (projectData.hasOwnProperty('serverMode')) { // Check if the serverMode property exists
const serverModeCheckbox = document.getElementById('server-mode');
if (serverModeCheckbox) serverModeCheckbox.checked = projectData.serverMode;
}
if (projectData.isCodeShort) {
// Set Code Short button as active
codeShortButton.classList.add('active-project-type');
projectButton.classList.remove('active-project-type');
} else {
// Set Project button as active by default
projectButton.classList.add('active-project-type');
codeShortButton.classList.remove('active-project-type');
}
}
translatePage(startingLanguage, newLanguage);
return publishMenuDiv;
}
async function unpublishProject() {
const currentUser = firebase.auth().currentUser;
if (!currentUser) {
customAlert("You must be logged in to modify files.");
return;
}
let project = findItemById(fileSystem.myProject, 'root');
const userId = currentUser.uid;
const projectRef = db.collection('Users').doc(userId).collection('Projects').doc(project.name);
await projectRef.update({ published: false })
.then(() => console.log("Project unpublished successfully"))
.catch(error => console.error("Error updating document: ", error));
const docSnapshot = await projectRef.get();
const fullProjectData = docSnapshot.data();
// Prepare the data for publishing
publishedData = {
...fullProjectData,
userId: userId // Add userId to the published data
};
// Reference to the published projects collection with a unique ID
const publishRef = db.collection('PublishedProjects').doc(project.name + "_" + userId);
// Set the data in the PublishedProjects collection
await publishRef.set(publishedData, { merge: true });
updateModalMessage("Project Unpublished!");
fileSystem.myProject.isPublished = false;
renderFilesystem();
setTimeout(() => startCloseCountdown(3000), 10);
}
function getPublishData() {
// Get description
const description = document.querySelector('textarea#descriptionTextarea').value;
// Get selected categories
const categoryCheckboxes = Array.from(document.querySelectorAll('.custom-checkbox'));
const selectedCategories = categoryCheckboxes
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.nextSibling.textContent);
// Get tags
const tags = document.querySelector('textarea#tags').value.split(',').map(tag => tag.trim());
// Get Server Mode setting
const serverMode = document.querySelector('input#server-mode').checked;
// Check if 'Code Short' button is active
const isCodeShort = document.querySelector('.code-short-project').classList.contains('active-project-type');
// Return all data, including isCodeShort
return { description, categories: selectedCategories, tags, serverMode, isCodeShort };
}
async function updateFirestore(publishData) {
const currentUser = firebase.auth().currentUser;
if (!currentUser) {
customAlert("You must be logged in to open files.");
return; // Exit if not logged in
}
let project = findItemById(fileSystem.myProject, 'root');
const userId = currentUser.uid;
const hasCoverImage = findFileByName(fileSystem, 'cover.png') !== null;
const projectRef = db.collection('Users').doc(userId).collection('Projects').doc(project.name);
const docSnapshot = await projectRef.get();
// Get the original and parent project data
const originalProjectName =
window.originalProjectName ||
(docSnapshot.exists ? docSnapshot.data().originalProjectName : document.querySelector('.project-creator-project-name').textContent.trim());
const originalUsername =
window.originalUsername ||
(docSnapshot.exists ? docSnapshot.data().originalUsername : document.querySelector('.project-creator-username').textContent.replace('by ', '').trim()) || 'Unknown';
const originalUserId =
window.originalUserId ||
(docSnapshot.exists ? docSnapshot.data().originalUserId : await getUidByUsername(originalUsername));
const parentProjectName = originalProjectName;
const parentUserId = originalUserId || null;
// Prepare project data
let projectData = {
...publishData,
hasCoverImage,
published: true,
originalProjectName: originalProjectName || false,
originalUserId: originalUserId || false,
originalUsername: originalUsername || false,
};
// Prepare published data
let publishedData = {
...publishData,
hasCoverImage,
published: true,
userId,
originalProjectName: originalProjectName || false,
originalUserId: originalUserId || false,
originalUsername: originalUsername || false,
};
try {
// Update project in user's collection
await projectRef.set(projectData, { merge: true });
console.log("Project data updated successfully");
const fullProjectData = docSnapshot.exists ? docSnapshot.data() : {};
// Merge with full data for publishing
publishedData = { ...fullProjectData, ...publishedData };
// Reference to the published projects collection
const publishRef = db.collection('PublishedProjects').doc(`${project.name}_${userId}`);
// Set the data in PublishedProjects collection
await publishRef.set(publishedData, { merge: true });
updateModalMessage("Project Published!");
fileSystem.myProject.isPublished = true;
renderFilesystem();
setTimeout(() => startCloseCountdown(3000), 10);
} catch (error) {
console.error("Error updating document: ", error);
}
}
async function createAIImager(file, containerId, containerPrefix = 'editorContainer') {
const parentContainer = document.getElementById(containerPrefix);
if (!parentContainer) {
console.error(`Parent container with id '${containerPrefix}' not found.`);
return;
}
// Check for an existing settings menu and remove it
const existingAIImagerMenu = parentContainer.querySelector(`#${containerId}`);
if (existingAIImagerMenu) {
existingAIImagerMenu.remove();
}
// Create the settings menu div
const AIImagerDiv = document.createElement('div');
// MintMenuDiv.classList.add('settings-menu');
AIImagerDiv.classList.add('ai-imager-menu');
AIImagerDiv.id = containerId; // Use the containerId for the settings menu's ID
parentContainer.appendChild(AIImagerDiv);
generateContent()
function generateContent() {
const contentDiv = document.querySelector('.ai-imager-menu');
const container = document.createElement('div');
container.id = 'stableDiffusionContainer';
// Create the top div
const topDiv = document.createElement('div');
topDiv.className = 'ai-top-div';
// Create and append the preview container to the top div
const previewContainer = document.createElement('div');
previewContainer.className = 'ai-preview-container';
// Create code preview container
const codePreview = document.createElement('div');
codePreview.className = 'ai-code-preview';
const codePreviewHeader = document.createElement('div');
codePreviewHeader.className = 'ai-code-preview-header';
codePreviewHeader.innerText = 'Code Preview';
const iframe = document.createElement('iframe');
iframe.src = 'about:blank';
iframe.id = "AIPreviewIframe"
codePreview.appendChild(codePreviewHeader);
codePreview.appendChild(iframe);
// Create output preview container
const outputPreview = document.createElement('div');
outputPreview.className = 'ai-output-preview';
const outputPreviewHeader = document.createElement('div');
outputPreviewHeader.className = 'ai-output-preview-header';
outputPreviewHeader.innerText = 'Stable Diffusion';
const addButton = document.createElement('button');
addButton.innerText = '';
addButton.classList.add('gallery-ai-button');
addButton.title = "Add to Gallery"
addButton.addEventListener('click', function () {
const imageUrl = document.getElementById('outputImage').src; // Get current image URL
addImageToGallery(imageUrl); // Call the function with the image URL
});
// Add the button to the header
outputPreviewHeader.appendChild(addButton);
const downloadButton = document.createElement('button');
downloadButton.innerText = '';
downloadButton.classList.add('download-ai-button');
downloadButton.title = "Download Image"
downloadButton.addEventListener('click', function () {
const imageUrl = document.getElementById('outputImage').src; // Get current image URL
downloadImage(imageUrl, "aijs_Render.png")
});
// Add the button to the header
outputPreviewHeader.appendChild(downloadButton);
const outputContainer = document.createElement('div');
outputContainer.className = 'ai-output-container';
const outputImage = document.createElement('img');
outputImage.id = 'outputImage';
outputImage.src = '';
outputImage.alt = 'Use `aijs.renderDone();` to capture your image';
outputContainer.appendChild(outputImage);
outputPreview.appendChild(outputPreviewHeader);
outputPreview.appendChild(outputContainer);
// Append previews to the preview container
previewContainer.appendChild(codePreview);
previewContainer.appendChild(outputPreview);
// Append the preview container to the top div
topDiv.appendChild(previewContainer);
// Create the bottom div
const bottomDiv = document.createElement('div');
bottomDiv.className = 'ai-bottom-div';
// Create controls div
const controlsDiv = document.createElement('div');
controlsDiv.className = 'ai-controls-div';
// Add inputs and button to controls div
const widthInput = createInput('widthInput', 'Width:', '512');
const heightInput = createInput('heightInput', 'Height:', '512');
const hashInput = createInput('hashInput', 'Seed:', generateHash());
const hashButton = document.createElement('button');
hashButton.id = 'ai-generate-hash-button';
hashButton.title = "Generate a random Seed"
hashButton.onclick = function () {
const newHash = generateHash();
document.getElementById('hashInput').value = newHash;
}
// Append inputs and button to controls div
controlsDiv.appendChild(widthInput.label);
controlsDiv.appendChild(widthInput.input);
controlsDiv.appendChild(heightInput.label);
controlsDiv.appendChild(heightInput.input);
controlsDiv.appendChild(hashInput.label);
controlsDiv.appendChild(hashInput.input);
controlsDiv.appendChild(hashButton);
// Create prompt div
const promptDiv = document.createElement('div');
promptDiv.className = 'ai-prompt-div';
const promptInput = document.createElement('textarea');
promptInput.id = 'promptInput';
promptInput.placeholder = 'Enter your prompt here...';
const generateButton = document.createElement('button');
generateButton.id = 'generateButton';
generateButton.textContent = 'Generate';
generateButton.onclick = async function () {
const width = parseInt(document.getElementById('widthInput').value);
const height = parseInt(document.getElementById('heightInput').value);
const hash = document.getElementById('hashInput').value;
const prompt = document.getElementById('promptInput').value;
await runCodeAiImager(width, height, hash, prompt);
}
// Append textarea and button to prompt div
promptDiv.appendChild(promptInput);
promptDiv.appendChild(generateButton);
const retryButton = document.createElement('button');
retryButton.id = 'aiRetryButton';
retryButton.title = "Retry"
retryButton.textContent = '';
retryButton.onclick = async function () {
const width = parseInt(document.getElementById('widthInput').value);
const height = parseInt(document.getElementById('heightInput').value);
const hash = document.getElementById('hashInput').value;
const prompt = document.getElementById('promptInput').value;
await runCodeAiImagerRetry(width, height, hash, prompt);
}
promptDiv.appendChild(retryButton);
// Append controls and prompt divs to bottom div
bottomDiv.appendChild(controlsDiv);
bottomDiv.appendChild(promptDiv);
// Append top and bottom divs to container
container.appendChild(topDiv);
container.appendChild(bottomDiv);
// Append container to the document body or any other container element
contentDiv.appendChild(container);
}
function createInput(id, labelText, defaultValue) {
const label = document.createElement('label');
label.htmlFor = id;
label.textContent = labelText; // It's better to use textContent here
const input = document.createElement('input');
input.type = 'text';
input.id = id;
input.value = defaultValue;
return { label, input }; // Return both created elements as an object
}
// Adding event listeners to enforce max value
widthInput.addEventListener('input', function () {
if (parseInt(this.value, 10) > 1024) {
this.value = '1024';
} else if (parseInt(this.value, 10) < 0) {
this.value = '1';
}
});
heightInput.addEventListener('input', function () {
if (parseInt(this.value, 10) > 1024) {
this.value = '1024';
} else if (parseInt(this.value, 10) < 0) {
this.value = '1';
}
});
hashInput.addEventListener('input', function () {
if (parseInt(this.value, 10) > 99999999) {
this.value = generateHash();
} else if (parseInt(this.value, 10) < 0) {
this.value = generateHash();
}
});
function generateHash(length = 8) { // 64 hex characters for a 256-bit hash
const characters = '0123456789'; // Hexadecimal characters
let result = ''; // Start with '0x' to match the format
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters[randomIndex];
}
return result;
}
translatePage(startingLanguage, newLanguage)
}
async function createMint(file, containerId, containerPrefix = 'editorContainer') {
const parentContainer = document.getElementById(containerPrefix);
if (!parentContainer) {
console.error(`Parent container with id '${containerPrefix}' not found.`);
return;
}
// Check for an existing settings menu and remove it
const existingMintMenu = parentContainer.querySelector(`#${containerId}`);
if (existingMintMenu) {
existingMintMenu.remove();
}
// Create the settings menu div
const MintMenuDiv = document.createElement('div');
// MintMenuDiv.classList.add('settings-menu');
MintMenuDiv.classList.add('mint-menu');
MintMenuDiv.id = containerId; // Use the containerId for the settings menu's ID
parentContainer.appendChild(MintMenuDiv);
generateContent(data)
function generateContent(data) {
const contentDiv = document.querySelector('.mint-menu');
// Create informational section
const infoSection = document.createElement("div")
infoSection.className = "info-section"
// Create buttons for the informational section
const mintingButton = document.createElement("button")
mintingButton.className = "info-section-button"
mintingButton.textContent = "What is Minting?"
const legalButton = document.createElement("button")
legalButton.className = "info-section-button"
legalButton.textContent = "Legal Concerns"
const closeButton = document.createElement("button")
closeButton.className = "dynamic-content-button-close"
closeButton.textContent = "Close Info"
closeButton.style.display = "none" // Initially hide the close button
const mintingInfo = document.createElement("span")
mintingInfo.textContent = `Minting transforms your digital art into a unique asset on a blockchain. This allows you to sell or trade your art securely. By minting your artwork, you create an NFT (Non-Fungible Token) which represents your digital piece on the blockchain, ensuring its uniqueness and ownership.`
mintingInfo.style.display = "none" // Initially hide the information
const legalInfo = document.createElement("span")
legalInfo.textContent = `Legal concerns regarding minting include copyright infringement, smart contract vulnerabilities, and regulatory compliance. It's crucial to ensure that you have the rights to your digital asset. Additionally, some libraries, such as p5play, have specific rules against using their software for minting NFTs. Always review and respect the licensing agreements of any software or libraries you use in your projects.`
legalInfo.style.display = "none" // Initially hide the information
// Add onclick events to buttons
mintingButton.onclick = function () {
mintingInfo.style.display = "block"
legalInfo.style.display = "none"
closeButton.style.display = "block"
}
legalButton.onclick = function () {
legalInfo.style.display = "block"
mintingInfo.style.display = "none"
closeButton.style.display = "block"
}
closeButton.onclick = function () {
mintingInfo.style.display = "none"
legalInfo.style.display = "none"
closeButton.style.display = "none"
}
// Append elements to the info section
infoSection.appendChild(mintingButton)
infoSection.appendChild(legalButton)
infoSection.appendChild(closeButton)
infoSection.appendChild(mintingInfo)
infoSection.appendChild(legalInfo)
// Append the info section to the contentDiv
contentDiv.insertBefore(infoSection, contentDiv.firstChild)
data.sections.forEach((section) => {
const sectionDiv = document.createElement("div")
sectionDiv.className = "dynamic-content-container"
const leftDiv = document.createElement("div")
leftDiv.className = "dynamic-content-left"
// Top button on left
const topButton = document.createElement("button")
topButton.className = "dynamic-content-button-top" // Updated
topButton.textContent = section.left.topButton.text
topButton.onclick = () => window.open(section.left.topButton.url, "_blank")
// Inside the forEach loop for data.sections
if (section.left.image) {
const imgButtonWrapper = document.createElement("div")
imgButtonWrapper.className = "img-button-wrapper"
const imgLink = document.createElement("a")
imgLink.href = section.left.image.url // Link URL
imgLink.target = "_blank" // Open in new tab
const img = document.createElement("img")
img.src = section.left.image.src // Image URL
img.className = "dynamic-content-image"
imgLink.appendChild(img)
imgButtonWrapper.appendChild(imgLink) // Add image link to wrapper
imgButtonWrapper.appendChild(topButton) // Add top button to wrapper
if (section.left.supporter) {
const supporterDiv = document.createElement("div")
supporterDiv.className = "supporter-indicator"
supporterDiv.title = "Financial Supporter"
imgButtonWrapper.appendChild(supporterDiv)
}
// Partnership div
if (section.left.partnership) {
const partnershipDiv = document.createElement("div")
partnershipDiv.className = "partnership-indicator"
partnershipDiv.title = "Partner"
imgButtonWrapper.appendChild(partnershipDiv)
}
leftDiv.appendChild(imgButtonWrapper) // Append the wrapper to leftDiv
} else {
leftDiv.appendChild(topButton) // If no image, just append the button
}
// Containers under top button
section.left.containers.forEach((container) => {
const fieldDiv = document.createElement("div")
fieldDiv.className = "dynamic-content-field"
const label = document.createElement("label")
label.className = "dynamic-content-label"
label.textContent = container.label
fieldDiv.appendChild(label)
// Check if container has buttons
if (container.button) {
const button = document.createElement("button");
button.className = "dynamic-content-button-left";
button.textContent = container.button.text;
if (container.label === "Start from:") {
button.onclick = async () => { // Make the function async
try {
const confirmed = await customConfirm("Are you sure you want to open this template? Doing so will remove all current files and folders.", true);
if (confirmed) {
await openProject(container.button.projectName, "GZKuUn50XIeInYxxEBGGy9uommg2"); // Assuming openProject returns a Promise
// Call openMint after openProject has successfully completed
// generateMint();
}
} catch (error) {
console.error("An error occurred:", error);
}
};
} else {
button.onclick = () => window.open(container.button.url, "_blank");
}
fieldDiv.appendChild(button);
}
// Check if container has blockchain icons
if (container.blockchains) {
container.blockchains.forEach((blockchain) => {
const iconLink = document.createElement("a")
// iconLink.href = blockchain.url
// iconLink.target = "_blank"
const iconImg = document.createElement("img")
iconImg.src = blockchain.icon
iconImg.className = "blockchain-icon"
// Extracting the hover title from the path
const fileName = blockchain.icon.split("/").pop(); // Gets the filename (e.g., "ethereum-eth-logo.png")
// Try to split by dash and use the first part; if that part is empty, use the whole fileName
let hoverTitleParts = fileName.split("-");
let hoverTitle = hoverTitleParts.length > 0 && hoverTitleParts[0] !== "" ? hoverTitleParts[0] : fileName;
// Remove the file extension if present
hoverTitle = hoverTitle.split(".")[0];
// Capitalizes the first letter for the title
iconImg.title = hoverTitle.charAt(0).toUpperCase() + hoverTitle.slice(1);
iconLink.appendChild(iconImg)
fieldDiv.appendChild(iconLink)
})
}
leftDiv.appendChild(fieldDiv)
})
const rightDiv = document.createElement("div")
rightDiv.className = "dynamic-content-right" // Updated
// Buttons in right
const buttonsWrapper = document.createElement("div");
buttonsWrapper.className = "right-buttons-wrapper"; // You'll define this in your CSS
section.right.buttons.forEach((btn) => {
const button = document.createElement("button");
button.className = "dynamic-content-button"; // Updated
button.textContent = btn.text;
button.onclick = btn.action;
buttonsWrapper.appendChild(button); // Append the button to the buttonsWrapper instead of rightDiv
});
rightDiv.appendChild(buttonsWrapper);
sectionDiv.appendChild(leftDiv)
sectionDiv.appendChild(rightDiv)
contentDiv.appendChild(sectionDiv)
// Start of new code for About button and info
const aboutButton = document.createElement("button");
aboutButton.className = "dynamic-content-button-about"; // Use existing style
aboutButton.textContent = `About ${section.left.topButton.text}`; // Assuming each section has a 'name' attribute
const aboutInfo = document.createElement("pre");
aboutInfo.className = "dynamic-content-about"; // New class for styling
aboutInfo.textContent = section.left.about; // Assuming 'about' attribute exists in each section
aboutInfo.style.display = "none"; // Initially hide the information
// Add onclick event to the About button
aboutButton.onclick = function () {
// Toggle display of the aboutInfo
aboutInfo.style.display = aboutInfo.style.display === "none" ? "block" : "none";
};
// Append the About button and info to the sectionDiv
rightDiv.appendChild(aboutButton);
rightDiv.appendChild(aboutInfo);
})
}
translatePage(startingLanguage, newLanguage);
}
async function createSettingsMenu(file, containerId, containerPrefix = 'editorContainer') {
// refreshColorPickerIfActive();
refreshGraphActive();
refreshNotesActive();
// Find the parent container using the containerPrefix
const parentContainer = document.getElementById(containerPrefix);
if (!parentContainer) {
console.error(`Parent container with id '${containerPrefix}' not found.`);
return;
}
// Check for an existing settings menu and remove it
const existingSettingsMenu = parentContainer.querySelector(`#${containerId}`);
if (existingSettingsMenu) {
existingSettingsMenu.remove();
}
// Create the settings menu div
const settingsMenuDiv = document.createElement('div');
settingsMenuDiv.classList.add('settings-menu');
settingsMenuDiv.id = containerId; // Use the containerId for the settings menu's ID
const settingContainer = document.createElement('div');
settingContainer.classList.add('slider-wrapper');
const languageSelector = document.createElement('select');
languageSelector.id = 'languageSelector2';
// Define the option values and their display text
const options = {
english: 'English',
chinese: '中文',
spanish: 'Español',
german: 'Deutsch'
};
// Populate the select element with options
for (const [value, text] of Object.entries(options)) {
const option = document.createElement('option');
option.value = value;
option.textContent = text;
languageSelector.appendChild(option);
}
const savedLanguage = localStorage.getItem('AIJS0Language');
if (savedLanguage) {
languageSelector.value = savedLanguage;
}
languageSelector.addEventListener('change', function () {
const selectedLanguage = this.value; // Get the selected language from the dropdown
translatePage(currentLanguage, selectedLanguage);
translateOtherPages(currentLanguage, selectedLanguage); // Translate the page to the new language
currentLanguage = selectedLanguage; // Update the currentLanguage
newLanguage = currentLanguage
localStorage.setItem('AIJS0Language', selectedLanguage); // Save the new language to local storage
});
// Add the select element to the container
// Create a reset button
const resetButton = document.createElement('button');
resetButton.classList.add('reset-button');
resetButton.textContent = 'Reset to Default';
resetButton.addEventListener('click', resetSettings);
settingContainer.appendChild(resetButton);
settingContainer.appendChild(languageSelector);
settingsMenuDiv.appendChild(settingContainer);
// Iterate over the currentSettings object
for (let key in currentSettings) {
// Create a container for the setting
const settingContainer = document.createElement('div');
settingContainer.classList.add('setting-container');
// Create a label for the setting
const settingLabel = document.createElement('label');
settingLabel.classList.add('settings-label');
settingLabel.textContent = key;
if (key === 'colorPicker' || key === 'autoTidy') {
} else {
settingContainer.appendChild(settingLabel);
}
// Add additional info text
const infoText = document.createElement('span');
infoText.className = 'setting-info';
infoText.textContent = settingsInformation[key] || 'No additional info available';
// Check the type of setting and create appropriate input element
if (key === 'fontSize' || key === 'theme' || key === 'apiKey' || key === 'geminiApiKey' || key === 'claudeApiKey') {
// Create a range input for fontSize or dropdown for theme
let inputElement
if (key === 'claudeApiKey') {
settingLabel.textContent = "Claude";
// Create a wrapper div for input and value display
const sliderWrapper = document.createElement('div');
sliderWrapper.classList.add('slider-wrapper');
// Create a div to display the value
const valueDisplay = document.createElement('div');
valueDisplay.id = 'claude-api-key-value-display'
valueDisplay.classList.add('value-display');
valueDisplay.textContent = 'Not set.';
// Create an input element for apiKey
inputElement = document.createElement('input');
inputElement.type = 'text';
inputElement.name = key;
inputElement.placeholder = 'Enter your API Key';
inputElement.classList.add('api-key-input');
inputElement.addEventListener('input', () => {
// Add logic here if needed for input event
});
// Create a save button
const saveButton = document.createElement('button');
saveButton.textContent = 'Save';
saveButton.classList.add('save-api-key-button');
// Add click event listener to the save button
saveButton.addEventListener('click', () => {
saveClaudeApiKey(inputElement.value); // Call saveApiKey function with the input value
});
// Create a remove button
const removeButton = document.createElement('button');
removeButton.textContent = 'Remove';
removeButton.classList.add('remove-api-key-button');
// Add click event listener to the remove button
removeButton.addEventListener('click', () => {
removeClaudeApiKey(); // Function to handle the removal of the API key
});
// Append the remove button to the wrapper
// Append elements to the wrapper
sliderWrapper.appendChild(inputElement);
sliderWrapper.appendChild(saveButton); // Add the button after the input
sliderWrapper.appendChild(removeButton);
sliderWrapper.appendChild(valueDisplay);
// Append the wrapper to the setting container
settingContainer.appendChild(infoText);
settingContainer.appendChild(sliderWrapper);
} else if (key === 'geminiApiKey') {
settingLabel.textContent = "Gemini";
// Create a wrapper div for input and value display
const sliderWrapper = document.createElement('div');
sliderWrapper.classList.add('slider-wrapper');
// Create a div to display the value
const valueDisplay = document.createElement('div');
valueDisplay.id = 'gemini-api-key-value-display'
valueDisplay.classList.add('value-display');
valueDisplay.textContent = 'Not set.';
// Create an input element for apiKey
inputElement = document.createElement('input');
inputElement.type = 'text';
inputElement.name = key;
inputElement.placeholder = 'Enter your API Key';
inputElement.classList.add('api-key-input');
inputElement.addEventListener('input', () => {
// Add logic here if needed for input event
});
// Create a save button
const saveButton = document.createElement('button');
saveButton.textContent = 'Save';
saveButton.classList.add('save-api-key-button');
// Add click event listener to the save button
saveButton.addEventListener('click', () => {
saveGeminiApiKey(inputElement.value); // Call saveApiKey function with the input value
});
// Create a remove button
const removeButton = document.createElement('button');
removeButton.textContent = 'Remove';
removeButton.classList.add('remove-api-key-button');
// Add click event listener to the remove button
removeButton.addEventListener('click', () => {
removeGeminiApiKey(); // Function to handle the removal of the API key
});
// Append the remove button to the wrapper
// Append elements to the wrapper
sliderWrapper.appendChild(inputElement);
sliderWrapper.appendChild(saveButton); // Add the button after the input
sliderWrapper.appendChild(removeButton);
sliderWrapper.appendChild(valueDisplay);
// Append the wrapper to the setting container
settingContainer.appendChild(infoText);
settingContainer.appendChild(sliderWrapper);
} else
if (key === 'apiKey') {
settingLabel.textContent = "OpenAI";
// Create a wrapper div for input and value display
const sliderWrapper = document.createElement('div');
sliderWrapper.classList.add('slider-wrapper');
// Create a div to display the value
const valueDisplay = document.createElement('div');
valueDisplay.id = 'api-key-value-display'
valueDisplay.classList.add('value-display');
valueDisplay.textContent = 'Not set.';
// Create an input element for apiKey
inputElement = document.createElement('input');
inputElement.type = 'text';
inputElement.name = key;
inputElement.placeholder = 'Enter your API Key';
inputElement.classList.add('api-key-input');
inputElement.addEventListener('input', () => {
// Add logic here if needed for input event
});
// Create a save button
const saveButton = document.createElement('button');
saveButton.textContent = 'Save';
saveButton.classList.add('save-api-key-button');
// Add click event listener to the save button
saveButton.addEventListener('click', () => {
saveApiKey(inputElement.value); // Call saveApiKey function with the input value
});
// Create a remove button
const removeButton = document.createElement('button');
removeButton.textContent = 'Remove';
removeButton.classList.add('remove-api-key-button');
// Add click event listener to the remove button
removeButton.addEventListener('click', () => {
removeApiKey(); // Function to handle the removal of the API key
});
// Append the remove button to the wrapper
// Append elements to the wrapper
sliderWrapper.appendChild(inputElement);
sliderWrapper.appendChild(saveButton); // Add the button after the input
sliderWrapper.appendChild(removeButton);
sliderWrapper.appendChild(valueDisplay);
// Append the wrapper to the setting container
settingContainer.appendChild(infoText);
settingContainer.appendChild(sliderWrapper);
}
else if (key === 'fontSize') {
settingLabel.textContent = "Font Size";
inputElement = document.createElement('input');
inputElement.name = key;
// Create a wrapper div for slider and value display
const sliderWrapper = document.createElement('div');
sliderWrapper.classList.add('slider-wrapper');
// Create a div to display the value
const valueDisplay = document.createElement('div');
valueDisplay.classList.add('value-display');
// Extract the numeric value from the fontSize setting
const fontSizeValue = parseInt(currentSettings[key]);
valueDisplay.textContent = fontSizeValue + 'px'; // Set the initial display text
valueDisplay.style.fontSize = fontSizeValue + 'px';
valueDisplay.id = 'slider-value'
inputElement.type = 'range';
inputElement.min = '8';
inputElement.max = '22';
inputElement.value = fontSizeValue; // Use the numeric value for the slider
inputElement.addEventListener('input', () => {
const sliderValue = inputElement.value;
updateSetting(key, sliderValue + 'px');
valueDisplay.textContent = sliderValue + 'px'; // Update the display text
setFontSize(sliderValue)
// Update the font size of the value display
valueDisplay.style.fontSize = sliderValue + 'px';
});
// Append elements to the wrapper and then to the setting container
sliderWrapper.appendChild(inputElement);
sliderWrapper.appendChild(valueDisplay);
settingContainer.appendChild(infoText);
settingContainer.appendChild(sliderWrapper);
} else { // key === 'theme'
settingLabel.textContent = "Theme";
inputElement = document.createElement('select');
inputElement.name = key;
// Create parent div for themes
const themesContainer = document.createElement('div');
themesContainer.className = 'themes-grid'; // Use this class to style the grid in CSS
//'codium_dark.json','codium_light.json',
const themeFiles = ['aijs', 'aijs_dark', 'aijs_dark_modern', 'aijs_light', 'aijs_blue', 'aijs_sleek',
'anysphere_dark', 'claude_light', 'claude_chat', 'claude_workbench', 'codium_dark', 'discord_dark', 'fiddle_dark', 'flexoki_dark', 'flexoki_light', 'gemini_dark', 'genify_dark',
'github_dark', 'gpt_dark', 'hc_black', 'one_dark', 'onyx_dark', 'p5_dark', 'p5_hc', 'pen_dark', 'reddit_dark', 'repl_dark', 'sandbox_dark', 'slack_dark', 'tender_dark', 'vs_dark', 'vs_dark_modern',
'vs_light'];
themeFiles.forEach(file => {
const option = document.createElement('option');
option.value = file;
option.textContent = file.replace('.json', '');
inputElement.appendChild(option);
// Create a div for each theme
const themeDiv = document.createElement('div');
themeDiv.dataset.theme = file; // Add data attribute for additional styling
themeDiv.title = file
themeDiv.className = 'theme-div'; // Use this class to style the themeDiv in CSS
if (file === currentSettings.theme) {
themeDiv.classList.add('active-theme'); // Add the class to highlight active theme
}
// Add three nested divs inside each themeDiv
for (let i = 1; i <= 3; i++) {
const colorDiv = document.createElement('div');
colorDiv.className = `color-div color-${i}`; // Use these classes to style each nested div in CSS
themeDiv.appendChild(colorDiv);
}
// Add click event to the div
themeDiv.addEventListener('click', () => {
inputElement.value = file; // Update the select element value
updateSetting(key, inputElement.value); // Update the setting
setTheme(inputElement.value); // Apply the new theme
// Remove 'active-theme' class from all theme divs
document.querySelectorAll('.theme-div.active-theme').forEach(activeDiv => {
activeDiv.classList.remove('active-theme');
});
// Add 'active-theme' class to the clicked div
themeDiv.classList.add('active-theme');
});
// Add the themeDiv to the themesContainer
themesContainer.appendChild(themeDiv);
});
inputElement.value = currentSettings.theme;
inputElement.addEventListener('change', () => {
const selectedTheme = inputElement.value; // Get the newly selected theme
updateSetting(key, selectedTheme); // Update the setting
setTheme(selectedTheme); // Apply the new theme
// Remove 'active-theme' class from all theme divs
document.querySelectorAll('.theme-div.active-theme').forEach(activeDiv => {
activeDiv.classList.remove('active-theme');
});
// Find the div that matches the selected theme and add 'active-theme'
const activeThemeDiv = themesContainer.querySelector(`.theme-div[data-theme="${selectedTheme}"]`);
if (activeThemeDiv) {
activeThemeDiv.classList.add('active-theme');
}
});
settingContainer.appendChild(infoText);
settingContainer.appendChild(inputElement);
settingContainer.appendChild(themesContainer); // Add the themes container to your settings container
}
} else {
if (key == 'autoRefresh') {
settingLabel.textContent = "Auto Refresh";
}
if (key == 'autoSave') {
settingLabel.textContent = "Auto Save";
}
if (key === 'fontWeight') {
settingLabel.textContent = "Font Weight";
}
if (key === 'wordWrap') {
settingLabel.textContent = "Word Wrap";
}
// Create a container for checkbox and additional info
const inputWrapper = document.createElement('div');
inputWrapper.className = 'input-wrapper';
// Create a checkbox for other settings
const settingCheckbox = document.createElement('input');
settingCheckbox.type = 'checkbox';
settingCheckbox.checked = currentSettings[key];
settingCheckbox.name = key;
settingCheckbox.id = key + '-checkbox'; // Unique ID for the checkbox
settingCheckbox.classList.add('custom-checkbox');
settingCheckbox.addEventListener('change', () => {
updateSetting(key, settingCheckbox.checked);
// If the changed setting is 'autoSave', re-initialize auto-save
if (key === 'autoSave') {
initializeAutoSave();
}
if (key === 'wordWrap') {
setWordWrap(settingCheckbox.checked);
}
if (key === 'fontWeight') {
if (settingCheckbox.checked) {
newFontWeight = 'bold'
} else {
newFontWeight = '400'
}
setFontWeight(newFontWeight)
}
});
const checkboxLabel = document.createElement('label');
checkboxLabel.htmlFor = key + '-checkbox'; // Associate the label with the checkbox
checkboxLabel.textContent = ''; // Replace with your desired text
inputWrapper.appendChild(settingCheckbox);
inputWrapper.appendChild(checkboxLabel);
inputWrapper.appendChild(infoText);
settingContainer.appendChild(inputWrapper);
if (key === 'colorPicker' || key === 'autoTidy') {
settingContainer.style.display = "none"
}
}
// Append the setting container to the settings menu
settingsMenuDiv.appendChild(settingContainer);
}
// Append the new settings menu div to the parent container
parentContainer.appendChild(settingsMenuDiv);
updateSettingsMenu()
translatePage(startingLanguage, newLanguage);
return settingsMenuDiv;
}
let autoSaveIntervalId;
// Refactored initializeAutoSave function
function initializeAutoSave() {
let autoSaveInterval = 5 * 60000; // 5 minutes in milliseconds
// autoSaveInterval = 5000
// Clear existing auto-save interval if it exists
if (autoSaveIntervalId) {
clearInterval(autoSaveIntervalId);
}
// Set up a new auto-save interval
autoSaveIntervalId = setInterval(() => {
if (currentSettings.autoSave && openedProjectName != null && checkIfModified(fileSystem)) {
saveProject();
}
}, autoSaveInterval);
}
function checkIfModified(fileSystem) {
let isModified = false;
function checkDirectory(directory) {
Object.values(directory.children).forEach(item => {
if (item.type === 'file' && item.isModified) {
isModified = true;
} else if (item.type === 'directory') {
checkDirectory(item); // Recursively process subdirectories
}
});
}
// Recursively process each directory in the file system
Object.values(fileSystem).forEach(rootDirectory => {
if (rootDirectory.type === 'directory') {
checkDirectory(rootDirectory);
}
});
return isModified;
}
function updateSettingsMenu() {
// Find all settings menus
const settingsMenus = document.querySelectorAll('.settings-menu');
settingsMenus.forEach(settingsMenuDiv => {
// Iterate over the currentSettings object
for (let key in currentSettings) {
// Find the input for the setting
const settingInput = settingsMenuDiv.querySelector(`.setting-container [name="${key}"]`);
if (!settingInput) {
console.error(`Input for setting '${key}' not found.`);
continue;
}
if (key === 'geminiApiKey') {
// console.log(currentSettings[key])
if (currentSettings[key] == true) {
const valueDisplayElement = document.getElementById('gemini-api-key-value-display');
if (valueDisplayElement) {
valueDisplayElement.textContent = 'Stored successfully';
valueDisplayElement.classList.add('api-key-stored'); // Add class for styling or icon
}
} else {
// const valueDisplayElement = document.getElementById('api-key-value-display');
// if (valueDisplayElement) {
// valueDisplayElement.textContent = 'Not Set.';
// valueDisplayElement.classList.remove('api-key-stored'); // Add class for styling or icon
// }
}
} else if (key === 'claudeApiKey') {
// console.log(currentSettings[key])
if (currentSettings[key] == true) {
const valueDisplayElement = document.getElementById('claude-api-key-value-display');
if (valueDisplayElement) {
valueDisplayElement.textContent = 'Stored successfully';
valueDisplayElement.classList.add('api-key-stored'); // Add class for styling or icon
}
} else {
// const valueDisplayElement = document.getElementById('api-key-value-display');
// if (valueDisplayElement) {
// valueDisplayElement.textContent = 'Not Set.';
// valueDisplayElement.classList.remove('api-key-stored'); // Add class for styling or icon
// }
}
} else if (key === 'apiKey') {
//console.log(currentSettings[key])
if (currentSettings[key] == true) {
const valueDisplayElement = document.getElementById('api-key-value-display');
if (valueDisplayElement) {
valueDisplayElement.textContent = 'Stored successfully';
valueDisplayElement.classList.add('api-key-stored'); // Add class for styling or icon
}
} else {
// const valueDisplayElement = document.getElementById('api-key-value-display');
// if (valueDisplayElement) {
// valueDisplayElement.textContent = 'Not Set.';
// valueDisplayElement.classList.remove('api-key-stored'); // Add class for styling or icon
// }
}
} else if (key === 'fontSize') {
// Update the range input for the fontSize setting
const fontSizeValue = parseInt(currentSettings[key]);
settingInput.value = fontSizeValue;
// Also update the display for the fontSize setting
const fontSizeDisplay = document.getElementById('slider-value');
if (fontSizeDisplay) {
fontSizeDisplay.textContent = fontSizeValue + 'px';
fontSizeDisplay.style.fontSize = fontSizeValue + 'px';
setFontSize(fontSizeValue)
}
} else if (key === 'fontWeight') {
//settingInput.checked = currentSettings[key];
// Update the range input for the fontSize setting
const fontWeightValue = currentSettings[key];
let newFontWeight
// Also update the display for the fontSize setting
if (fontWeightValue) {
newFontWeight = 'bold'
} else {
newFontWeight = '400'
}
setFontWeight(newFontWeight)
} else if (key === 'theme') {
// Update the dropdown for the theme setting
settingInput.value = currentSettings[key];
setTheme(settingInput.value);
} else {
// Update the checkbox for the other settings
settingInput.checked = currentSettings[key];
}
}
});
}
// Function to add a new tab
function addTab(file, targetContainer) {
const tabHeaders = ['tabHeader', 'tabHeader2', 'tabHeader3', 'tabHeader4'];
let targetIndex;
let tabHeader;
const existingTabInfo = findTabInfo(file.id);
if (existingTabInfo) {
// Tab already exists, just select it
selectTab(file.id, existingTabInfo.containerKey);
return;
}
const typeToIndexMap = {
'container': 0,
'container2': 1,
'container3': 2,
'container4': 3,
// Add more mappings as needed
};
targetIndex = typeToIndexMap[targetContainer || 'container'];
if (!openTabs[file.id]) {
// Create a new editor container and instance
const editorContainerId = `editor-${file.id}`;
let editorInstance
let extension
if (file.type == 'file') {
editorInstance = createEditor(file, editorContainerId, targetContainer);
extension = getFileExtension(file.name);
} else if (file.type == 'preview') {
editorInstance = createPreview(file, editorContainerId, targetContainer)
extension = 'preview'
} else if (file.type == 'console') {
editorInstance = createConsole(file, editorContainerId, targetContainer)
extension = 'console'
} else if (file.type == 'AI') {
editorInstance = createAssistant(file, editorContainerId, targetContainer)
extension = 'AI'
} else if (file.type == 'New_Tab') {
editorInstance = createNewTabList(editorContainerId, targetContainer);
extension = 'New_Tab';
} else if (file.type == 'Color_Picker') {
editorInstance = createColorPicker(file, editorContainerId, targetContainer)
extension = 'Color_Picker'
} else if (file.type == 'Settings_Menu') {
editorInstance = createSettingsMenu(file, editorContainerId, targetContainer)
extension = 'Settings_Menu'
} else if (file.type == 'gallery') {
editorInstance = createGallery(file, editorContainerId, targetContainer)
extension = 'gallery'
} else if (file.type == 'publish_project') {
editorInstance = createPublishMenu(file, editorContainerId, targetContainer)
extension = 'publish_project'
} else if (file.type == 'graph') {
editorInstance = createGraph(file, editorContainerId, targetContainer)
extension = 'graph'
} else if (file.type == 'notes') {
editorInstance = createNotes(file, editorContainerId, targetContainer)
extension = 'notes'
} else if (file.type == 'Mint') {
editorInstance = createMint(file, editorContainerId, targetContainer)
extension = 'Mint'
} else if (file.type == 'AIImager') {
editorInstance = createAIImager(file, editorContainerId, targetContainer)
extension = 'AIImager'
} else if (file.type == 'github') {
editorInstance = createGitHub(file, editorContainerId, targetContainer)
extension = 'github'
} else if (file.type == 'dependencies') {
editorInstance = createDependencies(file, editorContainerId, targetContainer)
extension = 'dependencies'
}
const tabHeader = document.getElementById(tabHeaders[targetIndex]);
const containerKey = targetIndex === 0 ? 'container' : `container${targetIndex + 1}`;
// Create a new tab
const tab = document.createElement('div');
tab.id = `tab-${file.id}`;
tab.setAttribute('data-id', file.id);
tab.classList.add('tab', 'inactive-tab'); // Add 'tab' and 'inactive-tab' classes
const fileItem = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileItem && fileItem.classList.contains('modified')) {
tab.classList.add('modified');
}
// Create a span for the tab text content
const tabText = document.createElement('span');
tabText.textContent = file.name;
tabText.classList.add('tab-text');
if (file.type != 'file') {
translateElement(tabText, startingLanguage, newLanguage);
}
tab.addEventListener('mousedown', function (e) {
// Check if the click target is the close button or its descendant
const isCloseButtonClicked = e.target.closest('.tab-close-button');
if (isCloseButtonClicked) {
// If the close button or its descendant was clicked, return early
return;
}
let mouseDown = true;
const mouseUpListener = () => {
if (mouseDown) {
const currentContainerKey = determineCurrentContainerKey(file.id);
selectTab(file.id, currentContainerKey);
}
mouseDown = false;
tab.removeEventListener('mouseup', mouseUpListener);
};
const mouseLeaveListener = () => {
mouseDown = false;
tab.removeEventListener('mouseleave', mouseLeaveListener);
};
tab.addEventListener('mouseup', mouseUpListener);
tab.addEventListener('mouseleave', mouseLeaveListener);
});
tabText.setAttribute('data-type', extension); // Set the data attribute based on the file type
tab.appendChild(tabText);
// Add a close button to the tab
if (file.type == 'AI') {
const newChatButton = document.createElement('span');
newChatButton.className = 'tab-new-chat-button';
newChatButton.title = 'Clear chat history'
newChatButton.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent triggering tab selection
newChat(); // Call your new chat function
});
tab.appendChild(newChatButton);
}
if (file.type == 'console') {
const clearConsoleButton = document.createElement('span');
clearConsoleButton.className = 'tab-new-chat-button';
clearConsoleButton.title = 'Clear console'
clearConsoleButton.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent triggering tab selection
clearConsole(); // Call your new chat function
});
tab.appendChild(clearConsoleButton);
}
const closeButton = document.createElement('span');
// closeButton.textContent = ' x';
closeButton.className = 'tab-close-button';
closeButton.addEventListener('click', (e) => {
if (!mousedownOccurred) { return; }
e.stopPropagation();
closeTab(file.id);
});
tab.appendChild(closeButton);
// Add the tab to the header and store the tab info
tabHeader.appendChild(tab);
if (!openTabs[containerKey][file.id]) {
openTabs[containerKey][file.id] = {
file,
tab,
editor: editorInstance,
editorContainerId
};
}
// Select the newly added tab
selectTab(file.id, containerKey);
} else {
// If the tab already exists, just select it
selectTab(file.id, containerKey);
}
}
// Function to determine the current container key of a tab
function determineCurrentContainerKey(fileId) {
for (const containerKey in openTabs) {
if (openTabs[containerKey].hasOwnProperty(fileId)) {
return containerKey;
}
}
console.error("File ID not found in any container:", fileId);
return null; // or a default container key if appropriate
}
// Helper function to find tab info across all containers
function findTabInfo(fileId) {
for (const containerKey in openTabs) {
if (openTabs[containerKey].hasOwnProperty(fileId)) {
return {
containerKey: containerKey,
tabInfo: openTabs[containerKey][fileId]
};
}
}
return null;
}
function initTabSorting() {
const sortableOptions = {
group: 'tabs',
animation: 150,
onEnd: function (evt) {
isDragging = false;
const draggedTabId = evt.item.id.replace('tab-', '');
const sourceHeader = evt.from; // Get the source tab header
const targetHeader = evt.to; // Get the target tab header
if (draggedTabId == 'Color_Picker') {
// loadSettingsFromCache()
// refreshColorPickerIfActive();
}
if (draggedTabId == 'graph') {
refreshGraphActive();
}
if (draggedTabId == 'notes') {
refreshNotesActive();
}
updateTabsOrder(targetHeader, draggedTabId, evt);
// Check if source header is empty after moving the tab
if (sourceHeader && sourceHeader.children.length === 0) {
const containerKey = getContainerKeyFromHeaderId(sourceHeader.id);
if (containerKey) {
// Open a new empty tab in the source header
containerTypes.New_Tab = {
id: generateUniqueId(),
name: 'New Tab',
type: 'New_Tab',
content: [],
path: null,
mimetype: null,
syntax: null,
lastModified: null,
size: null,
blobUrl: null,
saveStatus: 'saved',
isViewing: false,
hasTab: false,
comments: []
};
openNewEmptyTab(containerTypes.New_Tab, containerKey);
}
}
}
};
new Sortable(document.getElementById('tabHeader'), sortableOptions);
new Sortable(document.getElementById('tabHeader2'), sortableOptions);
new Sortable(document.getElementById('tabHeader3'), sortableOptions);
new Sortable(document.getElementById('tabHeader4'), sortableOptions);
}
function getContainerKeyFromHeaderId(headerId) {
const headerToContainerMap = {
'tabHeader': 'container',
'tabHeader2': 'container2',
'tabHeader3': 'container3',
'tabHeader4': 'container4'
// Add more mappings as needed
};
return headerToContainerMap[headerId];
}
function updateTabsOrder(newContainer, draggedTabId, evt) {
if (!newContainer) {
console.error("updateTabsOrder called without a valid container element");
return;
}
const tabHeaderId = newContainer.getAttribute('id');
const destinationContainerKey = tabHeaderId.replace('tabHeader', 'container');
const sourceContainerId = evt.from.getAttribute('id');
const sourceContainerKey = sourceContainerId.replace('tabHeader', 'container');
if (!openTabs[sourceContainerKey] || !openTabs[sourceContainerKey][draggedTabId]) {
console.error(`Source container key '${sourceContainerKey}' does not exist or does not have the dragged tab`);
return;
}
// Check if dragging within the same container
if (sourceContainerKey === destinationContainerKey) {
// Simply reorder tabs without changing active state
return;
}
if (!openTabs[destinationContainerKey]) {
openTabs[destinationContainerKey] = {};
}
let draggedTabInfo = openTabs[sourceContainerKey][draggedTabId];
const wasDraggedTabActive = draggedTabInfo.tab.classList.contains('active-tab');
delete openTabs[sourceContainerKey][draggedTabId];
openTabs[destinationContainerKey][draggedTabId] = draggedTabInfo;
draggedTabInfo.editorContainerId = `editor-${draggedTabId}`;
const editorContainer = document.getElementById(draggedTabInfo.editorContainerId);
if (editorContainer) {
document.getElementById(destinationContainerKey.replace('container', 'editorContainer')).appendChild(editorContainer);
}
// Deactivate all tabs in the destination container and activate the dragged tab
deactivateAllTabs(destinationContainerKey);
activateTab(draggedTabId, destinationContainerKey);
// If the dragged tab was active, activate the first inactive tab in the source container
if (wasDraggedTabActive) {
activateFirstTabInContainer(sourceContainerKey);
}
}
function deactivateAllTabs(containerKey) {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id)) {
const tabInfo = openTabs[containerKey][id];
tabInfo.tab.classList.remove('active-tab');
tabInfo.tab.classList.add('inactive-tab');
const editorContainer = document.getElementById(tabInfo.editorContainerId);
if (editorContainer) {
editorContainer.style.display = 'none';
}
}
}
}
function activateTab(fileId, containerKey) {
if (openTabs[containerKey].hasOwnProperty(fileId)) {
const tabInfo = openTabs[containerKey][fileId];
tabInfo.tab.classList.add('active-tab');
tabInfo.tab.classList.remove('inactive-tab');
const editorContainer = document.getElementById(tabInfo.editorContainerId);
if (editorContainer) {
editorContainer.style.display = 'block';
if (tabInfo && tabInfo.file.type === 'file' && tabInfo.editor) {
tabInfo.editor.layout();
}
}
}
}
function activateFirstTabInContainer(containerKey) {
const tabHeaderId = containerKey.replace('container', 'tabHeader');
const tabs = Array.from(document.getElementById(tabHeaderId).children);
if (tabs.length > 0) {
const firstInactiveTab = tabs.find(tab => !openTabs[containerKey][tab.id.replace('tab-', '')].tab.classList.contains('active-tab'));
if (firstInactiveTab) {
const firstTabId = firstInactiveTab.id.replace('tab-', '');
activateTab(firstTabId, containerKey);
}
}
}
function updateTabStates(containerId, activeTabId, wasDraggedTabActive) {
const containerKey = containerId.replace('tabHeader', 'container');
let activeTabExists = false;
// Check if there is an active tab in the container
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id)) {
if (openTabs[containerKey][id].tab.classList.contains('active-tab')) {
activeTabExists = true;
if (wasDraggedTabActive && id !== activeTabId) {
// Deactivate the currently active tab if a new active tab is moved in
openTabs[containerKey][id].tab.className = 'tab inactive-tab';
const editorContainer = document.getElementById(openTabs[containerKey][id].editorContainerId);
if (editorContainer) {
editorContainer.style.display = 'none';
}
}
break;
}
}
}
Array.from(document.getElementById(containerId).children).forEach(tab => {
const fileId = tab.id.replace('tab-', '');
if (fileId !== activeTabId) {
const tabInfo = openTabs[containerKey][fileId];
if (activeTabExists && !tabInfo.tab.classList.contains('active-tab')) {
// Deactivate non-active tabs if there's already an active tab
tabInfo.tab.classList.remove('active-tab');
tabInfo.tab.classList.add('inactive-tab');
const editorContainer = document.getElementById(tabInfo.editorContainerId);
if (editorContainer) {
editorContainer.style.display = 'none';
}
}
}
});
// Activate the new active tab if it is the dragged tab
if (openTabs[containerKey][activeTabId]) {
const activeTabInfo = openTabs[containerKey][activeTabId];
activeTabInfo.tab.className = 'tab active-tab';
const editorContainer = document.getElementById(activeTabInfo.editorContainerId);
if (editorContainer) {
editorContainer.style.display = 'block';
if (tabInfo && tabInfo.file.type === 'file' && tabInfo.editor) {
activeTabInfo.editor.layout();
}
}
}
}
let currentFile = 'sketch.js'
function updateActiveFileName(newFileName) {
const fileNameSpan = document.getElementById('file-name-span');
if (fileNameSpan) {
fileNameSpan.textContent = newFileName || 'No file selected';
currentFile = newFileName
}
}
function selectTab(fileId, containerKey) {
// updateFileSystemContent();
let found = false;
let parent = document.getElementById(containerKey).parentElement
if (parent.classList.contains('hidden2')) {
if (parent.id == "row1") {
toggleRightEditor()
} else if (parent.id == "row2") {
toggleLeftEditor()
}
}
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id)) {
const tabInfo = openTabs[containerKey][id];
if (fileId === id) {
// Activate the selected tab
tabInfo.tab.classList.add('active-tab');
tabInfo.tab.classList.remove('inactive-tab');
const editorContainer = document.getElementById(tabInfo.editorContainerId);
if (editorContainer) {
editorContainer.style.display = 'block';
// Retrieve the editor instance using the containerId
const editorInstance = editorInstances[tabInfo.editorContainerId];
if (editorInstance) {
activeEditor = editorInstance;
activeEditor.layout();
//activeEditor.focus();
// Update the active file name
updateActiveFileName(activeEditor.fileName);
}
}
found = true;
} else {
// Deactivate other tabs in the same container
tabInfo.tab.classList.remove('active-tab');
tabInfo.tab.classList.add('inactive-tab');
const editorContainer = document.getElementById(tabInfo.editorContainerId);
if (editorContainer) {
editorContainer.style.display = 'none';
}
}
}
}
setEditorMode()
if (!found) {
console.error("Tab with fileId not found in openTabs: ", fileId);
}
const fileItem = document.querySelector(`.file-item[data-id="${fileId}"]`);
if (fileItem && !fileItem.classList.contains('active-file')) {
fileItem.click();
}
resizeAllContainers();
}
// Function to select a tab in a specific container
function closeTab(fileId, selectNewTab = true) {
updateFileSystemContent()
// updateFileSystemOrganization()
let tabInfo;
let tabParent;
let containerKey;
updateActiveFileName('none');
['container', 'container2', 'container3', 'container4'].forEach(container => {
if (openTabs[container][fileId]) {
tabInfo = openTabs[container][fileId];
tabParent = tabInfo.tab.parentNode;
containerKey = container;
}
});
if (!tabInfo || !tabInfo.tab) {
console.error("Tab to close not found: ", fileId);
return;
}
// Dispose of the editor instance
if (editorInstances[tabInfo.editorContainerId]) {
editorInstances[tabInfo.editorContainerId].dispose();
delete editorInstances[tabInfo.editorContainerId];
}
tabParent.removeChild(tabInfo.tab);
const editorContainer = document.getElementById(tabInfo.editorContainerId);
if (editorContainer) {
editorContainer.parentNode.removeChild(editorContainer);
}
delete openTabs[containerKey][fileId];
if (selectNewTab && tabInfo.tab.classList.contains('active-tab')) {
activateFirstTabInContainer(containerKey);
}
if (tabParent && tabParent.children.length === 0 && !OpeningNewProject) {
containerTypes.New_Tab = {
id: generateUniqueId(),
name: 'New Tab',
type: 'New_Tab',
content: [],
path: null,
mimetype: null,
syntax: null,
lastModified: null,
size: null,
blobUrl: null,
saveStatus: 'saved',
isViewing: false,
hasTab: false,
comments: []
};
openNewEmptyTab(containerTypes.New_Tab, containerKey);
}
}
function activateFirstTabInContainer(containerKey) {
const tabs = Object.values(openTabs[containerKey]);
if (tabs.length > 0) {
selectTab(tabs[0].file.id, containerKey);
}
}
let lastUsedIndex = -1;
function determineTargetIndex(file) {
// Increment the index each time a tab is added
lastUsedIndex = (lastUsedIndex + 1) % 4; // There are 4 containers
return 0;
}
// Function to open a file
function openFile(file, targetContainer) {
if (row1.classList.contains('hidden2')) {
toggleRightEditor()
}
return new Promise((resolve, reject) => {
require(['vs/editor/editor.main'], function () {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
updateFilePaths(fileSystem);
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer || 'container');
initTabSorting();
// Highlight the corresponding file item
const fileElement = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileElement) {
fileElement.classList.add('active-file');
}
resolve(); // Resolve the promise when everything is done
});
});
}
openPreview(containerTypes.preview, 'container2')
function openPreview(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
// Highlight the corresponding file item
const fileElement = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileElement) {
fileElement.classList.add('active-file');
}
}
function openNotes(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
// Highlight the corresponding file item
const fileElement = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileElement) {
fileElement.classList.add('active-file');
}
}
// openGraph(containerTypes.graph, 'container2')
function openGraph(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
// Highlight the corresponding file item
const fileElement = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileElement) {
fileElement.classList.add('active-file');
}
}
function openColorPicker(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
// Highlight the corresponding file item
const fileElement = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileElement) {
fileElement.classList.add('active-file');
}
}
openConsole(containerTypes.console, 'container3')
function openConsole(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
// Highlight the corresponding file item
const fileElement = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileElement) {
fileElement.classList.add('active-file');
}
}
//openGallery(containerTypes.gallery, 'container');
function openGallery(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
function openAssistant(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
// openNewEmptyTab(containerTypes.New_Tab, 'container')
containerTypes.New_Tab = {
id: generateUniqueId(),
name: 'New Tab',
type: 'New_Tab',
content: [],
path: null,
mimetype: null,
syntax: null,
lastModified: null,
size: null,
blobUrl: null,
saveStatus: 'saved',
isViewing: false,
hasTab: false,
comments: []
};
openNewEmptyTab(containerTypes.New_Tab, 'container4');
function openNewEmptyTab(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
// Highlight the corresponding file item
// const fileElement = document.querySelector(`.file-item[data-id="${file.id}"]`);
// if (fileElement) {
// fileElement.classList.add('active-file');
// }
}
//openPublish(containerTypes.publish_project, 'container');
function openPublish(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
//openMint(containerTypes.Mint, 'container');
function openMint(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
//openAIImager(containerTypes.AIImager, 'container');
function openAIImager(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
// openGitHub(containerTypes.github, 'container2');
function openGitHub(file, targetContainer) {
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
function openDependencies(file, targetContainer) {
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
//openSettings(containerTypes.Settings_Menu, 'container');
function openSettings(file, targetContainer) {
// Find the current active tab ID across all containers
let currentActiveTabId = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].tab.classList.contains('active-tab')) {
currentActiveTabId = id;
break;
}
}
});
if (currentActiveTabId) {
//updateFileSystemContent(currentActiveTabId); // Assuming this function is correctly implemented
}
// Remove the active class from previously opened files
const previouslyActiveFile = document.querySelector('.file-item.active-file');
if (previouslyActiveFile) {
previouslyActiveFile.classList.remove('active-file');
}
addTab(file, targetContainer);
initTabSorting();
}
function findFileInDirectory(directory, filePath) {
for (const child of Object.values(directory.children)) {
const { type, path } = child;
if (type === 'file' && path === filePath) {
return child;
} else if (type === 'directory') {
const file = findFileInDirectory(child, filePath);
if (file) {
return file;
}
}
}
return null;
}
function updateAllContainerOverflows() {
// Iterate over each container
Object.keys(openTabs).forEach(containerKey => {
const editorContainer = document.getElementById(containerKey.replace('container', 'editorContainer'));
if (editorContainer) {
// Find the active tab in this container
let activeTabType = null;
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id)) {
const tabInfo = openTabs[containerKey][id];
if (tabInfo.tab.classList.contains('active-tab')) {
activeTabType = tabInfo.file.type;
break;
}
}
}
// Adjust the overflow based on the type of the active tab
if (activeTabType === 'file') {
editorContainer.style.overflow = 'hidden';
} else {
editorContainer.style.overflowY = 'auto';
editorContainer.style.overflowX = 'hidden';
}
}
});
//resizeAllContainers();
}
function updateAllFileLists() {
// Select all file list elements
const allFileLists = document.querySelectorAll('.file-list');
allFileLists.forEach(fileList => {
// Assuming you can derive the container ID from the file list's ID or other attributes
const containerId = fileList.id.replace('fileList-', '');
updateFileList(containerId);
});
}
function updateFilePaths(fileSystemObject) {
// Find the root directory
const rootKey = Object.keys(fileSystemObject).find(key => fileSystemObject[key].isRoot);
const rootDirectory = fileSystemObject[rootKey];
// Internal function to recursively update paths
function updatePaths(directory, basePath = "") {
directory.path = basePath + '/' + directory.name;
Object.keys(directory.children).forEach(childName => {
const child = directory.children[childName];
if (child.type === 'directory') {
updatePaths(child, directory.path);
} else if (child.type === 'file') {
child.path = directory.path + '/' + child.name;
}
updateFilePathDisplay(child.id, child.path)
});
}
const filePathDisplays = document.querySelectorAll('.file-path-display');
filePathDisplays.forEach(display => {
const fileName = display.textContent.split('/').pop(); // Get the last part of the path
const file = findFileByName(fileSystemObject, fileName);
if (file) {
const pathSegments = file.path.split('/');
const displayPath = pathSegments.slice(2).join('/'); // Skip the first empty segment and root name
display.textContent = displayPath;
}
});
if (rootDirectory) {
updatePaths(rootDirectory);
}
}
function updateFileSystemOrganization() {
organizeFileSystem(fileSystem);
updateFilePaths(fileSystem);
updateAllFileLists();
}
function updateFileSystemContent(currentActiveTabId = null) {
updateAllContainerOverflows();
// organizeFileSystem(fileSystem);
// updateFilePaths(fileSystem);
// updateAllFileLists();
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id)) {
const tabInfo = openTabs[containerKey][id];
if (tabInfo && tabInfo.file.type === 'file' && tabInfo.editor) {
const editorContent = tabInfo.editor.getValue();
//const fileObject = findFileInDirectory(fileSystem.myProject, tabInfo.file.path);
const fileObject = tabInfo.file
if (fileObject) {
fileObject.content = editorContent;
fileObject.hasTab = true;
fileObject.isViewing = (id === currentActiveTabId);
const tabText = tabInfo.tab.querySelector('.tab-text');
if (tabText) {
tabText.textContent = fileObject.name;
const extension = getFileExtension(fileObject.name);
tabText.setAttribute('data-type', extension);
// Check if it's a markdown file and add a preview button if it's missing
if (extension === 'md') {
const editorContainer = document.getElementById('editor-' + id); // Assuming id corresponds to the container id
if (editorContainer && !editorContainer.querySelector('.preview-markdown-btn')) {
// Create and append the markdown preview button
const previewButton = document.createElement('button');
previewButton.textContent = 'View Markdown';
translateElement(previewButton, startingLanguage, newLanguage);
previewButton.title = "Set this file as your source"
previewButton.className = 'preview-markdown-btn'; // Assign a class for styling
previewButton.onclick = function () {
if (!mousedownOccurred) { return; }
sourceDoc = fileObject.name; // Set the sourceDoc to the current file's name
setAsSource(sourceDoc);
runCode(); // Call your function to handle the markdown preview
};
editorContainer.appendChild(previewButton); // Append the button to the editor container
}
} else {
const editorContainer = document.getElementById('editor-' + id);
const markdownButton = editorContainer ? editorContainer.querySelector('.preview-markdown-btn') : null;
if (markdownButton) {
markdownButton.remove();
}
}
}
}
}
}
}
});
// Reset hasTab and isViewing for files that are not open
resetFileSystemState(fileSystem.myProject, openTabs);
// Update directory state (collapsed/expanded)
const directoryElements = document.getElementsByClassName('directory-item');
for (let dir of directoryElements) {
let dirName = dir.textContent;
if (dir.nextElementSibling) {
let isCollapsed = dir.nextElementSibling.classList.contains('hide');
updateDirectoryStateInFileSystem(fileSystem.myProject, dirName, isCollapsed);
}
}
setEditorMode()
}
function resetFileSystemState(directory, openTabs) {
Object.keys(directory.children).forEach(childName => {
const child = directory.children[childName];
if (child.type === 'file') {
// Reset hasTab and isViewing if file is not open
if (!openTabs[child.id]) {
child.hasTab = false;
child.isViewing = false;
}
} else if (child.type === 'directory') {
resetFileSystemState(child, openTabs);
}
});
}
function updateDirectoryStateInFileSystem(directory, dirName, isCollapsed) {
if (directory.name === dirName) {
directory.isOpen = !isCollapsed;
}
Object.keys(directory.children).forEach(childName => {
if (directory.children[childName].type === 'directory') {
updateDirectoryStateInFileSystem(directory.children[childName], dirName, isCollapsed);
}
});
}
// Initialize the file system viewer
function loadInitialDocument() {
let sourceSet = false;
if (openSpecificFile('sketch.js', '')) { // Open sketch.js but don't set the source here
// After opening sketch.js, try to find and set an appropriate HTML file as the source
sourceSet = setHtmlSource();
} else if (openSpecificFile('mySketch.js', '')) { // Open sketch.js but don't set the source here
// After opening sketch.js, try to find and set an appropriate HTML file as the source
sourceSet = setHtmlSource();
} else if (openSpecificFile('shader.frag', '')) { // Open sketch.js but don't set the source here
// After opening sketch.js, try to find and set an appropriate HTML file as the source
sourceSet = setHtmlSource();
} else if (openSpecificFile('sketch.py', '')) { // Open sketch.js but don't set the source here
// After opening sketch.js, try to find and set an appropriate HTML file as the source
sourceSet = setHtmlSource();
} else if (openSpecificFile('sketch.ts', '')) { // Open sketch.js but don't set the source here
// After opening sketch.js, try to find and set an appropriate HTML file as the source
sourceSet = setHtmlSource();
}
if (!sourceSet) {
// If source isn't set yet, either because sketch.js wasn't found or no HTML file was set as source
if (openSpecificFile('index.html', 'index.html')) {
// index.html opened and set as source, nothing more to do
sourceSet = true;
} else {
// Attempt to open the first file with .html extension
const htmlFile = findFirstFileWithExtension('html');
if (htmlFile) {
openSpecificFile(htmlFile, htmlFile);
sourceSet = true;
} else {
// Open the first file found if no HTML file is found
const firstFile = findFirstFile();
if (firstFile) {
openSpecificFile(firstFile, 'index.html');
sourceSet = true;
} else {
console.error("No files to open");
return; // Exit the function if no files are found
}
}
}
}
updateFileSystemOrganization()
}
function setHtmlSource() {
// This function tries to find and set an HTML file as the source without opening it again if it's already the target
const htmlFile = findFirstFileWithExtension('html');
if (htmlFile) {
setAsSource(htmlFile); // Assume setAsSource is a function that sets the given file as the source document
return true;
} else {
console.error("No HTML file found to set as source");
return false;
}
}
function findFirstFileWithExtension(extension) {
const fileItems = document.querySelectorAll('.file-item .text-content');
for (const item of fileItems) {
if (getFileExtension(item.textContent.trim()) === extension) {
return item.textContent.trim();
}
}
return null; // Return null if no file with the given extension is found
}
function findFirstFile() {
const fileItem = document.querySelector('.file-item .text-content');
return fileItem ? fileItem.textContent.trim() : null;
}
function openSpecificFile(fileName, source) {
const fileItems = document.querySelectorAll('.file-item .text-content');
for (const item of fileItems) {
if (item.textContent.trim() === fileName) {
item.parentElement.click(); // Trigger click on the parent element (file item)
setAsSource(source);
return true;
}
}
return false; // Return false if the file was not found
}
window.onclick = function (event) {
if (!event.target.matches('.menu-button')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
for (var i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
};
let currentEditorMode = 'javascript';
function setEditorMode() {
if (activeEditor) {
// Find the tab information for the active editor
let tabInfo = null;
['container', 'container2', 'container3', 'container4'].forEach(containerKey => {
for (let id in openTabs[containerKey]) {
if (openTabs[containerKey].hasOwnProperty(id) && openTabs[containerKey][id].editor === activeEditor) {
tabInfo = openTabs[containerKey][id];
break;
}
}
});
if (tabInfo && tabInfo.file.type === 'file') {
const fileExtension = getFileExtension(tabInfo.file.name);
let currentLanguage = syntaxMapping[fileExtension] || 'plaintext';
// if (currentLanguage === 'python') {
// currentLanguage = 'myPython';
// console.log(currentLanguage)
// }
monaco.editor.setModelLanguage(activeEditor.getModel(), currentLanguage);
currentEditorMode = currentLanguage;
} else {
}
} else {
}
}
let contentChangeListener;
function setupAutoRefreshForEditor(file) {
if (activeEditor) {
// Dispose of the previous listener if it exists
if (contentChangeListener) {
contentChangeListener.dispose();
}
// Attach a new listener and store the reference
contentChangeListener = activeEditor.onDidChangeModelContent((event) => {
// Create a new Date object with the current date and time
var currentDate = new Date();
// Convert the date object to an ISO 8601 string
var isoString = currentDate.toISOString();
// Set the lastModified field to the isoString
file.lastModified = isoString;
file.isModified = true;
const fileItem = document.querySelector(`.file-item[data-id="${file.id}"]`);
if (fileItem) {
fileItem.classList.add('modified');
// console.log(file.name)
}
const tabItem = document.querySelector(`.tab[data-id="${file.id}"]`);
if (tabItem) {
tabItem.classList.add('modified');
}
if (isRunningCode && currentSettings.autoRefresh) {
debounceRunCode();
}
});
}
}
let debounceTimer;
function debounceRunCode() {
let debounceAmnt = isServerPreview ? 500 : 500;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(runCode, debounceAmnt); // Adjust the delay as needed
}
function deactivateEditor() {
if (contentChangeListener) {
contentChangeListener.dispose();
}
activeEditor = null;
clearTimeout(debounceTimer);
}
async function stopCode() {
clearConsole();
isRunningCode = false;
if (!myIframes) { return; }
deactivateEditor();
document.querySelector('.run-code-button').classList.remove('running-state');
for (let iframe of myIframes) {
if (iframe) {
iframe.onload = () => {
const width = iframe.offsetWidth;
const height = iframe.offsetHeight;
};
iframe.contentWindow.postMessage({ type: 'clearSessionStorage' }, '*');
iframe.src = "about:blank";
}
}
if (isLivePreview && livePreviewTab) {
livePreviewTab.postMessage({ type: 'clearSessionStorage' }, '*');
}
removeIframeOverlays()
}
let myIframes;
let livePreviewTab = null;
previewSource = 'preview.html';
previewSource = 'https://preview-aijs.web.app'
// previewSource = 'preview-test.html';
// previewSource = 'preview2.html';//AI Imager
let myfiles = [];
let isRunningCode = false;
// const cloudFunctionUrl = 'https://us-central1-aijs-code-editor.cloudfunctions.net/serveWebsiteTEST/';
const cloudFunctionUrl = 'https://aijs-code-editor-user-content.web.app/';
async function runCodeAiImagerRetry(width, height, hash, promptText) {
createNotification(true); // false indicates no close button
updateModalMessage("Retrying with most recent image...");
const userId = firebase.auth().currentUser.uid; // Make sure the user is logged in
const imagePath = `Users/${userId}/StableDiffusion/image.png`; // Image path in Firebase Storage
const storageRef = firebase.storage().ref().child(imagePath);
const firebaseStorageURL = await storageRef.getDownloadURL();
console.log(firebaseStorageURL)
runStableDiffusion(width, height, hash, promptText, firebaseStorageURL)
}
async function runCodeAiImager(width, height, hash, promptText) {
createNotification(true); // false indicates no close button
updateModalMessage("Starting AI image generation process...");
// setTimeout(() => startCloseCountdown(3000), 10);
clearConsole();
updateFileSystemContent();
let AIpreviewSource = 'preview2.html';
const allFiles = flattenFileSystem(fileSystem);
myfiles = JSON.parse(JSON.stringify(allFiles));
myIframes = [document.getElementById("AIPreviewIframe")];
removeIframeOverlays();
for (let iframe of myIframes) {
if (iframe) {
//console.log('Setting iframe source...');
iframe.src = AIpreviewSource;
await new Promise(resolve => {
iframe.onload = () => {
// console.log('Iframe loaded successfully.');
resolve();
};
});
// console.log('Triggering renderDone on preview site...');
}
}
updateModalMessage("Waiting for `aijs.renderDone();`...");
const dataURL = await new Promise((resolve, reject) => {
function handleMessage(event) {
if (event.data.type === 'canvasDataAI') {
window.removeEventListener('message', handleMessage); // Clean up listener
resolve(event.data.dataURL); // Resolve promise with the data URL
}
}
window.addEventListener('message', handleMessage);
});
updateModalMessage("Received Image from code preview...");
// Convert dataURL to a blob
const response = await fetch(dataURL);
const blob = await response.blob();
// Define Firebase storage reference path
const userId = firebase.auth().currentUser.uid; // Make sure the user is logged in
const imagePath = `Users/${userId}/StableDiffusion/image.png`; // Image path in Firebase Storage
updateModalMessage("Storing Image to server...");
const storageRef = firebase.storage().ref().child(imagePath);
// Delete existing image if exists
await storageRef.delete().catch(error => console.log('No existing image to delete, continuing...'));
// Upload new image to Firebase Storage
await storageRef.put(blob);
console.log('Image saved to Firebase Storage.');
updateModalMessage("Image saved to storage...");
// Retrieve the URL of the uploaded image
const firebaseStorageURL = await storageRef.getDownloadURL();
// Now run the stable diffusion function with the new URL
updateModalMessage("Running stableDiffusion...");
runStableDiffusion(width, height, hash, promptText, firebaseStorageURL)
}
async function runStableDiffusion(width, height, hash, promptText, firebaseStorageURL) {
let data = {
prompt: promptText || "Enhance",
negative_prompt: null,
init_image: firebaseStorageURL,
width: width,
height: height,
samples: 1,
temp: false,
safety_checker: false,
strength: 0.7,
seed: hash,
webhook: null,
track_id: null,
};
try {
const callStableDiffusionImage = firebase.functions().httpsCallable('callStableDiffusionImage');
const result = await callStableDiffusionImage(data);
const imageData = result.data; // The result of calling the Firebase function
console.log(imageData);
await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second delay
const outPutImage = document.getElementById('outputImage');
if (imageData && imageData.status === "processing" && imageData.eta && imageData.fetch_result) {
// Image is processing, schedule a new fetch after ETA seconds
updateModalMessage(`Image is processing, please wait approximately ${imageData.eta} seconds.`);
setTimeout(async () => {
runStableDiffusionQueued(imageData.id)
}, (imageData.eta * 1000) + 1000);
} else if (imageData && imageData.output && imageData.output.length > 0) {
const imageUrl = imageData.output[0]; // Accessing the image URL
outPutImage.src = imageUrl;
updateModalMessage("Success!");
setTimeout(() => startCloseCountdown(2000), 10);
} else {
updateModalMessage("No image was generated, please try again.");
setTimeout(() => startCloseCountdown(3000), 10);
outPutImage.src = "";
}
} catch (error) {
console.error('Error:', error);
updateModalMessage("An error occurred, please try again.");
setTimeout(() => startCloseCountdown(3000), 10);
const outPutImage = document.getElementById('outputImage');
outPutImage.src = "";
}
}
async function runStableDiffusionQueued(id) {
let data = {
id: id,
}
try {
const outPutImage = document.getElementById('outputImage');
const callStableDiffusionImageQueued = firebase.functions().httpsCallable('callStableDiffusionImageQueued');
const result = await callStableDiffusionImageQueued(data);
const imageData = result.data;
await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second delay
if (imageData && imageData.output && imageData.output.length > 0) {
const imageUrl = imageData.output[0]; // Accessing the image URL
outPutImage.src = imageUrl;
updateModalMessage("Success!");
setTimeout(() => startCloseCountdown(2000), 10);
} else {
updateModalMessage("No image was generated, please try again.");
setTimeout(() => startCloseCountdown(3000), 10);
outPutImage.src = "";
}
} catch (error) {
console.error('Error:', error);
updateModalMessage("An error occurred, please try again.");
setTimeout(() => startCloseCountdown(3000), 10);
const outPutImage = document.getElementById('outputImage');
outPutImage.src = "";
}
}
function processCanvasDataAndRunStableDiffusion(dataURL, fileName) {
// Convert data URL to Blob and create a file-like object
if (dataURL) {
fetch(dataURL)
.then(res => res.blob())
.then(blob => {
const newFile = new File([blob], fileName, { type: 'image/png' });
const directoryId = 'root'; // Change this to the desired directory ID
const directory = findItemById(fileSystem.myProject, directoryId);
if (directory && directory.type === 'directory') {
readAndAddFile(newFile, directory).then(() => {
updateFileSystemContent();
renderFilesystem();
});
}
});
} else {
createNotification(true); // false indicates no close button
updateModalMessage("To add an image you must be running a sketch with a canvas.");
setTimeout(() => startCloseCountdown(3000), 10);
}
}
async function runCodeServer() {
clearConsole();
isRunningCode = false;
if (myIframes) {
// deactivateEditor();
// for (let iframe of myIframes) {
// if (iframe) {
// iframe.src = "about:blank";
// }
// }
}
// console.log(openedProjectName)
if (openedProjectName != null) {
userUID = currentUser.uid
} else {
userUID = otherUserId
// console.log(otherUserId)
}
if (openedProjectName != null) {
try {
await saveProject(true); // This ensures saveProject() completes before moving on.
} catch (error) {
console.error("Error during project save:", error);
// Handle any errors that occur during saving, possibly return or show a message
return; // Stop further execution in case of error
}
} else {
logToConsoleEditor("You are viewing a server project that hasn't been saved by you. Changes will be visible only after you initially save it.", "info");
}
myIframes = [document.getElementById("resultFrame")];
let projectPath = findFileByName(fileSystem, sourceDoc).path.replace(/^\//, '');
const sourceDocPath = `${userUID}/Projects/${projectPath}`;
const websiteUrl = cloudFunctionUrl + sourceDocPath;
if (isLivePreview) {
// If livePreviewTab already exists, reuse it; otherwise, create a new one
if (!livePreviewTab || livePreviewTab.closed) {
livePreviewTab = window.open(websiteUrl, '_blank');
} else {
livePreviewTab.location.href = websiteUrl;
}
const overlay = document.createElement('div');
overlay.className = 'iframe-overlay';
const message = document.createElement('div');
message.className = 'iframe-overlay-message';
message.textContent = 'Running Live Preview.';
translateElement(message, startingLanguage, newLanguage);
overlay.appendChild(message);
let containers = document.querySelectorAll('.preview-container');
for (let container of containers) {
if (container) {
container.appendChild(overlay);
}
}
} else {
removeIframeOverlays();
// const overlay = document.createElement('div');
// overlay.className = 'iframe-overlay';
// overlay.style.opacity ='0.2'
// const throbber = document.createElement('div');
// throbber.className = 'throbber';
// overlay.appendChild(throbber);
// let containers = document.querySelectorAll('.preview-container');
// for (let container of containers) {
// if (container) {
// container.appendChild(overlay);
// }
// }
for (let iframe of myIframes) {
if (iframe) {
iframe.onload = function () {
removeIframeOverlays();
};
iframe.src = websiteUrl;
}
}
}
isRunningCode = true;
document.querySelector('.run-code-button').classList.add('running-state');
}
function replaceImagePathsInMarkdown(markdownContent, fileSystem) {
// Preprocessing: remove directory paths from the image paths
let preprocessedMarkdownContent = markdownContent.replace(/(!\[[^\]]*\]\()(?:\.\/|\/)?([^\s)]+\.[^\s)]+)/g, '$1$2');
// Regular expression to match markdown image syntax, capturing the entire syntax and isolating the file path
const imagePathRegex = /(!\[[^\]]*\]\((.*?\.[^\s)]+)\))/g;
// Replace function
const replacer = (match, p1, filePath) => {
//console.log("Found path:", filePath);
// Remove any preceding directory structure from filePath
const fileName = filePath.split('/').pop(); // This isolates the file name from any directory path
// Find the file in the file system
const file = findFileByName(fileSystem, fileName);
if (file) {
// console.log("Replacing path with data URL:", file.dataUrl);
// Return the markdown image syntax with the data URL
return `![${file.name}](${file.dataUrl})`;
} else {
//console.log("No file found for path:", fileName);
// If the file is not found, return the original match
return match;
}
};
// Replace image paths in the preprocessed markdown content
const newMarkdownContent = preprocessedMarkdownContent.replace(imagePathRegex, replacer);
//console.log("Final markdown content:", newMarkdownContent);
return newMarkdownContent;
}
async function runCodeMarkdown() {
let cssVariables = getAllCssVariables();
let markdownContent = findFileByName(fileSystem, sourceDoc).content;
markdownContent = replaceImagePathsInMarkdown(markdownContent, fileSystem);
myIframes = [document.getElementById("resultFrame")];
websiteUrl = "markdown-preview.html"
if (isLivePreview) {
const overlay = document.createElement('div');
overlay.className = 'iframe-overlay';
const message = document.createElement('div');
message.className = 'iframe-overlay-message';
message.textContent = 'Live preview not supported for Markdown';
overlay.appendChild(message);
let containers = document.querySelectorAll('.preview-container');
for (let container of containers) {
if (container) {
container.appendChild(overlay);
}
}
} else {
removeIframeOverlays();
const overlay = document.createElement('div');
overlay.className = 'iframe-overlay';
// const message = document.createElement('div');
// message.className = 'iframe-overlay-message';
// message.textContent = 'Fetching Markdown...';
// overlay.appendChild(message);
const throbber = document.createElement('div');
throbber.className = 'throbber';
overlay.appendChild(throbber);
let containers = document.querySelectorAll('.preview-container');
for (let container of containers) {
if (container) {
container.appendChild(overlay);
}
}
for (let iframe of myIframes) {
if (iframe) {
iframe.onload = function () {
iframe.contentWindow.postMessage({ type: 'markdownContent', content: markdownContent, cssVariables: cssVariables }, '*');
removeIframeOverlays();
};
iframe.src = websiteUrl;
}
}
}
isRunningCode = true;
document.querySelector('.run-code-button').classList.add('running-state');
}
function getAllCssVariables() {
const root = document.documentElement;
const cssVars = {};
const style = getComputedStyle(root);
Array.from(style).filter(name => name.startsWith('--')).forEach(name => {
cssVars[name] = style.getPropertyValue(name).trim();
});
return cssVars;
}
async function runCode() {
//await stopCode()
let isMarkdown = sourceDoc.endsWith(".md");
await clearConsole();
if (!isLivePreview) {
generatePreview();
}
updateFileSystemContent();
if (isMarkdown) {
runCodeMarkdown()
return;
}
if (isServerPreview) {
runCodeServer()
return;
}
const allFiles = flattenFileSystem(fileSystem); // Store the result of flattenFileSystem
myfiles = JSON.parse(JSON.stringify(allFiles));
myIframes = [document.getElementById("resultFrame")];
if (isLivePreview) {
// If livePreviewTab already exists, reuse it; otherwise, create a new one
if (!livePreviewTab || livePreviewTab.closed) {
livePreviewTab = window.open(previewSource, '_blank');
livePreviewTab.addEventListener('load', () => {
livePreviewTab.postMessage({
type: 'dataReady',
content: myfiles,
hashValue: null,
sourceDoc: sourceDoc,
}, '*');
});
setTimeout(runCode, 250);
} else {
livePreviewTab.location.href = previewSource;
livePreviewTab.postMessage({
type: 'refresh',
content: myfiles,
hashValue: null,
sourceDoc: sourceDoc,
}, '*');
}
const overlay = document.createElement('div');
overlay.className = 'iframe-overlay';
const message = document.createElement('div');
message.className = 'iframe-overlay-message';
message.textContent = 'Running Live Preview.';
translateElement(message, startingLanguage, newLanguage);
overlay.appendChild(message);
let containers = document.querySelectorAll('.preview-container');
for (let container of containers) {
if (container) {
container.appendChild(overlay);
}
}
// livePreviewTab.focus();
} else {
removeIframeOverlays();
for (let iframe of myIframes) {
if (iframe) {
iframe.src = previewSource;
// iframe.onload = () => {
// console.log("running code in iframe");
// };
}
}
}
isRunningCode = true;
document.querySelector('.run-code-button').classList.add('running-state');
}
function removeIframeOverlays() {
let overlays = document.querySelectorAll('.iframe-overlay');
overlays.forEach(overlay => {
overlay.parentNode.removeChild(overlay);
});
}
function toggleLivePreview() {
isLivePreview = !isLivePreview; // Toggle the state
// Optionally, update the button's appearance based on the state
const liveButton = document.querySelector('.live-button');
const liveButton2 = document.getElementById('toggle-live-button');
if (isLivePreview) {
if (liveButton2) {
liveButton2.classList.add('active');
}
if (liveButton) {
liveButton.classList.add('active'); // Indicate live preview is active
}
} else {
if (liveButton2) {
liveButton2.classList.remove('active');
}
if (liveButton) {
liveButton.classList.remove('active'); // Indicate live preview is inactive
}
}
}
function toggleServer(forceState) { // Optional argument
if (typeof forceState === 'boolean') {
isServerPreview = forceState;
} else {
isServerPreview = !isServerPreview; // Toggle the state
}
// Get the elements
const liveButton2 = document.getElementById('toggle-server-button');
const galleryButton = document.getElementById('gallery-save-button');
const previewButton = document.getElementById('preview-add-button');
const previewSaveButton = document.getElementById('preview-save-button');
// Update the server button's appearance based on the state
if (liveButton2) {
if (isServerPreview) {
liveButton2.classList.add('active');
} else {
liveButton2.classList.remove('active');
}
}
// Disable or enable gallery and preview buttons based on the server state
if (isServerPreview) {
// If server mode is on, disable buttons
galleryButton.disabled = true;
previewButton.disabled = true;
previewSaveButton.disabled = true;
// Change their opacity
galleryButton.style.opacity = "0.5";
previewButton.style.opacity = "0.5";
previewSaveButton.style.opacity = "0.5";
// Change their titles
galleryButton.title = "Disabled in server mode";
previewButton.title = "Disabled in server mode";
previewSaveButton.title = "Disabled in server mode";
} else {
// If server mode is off, enable buttons
galleryButton.disabled = false;
previewButton.disabled = false;
previewSaveButton.disabled = false;
// Reset their opacity
galleryButton.style.opacity = "1";
previewButton.style.opacity = "1";
previewSaveButton.style.opacity = "1";
// Reset their titles
galleryButton.title = "Add to Gallery";
previewButton.title = "Add to project files";
previewSaveButton.title = "Download canvas image";
}
}
function isBinaryFile(fileName) {
const binaryExtensions = [
'ico', 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'wav', 'mp3', 'ogg', 'webp', // existing ones
'mp4', 'webm', 'avi', 'mov', 'flv', // video formats
'pdf', 'docx', 'xlsx', // document formats
'woff', 'woff2', 'ttf', 'otf', 'glb', // font files
'stl'
];
return binaryExtensions.some(ext => fileName.toLowerCase().endsWith('.' + ext));
}
function isSvgFile(fileName) {
return fileName.toLowerCase().endsWith('.svg');
}
function flattenFileSystem(fileSystem) {
function arrayBufferToBase64(buffer) {
const binaryString = new Uint8Array(buffer).reduce((acc, byte) => acc + String.fromCharCode(byte), '');
return window.btoa(binaryString);
}
function processDirectory(directory, parentEntry) {
Object.values(directory.children).forEach(item => {
const filePath = (parentEntry.path === '/' ? '' : parentEntry.path) + '/' + item.name;
if (item.type === 'file') {
if (isBinaryFile(item.name)) {
const base64Data = arrayBufferToBase64(item.content);
const dataURL = `data:${item.mimetype};base64,${base64Data}`;
parentEntry.children.push({
...item,
path: filePath,
storageURL: item.storageURL,
content: item.dataUrl, // Data URL
isUrl: true,
mimetype: item.mimetype
});
} else if (isSvgFile(item.name)) {
// Convert SVG text content to a data URL for rendering
const svgContent = item.content;
const base64SvgContent = window.btoa(unescape(encodeURIComponent(svgContent)));
const svgDataUrl = `data:image/svg+xml;base64,${base64SvgContent}`;
parentEntry.children.push({
...item,
path: filePath,
storageURL: item.storageURL,
content: svgDataUrl, // SVG Data URL
originalContent: svgContent, // Store original text content for editing
isUrl: true,
mimetype: 'image/svg+xml'
});
} else {
parentEntry.children.push({
...item,
path: filePath,
storageURL: item.storageURL,
content: item.content,
mimetype: item.mimetype
});
}
} else if (item.type === 'directory') {
const folder = {
...item,
type: 'folder',
path: filePath,
children: []
};
parentEntry.children.push(folder);
processDirectory(item, folder);
}
});
}
// Find the root directory based on the 'isRoot' property
const rootKey = Object.keys(fileSystem).find(key => fileSystem[key].isRoot);
const rootDirectory = fileSystem[rootKey];
if (rootDirectory) {
const rootFolder = {
id: rootDirectory.id,
type: "folder",
name: rootDirectory.name,
path: rootDirectory.name, // Set the root path to the root directory's name
children: []
};
processDirectory(rootDirectory, rootFolder, rootDirectory.name); // Use the root directory's name as the base path
return [rootFolder];
} else {
console.error("Root directory not found in the file system object.");
return [];
}
}
sourceDoc = 'index.html'
let sourceDocRef = sourceDoc;
let sourceDocPre = null;
function processCanvasDataAndAddToFileSystem(dataURL, fileName) {
// Convert data URL to Blob and create a file-like object
if (dataURL) {
fetch(dataURL)
.then(res => res.blob())
.then(blob => {
const newFile = new File([blob], fileName, { type: 'image/png' });
const directoryId = 'root'; // Change this to the desired directory ID
const directory = findItemById(fileSystem.myProject, directoryId);
if (directory && directory.type === 'directory') {
readAndAddFile(newFile, directory).then(() => {
updateFileSystemContent();
updateFileSystemOrganization()
renderFilesystem();
});
}
});
} else {
createNotification(true); // false indicates no close button
updateModalMessage("To add an image you must be running a sketch with a canvas.");
setTimeout(() => startCloseCountdown(3000), 10);
}
}
function debounceGallery(func, wait) {
let timeout;
// This returns a debounced function that will delay invoking 'func'
// until after 'wait' milliseconds have elapsed since the last time it was invoked.
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Your existing function
function processGalleryDataAndAddToGallery(dataURL, imageName) {
if (dataURL) {
// Call 'addImageToGallery' to add the image to the gallery
addImageToGallery(dataURL);
} else {
createNotification(true); // Assuming true indicates showing a close button, based on the context
updateModalMessage("To add an image you must be running a sketch with a canvas.");
setTimeout(() => startCloseCountdown(3000), 10);
}
}
// Wrap your function with debounce to ensure it only runs once per second (1000 ms)
const debouncedProcessGalleryDataAndAddToGallery = debounceGallery(processGalleryDataAndAddToGallery, 150);
window.addEventListener('message', function (event) {
if (event.data.type === 'galleryData') {
const dataURL = event.data.dataURL;
debouncedProcessGalleryDataAndAddToGallery(dataURL, "cover.png");
}
if (event.data.type === 'canvasData') {
const dataURL = event.data.dataURL;
processCanvasDataAndAddToFileSystem(dataURL, "cover.png");
}
// if (event.data.type === 'canvasDataAI') {
// const dataURL = event.data.dataURL;
// console.log('Processing AI canvas data and running Stable Diffusion...');
// //processCanvasDataAndRunStableDiffusion(dataURL, "cover.png"); // Ensure this function is defined
// }
if (event.data.type === 'sendFIlesAiPreview') {
if (!myIframes) {
return
}
for (let iframe of myIframes) {
if (iframe) {
const width = iframe.offsetWidth;
const height = iframe.offsetHeight;
iframe.contentWindow.postMessage({
type: 'dataReady',
content: myfiles,
hashValue: null,
sourceDoc: sourceDoc,
}, '*');
}
}
}
if (!isRunningCode) {
return;
}
if (event.data.type === 'changeSourceLive') {
sourceDocRef = event.data.href;
// updatePreviewInput(sourceDocRef)
// sourceDoc = event.data.href;
if (livePreviewTab && !livePreviewTab.closed) {
// updatePreviewInput(event.data.href)
livePreviewTab.postMessage({
type: 'dataReady',
content: myfiles,
hashValue: null,
sourceDoc: event.data.href,
}, '*');
}
}
if (event.data.type === 'livePreviewReloaded' && event.data.source === 'livePreviewTab') {
if (livePreviewTab && !livePreviewTab.closed) {
livePreviewTab.postMessage({
type: 'dataReady',
content: myfiles,
hashValue: null,
sourceDoc: sourceDoc,
}, '*');
}
}
if (event.data.type === 'sendivePreviewFiles') {
livePreviewTab.postMessage({
type: 'dataReady',
content: myfiles,
hashValue: null,
sourceDoc: sourceDoc,
}, '*');
}
if (event.data.type === 'sendFIles') {
if (!myIframes) {
return
}
for (let iframe of myIframes) {
if (iframe) {
const width = iframe.offsetWidth;
const height = iframe.offsetHeight;
iframe.contentWindow.postMessage({
type: 'dataReady',
content: myfiles,
hashValue: null,
sourceDoc: sourceDoc,
}, '*');
// iframe.contentWindow.postMessage({
// type: 'resize',
// width,
// height
// }, '*');
}
}
}
if (event.data.type === 'changeSource') {
// sourceDoc = event.data.href;
sourceDocRef = event.data.href;
updatePreviewInput(sourceDocRef)
for (let iframe of myIframes) {
if (iframe) {
const width = iframe.offsetWidth;
const height = iframe.offsetHeight;
iframe.contentWindow.postMessage({
type: 'dataReady',
content: myfiles,
hashValue: null,
sourceDoc: event.data.href,
}, '*');
}
}
}
if (event.data.type === 're-run') {
sourceDocPre = sourceDoc;
sourceDoc = sourceDocRef
runCode();
// updatePreviewInput(sourceDoc)
// $("#runbtn").click()
// setTimeout(() => {
// sourceDoc = sourceDocPre;
// }, 300);
}
}, false);
window.addEventListener('message', function (event) {
// console.log("Received message:", event.data); // Log the received message for debugging
switch (event.data.type) {
case 'log':
if (editorMode != 'fullscreen') {
//generateConsole();
logToConsoleEditor(event.data.content.join(' '), 'log');
}
break;
case 'info':
if (editorMode != 'fullscreen') {
// generateConsole();
logToConsoleEditor(event.data.content.join(' '), 'info');
}
break;
case 'warn':
if (editorMode != 'fullscreen') {
// generateConsole();
logToConsoleEditor(event.data.content.join(' '), 'warn');
}
break;
case 'error':
if (editorMode != 'fullscreen') {
// generateConsole();
// For errors, event.data.content might be an array with a single error object
if (typeof event.data.content === 'string') {
logToConsoleEditor(event.data.content, 'error');
} else {
// If the error content is an object, handle it appropriately
// For example, you might want to stringify it or extract certain properties
logToConsoleEditor(JSON.stringify(event.data.content), 'error');
}
}
break;
}
if (event.data.type === 'consoleLog' || event.data.type === 'consoleLogObject') {
if (editorMode != 'fullscreen') {
// generateConsole()
logToConsoleEditor(event.data.content, event.data.method);
}
}
}, false);
let logCounts = {};
let loggedMessages = new Set();
function logToConsoleEditor(logMessage, type) {
let consoleEditor = document.querySelector(".console");
if (!consoleEditor) {
generateConsole();
consoleEditor = document.querySelector(".console");
}
if (loggedMessages.has(logMessage)) {
return;
} else {
loggedMessages.add(logMessage);
}
// List of ignored messages
const ignoredMessages = [
"You are using the in-browser Babel transformer",
"Be sure to precompile your scripts for production"
];
// Check if logMessage contains any ignored message
let logMessageContent = typeof logMessage === 'object' && logMessage !== null ? logMessage.message : logMessage;
logMessageContent = typeof logMessageContent === 'string' ? logMessageContent : String(logMessageContent);
const shouldIgnore = ignoredMessages.some(ignoredMsg => logMessageContent.includes(ignoredMsg));
if (shouldIgnore) {
return; // Skip logging this message
}
const logContainer = document.createElement("div");
logContainer.classList.add("log-container");
// Check for module-related errors
const moduleErrors = [
"SyntaxError: import declarations may only appear at top level of a module",
"SyntaxError: export declarations may only appear at top level of a module"
];
const moduleErrorDetected = moduleErrors.some(errorMsg => logMessageContent.includes(errorMsg));
if (moduleErrorDetected) {
let warningMessage = "To utilize modules, please activate Server Mode by clicking the globe icon in the preview tab's search bar.";
logToConsoleEditor(warningMessage, "warn");
}
const gptButton = document.createElement("button");
gptButton.classList.add("gpt-button");
gptButton.setAttribute('data-type', type);
gptButton.textContent = "ask AI";
gptButton.addEventListener("click", function () {
generateAI();
displayUserMessage(logMessage);
callOpenAI(logMessage);
});
switch (type) {
case "object":
logMessage = JSON.parse(logMessage);
if (isEmptyObject(logMessage)) return;
const objectWrapper = document.createElement("div");
objectWrapper.classList.add("object-wrapper");
if (Array.isArray(logMessage) && logMessage.every(item => typeof item === "object")) {
objectWrapper.appendChild(tableToDiv(logMessage));
} else {
objectWrapper.appendChild(objectToNestedDiv(logMessage));
}
logContainer.appendChild(objectWrapper);
break;
default:
const message = document.createElement("pre");
message.textContent = logMessage;
message.classList.add(type, "log-message");
message.innerHTML = formatLinks(String(logMessage));
logContainer.appendChild(message);
break;
}
logContainer.appendChild(gptButton);
consoleEditor.appendChild(logContainer);
}
function formatLinks(message) {
const urlRegex = /https?:\/\/[^\s]+/g;
return message.replace(urlRegex, function (url) {
return `${url}`;
});
}
function isEmptyObject(obj) {
return Object.keys(obj).length === 0 && obj.constructor === Object;
}
function objectToNestedDiv(object) {
const div = document.createElement("div");
if (isEmptyObject(object)) return div;
div.classList.add("object");
for (const key in object) {
const keyDiv = document.createElement("div");
keyDiv.classList.add("object-key");
const text = document.createTextNode(`${key}: `);
if (typeof object[key] === "object" && object[key] !== null) {
keyDiv.addEventListener("click", function () {
const sibling = keyDiv.nextElementSibling;
if (sibling) {
sibling.style.display = sibling.style.display === "none" ? "block" : "none";
}
keyDiv.classList.toggle("expanded");
});
} else {
keyDiv.classList.add("no-expand");
}
keyDiv.appendChild(text);
div.appendChild(keyDiv);
if (typeof object[key] === "object" && object[key] !== null) {
const childObjDiv = objectToNestedDiv(object[key]);
childObjDiv.style.display = "none";
div.appendChild(childObjDiv);
} else {
const valueText = document.createTextNode(object[key]);
keyDiv.appendChild(valueText);
}
}
return div;
}
function tableToText(tableData) {
let tableText = '';
if (tableData.length === 0) return tableText;
const headers = Object.keys(tableData[0]);
tableText += headers.join('\t') + '\n';
tableText += '-'.repeat(tableText.length - 1) + '\n';
for (const row of tableData) {
const values = headers.map(header => row[header] !== undefined ? row[header] : '');
tableText += values.join('\t') + '\n';
}
return tableText;
}
function tableToDiv(tableData) {
const tableDiv = document.createElement("div");
tableDiv.classList.add("table");
const tableHeader = document.createElement("div");
tableHeader.classList.add("table-header", "expanded");
tableHeader.textContent = "Table";
tableHeader.addEventListener("click", function () {
const tableBody = tableDiv.querySelector(".table-body");
if (tableBody) {
tableBody.style.display = tableBody.style.display === "none" ? "block" : "none";
}
tableHeader.classList.toggle("expanded");
});
const tableElement = document.createElement("table");
const thead = document.createElement("thead");
const tbody = document.createElement("tbody");
const headers = Object.keys(tableData[0]);
const headerRow = document.createElement("tr");
headers.forEach((header) => {
const th = document.createElement("th");
th.textContent = header;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
tableData.forEach((row) => {
const tr = document.createElement("tr");
headers.forEach((header) => {
const td = document.createElement("td");
td.textContent = row[header] !== undefined ? row[header] : "";
tr.appendChild(td);
});
tbody.appendChild(tr);
});
tableElement.appendChild(thead);
tableElement.appendChild(tbody);
const tableBody = document.createElement("div");
tableBody.classList.add("table-body");
tableBody.style.display = "block";
tableBody.appendChild(tableElement);
tableDiv.appendChild(tableHeader);
tableDiv.appendChild(tableBody);
return tableDiv;
}
async function clearConsole() {
loggedMessages.clear();
const consoleEditor = document.querySelector(".console");
if (consoleEditor) {
while (consoleEditor.firstChild) {
consoleEditor.firstChild.remove();
}
}
messageCount = {};
messageElements = {};
}
function closeLoadingScreen() {
var backdrop = document.querySelector('.backdrop');
backdrop.style.transition = 'all 1s ease-in-out';
backdrop.style.transform = 'translateY(250vh)'; // Move off-screen downwards
backdrop.style.borderRadius = '80%'
setTimeout(function () {
// backdrop.parentNode.removeChild(backdrop); // Remove from the DOM
}, 1000); // Matches the duration of the transition
}
async function newProjectFromOpenProcessing(codeDataArray, OpenProcessingID) {
OpeningNewProject = true;
try {
const response = await fetch(`https://openprocessing.org/api/sketch/${OpenProcessingID}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const codeData = await response.json(); // Parse JSON data from the response
const projectName = sanitizeFilename(codeData.title);
clearFileSystem(projectName);
} catch (error) {
console.error('Error fetching OpenProcessing sketch code:', error);
}
//clearFileSystem("myProject");
closeAllTabsInContainer('container');
closeAllTabsInContainer('container2');
closeAllTabsInContainer('container3');
closeAllTabsInContainer('container4');
updateFileSystemContent();
updateFileSystemOrganization()
renderFilesystem();
let scriptFiles = [];
let cssFiles = [];
// Fetch CDN libraries from OpenProcessing
let libraryScripts = '';
let urls;
let isP5Play = false;
try {
const response = await fetch(`https://openprocessing.org/api/sketch/${OpenProcessingID}/libraries?limit=10&offset=0`);
if (!response.ok) throw new Error('Failed to fetch libraries');
const libraries = await response.json();
console.log(libraries);
libraryScripts = libraries.map(function (library) {
// Split the library URL in case it contains multiple URLs separated by semicolons
urls = library.url.split(';');
// Map each individual URL to its own