adding start/stop/pause/resume functionality

This commit is contained in:
Michi 2024-11-07 21:31:33 +01:00
parent b1c9b26e23
commit cd2de15b2b
5 changed files with 104 additions and 49 deletions

View file

@ -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] create a screenshot of the steps
- [x] ability to save multiple steps - [x] ability to save multiple steps
- [x] export guide as json file - [x] export guide as json file
- [ ] add recording start/stop functionality - [x] add recording start/stop functionality
- [x] create basic popup UI - [x] create basic popup UI
### Additional nice to have: ### Additional nice to have:
- [ ] Viewer for Guide JSON - [ ] Viewer for Guide JSON
- [ ] PDF export
- [ ] Editor for Guide - [ ] Editor for Guide
- [ ] improve error handling - [ ] improve error handling
- [ ] Blur sensitive information
- [x] Pause/resume recording feature
### Maybe far in the future: ### Maybe far in the future:
- [ ] Optional cloud for sharing guide as link - [ ] Optional cloud for sharing guide as link

View file

@ -31,7 +31,13 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
return sendResponse({ success: false, message: error }); return sendResponse({ success: false, message: error });
} }
case "generateGuide": 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": case "debug":
default: default:
console.log(message.action); console.log(message.action);

View file

@ -1,46 +1,60 @@
// Script which is executed on all web pages // Script which is executed on all web pages
//document.body.style.background = "blue"; // load recording status & react to updates
document.body.style.border = "red 20px solid"; // just to identify if the extension is active on this tab let isRecording = false;
chrome.storage.local.get("recording", (data) => {
document.addEventListener('click', function(){ isRecording = data.recording || false;
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) chrome.storage.onChanged.addListener((changes) => {
if (changes.recording) {
const rect = element.getBoundingClientRect(); // create a rectangle from the element isRecording = changes.recording.newValue;
}
chrome.runtime.sendMessage({action: "captureScreenshot"}, (response) => { // signalize recording status
if (response && response.success) { document.body.style.border = isRecording ? "red 20px solid" : "none";
const img = new Image(); });
img.src = response.screenshot;
img.onload = () => { // add recording functionality on click
const pixelZoom = 350; document.addEventListener('click', function(){
if(isRecording){
const canvas = document.createElement("canvas"); const element = event.target;
canvas.width = rect.width + pixelZoom; const elementText = element.textContent; // gets the text of the clicked element
canvas.height = rect.height + pixelZoom; const elementSelector = element.id; // the id of the clicked element, if exists (else its empty)
const ctx = canvas.getContext("2d");
const rect = element.getBoundingClientRect(); // create a rectangle from the element
ctx.drawImage(
img, chrome.runtime.sendMessage({action: "captureScreenshot"}, (response) => {
rect.left, rect.top, rect.width + pixelZoom, rect.height + pixelZoom, // source size if (response && response.success) {
0, 0, rect.width + pixelZoom, rect.height + pixelZoom // result size const img = new Image();
); img.src = response.screenshot;
img.onload = () => {
const croppedDataUrl = canvas.toDataURL("image/jpeg"); const pixelZoom = 350;
chrome.runtime.sendMessage({
action: "saveStep", const canvas = document.createElement("canvas");
image: croppedDataUrl, canvas.width = rect.width + pixelZoom;
stepLabel: elementText, canvas.height = rect.height + pixelZoom;
stepElement: elementSelector, const ctx = canvas.getContext("2d");
triggerName: "click"
}, (response) => { ctx.drawImage(
console.log(response); img,
}); rect.left, rect.top, rect.width + pixelZoom, rect.height + pixelZoom, // source size
}; 0, 0, rect.width + pixelZoom, rect.height + pixelZoom // result size
} else { );
console.error("Error, while capturing the screenshot:", response.error);
} 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);
}
});
}
}); });

View file

@ -1,9 +1,9 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Cl1ck Gu1de", "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.", "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": ["<all_urls>"], "host_permissions": ["<all_urls>"],
"background": { "background": {
"service_worker": "background.js" "service_worker": "background.js"

View file

@ -1,11 +1,22 @@
// Script for Popup // 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 toggleRecordingBtn = document.getElementById("toggleRecording");
const exportBtn = document.getElementById("exportBtn"); const exportBtn = document.getElementById("exportBtn");
toggleRecordingBtn.addEventListener('click', () => { toggleRecordingBtn.addEventListener('click', () => {
console.log("Button clicked"); //if(isRecording) chrome.runtime.sendMessage({action: "clearGuide"});
chrome.runtime.sendMessage({action: 'triggerName'}); isRecording = !isRecording; // toggle status
updateRecordingLabel(isRecording); // update label
chrome.storage.local.set({ recording: isRecording }); // save status
}); });
exportBtn.addEventListener('click', () => { 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){ function exportJSON(object, filename){
const json = JSON.stringify(object, null, 2); const json = JSON.stringify(object, null, 2);
@ -30,4 +59,7 @@ function exportJSON(object, filename){
link.click(); link.click();
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
// clear guide
chrome.runtime.sendMessage({action: "clearGuide"});
} }