feat: introduce local-first SVG workbench

This commit is contained in:
2026-08-02 16:31:49 +02:00
commit 39d9802daa
97 changed files with 20702 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
(() => {
"use strict";
const controllerScript = document.currentScript;
const channel = document.documentElement.dataset.svgToolsChannel;
if (!channel) {
controllerScript?.remove();
return;
}
const send = (message) => parent.postMessage({ ...message, channel }, "*");
const find = (key) =>
typeof key === "string"
? document.querySelector(`[data-svg-tools-node="${CSS.escape(key)}"]`)
: null;
const reportBox = (key) => {
const element = find(key);
let box = null;
try {
if (element && typeof element.getBBox === "function") {
const candidate = element.getBBox();
if (
[candidate.x, candidate.y, candidate.width, candidate.height].every(
Number.isFinite,
)
) {
box = {
x: candidate.x,
y: candidate.y,
width: candidate.width,
height: candidate.height,
};
}
}
} catch {
// Some SVG elements do not expose geometry in every browser.
}
send({ type: "selection-box", key, box });
};
document.addEventListener("click", (event) => {
const target =
event.target instanceof Element
? event.target.closest("[data-svg-tools-node]")
: null;
const key = target?.getAttribute("data-svg-tools-node");
if (key) send({ type: "select", key });
if (event.target instanceof Element && event.target.closest("a")) {
event.preventDefault();
}
});
addEventListener("message", (event) => {
if (event.source !== parent) return;
const message = event.data;
if (!message || message.channel !== channel) return;
if (message.type === "ping") send({ type: "ready" });
if (message.type === "selection" && typeof message.key === "string") {
reportBox(message.key);
}
if (
message.type === "path" &&
typeof message.key === "string" &&
typeof message.data === "string"
) {
find(message.key)?.setAttribute("d", message.data);
reportBox(message.key);
}
if (
message.type === "transform" &&
typeof message.key === "string" &&
(typeof message.value === "string" || message.value === null)
) {
const element = find(message.key);
if (!element) return;
if (message.value) element.setAttribute("transform", message.value);
else element.removeAttribute("transform");
reportBox(message.key);
}
});
send({ type: "ready" });
controllerScript?.remove();
})();
+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-labelledby="title desc">
<title id="title">SVG Tools</title>
<desc id="desc">A vector document with an editable curve and path nodes.</desc>
<path fill="#f7f9fc" stroke="#233047" stroke-width="3" d="M12 5h28l12 12v42H12z"/>
<path fill="#dbe7ff" stroke="#233047" stroke-width="3" stroke-linejoin="round" d="M40 5v13h12"/>
<path fill="none" stroke="#7357ff" stroke-width="4" stroke-linecap="round" d="M20 45C26 22 39 52 47 29"/>
<g fill="#fff" stroke="#7357ff" stroke-width="2">
<rect x="17" y="42" width="6" height="6" rx="1"/>
<circle cx="33" cy="36" r="3"/>
<rect x="44" y="26" width="6" height="6" rx="1"/>
</g>
<path fill="none" stroke="#9a8cff" stroke-width="1.5" stroke-dasharray="2 2" d="M20 45L33 36 47 29"/>
</svg>

After

Width:  |  Height:  |  Size: 827 B

+49
View File
@@ -0,0 +1,49 @@
{
"$schema": "https://git.add-ideas.de/lotobo/toolbox-sdk/raw/branch/main/schemas/toolbox-app.v1.schema.json",
"schemaVersion": 1,
"id": "de.add-ideas.svg-tools",
"name": "SVG Tools",
"version": "0.1.0",
"description": "Inspect, edit, optimize and transform SVG documents locally in the browser.",
"entry": "./",
"icon": "./favicon.svg",
"categories": ["graphics", "design", "developer"],
"tags": [
"svg",
"vector",
"path",
"xml",
"transform",
"optimize",
"accessibility"
],
"integration": {
"contextVersion": 1,
"launchModes": ["navigate", "new-tab"],
"embedding": "unsupported"
},
"requirements": {
"secureContext": false,
"workers": true,
"indexedDb": false,
"crossOriginIsolated": false,
"topLevelContext": false
},
"privacy": {
"processing": "local",
"fileUploads": false,
"telemetry": false
},
"source": {
"repository": "https://git.add-ideas.de/lotobo/svg-tools",
"license": "GPL-3.0-or-later"
},
"actions": [
{
"id": "source",
"label": "Source",
"url": "https://git.add-ideas.de/lotobo/svg-tools"
}
],
"assets": ["./canvas-frame-controller.js"]
}