From 5e51589ffa80dd102834b140ca181d18ee1d701d Mon Sep 17 00:00:00 2001 From: MassiveBox Date: Thu, 3 Apr 2025 00:12:36 +0200 Subject: [PATCH] Internal file paths are now Markdown image paths In the last commit, file paths were implemented to be the full path for the API, however that is not necessary. Before this change: /data/assets/filename.svg After: assets/filename.svg The new method is what SiYuan uses for Markdown images, like ![Drawing](assets/filename.svg) --- public/webapp/button.js | 2 +- public/webapp/draw.js | 2 +- public/webapp/index.html | 2 +- src/const.ts | 5 +++-- src/editorTab.ts | 6 +++--- src/helper.ts | 9 ++++++-- src/index.ts | 8 +++---- src/protyle.ts | 46 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 src/protyle.ts diff --git a/public/webapp/button.js b/public/webapp/button.js index 5aa902d..8be8fd4 100644 --- a/public/webapp/button.js +++ b/public/webapp/button.js @@ -2,7 +2,7 @@ function copyEditLink(path) { navigator.clipboard.writeText(getEditLink(path)); } function copyImageLink(path) { - navigator.clipboard.writeText(`![Drawing](${path.replace("/data/", "")})`); + navigator.clipboard.writeText(`![Drawing](${path})`); } function refreshPage() { diff --git a/public/webapp/draw.js b/public/webapp/draw.js index baaf005..187dab2 100644 --- a/public/webapp/draw.js +++ b/public/webapp/draw.js @@ -29,7 +29,7 @@ async function getFile(path) { async function getSVG(path) { - const resp = await getFile(path); + const resp = await getFile("/data/" + path); if(resp == null) { return FALLBACK; } diff --git a/public/webapp/index.html b/public/webapp/index.html index c40fb79..cb4343c 100644 --- a/public/webapp/index.html +++ b/public/webapp/index.html @@ -8,7 +8,7 @@ let path = urlParams.get('path'); if(path === null) { const fileID = urlParams.get('id'); // legacy support - path = "/data/assets/" + fileID + ".svg"; + path = "assets/" + fileID + ".svg"; } document.addEventListener('DOMContentLoaded', async () => { diff --git a/src/const.ts b/src/const.ts index 9e5c04e..437af37 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1,7 +1,8 @@ export const SVG_MIME = "image/svg+xml"; export const JSON_MIME = "application/json"; -export const DATA_PATH = "/data/assets/"; -export const STORAGE_PATH = "/data/storage/petal/siyuan-jsdraw-plugin"; +export const DATA_PATH = "/data/"; +export const ASSETS_PATH = "assets/"; +export const STORAGE_PATH = DATA_PATH + "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/?path="; \ No newline at end of file diff --git a/src/editorTab.ts b/src/editorTab.ts index 0399dad..97f2cbc 100644 --- a/src/editorTab.ts +++ b/src/editorTab.ts @@ -3,7 +3,7 @@ import Editor, {BaseWidget, EditorEventType} from "js-draw"; import { MaterialIconProvider } from '@js-draw/material-icons'; import 'js-draw/styles'; import {getFile, saveFile} from "@/file"; -import {JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const"; +import {DATA_PATH, JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const"; import {idToPath} from "@/helper"; export function openEditorTab(p: Plugin, path: string) { @@ -21,7 +21,7 @@ export function openEditorTab(p: Plugin, path: string) { async function saveCallback(editor: Editor, path: string, saveButton: BaseWidget) { const svgElem = editor.toSVG(); try { - saveFile(path, SVG_MIME, svgElem.outerHTML); + saveFile(DATA_PATH + path, SVG_MIME, svgElem.outerHTML); saveButton.setDisabled(true); setTimeout(() => { // @todo improve save button feedback saveButton.setDisabled(false); @@ -59,7 +59,7 @@ export function createEditor(i: ITabModel) { } }); // restore drawing - getFile(path).then(svg => { + getFile(DATA_PATH + path).then(svg => { if(svg != null) { editor.loadFromSVG(svg); } diff --git a/src/helper.ts b/src/helper.ts index 7e132ac..d956158 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -74,15 +74,20 @@ export function findImgSrc(element: HTMLElement): string | null { return null; } -export function imgSrcToAbsolutePath(imgSrc: string | null): string | null { +export function imgSrcToPath(imgSrc: string | null): string | null { if (!imgSrc) return null; const url = new URL(imgSrc); imgSrc = decodeURIComponent(url.pathname); if(imgSrc.startsWith('/assets/')) { - return "/data" + imgSrc; + return imgSrc.substring(1); } return null } + +// Helper to safely escape regex special characters +export function escapeRegExp(string: string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 851d6e5..b5d4e90 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,10 +5,10 @@ import { getMenuHTML, generateSiyuanId, findImgSrc, - imgSrcToAbsolutePath + imgSrcToPath } from "@/helper"; import {createEditor, openEditorTab} from "@/editorTab"; -import {DATA_PATH} from "@/const"; +import {ASSETS_PATH} from "@/const"; export default class DrawJSPlugin extends Plugin { onload() { @@ -26,14 +26,14 @@ 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 path = DATA_PATH + generateSiyuanId() + ".svg"; + const path = ASSETS_PATH + generateSiyuanId() + ".svg"; protyle.insert(getPreviewHTML(path), true, false); openEditorTab(this, path); } }]; this.eventBus.on("open-menu-image", (e: any) => { - const path = imgSrcToAbsolutePath(findImgSrc(e.detail.element)); + const path = imgSrcToPath(findImgSrc(e.detail.element)); if(path === null) { return; } diff --git a/src/protyle.ts b/src/protyle.ts new file mode 100644 index 0000000..500874a --- /dev/null +++ b/src/protyle.ts @@ -0,0 +1,46 @@ +import {getBlockByID, sql, updateBlock} from "@/api"; +import {escapeRegExp} from "@/helper"; + +export async function findImageBlocks(src: string) { + + const sqlQuery = ` + SELECT id, markdown + FROM blocks + WHERE markdown like '%${src}%' + `; + + try { + return await sql(sqlQuery); + } catch (error) { + console.error('Error searching for image blocks:', error); + return []; + } + +} +export async function replaceBlockContent( + blockId: string, + searchStr: string, + replaceStr: string +): Promise { + try { + + const block = await getBlockByID(blockId); + if (!block) { + throw new Error('Block not found'); + } + + const originalContent = block.markdown; + const newContent = originalContent.replace(escapeRegExp(searchStr), replaceStr); + + if (newContent === originalContent) { + return false; + } + + await updateBlock('markdown', newContent, blockId); + return true; + + } catch (error) { + console.error('Failed to replace block content:', error); + return false; + } +}