-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* code-editor init * Refactor assembleCode and parseAndDisplayData functions - Separate agregore's basic CSS and user's CSS in assembleCode - Update CSS extraction logic to exclude the display of agregore theme CSS in the code-editor * make spinner round * Refactor fetchButton event listener into fetchFromDWeb function - Refactor button event listener to its own fetchFromDWeb function - Add protocol detection so that users paste their CID with the protocol prefix - Removes the dependency on a separate protocol selector. - Improves the code's modularity and user input handling by directly - Detects whether the URL starts with 'ipfs://' or 'hyper://', and alerts the user for invalid protocols. * Enhance URL Display with Inline Copy-to-Clipboard Feature - Refactoring of the addURL function to improve UX - Adds a 20x20 copy to clipboard icon that loads `var(--ag-theme-primary)` as fill color - Implements a copy-to-clipboard functionality for quick sharing of the code's URL - The Function temporarily replaces the SVG with a "Copied!" message upon successful copy, reverting back to the original SVG after 3 seconds. These enhancements contribute to a more interactive and user-friendly interface. Streamlines the process of copying URLs directly from the displayed list. * Refactor HTML for dweb fetch and upload containers * Switch to secondary colour for bigger visual impact * Improve alignment for dweb container's children elements * Add media query for mobile devices * Split script script.js into modules; dweb, code editor and common functions * Load new js files in index * Update code editor layout * Update protocol labels and button text * Add responsive layout for mobile devices on uploadand fetch buttons * Update code editor layout by moving list at the bottom * Refactor code editor styles and improve responsiveness * Remove script.js * Update script imports in index.html * Remove console.log statements in parseAndDisplayData function * Refactor code editor event listeners and remove unused code * Add Tutorial (v1) for code editor app * Update code editor styles, replace bottom margin with gap * Improve comments for clarity * Code Editor Retrospective v0.0.1 * Fix typo, explain jsfiddle and codepen * Consolidate achievements and reduce repetition * Rename code editor tutorial, move to docs/tutorials/ * Remove DOMContentLoaded listener * Transition to using the $ selector * Streamline event listener attachment in codeEditoor.js * Simplify fetchFromDWeb function to streamline URL validation * Replace SVG loading spinner with CSS-animated emoji for simplicity - Removed SVG path from HTML - Introduced emoji-based loader with CSS rotation animation - Adjusted styles for new loading animation, ensuring consistency with design * Rename code editor tutorial files and update links * Add P2Pad Code Editor links to documentation and explore pages * Fix display on spinner * Simplify clipboard copy icon implementation * Move Copy Icon Style to CSS * Update tutorial following review
- Loading branch information
1 parent
4ad4bfd
commit 60a6882
Showing
9 changed files
with
1,254 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# P2Pad Retrospective: Building a Real-Time Browser-Based Code Editor with Agregore 🌐 | ||
|
||
## Reflecting on the Journey 🎉 | ||
|
||
### Original Objective and Outcome 🎯 | ||
The goal was to create an introductory tutorial for building a real-time, browser-based code editor similar to platforms like jsfiddle or codepen, using Agregore. Focusing on simplicity and beginner-friendliness, the tutorial aimed to cover HTML, CSS, and JavaScript essentials, along with an introduction to decentralized web protocols. The end result is a functional code editor that facilitates interaction with IPFS and Hypercore, aligning well with these objectives. | ||
|
||
### Achievements and Evolution 📈 | ||
- Successfully developed P2Pad, a user-friendly, real-time code editor capable of interacting with decentralized web technologies. | ||
- Struck a delicate balance between simplicity for beginners and a meaningful introduction to the practical applications of decentralized web protocols. | ||
|
||
### Challenges and Insights 💡 | ||
- Balancing simplicity with the complexity of a real-time in-browser code editor that interfaces with the decentralized web. | ||
- Keeping it simple without oversimplifying was crucial for the tutorial's effectiveness. | ||
- Insight: Keeping tutorials modular and reusing code enhances learning and development efficiency. | ||
|
||
## The Development Experience 🛠️ | ||
|
||
### Crafting the Application 🏗️ | ||
- Constructed using a blend of HTML, CSS, and JavaScript, with detailed steps provided in the tutorial. | ||
- Key aspects of the development process: | ||
- **What Went Well:** The integration of basic web technologies and the exploration of the potential of decentralized web protocols. | ||
- **Areas of Complexity:** Ensuring that the code was both simple enough for beginners while offering the desired DWeb interactions. | ||
- **Discovery:** The power of modularity and reusability in coding tutorials, especially when dealing with multiple features. | ||
|
||
### Advice for Future Endeavors 🗣️ | ||
- Emphasize the importance of modularity and reusability in tutorial design as I found myself reusing some code from my previous Drag and Drop tutorial. | ||
- Consider developing a dedicated library for DWeb interactions to streamline future tutorial creation. | ||
|
||
## Future Directions and Discussions 🚀 | ||
|
||
### Potential Improvements and Extensions 🔧 | ||
- **Simplification:** Identify areas where the tutorial or the app itself could be simplified further, making it even more accessible to beginners. | ||
- **Modularity:** Enhance the separation of concerns and organize code more optimally, possibly through a dedicated library for DWeb interactions. | ||
- **Documentation:** Improve and expand documentation for reusable code snippets, aiding future users in locating and applying these functions effectively. | ||
|
||
### Suggestions for Agregore Styling Guidelines 📝 | ||
- **Agregore Style Guide:** A formal style guide that taps into and extends the Agregore theme would be highly beneficial. This could standardize the look and feel of applications developed using Agregore, leading to a more cohesive user experience. | ||
- **Documentation Emphasis:** The value of comprehensive and accessible documentation cannot be overstated, especially for beginners exploring new technologies like Agregore and the decentralized web. | ||
|
||
## Conclusion 🌟 | ||
This project was a great opportunity for a continued exploration into the realm of web development and decentralized web technologies. It underscored the importance of keeping tutorials simple yet informative, modular, and reusable. The journey highlighted the need for clear documentation and style guidelines in emerging software like Agregore, paving the way for future innovations and learning. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { $, loadingSpinner, backdrop, iframe } from './common.js'; // Import common functions | ||
|
||
// Attach event listeners directly using the $ selector function | ||
[$('#htmlCode'), $('#javascriptCode'), $('#cssCode')].forEach(element => { | ||
element.addEventListener('input', () => update()); | ||
}); | ||
|
||
// Import CSS from Agregore theme to use in the iframe preview | ||
export let basicCSS = ` | ||
@import url("agregore://theme/vars.css"); | ||
body, * { | ||
font-size: 1.2rem; | ||
margin: 0; | ||
padding: 0; | ||
font-family: var(--ag-theme-font-family); | ||
background: var(--ag-theme-background); | ||
color: var(--ag-theme-text); | ||
} | ||
`; | ||
|
||
//Function for live Rendering | ||
export function update() { | ||
let htmlCode = $('#htmlCode').value; | ||
console.log('HTML Code:', htmlCode); | ||
let cssCode = $('#cssCode').value; | ||
console.log('CSS Code:', cssCode); | ||
let javascriptCode = $('#javascriptCode').value; | ||
console.log('JavaScript Code:', javascriptCode); | ||
// Assemble all elements and Include the basic CSS from Agregore theme | ||
let iframeContent = ` | ||
<style>${basicCSS}</style> | ||
<style>${cssCode}</style> | ||
<script>${javascriptCode}</script> | ||
${htmlCode} | ||
`; | ||
|
||
let iframeDoc = iframe.contentWindow.document; | ||
iframeDoc.open(); | ||
iframeDoc.write(iframeContent); | ||
iframeDoc.close(); | ||
} | ||
|
||
|
||
// Show or hide the loading spinner | ||
export function showSpinner(show) { | ||
backdrop.style.display = show ? 'block' : 'none'; | ||
loadingSpinner.style.display = show ? 'block' : 'none'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Common module for exports | ||
export function $(query) { | ||
return document.querySelector(query); | ||
} | ||
|
||
export const uploadButton = $('#uploadButton'); | ||
export const protocolSelect = $('#protocolSelect'); | ||
export const loadingSpinner = $('#loadingSpinner'); | ||
export const backdrop = $('#backdrop'); | ||
export const iframe = $('#viewer'); | ||
export const fetchButton = $('#fetchButton'); | ||
export const fetchCidInput = $('#fetchCidInput'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import { update, showSpinner, basicCSS } from './codeEditor.js'; | ||
import { $, uploadButton, protocolSelect, fetchButton, fetchCidInput } from './common.js'; | ||
|
||
// assemble code before uploading | ||
export async function assembleCode() { | ||
// Display loading spinner | ||
showSpinner(true); | ||
|
||
// Combine your code into a single HTML file | ||
let combinedCode = ` | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<style>${basicCSS}</style> | ||
<style>${document.getElementById("cssCode").value}</style> | ||
</head> | ||
<body> | ||
${document.getElementById("htmlCode").value} | ||
<script>${document.getElementById("javascriptCode").value}</script> | ||
</body> | ||
</html>`; | ||
|
||
// Convert the combined code into a Blob | ||
const blob = new Blob([combinedCode], { type: 'text/html' }); | ||
const file = new File([blob], "index.html", { type: 'text/html' }); | ||
|
||
// Upload the file | ||
await uploadFile(file); | ||
showSpinner(false); | ||
} | ||
|
||
uploadButton.addEventListener('click', assembleCode); | ||
|
||
// Upload code to Dweb | ||
async function uploadFile(file) { | ||
const protocol = protocolSelect.value; | ||
|
||
const formData = new FormData(); | ||
|
||
// Append file to the FormData | ||
formData.append('file', file, file.name); | ||
|
||
|
||
// Construct the URL based on the protocol | ||
let url; | ||
if (protocol === 'hyper') { | ||
const hyperdriveUrl = await generateHyperdriveKey('drag-and-drop'); | ||
url = `${hyperdriveUrl}`; | ||
} else { | ||
url = `ipfs://bafyaabakaieac/`; | ||
} | ||
|
||
// Perform the upload for each file | ||
try { | ||
const response = await fetch(url, { | ||
method: 'PUT', | ||
body: formData, | ||
}); | ||
|
||
if (!response.ok) { | ||
addError(file, await response.text()); | ||
} | ||
const urlResponse = protocol === 'hyper' ? response.url : response.headers.get('Location'); | ||
addURL(urlResponse); | ||
} catch (error) { | ||
console.error(`Error uploading ${file}:`, error); | ||
} finally { | ||
showSpinner(false); | ||
} | ||
} | ||
|
||
|
||
|
||
async function generateHyperdriveKey(name) { | ||
try { | ||
const response = await fetch(`hyper://localhost/?key=${name}`, { method: 'POST' }); | ||
if (!response.ok) { | ||
throw new Error(`Failed to generate Hyperdrive key: ${response.statusText}`); | ||
} | ||
return await response.text(); // This returns the hyper:// URL | ||
} catch (error) { | ||
console.error('Error generating Hyperdrive key:', error); | ||
throw error; | ||
} | ||
} | ||
|
||
|
||
function addURL(url) { | ||
const listItem = document.createElement('li'); | ||
const link = document.createElement('a'); | ||
link.href = url; | ||
link.textContent = url; | ||
|
||
const copyContainer = document.createElement('span'); | ||
const copyIcon = '⊕' | ||
copyContainer.innerHTML = copyIcon; | ||
copyContainer.onclick = function() { | ||
navigator.clipboard.writeText(url).then(() => { | ||
copyContainer.textContent = ' Copied!'; | ||
setTimeout(() => { | ||
copyContainer.innerHTML = copyIcon; | ||
}, 3000); | ||
}).catch(err => { | ||
console.error('Error in copying text: ', err); | ||
}); | ||
}; | ||
|
||
listItem.appendChild(link); | ||
listItem.appendChild(copyContainer); | ||
uploadListBox.appendChild(listItem); | ||
} | ||
|
||
|
||
function addError(name, text) { | ||
uploadListBox.innerHTML += `<li class="log">Error in ${name}: ${text}</li>` | ||
} | ||
|
||
// The fetchFromDWeb function detects which protocol is used and fetches the content | ||
async function fetchFromDWeb(url) { | ||
if (!url) { | ||
alert("Please enter a CID or Name."); | ||
return; | ||
} | ||
|
||
if (!url.startsWith('ipfs://') && !url.startsWith('hyper://')) { | ||
alert("Invalid protocol. URL must start with ipfs:// or hyper://"); | ||
return; | ||
} | ||
|
||
try { | ||
const response = await fetch(url); | ||
const data = await response.text(); | ||
parseAndDisplayData(data); | ||
} catch (error) { | ||
console.error("Error fetching from DWeb:", error); | ||
alert("Failed to fetch from DWeb."); | ||
} | ||
} | ||
|
||
// Modified event listener for fetchButton | ||
fetchButton.addEventListener('click', () => { | ||
const cidOrName = fetchCidInput.value; | ||
fetchFromDWeb(cidOrName); | ||
}); | ||
|
||
// Parse the data and display it in the code editor | ||
function parseAndDisplayData(data) { | ||
const parser = new DOMParser(); | ||
const doc = parser.parseFromString(data, 'text/html'); | ||
|
||
// Extracting CSS | ||
const styleElements = Array.from(doc.querySelectorAll('style')); | ||
|
||
// Remove the first element (agregore theme CSS) | ||
styleElements.shift(); | ||
|
||
// Now combine the CSS from the remaining <style> elements | ||
let cssContent = styleElements.map(style => style.innerHTML).join(''); | ||
|
||
// Extracting JavaScript | ||
const jsContent = doc.querySelector('script') ? doc.querySelector('script').innerHTML : ''; | ||
|
||
// Remove script and style tags from the HTML content | ||
doc.querySelectorAll('script, style').forEach(el => el.remove()); | ||
const htmlContent = doc.body.innerHTML; // Get the content inside the body tag without script/style tags | ||
|
||
// Displaying the content in respective textareas | ||
$('#htmlCode').value = htmlContent; | ||
$('#cssCode').value = cssContent; | ||
$('#javascriptCode').value = jsContent; | ||
update(0); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<meta lang="en"> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>P2Pad: Real-Time Editor</title> | ||
<link rel="stylesheet" type="text/css" href="styles.css"> | ||
|
||
<main> | ||
<div id="backdrop"></div> | ||
<div id="loadingSpinner" style="display: none;"> | ||
<div class="emoji-loader"> ↻ </div> | ||
</div> | ||
<div class="grid-container"> | ||
|
||
<!-- Text area for Html Code --> | ||
<textarea id="htmlCode" placeholder="Type HTML code here" spellcheck="false"></textarea> | ||
|
||
<!-- Text area for Javascript Code --> | ||
<textarea id="javascriptCode" spellcheck="false" placeholder="Type JavaScript code here"></textarea> | ||
|
||
<!-- Text area for Css Code --> | ||
<textarea id="cssCode" placeholder="Type CSS code here" spellcheck="false"></textarea> | ||
|
||
<!-- Iframe for Code Output --> | ||
<iframe id="viewer"></iframe> | ||
|
||
</div> | ||
<div id="dweb-container"> | ||
<div> | ||
<label for="protocolSelect"> | ||
Protocol: | ||
<select id="protocolSelect"> | ||
<option value="ipfs" selected>Inter-Planetary File System (IPFS://)</option> | ||
<option value="hyper">Hypercore-Protocol (HYPER://)</option> | ||
</select> | ||
</label> | ||
<button id="uploadButton">Upload to DWeb</button> | ||
|
||
</div> | ||
<div id="fetchContainer"> | ||
<input id="fetchCidInput" type="text" placeholder="Enter IPFS CID or Hyperdrive URL "> | ||
<button id="fetchButton">Fetch from DWeb</button> | ||
</div> | ||
|
||
</div> | ||
<ul id="uploadListBox"></ul> | ||
</main> | ||
|
||
<script type="module" src="common.js"></script> | ||
<script type="module" src="dweb.js"></script> | ||
<script type="module" src="codeEditor.js"></script> | ||
|
Oops, something went wrong.