diff --git a/README.md b/README.md index 5ba9343..6d3e31a 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,11 @@ Similar to tango.us/dubble.so/folge.me/magichow.co but all local - no data leave - [x] create basic popup UI ### Additional nice to have: +- [x] Meta data in guide (title, description, author, date) - [ ] Viewer for Guide JSON - [ ] PDF export - [ ] Editor for Guide -- [ ] improve error handling +- [x] improve error handling - [ ] Blur sensitive information - [x] Pause/resume recording feature diff --git a/chrome/background.js b/chrome/background.js index 5f06aa1..348f96b 100644 --- a/chrome/background.js +++ b/chrome/background.js @@ -1,20 +1,33 @@ // Background Worker +const dateToday = new Date().toISOString().split('T')[0]; + +let guide = { + "title":"Guide", + "description":"", + "date":dateToday, + "author":"Me", +}; let steps = []; chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { switch(message.action){ case "captureScreenshot": - chrome.tabs.captureVisibleTab(null, { format: "jpeg" }, (dataUrl) => { - if (chrome.runtime.lastError) { - console.error("Error capturing screenshot: ", chrome.runtime.lastError); - sendResponse({ success: false, error: chrome.runtime.lastError }); - return; - } - - sendResponse({ success: true, screenshot: dataUrl }); - }); - return true; + try{ + chrome.tabs.captureVisibleTab(null, { format: "jpeg" }, (dataUrl) => { + if (chrome.runtime.lastError) { + console.error("Error capturing screenshot: ", chrome.runtime.lastError); + sendResponse({ success: false, error: chrome.runtime.lastError }); + return; + } + + sendResponse({ success: true, screenshot: dataUrl }); + }); + return true; + } + catch(error){ + return sendResponse({ success: false, message: error }); + } case "saveStep": try{ const step = { @@ -31,14 +44,35 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { return sendResponse({ success: false, message: error }); } case "generateGuide": - return sendResponse({ success: true, guide: steps }); + try{ + let filename = "guide.json"; + chrome.storage.local.get(["guideTitle", "exportFilename"], (data) => { + if(data.guideTitle) guide.title = `Guide: ${data.guideTitle}`; + if(data.exportFilename) filename = `guide-${data.exportFilename}.json`; + guide.steps = steps; + return sendResponse({ success: true, guide: guide, filename: filename }); + }); + return true; + } + catch(error){ + return sendResponse({ success: false, message: error }); + } case "clearGuide": - steps = []; - return sendResponse({ success: true, message: "Cleared guide successfully" }); + try{ + steps = []; + return sendResponse({ success: true, message: "Cleared guide successfully" }); + } + catch(error){ + return sendResponse({ success: false, message: error }); + } case "guideLenght": - count = steps.length; - return sendResponse({ success: true, stepCount: count }); - case "debug": + try{ + count = steps.length; + return sendResponse({ success: true, stepCount: count }); + } + catch(error){ + return sendResponse({ success: false, message: error }); + } default: console.log(message.action); break; diff --git a/chrome/content.js b/chrome/content.js index a133de3..5660e19 100644 --- a/chrome/content.js +++ b/chrome/content.js @@ -1,5 +1,22 @@ // Script which is executed on all web pages +// Page meta data +const pageTitle = document.title; +const domainFormatted = window.location.host.replace(/\./g, "-"); + +// set guide title +chrome.storage.local.get("guideTitle", (data) => { + if(!data.guideTitle){ + try{ + chrome.storage.local.set({ guideTitle: pageTitle }); + chrome.storage.local.set({ exportFilename: domainFormatted }); + } + catch(error){ + console.error(`Error while saving guide metadata: ${error}`) + } + } +}); + // load recording status & react to updates let isRecording = false; chrome.storage.local.get("recording", (data) => { diff --git a/chrome/manifest.json b/chrome/manifest.json index 85d2070..3a20e7e 100644 --- a/chrome/manifest.json +++ b/chrome/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Cl1ck Gu1de", - "version": "0.0.3", + "version": "0.0.4", "description": "A browser add-on which records the steps clicked on a page and generates a easy to understand guide from it.", "permissions": ["scripting", "activeTab", "downloads", "tabs", "storage"], "host_permissions": [""], diff --git a/chrome/popup.js b/chrome/popup.js index 4c3c279..d1666c4 100644 --- a/chrome/popup.js +++ b/chrome/popup.js @@ -4,7 +4,6 @@ let isRecording = false; chrome.storage.local.get("recording", (data) => { isRecording = data.recording || false; - console.log(data); updateRecordingLabel(isRecording); }); @@ -13,10 +12,14 @@ const toggleRecordingBtn = document.getElementById("toggleRecording"); const exportBtn = document.getElementById("exportBtn"); toggleRecordingBtn.addEventListener('click', () => { - //if(isRecording) chrome.runtime.sendMessage({action: "clearGuide"}); - isRecording = !isRecording; // toggle status - updateRecordingLabel(isRecording); // update label - chrome.storage.local.set({ recording: isRecording }); // save status + try{ + isRecording = !isRecording; // toggle status + updateRecordingLabel(isRecording); // update label + chrome.storage.local.set({ recording: isRecording }); // save status + } + catch(error){ + console.error(`Got error on toggling recording: ${error}`) + } }); exportBtn.addEventListener('click', () => { @@ -24,8 +27,14 @@ exportBtn.addEventListener('click', () => { chrome.runtime.sendMessage({ action: "generateGuide" }, (response) => { - const guide = response.guide; - exportJSON(guide, "guide.json"); + if(response.success){ + const guide = response.guide; + const filename = response.filename; + return exportJSON(guide, filename); + } + else{ + console.error(`Failed to export guide: ${response.message} `); + } }); }); @@ -48,18 +57,29 @@ function updateRecordingLabel(bool){ // export guide as json function exportJSON(object, filename){ - const json = JSON.stringify(object, null, 2); + try{ + const json = JSON.stringify(object, null, 2); - const blob = new Blob([json], { type: "application/json" }); - const url = URL.createObjectURL(blob); + const blob = new Blob([json], { type: "application/json" }); + const url = URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = url; + link.download = filename; + link.click(); + + URL.revokeObjectURL(url); + } + catch(error){ + return error + } - const link = document.createElement("a"); - link.href = url; - link.download = filename; - link.click(); - URL.revokeObjectURL(url); - - // clear guide - chrome.runtime.sendMessage({action: "clearGuide"}); + try{ + // clear guide + return chrome.runtime.sendMessage({action: "clearGuide"}); + } + catch(error){ + return error + } } \ No newline at end of file