Move from file IDs to file paths (with retrocompatibility)

This commit is contained in:
MassiveBox 2025-04-02 20:15:48 +02:00
parent 56cf62f1eb
commit a2503d5def
Signed by: massivebox
GPG key ID: 9B74D3A59181947D
7 changed files with 62 additions and 49 deletions

View file

@ -1,15 +1,15 @@
function copyEditLink(fileID) {
navigator.clipboard.writeText(getEditLink(fileID));
function copyEditLink(path) {
navigator.clipboard.writeText(getEditLink(path));
}
function copyImageLink(fileID) {
navigator.clipboard.writeText(`![Drawing](assets/${fileID}.svg)`);
function copyImageLink(path) {
navigator.clipboard.writeText(`![Drawing](${path.replace("/data/", "")})`);
}
function refreshPage() {
window.location.reload();
}
function addButton(document, fileID) {
function addButton(document, path) {
// Add floating button
const floatingButton = document.createElement('button');
@ -22,8 +22,8 @@ function addButton(document, fileID) {
popupMenu.id = 'popupMenu';
popupMenu.innerHTML = `
<button onclick="refreshPage()">Refresh</button>
<button onclick="copyEditLink('${fileID}')">Copy Direct Edit Link</button>
<button onclick="copyImageLink('${fileID}')">Copy Image Link</button>
<button onclick="copyEditLink('${path}')">Copy Direct Edit Link</button>
<button onclick="copyImageLink('${path}')">Copy Image Link</button>
`;
document.body.appendChild(popupMenu);

View file

@ -27,9 +27,9 @@ async function getFile(path) {
}
async function getSVG(fileID) {
async function getSVG(path) {
const resp = await getFile("/data/assets/" + fileID + '.svg');
const resp = await getFile(path);
if(resp == null) {
return FALLBACK;
}
@ -37,10 +37,10 @@ async function getSVG(fileID) {
}
function getEditLink(fileID) {
function getEditLink(path) {
const data = encodeURIComponent(
JSON.stringify({
id: fileID
path: path,
})
)
return `siyuan://plugins/siyuan-jsdraw-pluginwhiteboard/?icon=iconDraw&title=Drawing&data=${data}`;

View file

@ -5,18 +5,22 @@
<script src="button.js"></script>
<script>
const urlParams = new URLSearchParams(window.location.search);
const fileID = urlParams.get('id');
let path = urlParams.get('path');
if(path === null) {
const fileID = urlParams.get('id'); // legacy support
path = "/data/assets/" + fileID + ".svg";
}
document.addEventListener('DOMContentLoaded', async () => {
const editLink = document.createElement('a');
editLink.href = getEditLink(fileID);
editLink.href = getEditLink(path);
document.body.appendChild(editLink);
const htmlContainer = document.createElement('div');
htmlContainer.innerHTML = await getSVG(fileID);
htmlContainer.innerHTML = await getSVG(path);
editLink.appendChild(htmlContainer);
addButton(document, fileID);
addButton(document, path);
});
</script>
<link rel="stylesheet" href="index.css">

View file

@ -1,7 +1,7 @@
export const SVG_MIME = "image/svg+xml";
export const JSON_MIME = "application/json";
export const DATA_PATH = "/data/assets";
export const DATA_PATH = "/data/assets/";
export const STORAGE_PATH = "/data/storage/petal/siyuan-jsdraw-plugin";
export const TOOLBAR_PATH = STORAGE_PATH + "/toolbar.json";
export const CONFIG_PATH = STORAGE_PATH + "/conf.json";
export const EMBED_PATH = "/plugins/siyuan-jsdraw-plugin/webapp/?id=";
export const EMBED_PATH = "/plugins/siyuan-jsdraw-plugin/webapp/?path=";

View file

@ -6,22 +6,22 @@ import {getFile, saveFile} from "@/file";
import {JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const";
import {idToPath} from "@/helper";
export function openEditorTab(p: Plugin, fileID: string) {
export function openEditorTab(p: Plugin, path: string) {
openTab({
app: p.app,
custom: {
title: 'Drawing',
icon: 'iconDraw',
id: "siyuan-jsdraw-pluginwhiteboard",
data: { id: fileID }
data: { path: path }
}
});
}
async function saveCallback(editor: Editor, fileID: string, saveButton: BaseWidget) {
async function saveCallback(editor: Editor, path: string, saveButton: BaseWidget) {
const svgElem = editor.toSVG();
try {
saveFile(idToPath(fileID), SVG_MIME, svgElem.outerHTML);
saveFile(path, SVG_MIME, svgElem.outerHTML);
saveButton.setDisabled(true);
setTimeout(() => { // @todo improve save button feedback
saveButton.setDisabled(false);
@ -36,11 +36,15 @@ async function saveCallback(editor: Editor, fileID: string, saveButton: BaseWidg
export function createEditor(i: ITabModel) {
const fileID = i.data.id;
let path = i.data.path;
if(path == null) {
const fileID = i.data.id; // legacy compatibility
if (fileID == null) {
alert("File ID missing - couldn't open file.")
alert("File ID and path missing - couldn't open file.")
return;
}
path = idToPath(fileID);
}
const editor = new Editor(i.element, {
iconProvider: new MaterialIconProvider(),
@ -55,14 +59,14 @@ export function createEditor(i: ITabModel) {
}
});
// restore drawing
getFile(idToPath(fileID)).then(svg => {
getFile(path).then(svg => {
if(svg != null) {
editor.loadFromSVG(svg);
}
});
// save logic
const saveButton = toolbar.addSaveButton(() => saveCallback(editor, fileID, saveButton));
const saveButton = toolbar.addSaveButton(() => saveCallback(editor, path, saveButton));
// save toolbar config on tool change (toolbar state is not saved in SVGs!)
editor.notifier.on(EditorEventType.ToolUpdated, () => {

View file

@ -45,14 +45,14 @@ export function generateSiyuanId() {
}
export function idToPath(id: string) {
return DATA_PATH + '/' + id + '.svg';
return DATA_PATH + id + '.svg';
}
// [Edit](siyuan://plugins/siyuan-jsdraw-pluginwhiteboard/?icon=iconDraw&title=Drawing&data={"id":"${id}"})
// ![Drawing](assets/${id}.svg)
export function getPreviewHTML(id: string): string {
export function getPreviewHTML(path: string): string {
return `
<iframe src="${EMBED_PATH + id}&antiCache=0"></iframe>
<iframe src="${EMBED_PATH + path}&antiCache=0"></iframe>
`
}
@ -60,12 +60,7 @@ export function getPreviewHTML(id: string): string {
export function findImgSrc(element: HTMLElement): string | null {
// Base case: if current element is an image
if (element.tagName === 'IMG') {
const fullSrc = (element as HTMLImageElement).src;
// Extract the path after host:port using URL API
const url = new URL(fullSrc);
return url.pathname.startsWith('/assets/')
? url.pathname.substring(1) // Remove leading slash
: null;
return (element as HTMLImageElement).src;
}
// Recursively check children
@ -79,12 +74,15 @@ export function findImgSrc(element: HTMLElement): string | null {
return null;
}
export function extractFileID(imgSrc: string | null): string | null {
export function imgSrcToAbsolutePath(imgSrc: string | null): string | null {
if (!imgSrc) return null;
const [pathPart] = imgSrc.split('?');
// Match pattern: assets/{fileID}.svg
const match = pathPart.match(/^assets\/([^\/]+)\.svg$/i);
const url = new URL(imgSrc);
imgSrc = decodeURIComponent(url.pathname);
if(imgSrc.startsWith('/assets/')) {
return "/data" + imgSrc;
}
return null
return match?.[1] || null;
}

View file

@ -1,6 +1,14 @@
import {Plugin, Protyle} from 'siyuan';
import {getPreviewHTML, loadIcons, getMenuHTML, generateSiyuanId, findImgSrc, extractFileID} from "@/helper";
import {
getPreviewHTML,
loadIcons,
getMenuHTML,
generateSiyuanId,
findImgSrc,
imgSrcToAbsolutePath
} from "@/helper";
import {createEditor, openEditorTab} from "@/editorTab";
import {DATA_PATH} from "@/const";
export default class DrawJSPlugin extends Plugin {
onload() {
@ -18,23 +26,22 @@ export default class DrawJSPlugin extends Plugin {
filter: ["Insert Drawing", "Add drawing", "whiteboard", "freehand", "graphics", "jsdraw"],
html: getMenuHTML("iconDraw", this.i18n.insertDrawing),
callback: (protyle: Protyle) => {
const uid = generateSiyuanId();
protyle.insert(getPreviewHTML(uid), true, false);
openEditorTab(this, uid);
const path = DATA_PATH + generateSiyuanId() + ".svg";
protyle.insert(getPreviewHTML(path), true, false);
openEditorTab(this, path);
}
}];
this.eventBus.on("open-menu-image", (e: any) => {
const fileID = extractFileID(findImgSrc(e.detail.element));
if(fileID === null) {
const path = imgSrcToAbsolutePath(findImgSrc(e.detail.element));
if(path === null) {
return;
}
console.log("got ID" + fileID);
e.detail.menu.addItem({
icon: "iconDraw",
label: "Edit with js-draw",
click: () => {
openEditorTab(this, fileID);
openEditorTab(this, path);
}
})
})