parent
fe32505873
commit
7e4da82b82
3 changed files with 63 additions and 38 deletions
|
@ -3,7 +3,7 @@ import {PluginAsset, PluginFile} from "@/file";
|
||||||
import {JSON_MIME, STORAGE_PATH, SVG_MIME, TOOLBAR_FILENAME} from "@/const";
|
import {JSON_MIME, STORAGE_PATH, SVG_MIME, TOOLBAR_FILENAME} from "@/const";
|
||||||
import Editor, {BaseWidget, EditorEventType} from "js-draw";
|
import Editor, {BaseWidget, EditorEventType} from "js-draw";
|
||||||
import {Dialog, Plugin, openTab, getFrontend} from "siyuan";
|
import {Dialog, Plugin, openTab, getFrontend} from "siyuan";
|
||||||
import {replaceSyncID} from "@/protyle";
|
import {findSyncIDInProtyle, replaceSyncID} from "@/protyle";
|
||||||
|
|
||||||
export class PluginEditor {
|
export class PluginEditor {
|
||||||
|
|
||||||
|
@ -15,15 +15,15 @@ export class PluginEditor {
|
||||||
|
|
||||||
private readonly fileID: string;
|
private readonly fileID: string;
|
||||||
private syncID: string;
|
private syncID: string;
|
||||||
private readonly initialSyncID: string;
|
|
||||||
|
|
||||||
getElement(): HTMLElement { return this.element; }
|
getElement(): HTMLElement { return this.element; }
|
||||||
getEditor(): Editor { return this.editor; }
|
getEditor(): Editor { return this.editor; }
|
||||||
getFileID(): string { return this.fileID; }
|
getFileID(): string { return this.fileID; }
|
||||||
getSyncID(): string { return this.syncID; }
|
getSyncID(): string { return this.syncID; }
|
||||||
getInitialSyncID(): string { return this.initialSyncID; }
|
|
||||||
|
|
||||||
constructor(fileID: string, initialSyncID: string) {
|
constructor(fileID: string) {
|
||||||
|
|
||||||
|
this.fileID = fileID;
|
||||||
|
|
||||||
this.element = document.createElement("div");
|
this.element = document.createElement("div");
|
||||||
this.element.style.height = '100%';
|
this.element.style.height = '100%';
|
||||||
|
@ -31,22 +31,20 @@ export class PluginEditor {
|
||||||
iconProvider: new MaterialIconProvider(),
|
iconProvider: new MaterialIconProvider(),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.fileID = fileID;
|
this.genToolbar().then(() => {
|
||||||
this.initialSyncID = initialSyncID;
|
this.editor.dispatch(this.editor.setBackgroundStyle({ autoresize: true }), false);
|
||||||
this.syncID = initialSyncID;
|
this.editor.getRootElement().style.height = '100%';
|
||||||
|
|
||||||
this.genToolbar();
|
|
||||||
|
|
||||||
// restore drawing
|
|
||||||
this.drawingFile = new PluginAsset(fileID, initialSyncID, SVG_MIME);
|
|
||||||
this.drawingFile.loadFromSiYuanFS().then(() => {
|
|
||||||
if(this.drawingFile.getContent() != null) {
|
|
||||||
this.editor.loadFromSVG(this.drawingFile.getContent());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.dispatch(this.editor.setBackgroundStyle({ autoresize: true }), false);
|
findSyncIDInProtyle(this.fileID).then(async (syncID) => {
|
||||||
this.editor.getRootElement().style.height = '100%';
|
this.syncID = syncID;
|
||||||
|
// restore drawing
|
||||||
|
this.drawingFile = new PluginAsset(this.fileID, syncID, SVG_MIME);
|
||||||
|
await this.drawingFile.loadFromSiYuanFS();
|
||||||
|
if(this.drawingFile.getContent() != null) {
|
||||||
|
await this.editor.loadFromSVG(this.drawingFile.getContent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,24 +125,17 @@ export class EditorManager {
|
||||||
'type': "whiteboard",
|
'type': "whiteboard",
|
||||||
init() {
|
init() {
|
||||||
const fileID = this.data.fileID;
|
const fileID = this.data.fileID;
|
||||||
const initialSyncID = this.data.initialSyncID;
|
if (fileID == null) {
|
||||||
if (fileID == null || initialSyncID == null) {
|
alert("File ID missing - couldn't open file.")
|
||||||
alert("File or Sync ID and path missing - couldn't open file.")
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const editor = new PluginEditor(fileID, initialSyncID);
|
const editor = new PluginEditor(fileID);
|
||||||
this.element.appendChild(editor.getElement());
|
this.element.appendChild(editor.getElement());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toTab(p: Plugin) {
|
toTab(p: Plugin) {
|
||||||
for(const tab of p.getOpenedTab()["whiteboard"]) {
|
|
||||||
if(tab.data.fileID == this.editor.getFileID()) {
|
|
||||||
alert("File is already open in another editor tab!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
openTab({
|
openTab({
|
||||||
app: p.app,
|
app: p.app,
|
||||||
custom: {
|
custom: {
|
||||||
|
@ -153,7 +144,6 @@ export class EditorManager {
|
||||||
id: "siyuan-jsdraw-pluginwhiteboard",
|
id: "siyuan-jsdraw-pluginwhiteboard",
|
||||||
data: {
|
data: {
|
||||||
fileID: this.editor.getFileID(),
|
fileID: this.editor.getFileID(),
|
||||||
initialSyncID: this.editor.getInitialSyncID()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -168,7 +158,7 @@ export class EditorManager {
|
||||||
dialog.element.querySelector("#DrawingPanel").appendChild(this.editor.getElement());
|
dialog.element.querySelector("#DrawingPanel").appendChild(this.editor.getElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
open(p: Plugin) {
|
async open(p: Plugin) {
|
||||||
if(getFrontend() != "mobile") {
|
if(getFrontend() != "mobile") {
|
||||||
this.toTab(p);
|
this.toTab(p);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default class DrawJSPlugin extends Plugin {
|
||||||
const fileID = generateRandomString();
|
const fileID = generateRandomString();
|
||||||
const syncID = generateTimeString() + '-' + generateRandomString();
|
const syncID = generateTimeString() + '-' + generateRandomString();
|
||||||
protyle.insert(getMarkdownBlock(fileID, syncID), true, false);
|
protyle.insert(getMarkdownBlock(fileID, syncID), true, false);
|
||||||
new EditorManager(new PluginEditor(fileID, syncID)).open(this)
|
new EditorManager(new PluginEditor(fileID)).open(this);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export default class DrawJSPlugin extends Plugin {
|
||||||
label: "Edit with js-draw",
|
label: "Edit with js-draw",
|
||||||
click: () => {
|
click: () => {
|
||||||
void this.analytics.sendEvent('edit');
|
void this.analytics.sendEvent('edit');
|
||||||
new EditorManager(new PluginEditor(ids.fileID, ids.syncID)).open(this)
|
new EditorManager(new PluginEditor(ids.fileID)).open(this)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,37 @@
|
||||||
import {getBlockByID, sql, updateBlock} from "@/api";
|
import {getBlockByID, sql, updateBlock} from "@/api";
|
||||||
import {IDsToAssetPath} from "@/helper";
|
import {assetPathToIDs, IDsToAssetPath} from "@/helper";
|
||||||
|
|
||||||
|
export async function findSyncIDInProtyle(fileID: string, iter?: number): Promise<string> {
|
||||||
|
|
||||||
|
const search = `assets/${fileID}-`;
|
||||||
|
const blocks = await findImageBlocks(search);
|
||||||
|
|
||||||
|
let syncID = null;
|
||||||
|
|
||||||
|
for(const block of blocks) {
|
||||||
|
const sources = extractImageSourcesFromMarkdown(block.markdown, search);
|
||||||
|
for(const source of sources) {
|
||||||
|
const ids = assetPathToIDs(source);
|
||||||
|
if(syncID == null) {
|
||||||
|
syncID = ids.syncID;
|
||||||
|
}else if(ids.syncID !== syncID) {
|
||||||
|
throw new Error("Multiple syncIDs found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!iter) iter = 0;
|
||||||
|
if(syncID == null) {
|
||||||
|
// when the block has just been created, we need to wait a bit before it can be found
|
||||||
|
if(iter < 4) { // cap max time at 2s, it should be ok by then
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
return await findSyncIDInProtyle(fileID, iter + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncID;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export async function findImageBlocks(src: string) {
|
export async function findImageBlocks(src: string) {
|
||||||
|
|
||||||
|
@ -45,6 +77,13 @@ export async function replaceBlockContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractImageSourcesFromMarkdown(markdown: string, mustStartWith?: string) {
|
||||||
|
const imageRegex = /!\[.*?\]\((.*?)\)/g; // only get images
|
||||||
|
return Array.from(markdown.matchAll(imageRegex))
|
||||||
|
.map(match => match[1])
|
||||||
|
.filter(source => source.startsWith(mustStartWith)) // discard other images
|
||||||
|
}
|
||||||
|
|
||||||
export async function replaceSyncID(fileID: string, oldSyncID: string, newSyncID: string) {
|
export async function replaceSyncID(fileID: string, oldSyncID: string, newSyncID: string) {
|
||||||
|
|
||||||
const search = encodeURI(IDsToAssetPath(fileID, oldSyncID)); // the API uses URI-encoded
|
const search = encodeURI(IDsToAssetPath(fileID, oldSyncID)); // the API uses URI-encoded
|
||||||
|
@ -56,12 +95,8 @@ export async function replaceSyncID(fileID: string, oldSyncID: string, newSyncID
|
||||||
|
|
||||||
// get all the image sources, with parameters
|
// get all the image sources, with parameters
|
||||||
const markdown = block.markdown;
|
const markdown = block.markdown;
|
||||||
const imageRegex = /!\[.*?\]\((.*?)\)/g; // only get images
|
|
||||||
const sources = Array.from(markdown.matchAll(imageRegex))
|
|
||||||
.map(match => match[1])
|
|
||||||
.filter(source => source.startsWith(search)) // discard other images
|
|
||||||
|
|
||||||
for(const source of sources) {
|
for(const source of extractImageSourcesFromMarkdown(markdown, search)) {
|
||||||
const newSource = IDsToAssetPath(fileID, newSyncID);
|
const newSource = IDsToAssetPath(fileID, newSyncID);
|
||||||
const changed = await replaceBlockContent(block.id, source, newSource);
|
const changed = await replaceBlockContent(block.id, source, newSource);
|
||||||
if(!changed) return false
|
if(!changed) return false
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue