Loading...
Files
Tools
Forgot Password?
OR
OR

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