Skip to content

Commit

Permalink
Set fallback core
Browse files Browse the repository at this point in the history
  • Loading branch information
Acylation committed Apr 27, 2024
1 parent 417c471 commit 226f6de
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 47 deletions.
27 changes: 23 additions & 4 deletions src/global/chemCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,33 @@ import RDKitCore from '../lib/core/rdkitCore';

export let gRenderCore: ChemCore;

export const setCore = async (settings: ChemPluginSettings) => {
export const setCore = async (
settings: ChemPluginSettings,
onFallback: (error: string) => void
) => {
if (!gRenderCore || settings.core !== gRenderCore.id) {
if (settings.core == 'smiles-drawer')
if (settings.core === 'smiles-drawer') {
gRenderCore = new SmilesDrawerCore(settings);
if (settings.core == 'rdkit') {
gRenderCore = await RDKitCore.init(settings);
updateCoreSettings(settings);
} else if (settings.core === 'rdkit') {
try {
gRenderCore = await RDKitCore.init(settings);
updateCoreSettings(settings);
} catch (error) {
onFallback(error);
}
} else {
onFallback(`invalid chem core id. ${settings.core}`);
}
}
};

export const setFallbackCore = async (settings: ChemPluginSettings) => {
gRenderCore = new SmilesDrawerCore(settings);
updateCoreSettings(settings);
};

export const updateCoreSettings = (settings: ChemPluginSettings) => {
gRenderCore.settings = settings;
};

Expand Down
50 changes: 50 additions & 0 deletions src/lib/core/coreFallbackModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Modal, App, ButtonComponent } from 'obsidian';
import { i18n } from 'src/lib/i18n';

export class CoreFallbackModal extends Modal {
msg: string;
onConfirm: () => void;

constructor(app: App, text: string, onConfrim: () => void) {
super(app);
this.msg = text;
this.onConfirm = onConfrim;
}

onOpen() {
let { contentEl } = this;

Check failure on line 15 in src/lib/core/coreFallbackModal.ts

View workflow job for this annotation

GitHub Actions / build

'contentEl' is never reassigned. Use 'const' instead

contentEl.createEl('h3', {
text: i18n.t('modals.core-fallback.title'),
});

contentEl.createEl('div', {
text: i18n.t('modals.core-fallback.message'),
});

contentEl.createEl('br');

contentEl.createEl('div', {
text: this.msg,
});

const div = contentEl.createDiv();
div.style.display = 'flex';
div.style.flex = '1 1 auto';
div.style.justifyContent = 'flex-end';
div.style.padding = '3px';

new ButtonComponent(div)
.setCta()
.setButtonText(i18n.t('modals.core-fallback.confirm'))
.onClick(() => {
this.close();
});
}

onClose() {
this.onConfirm();
let { contentEl } = this;

Check failure on line 47 in src/lib/core/coreFallbackModal.ts

View workflow job for this annotation

GitHub Actions / build

'contentEl' is never reassigned. Use 'const' instead
contentEl.empty();
}
}
81 changes: 54 additions & 27 deletions src/lib/core/rdkitCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getCurrentTheme } from 'src/lib/themes/getCurrentTheme';

import { normalizePath, requestUrl, Notice } from 'obsidian';
import { i18n } from '../i18n';
import * as path from 'path';
import { githubAsset } from 'typings/githubAsset';

export default class RDKitCore implements ChemCore {
id: 'rdkit';
Expand All @@ -24,7 +24,19 @@ export default class RDKitCore implements ChemCore {
}

static async init(settings: ChemPluginSettings) {
if (!window.RDKit) window.RDKit = await loadRDKit();
if (!window.RDKit) {
try {
window.RDKit = await loadRDKit();
} catch (e) {
try {
window.RDKit = await loadRDKitUnpkg();
} catch (e) {
throw Error(
"Initializing rdkit failed: Can't fetch resources from unpkg."
);
}
}
}
return new RDKitCore(settings, window.RDKit);
}

Expand All @@ -39,7 +51,7 @@ export default class RDKitCore implements ChemCore {
if (!rxn) return this.logError(source);
svgstr = await this.drawReaction(rxn);
} else {
const mol = this.core.get_mol(source);
const mol = this.core.get_mol(source, JSON.stringify({}));
if (!mol) return this.logError(source);

// https://greglandrum.github.io/rdkit-blog/posts/2024-01-11-using-abbreviations.html
Expand Down Expand Up @@ -119,9 +131,13 @@ export default class RDKitCore implements ChemCore {

private logError = (source: string) => {
const div = createDiv();
div.createDiv().setText(i18n.t('errors.source.title', { source }));
div.createDiv('error-source').setText(
i18n.t('errors.source.title', { source })
);
div.createEl('br');
div.createDiv().setText(i18n.t('errors.rdkit.title'));
div.style.wordBreak = `break-word`;
div.style.userSelect = `text`;
return div;
};
}
Expand All @@ -130,77 +146,88 @@ export default class RDKitCore implements ChemCore {
// Initialize reference: https://github.com/rdkit/rdkit-js/tree/master/typescript
const loadRDKit = async () => {
const assetPath = normalizePath(
path.join(app.vault.configDir, 'plugins', 'chem', 'rdkit')
[app.vault.configDir, 'plugins', 'chem', 'rdkit'].join('/')
);
if (!(await app.vault.adapter.exists(assetPath))) {
console.log(assetPath);
await app.vault.adapter.mkdir(assetPath);
}

const jsPath = path.join(assetPath, 'RDKit_minimal.js');
const jsPath = [assetPath, 'RDKit_minimal.js'].join('/');
await checkOrDownload('RDKit_minimal.js');

const wasmPath = path.join(assetPath, 'RDKit_minimal.wasm');
const wasmPath = [assetPath, 'RDKit_minimal.wasm'].join('/');
await checkOrDownload('RDKit_minimal.wasm');

const rdkitBundler = document.body.createEl('script');
rdkitBundler.type = 'text/javascript';
rdkitBundler.id = 'chem-rdkit-bundler';
rdkitBundler.innerHTML = await app.vault.adapter.read(jsPath);
// backup rdkitBundler.src = 'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.js'

const getWasmURL = async () =>
URL.createObjectURL(
new Blob([await app.vault.adapter.readBinary(wasmPath)], {
type: 'application/wasm',
})
);
const url = await getWasmURL(); //backup: https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.wasm
const url = await getWasmURL();
const RDKit = await window.initRDKitModule({
locateFile: () => url,
});
URL.revokeObjectURL(url);
return RDKit;
};

const fetchAsset = async (target: string, localPath: string) => {
let res;
let data;
// See https://github.com/rdkit/rdkit-js/issues/160
const loadRDKitUnpkg = async () => {
const rdkitBundler = document.body.createEl('script');
new Notice('Fetching RDKit.js from unpkg...');

rdkitBundler.innerHTML = await requestUrl(
'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.js'
).text;

const RDKit = await window.initRDKitModule({
locateFile: () =>
'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.wasm',
});
new Notice('RDKit.js has been successfully loaded.');
return RDKit;
};

res = requestUrl(
const fetchAsset = async (target: string, localPath: string) => {
const assetInfo = await requestUrl(
`https://api.github.com/repos/acylation/obsidian-chem/releases/tags/${
app.plugins.getPlugin('chem')?.manifest.version ?? '0.4.0'
}`
);
data = await res.json;
const asset = data.assets.find((v: any) => v.name == target);
if (asset == undefined) {
throw 'Could not find the online asset!';
}
res = requestUrl({
).json;
const asset = assetInfo.assets.find((a: githubAsset) => a.name == target);
if (asset === undefined) throw Error('Could not find the online asset!');

const data = await requestUrl({
url: asset.url,
headers: { Accept: 'application/octet-stream' },
});
data = await res.arrayBuffer;
}).arrayBuffer;
await app.vault.adapter.writeBinary(localPath, data);
};

// TODO: i18n
const checkOrDownload = async (target: string) => {
const assetPath = normalizePath(
path.join(app.vault.configDir, 'plugins', 'chem', 'rdkit', target)
[app.vault.configDir, 'plugins', 'chem', 'rdkit', target].join('/')
);

if (!(await app.vault.adapter.exists(assetPath))) {
new Notice(`Chem: Downloading ${target}!`, 5000);
try {
await fetchAsset(target, assetPath);
new Notice(
`Chem: Resource ${target} successfully downloaded!`,
`Chem: Resource ${target} successfully downloaded! ✔️`,
5000
);
} catch (error) {
new Notice(`Chem: Failed to fetch ${target}: ` + error);
new Notice(`Chem: Failed to fetch ${target}: ${error} ❌`);
throw Error(
`Failed to fetch resource ${target} from GitHub release.`
);
}
}
};
7 changes: 7 additions & 0 deletions src/lib/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@
"name": "Inline SMILES Prefix",
"description": "The prefix to inline SMILES."
}
},
"modals": {
"core-fallback": {
"title": "Switch core failed",
"confirm": "Confirm",
"message": "Failed to switch to the target package, and will fallback to the default Smiles Drawer core."
}
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/lib/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@
"description": "行内 SMILES 的前缀。"
}
}
},
"modals": {
"core-fallback": {
"title": "切换渲染器失败",
"confirm": "确认",
"message": "切换目标渲染器失败,将回退为默认渲染器 Smiles Drawer。"
}
}
}
}
11 changes: 9 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { ChemSettingTab } from './settings/SettingTab';
import { SmilesBlock } from './SmilesBlock';
import { inlinePlugin } from './SmilesInline';

import { setCore, clearCore } from './global/chemCore';
import { setCore, clearCore, setFallbackCore } from './global/chemCore';
import { setBlocks, clearBlocks } from './global/blocks';
import { getDataview, clearDataview } from './global/dataview';
import { setObserver, detachObserver } from './lib/themes/themeObserver';
import { CoreFallbackModal } from './lib/core/coreFallbackModal';

export default class ChemPlugin extends Plugin {
settings: ChemPluginSettings;
Expand All @@ -19,7 +20,13 @@ export default class ChemPlugin extends Plugin {
// initialize global variables
setBlocks();
setObserver();
await setCore(this.settings);
setCore(this.settings, (error: string) => {
new CoreFallbackModal(this.app, error, async () => {
this.settings.core = 'smiles-drawer';
await this.saveSettings();
await setFallbackCore(this.settings);
}).open();
});

this.addSettingTab(new ChemSettingTab({ app: this.app, plugin: this }));

Expand Down
35 changes: 21 additions & 14 deletions src/settings/SettingTab.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import ChemPlugin from '../main';
import { DEFAULT_SETTINGS } from './base';
import { themeList } from '../lib/themes/theme';

import { setCore } from 'src/global/chemCore';
import {
setCore,
updateCoreSettings,
setFallbackCore,
} from 'src/global/chemCore';
import { refreshBlocks } from 'src/global/blocks';
import { clearDataview, getDataview } from 'src/global/dataview';

import { LivePreview } from './LivePreview';

import {
App,
Platform,
PluginSettingTab,
Setting,
SliderComponent,
} from 'obsidian';
import { App, PluginSettingTab, Setting, SliderComponent } from 'obsidian';
import { i18n } from 'src/lib/i18n';
import { LivePreview } from './LivePreview';
import { CoreFallbackModal } from '../lib/core/coreFallbackModal';

export class ChemSettingTab extends PluginSettingTab {
plugin: ChemPlugin;
Expand Down Expand Up @@ -140,12 +137,22 @@ export class ChemSettingTab extends PluginSettingTab {
rdkit: 'RDKit.js',
'smiles-drawer': 'Smiles Drawer',
})
.setDisabled(Platform.isIosApp)
.setValue(this.plugin.settings.core ?? false)
.onChange(async (value: 'rdkit' | 'smiles-drawer') => {
this.plugin.settings.core = value;
await this.plugin.saveSettings();
await this.updateCore();
await setCore(
this.plugin.settings,
async (error: string) => {
new CoreFallbackModal(app, error, async () => {
dropdown.setValue('smiles-drawer');
this.plugin.settings.core = 'smiles-drawer';
await this.plugin.saveSettings();
setFallbackCore(this.plugin.settings);
onSettingsChange();
}).open();
}
);
onSettingsChange();
})
);
Expand Down Expand Up @@ -379,5 +386,5 @@ export class ChemSettingTab extends PluginSettingTab {
refreshBlocks();
}

updateCore = async () => await setCore(this.plugin.settings);
updateCore = () => updateCoreSettings(this.plugin.settings);
}
Loading

0 comments on commit 226f6de

Please sign in to comment.