From cd2de15b2b6a1d28f0558d243c590025df32468c Mon Sep 17 00:00:00 2001 From: michivonah Date: Thu, 7 Nov 2024 21:31:33 +0100 Subject: [PATCH] adding start/stop/pause/resume functionality --- README.md | 5 ++- chrome/background.js | 8 +++- chrome/content.js | 100 ++++++++++++++++++++++++------------------- chrome/manifest.json | 4 +- chrome/popup.js | 36 +++++++++++++++- 5 files changed, 104 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 2341710..5ba9343 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,16 @@ Similar to tango.us/dubble.so/folge.me/magichow.co but all local - no data leave - [x] create a screenshot of the steps - [x] ability to save multiple steps - [x] export guide as json file -- [ ] add recording start/stop functionality +- [x] add recording start/stop functionality - [x] create basic popup UI ### Additional nice to have: - [ ] Viewer for Guide JSON + - [ ] PDF export - [ ] Editor for Guide - [ ] improve error handling +- [ ] Blur sensitive information +- [x] Pause/resume recording feature ### Maybe far in the future: - [ ] Optional cloud for sharing guide as link diff --git a/chrome/background.js b/chrome/background.js index b2b4908..5f06aa1 100644 --- a/chrome/background.js +++ b/chrome/background.js @@ -31,7 +31,13 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { return sendResponse({ success: false, message: error }); } case "generateGuide": - return sendResponse({ success: true, guide: steps });; + return sendResponse({ success: true, guide: steps }); + case "clearGuide": + steps = []; + return sendResponse({ success: true, message: "Cleared guide successfully" }); + case "guideLenght": + count = steps.length; + return sendResponse({ success: true, stepCount: count }); case "debug": default: console.log(message.action); diff --git a/chrome/content.js b/chrome/content.js index 7418567..a133de3 100644 --- a/chrome/content.js +++ b/chrome/content.js @@ -1,46 +1,60 @@ // Script which is executed on all web pages -//document.body.style.background = "blue"; -document.body.style.border = "red 20px solid"; // just to identify if the extension is active on this tab - -document.addEventListener('click', function(){ - const element = event.target; - const elementText = element.textContent; // gets the text of the clicked element - const elementSelector = element.id; // the id of the clicked element, if exists (else its empty) - - const rect = element.getBoundingClientRect(); // create a rectangle from the element - - chrome.runtime.sendMessage({action: "captureScreenshot"}, (response) => { - if (response && response.success) { - const img = new Image(); - img.src = response.screenshot; - img.onload = () => { - const pixelZoom = 350; - - const canvas = document.createElement("canvas"); - canvas.width = rect.width + pixelZoom; - canvas.height = rect.height + pixelZoom; - const ctx = canvas.getContext("2d"); - - ctx.drawImage( - img, - rect.left, rect.top, rect.width + pixelZoom, rect.height + pixelZoom, // source size - 0, 0, rect.width + pixelZoom, rect.height + pixelZoom // result size - ); - - const croppedDataUrl = canvas.toDataURL("image/jpeg"); - chrome.runtime.sendMessage({ - action: "saveStep", - image: croppedDataUrl, - stepLabel: elementText, - stepElement: elementSelector, - triggerName: "click" - }, (response) => { - console.log(response); - }); - }; - } else { - console.error("Error, while capturing the screenshot:", response.error); - } - }); +// load recording status & react to updates +let isRecording = false; +chrome.storage.local.get("recording", (data) => { + isRecording = data.recording || false; +}); + +chrome.storage.onChanged.addListener((changes) => { + if (changes.recording) { + isRecording = changes.recording.newValue; + } + // signalize recording status + document.body.style.border = isRecording ? "red 20px solid" : "none"; +}); + +// add recording functionality on click +document.addEventListener('click', function(){ + if(isRecording){ + const element = event.target; + const elementText = element.textContent; // gets the text of the clicked element + const elementSelector = element.id; // the id of the clicked element, if exists (else its empty) + + const rect = element.getBoundingClientRect(); // create a rectangle from the element + + chrome.runtime.sendMessage({action: "captureScreenshot"}, (response) => { + if (response && response.success) { + const img = new Image(); + img.src = response.screenshot; + img.onload = () => { + const pixelZoom = 350; + + const canvas = document.createElement("canvas"); + canvas.width = rect.width + pixelZoom; + canvas.height = rect.height + pixelZoom; + const ctx = canvas.getContext("2d"); + + ctx.drawImage( + img, + rect.left, rect.top, rect.width + pixelZoom, rect.height + pixelZoom, // source size + 0, 0, rect.width + pixelZoom, rect.height + pixelZoom // result size + ); + + const croppedDataUrl = canvas.toDataURL("image/jpeg"); + chrome.runtime.sendMessage({ + action: "saveStep", + image: croppedDataUrl, + stepLabel: elementText, + stepElement: elementSelector, + triggerName: "click" + }, (response) => { + console.log(response); + }); + }; + } else { + console.error("Error, while capturing the screenshot:", response.error); + } + }); + } }); diff --git a/chrome/manifest.json b/chrome/manifest.json index 0684248..85d2070 100644 --- a/chrome/manifest.json +++ b/chrome/manifest.json @@ -1,9 +1,9 @@ { "manifest_version": 3, "name": "Cl1ck Gu1de", - "version": "0.0.2", + "version": "0.0.3", "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"], + "permissions": ["scripting", "activeTab", "downloads", "tabs", "storage"], "host_permissions": [""], "background": { "service_worker": "background.js" diff --git a/chrome/popup.js b/chrome/popup.js index 94f6a74..89d5543 100644 --- a/chrome/popup.js +++ b/chrome/popup.js @@ -1,11 +1,22 @@ // Script for Popup +// load recording status +let isRecording = false; +chrome.storage.local.get("recording", (data) => { + isRecording = data.recording || false; + console.log(data); + updateRecordingLabel(isRecording); +}); + +// define buttons const toggleRecordingBtn = document.getElementById("toggleRecording"); const exportBtn = document.getElementById("exportBtn"); toggleRecordingBtn.addEventListener('click', () => { - console.log("Button clicked"); - chrome.runtime.sendMessage({action: 'triggerName'}); + //if(isRecording) chrome.runtime.sendMessage({action: "clearGuide"}); + isRecording = !isRecording; // toggle status + updateRecordingLabel(isRecording); // update label + chrome.storage.local.set({ recording: isRecording }); // save status }); exportBtn.addEventListener('click', () => { @@ -18,6 +29,24 @@ exportBtn.addEventListener('click', () => { }); }); +// update button label +function updateRecordingLabel(bool){ + let startLabel = "Start recording"; + + try{ + chrome.runtime.sendMessage({ + action: "guideLenght" + }, (response) => { + if(response.stepCount !== 0) startLabel = "Resume"; + toggleRecordingBtn.textContent = bool ? "Pause/Stop" : startLabel; + }); + } + catch(error){ + console.error(`Error while updating recording button label: ${error}`); + } +} + +// export guide as json function exportJSON(object, filename){ const json = JSON.stringify(object, null, 2); @@ -30,4 +59,7 @@ function exportJSON(object, filename){ link.click(); URL.revokeObjectURL(url); + + // clear guide + chrome.runtime.sendMessage({action: "clearGuide"}); } \ No newline at end of file