feat: introduce local-first SVG workbench
This commit is contained in:
@@ -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();
|
||||
})();
|
||||
@@ -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 |
@@ -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"]
|
||||
}
|
||||
Reference in New Issue
Block a user