Improve error handling
All checks were successful
Build on Push and create Release on Tag / build (push) Successful in 1m40s

This commit is contained in:
MassiveBox 2025-07-16 15:57:14 +02:00
parent 05984a8913
commit eaf4a8e39e
7 changed files with 109 additions and 45 deletions

View file

@ -1,8 +1,9 @@
import {PluginFile} from "@/file";
import {CONFIG_FILENAME, JSON_MIME, STORAGE_PATH} from "@/const";
import {Plugin, showMessage} from "siyuan";
import {Plugin} from "siyuan";
import {SettingUtils} from "@/libs/setting-utils";
import {getFirstDefined} from "@/helper";
import {ErrorReporter, InvalidBackgroundColorError} from "@/errors";
export interface Options {
dialogOnDesktop: boolean
@ -90,7 +91,7 @@ export class PluginConfigViewer {
let color = data.backgroundDropdown === "CUSTOM" ? data.background : data.backgroundDropdown;
if(!PluginConfig.validateColor(color)) {
showMessage(this.plugin.i18n.errInvalidBackgroundColor, 0, 'error');
ErrorReporter.error(new InvalidBackgroundColorError());
data.background = this.config.options.editorOptions.background;
this.settingUtils.set('background', data.background);
}

View file

@ -10,12 +10,16 @@ import Editor, {
Vec2,
Viewport
} from "js-draw";
import {Dialog, getFrontend, openTab, Plugin, showMessage} from "siyuan";
import {Dialog, getFrontend, openTab, Plugin} from "siyuan";
import {findSyncIDInProtyle, replaceSyncID} from "@/protyle";
import DrawJSPlugin from "@/index";
import {EditorOptions} from "@/config";
import 'js-draw/styles';
import {SyncIDNotFoundError, UnchangedProtyleError} from "@/errors";
import {
ErrorReporter,
GenericSaveError, InternationalizedError, NoFileIDError, SyncIDNotFoundError,
UnchangedProtyleError
} from "@/errors";
export class PluginEditor {
@ -65,7 +69,7 @@ export class PluginEditor {
let syncID = await findSyncIDInProtyle(fileID);
if(syncID == null) {
throw new SyncIDNotFoundError(fileID);
throw new SyncIDNotFoundError();
}
instance.setSyncID(syncID);
await instance.restoreOrInitFile(defaultEditorOptions);
@ -157,12 +161,13 @@ export class PluginEditor {
saveButton.setDisabled(false);
}, 500);
} catch (error) {
showMessage("Error saving! The current drawing has been copied to your clipboard. You may need to create a new drawing and paste it there.", 0, 'error');
if(error instanceof UnchangedProtyleError) {
showMessage("Make sure the image you're trying to edit still exists in your documents.", 0, 'error');
if(error instanceof InternationalizedError) {
ErrorReporter.error(error);
}else{
ErrorReporter.error(new GenericSaveError());
console.error(error);
}
await navigator.clipboard.writeText(svgElem.outerHTML);
console.error(error);
console.log("Couldn't save SVG: ", svgElem.outerHTML)
return;
}
@ -184,7 +189,7 @@ export class EditorManager {
let editor = await PluginEditor.create(fileID, p.config.options.editorOptions);
instance.setEditor(editor);
}catch (error) {
EditorManager.handleCreationError(error, p);
ErrorReporter.error(error);
}
return instance;
}
@ -195,28 +200,19 @@ export class EditorManager {
async init() {
const fileID = this.data.fileID;
if (fileID == null) {
alert(p.i18n.errNoFileID);
ErrorReporter.error(new NoFileIDError());
return;
}
try {
const editor = await PluginEditor.create(fileID, p.config.options.editorOptions);
this.element.appendChild(editor.getElement());
}catch (error){
EditorManager.handleCreationError(error, p);
ErrorReporter.error(error);
}
}
});
}
static handleCreationError(error: any, p: DrawJSPlugin) {
console.error(error);
let errorTxt = p.i18n.errCreateUnknown;
if(error instanceof SyncIDNotFoundError) {
errorTxt = p.i18n.errSyncIDNotFound;
}
showMessage(errorTxt, 0, 'error');
}
toTab(p: Plugin) {
openTab({
app: p.app,

View file

@ -1,12 +1,80 @@
import {showMessage} from "siyuan";
export class SyncIDNotFoundError extends Error {
readonly fileID: string;
export class InternationalizedError extends Error {
readonly key: string;
constructor(fileID: string) {
super(`SyncID not found for file ${fileID}`);
this.fileID = fileID;
Object.setPrototypeOf(this, new.target.prototype);
constructor(key: string) {
super(key);
this.key = key;
}
}
export class UnchangedProtyleError extends Error {}
export class ErrorReporter {
static i18n: any;
constructor(i18n: any) {
ErrorReporter.i18n = i18n;
}
static error(err: Error, timeout?: number) {
console.error(err);
let errorTxt = err.message;
if(err instanceof InternationalizedError) {
errorTxt = ErrorReporter.i18n[err.key];
}
if(!timeout) {
timeout = 0;
}
showMessage(errorTxt, timeout, 'error');
}
}
export class SyncIDNotFoundError extends InternationalizedError {
constructor() {
super('errSyncIDNotFound');
}
}
export class UnchangedProtyleError extends InternationalizedError {
constructor() {
super('errUnchangedProtyle');
}
}
export class MultipleSyncIDsError extends InternationalizedError {
constructor() {
super('errMultipleSyncIDs');
}
}
export class GenericSaveError extends InternationalizedError {
constructor() {
super('errSaveGeneric');
}
}
export class NotAWhiteboardError extends InternationalizedError {
constructor() {
super('errNotAWhiteboard');
}
}
export class InvalidBackgroundColorError extends InternationalizedError {
constructor() {
super('errInvalidBackgroundColor');
}
}
export class NoFileIDError extends InternationalizedError {
constructor() {
super('errNoFileID');
}
}
export class MustSelectError extends InternationalizedError {
constructor() {
super('errMustSelect');
}
}

View file

@ -52,7 +52,7 @@ abstract class PluginFileBase {
protected toFile(customFilename?: string): File {
let filename = customFilename || this.fileName;
const blob = new Blob([this.content], { type: this.mimeType });
return new File([blob], filename, { type: this.mimeType });
return new File([blob], filename, { type: this.mimeType, lastModified: Date.now() });
}
}

View file

@ -1,4 +1,4 @@
import {Plugin, Protyle, showMessage} from 'siyuan';
import {Plugin, Protyle} from 'siyuan';
import {
getMarkdownBlock,
loadIcons,
@ -10,6 +10,7 @@ import {migrate} from "@/migration";
import {EditorManager} from "@/editor";
import {PluginConfig, PluginConfigViewer} from "@/config";
import {Analytics} from "@/analytics";
import {ErrorReporter, MustSelectError, NotAWhiteboardError} from "@/errors";
export default class DrawJSPlugin extends Plugin {
@ -18,6 +19,7 @@ export default class DrawJSPlugin extends Plugin {
async onload() {
new ErrorReporter(this.i18n);
loadIcons(this);
EditorManager.registerTab(this);
migrate()
@ -55,7 +57,7 @@ export default class DrawJSPlugin extends Plugin {
langKey: "editShortcut",
hotkey: "⌥⇧D",
callback: async () => {
await this.editSelectedImg();
this.editSelectedImg().catch(e => ErrorReporter.error(e, 5000));
},
})
@ -63,7 +65,7 @@ export default class DrawJSPlugin extends Plugin {
icon: "iconDraw",
title: this.i18n.editShortcut,
callback: async () => {
await this.editSelectedImg();
await this.editSelectedImg().catch(e => ErrorReporter.error(e, 5000));
},
position: "left"
})
@ -82,14 +84,12 @@ export default class DrawJSPlugin extends Plugin {
let selectedImg = document.getElementsByClassName('img--select');
if(selectedImg.length == 0) {
showMessage(this.i18n.msgMustSelect + this.i18n.usageInstructionsLink, 5000, 'info');
return;
throw new MustSelectError();
}
let ids = imgSrcToIDs(findImgSrc(selectedImg[0] as HTMLElement));
if(ids == null) {
showMessage(this.i18n.errNotAWhiteboard + + this.i18n.usageInstructionsLink, 5000, 'error');
return;
throw new NotAWhiteboardError();
}
void this.analytics.sendEvent('edit');
(await EditorManager.create(ids.fileID, this)).open(this);

View file

@ -1,5 +1,6 @@
import {getBlockByID, sql, updateBlock} from "@/api";
import {assetPathToIDs, IDsToAssetPath} from "@/helper";
import {MultipleSyncIDsError} from "@/errors";
export async function findSyncIDInProtyle(fileID: string, iter?: number): Promise<string> {
@ -15,11 +16,7 @@ export async function findSyncIDInProtyle(fileID: string, iter?: number): Promis
if(syncID == null) {
syncID = ids.syncID;
}else if(ids.syncID !== syncID) {
throw new Error(
"Multiple syncIDs found in documents. Remove the drawings that don't exist from your documents.\n" +
"Sync conflict copies can cause this error, so make sure to delete them, or at least the js-draw drawings they contain.\n" +
"File IDs must be unique. Close this editor tab now."
);
throw new MultipleSyncIDsError();
}
}
}
@ -82,7 +79,7 @@ export async function replaceBlockContent(
}
function extractImageSourcesFromMarkdown(markdown: string, mustStartWith?: string) {
const imageRegex = /!\[.*?\]\((.*?)\)/g; // only get images
const imageRegex = /!\[.*?\]\(([^)\s]+)(?:\s+"[^"]+")?\)/g; // only get images
return Array.from(markdown.matchAll(imageRegex))
.map(match => match[1])
.filter(source => source.startsWith(mustStartWith)) // discard other images