diff --git a/wled00/data/common.js b/wled00/data/common.js
index a6223daa7c..0b66ca1a78 100644
--- a/wled00/data/common.js
+++ b/wled00/data/common.js
@@ -126,6 +126,10 @@ function getLoc() {
}
}
function getURL(path) { return (loc ? locproto + "//" + locip : "") + path; }
+// HTML entity escaper – use on any remote/user-supplied text inserted into innerHTML
+function esc(s) { return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
+// URL sanitizer – blocks javascript: and data: URIs, use for externally supplied URLs for some basic safety
+function safeUrl(u) { return /^https?:\/\//.test(u) ? u : '#'; }
function B() { window.open(getURL("/settings"),"_self"); }
var timeout;
function showToast(text, error = false) {
diff --git a/wled00/data/pixelforge/pixelforge.htm b/wled00/data/pixelforge/pixelforge.htm
index 5fb3bb0a1d..1e1a5308db 100644
--- a/wled00/data/pixelforge/pixelforge.htm
+++ b/wled00/data/pixelforge/pixelforge.htm
@@ -391,30 +391,9 @@
Available Tokens
Not available in 1D
-
-
-
Pixel Paint
-
Interactive painting tool
-
-
-
-
-
-
-
Video Lab
-
Stream video and generate animated GIFs (beta)
-
-
-
-
-
-
-
PIXEL MAGIC Tool
-
Legacy pixel art editor
-
-
+
+
Loading tools...
-
@@ -442,6 +421,11 @@
PIXEL MAGIC Tool
let iL=[]; // image list
let gF=null,gI=null,aT=null;
let fL; // file list
+let pT = []; // local tools list from JSON
+const remoteURL = 'https://dedehai.github.io/pf_tools.json'; // Change to your actual repo
+const toolsjson = 'pf_tools.json';
+// note: the pf_tools.json must use major.minor for tool versions (e.g. 0.95 or 1.1), otherwise the update check won't work
+// also the code assumes that the tool url points to a gz file
// load external resources in sequence to avoid 503 errors if heap is low, repeats indefinitely until loaded
(function loadFiles() {
@@ -459,21 +443,19 @@
PIXEL MAGIC Tool
getLoc();
// create off screen canvas
rv = cE('canvas');
- rvc = rv.getContext('2d',{willReadFrequently:true});
+ rvc = rv.getContext('2d',{willReadFrequently:true});
rv.width = cv.width; rv.height = cv.height;
-
+ tabSw(localStorage.tab||'img'); // switch to last open tab or image tab by default
await segLoad(); // load available segments
await flU(); // update file list
- toolChk('pixelpaint.htm','t1'); // update buttons of additional tools
- toolChk('videolab.htm','t2');
- toolChk('pxmagic.htm','t3');
+ await loadTools(); // load additional tools list from pf_tools.json
await fsMem(); // show file system memory info
}
/* update file list */
async function flU(){
try{
- const r = await fetch(getURL('/edit?list=/'));
+ const r = await fetch(getURL('/edit?list=/&cb=' + Date.now()));
fL = await r.json();
}catch(e){console.error(e);}
}
@@ -576,14 +558,14 @@
}catch(e){msg('Download failed','err');}
menuClose();
}
-async function imgDel(){
- if(!confirm(`Delete ${sI.name}?`))return;
+
+async function deleteFile(name){
+ name = name.replace('/',''); // remove leading slash if present (just in case)
+ if (fL.some(f => f.name.replace('/','') === `${name}.gz`))
+ name += '.gz'; // if .gz version of file exists, delete that (handles tools which are stored gzipped on device)
+ if(!confirm(`Delete ${name}?`))return;
ovShow();
try{
- const r = await fetch(getURL(`/edit?func=delete&path=/${sI.name}`));
- if(r.ok){ msg('Deleted'); imgRm(sI.name); }
+ const r = await fetch(getURL(`/edit?func=delete&path=/${name}`));
+ if(r.ok){
+ msg('Deleted');
+ imgRm(name); // remove image from grid (if this was not an image, this does nothing)
+ }
else msg('Delete failed! File in use?','err');
}catch(e){msg('Delete failed','err');}
finally{ovHide();}
- menuClose();
+ fsMem(); // refresh memory info after delete
+ menuClose(); // close menu (used for image delete button)
+ await flU(); // update file list
+ renderTools(); // re-render tools list
}
/* tab select and additional tools */
@@ -1186,7 +1241,6 @@