Skip to content

Commit 226f6de

Browse files
committed
Set fallback core
1 parent 417c471 commit 226f6de

File tree

8 files changed

+207
-47
lines changed

8 files changed

+207
-47
lines changed

src/global/chemCore.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,33 @@ import RDKitCore from '../lib/core/rdkitCore';
66

77
export let gRenderCore: ChemCore;
88

9-
export const setCore = async (settings: ChemPluginSettings) => {
9+
export const setCore = async (
10+
settings: ChemPluginSettings,
11+
onFallback: (error: string) => void
12+
) => {
1013
if (!gRenderCore || settings.core !== gRenderCore.id) {
11-
if (settings.core == 'smiles-drawer')
14+
if (settings.core === 'smiles-drawer') {
1215
gRenderCore = new SmilesDrawerCore(settings);
13-
if (settings.core == 'rdkit') {
14-
gRenderCore = await RDKitCore.init(settings);
16+
updateCoreSettings(settings);
17+
} else if (settings.core === 'rdkit') {
18+
try {
19+
gRenderCore = await RDKitCore.init(settings);
20+
updateCoreSettings(settings);
21+
} catch (error) {
22+
onFallback(error);
23+
}
24+
} else {
25+
onFallback(`invalid chem core id. ${settings.core}`);
1526
}
1627
}
28+
};
29+
30+
export const setFallbackCore = async (settings: ChemPluginSettings) => {
31+
gRenderCore = new SmilesDrawerCore(settings);
32+
updateCoreSettings(settings);
33+
};
34+
35+
export const updateCoreSettings = (settings: ChemPluginSettings) => {
1736
gRenderCore.settings = settings;
1837
};
1938

src/lib/core/coreFallbackModal.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Modal, App, ButtonComponent } from 'obsidian';
2+
import { i18n } from 'src/lib/i18n';
3+
4+
export class CoreFallbackModal extends Modal {
5+
msg: string;
6+
onConfirm: () => void;
7+
8+
constructor(app: App, text: string, onConfrim: () => void) {
9+
super(app);
10+
this.msg = text;
11+
this.onConfirm = onConfrim;
12+
}
13+
14+
onOpen() {
15+
let { contentEl } = this;
16+
17+
contentEl.createEl('h3', {
18+
text: i18n.t('modals.core-fallback.title'),
19+
});
20+
21+
contentEl.createEl('div', {
22+
text: i18n.t('modals.core-fallback.message'),
23+
});
24+
25+
contentEl.createEl('br');
26+
27+
contentEl.createEl('div', {
28+
text: this.msg,
29+
});
30+
31+
const div = contentEl.createDiv();
32+
div.style.display = 'flex';
33+
div.style.flex = '1 1 auto';
34+
div.style.justifyContent = 'flex-end';
35+
div.style.padding = '3px';
36+
37+
new ButtonComponent(div)
38+
.setCta()
39+
.setButtonText(i18n.t('modals.core-fallback.confirm'))
40+
.onClick(() => {
41+
this.close();
42+
});
43+
}
44+
45+
onClose() {
46+
this.onConfirm();
47+
let { contentEl } = this;
48+
contentEl.empty();
49+
}
50+
}

src/lib/core/rdkitCore.ts

+54-27
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { getCurrentTheme } from 'src/lib/themes/getCurrentTheme';
88

99
import { normalizePath, requestUrl, Notice } from 'obsidian';
1010
import { i18n } from '../i18n';
11-
import * as path from 'path';
11+
import { githubAsset } from 'typings/githubAsset';
1212

1313
export default class RDKitCore implements ChemCore {
1414
id: 'rdkit';
@@ -24,7 +24,19 @@ export default class RDKitCore implements ChemCore {
2424
}
2525

2626
static async init(settings: ChemPluginSettings) {
27-
if (!window.RDKit) window.RDKit = await loadRDKit();
27+
if (!window.RDKit) {
28+
try {
29+
window.RDKit = await loadRDKit();
30+
} catch (e) {
31+
try {
32+
window.RDKit = await loadRDKitUnpkg();
33+
} catch (e) {
34+
throw Error(
35+
"Initializing rdkit failed: Can't fetch resources from unpkg."
36+
);
37+
}
38+
}
39+
}
2840
return new RDKitCore(settings, window.RDKit);
2941
}
3042

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

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

120132
private logError = (source: string) => {
121133
const div = createDiv();
122-
div.createDiv().setText(i18n.t('errors.source.title', { source }));
134+
div.createDiv('error-source').setText(
135+
i18n.t('errors.source.title', { source })
136+
);
123137
div.createEl('br');
124138
div.createDiv().setText(i18n.t('errors.rdkit.title'));
139+
div.style.wordBreak = `break-word`;
140+
div.style.userSelect = `text`;
125141
return div;
126142
};
127143
}
@@ -130,77 +146,88 @@ export default class RDKitCore implements ChemCore {
130146
// Initialize reference: https://github.com/rdkit/rdkit-js/tree/master/typescript
131147
const loadRDKit = async () => {
132148
const assetPath = normalizePath(
133-
path.join(app.vault.configDir, 'plugins', 'chem', 'rdkit')
149+
[app.vault.configDir, 'plugins', 'chem', 'rdkit'].join('/')
134150
);
135151
if (!(await app.vault.adapter.exists(assetPath))) {
136-
console.log(assetPath);
137152
await app.vault.adapter.mkdir(assetPath);
138153
}
139154

140-
const jsPath = path.join(assetPath, 'RDKit_minimal.js');
155+
const jsPath = [assetPath, 'RDKit_minimal.js'].join('/');
141156
await checkOrDownload('RDKit_minimal.js');
142157

143-
const wasmPath = path.join(assetPath, 'RDKit_minimal.wasm');
158+
const wasmPath = [assetPath, 'RDKit_minimal.wasm'].join('/');
144159
await checkOrDownload('RDKit_minimal.wasm');
145160

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

152166
const getWasmURL = async () =>
153167
URL.createObjectURL(
154168
new Blob([await app.vault.adapter.readBinary(wasmPath)], {
155169
type: 'application/wasm',
156170
})
157171
);
158-
const url = await getWasmURL(); //backup: https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.wasm
172+
const url = await getWasmURL();
159173
const RDKit = await window.initRDKitModule({
160174
locateFile: () => url,
161175
});
162176
URL.revokeObjectURL(url);
163177
return RDKit;
164178
};
165179

166-
const fetchAsset = async (target: string, localPath: string) => {
167-
let res;
168-
let data;
180+
// See https://github.com/rdkit/rdkit-js/issues/160
181+
const loadRDKitUnpkg = async () => {
182+
const rdkitBundler = document.body.createEl('script');
183+
new Notice('Fetching RDKit.js from unpkg...');
184+
185+
rdkitBundler.innerHTML = await requestUrl(
186+
'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.js'
187+
).text;
188+
189+
const RDKit = await window.initRDKitModule({
190+
locateFile: () =>
191+
'https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.wasm',
192+
});
193+
new Notice('RDKit.js has been successfully loaded.');
194+
return RDKit;
195+
};
169196

170-
res = requestUrl(
197+
const fetchAsset = async (target: string, localPath: string) => {
198+
const assetInfo = await requestUrl(
171199
`https://api.github.com/repos/acylation/obsidian-chem/releases/tags/${
172200
app.plugins.getPlugin('chem')?.manifest.version ?? '0.4.0'
173201
}`
174-
);
175-
data = await res.json;
176-
const asset = data.assets.find((v: any) => v.name == target);
177-
if (asset == undefined) {
178-
throw 'Could not find the online asset!';
179-
}
180-
res = requestUrl({
202+
).json;
203+
const asset = assetInfo.assets.find((a: githubAsset) => a.name == target);
204+
if (asset === undefined) throw Error('Could not find the online asset!');
205+
206+
const data = await requestUrl({
181207
url: asset.url,
182208
headers: { Accept: 'application/octet-stream' },
183-
});
184-
data = await res.arrayBuffer;
209+
}).arrayBuffer;
185210
await app.vault.adapter.writeBinary(localPath, data);
186211
};
187212

188-
// TODO: i18n
189213
const checkOrDownload = async (target: string) => {
190214
const assetPath = normalizePath(
191-
path.join(app.vault.configDir, 'plugins', 'chem', 'rdkit', target)
215+
[app.vault.configDir, 'plugins', 'chem', 'rdkit', target].join('/')
192216
);
193217

194218
if (!(await app.vault.adapter.exists(assetPath))) {
195219
new Notice(`Chem: Downloading ${target}!`, 5000);
196220
try {
197221
await fetchAsset(target, assetPath);
198222
new Notice(
199-
`Chem: Resource ${target} successfully downloaded!`,
223+
`Chem: Resource ${target} successfully downloaded! ✔️`,
200224
5000
201225
);
202226
} catch (error) {
203-
new Notice(`Chem: Failed to fetch ${target}: ` + error);
227+
new Notice(`Chem: Failed to fetch ${target}: ${error} ❌`);
228+
throw Error(
229+
`Failed to fetch resource ${target} from GitHub release.`
230+
);
204231
}
205232
}
206233
};

src/lib/translations/en.json

+7
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@
101101
"name": "Inline SMILES Prefix",
102102
"description": "The prefix to inline SMILES."
103103
}
104+
},
105+
"modals": {
106+
"core-fallback": {
107+
"title": "Switch core failed",
108+
"confirm": "Confirm",
109+
"message": "Failed to switch to the target package, and will fallback to the default Smiles Drawer core."
110+
}
104111
}
105112
}
106113
}

src/lib/translations/zh-CN.json

+7
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@
102102
"description": "行内 SMILES 的前缀。"
103103
}
104104
}
105+
},
106+
"modals": {
107+
"core-fallback": {
108+
"title": "切换渲染器失败",
109+
"confirm": "确认",
110+
"message": "切换目标渲染器失败,将回退为默认渲染器 Smiles Drawer。"
111+
}
105112
}
106113
}
107114
}

src/main.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import { ChemSettingTab } from './settings/SettingTab';
55
import { SmilesBlock } from './SmilesBlock';
66
import { inlinePlugin } from './SmilesInline';
77

8-
import { setCore, clearCore } from './global/chemCore';
8+
import { setCore, clearCore, setFallbackCore } from './global/chemCore';
99
import { setBlocks, clearBlocks } from './global/blocks';
1010
import { getDataview, clearDataview } from './global/dataview';
1111
import { setObserver, detachObserver } from './lib/themes/themeObserver';
12+
import { CoreFallbackModal } from './lib/core/coreFallbackModal';
1213

1314
export default class ChemPlugin extends Plugin {
1415
settings: ChemPluginSettings;
@@ -19,7 +20,13 @@ export default class ChemPlugin extends Plugin {
1920
// initialize global variables
2021
setBlocks();
2122
setObserver();
22-
await setCore(this.settings);
23+
setCore(this.settings, (error: string) => {
24+
new CoreFallbackModal(this.app, error, async () => {
25+
this.settings.core = 'smiles-drawer';
26+
await this.saveSettings();
27+
await setFallbackCore(this.settings);
28+
}).open();
29+
});
2330

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

src/settings/SettingTab.ts

+21-14
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
import ChemPlugin from '../main';
22
import { DEFAULT_SETTINGS } from './base';
33
import { themeList } from '../lib/themes/theme';
4-
5-
import { setCore } from 'src/global/chemCore';
4+
import {
5+
setCore,
6+
updateCoreSettings,
7+
setFallbackCore,
8+
} from 'src/global/chemCore';
69
import { refreshBlocks } from 'src/global/blocks';
710
import { clearDataview, getDataview } from 'src/global/dataview';
811

9-
import { LivePreview } from './LivePreview';
10-
11-
import {
12-
App,
13-
Platform,
14-
PluginSettingTab,
15-
Setting,
16-
SliderComponent,
17-
} from 'obsidian';
12+
import { App, PluginSettingTab, Setting, SliderComponent } from 'obsidian';
1813
import { i18n } from 'src/lib/i18n';
14+
import { LivePreview } from './LivePreview';
15+
import { CoreFallbackModal } from '../lib/core/coreFallbackModal';
1916

2017
export class ChemSettingTab extends PluginSettingTab {
2118
plugin: ChemPlugin;
@@ -140,12 +137,22 @@ export class ChemSettingTab extends PluginSettingTab {
140137
rdkit: 'RDKit.js',
141138
'smiles-drawer': 'Smiles Drawer',
142139
})
143-
.setDisabled(Platform.isIosApp)
144140
.setValue(this.plugin.settings.core ?? false)
145141
.onChange(async (value: 'rdkit' | 'smiles-drawer') => {
146142
this.plugin.settings.core = value;
147143
await this.plugin.saveSettings();
148-
await this.updateCore();
144+
await setCore(
145+
this.plugin.settings,
146+
async (error: string) => {
147+
new CoreFallbackModal(app, error, async () => {
148+
dropdown.setValue('smiles-drawer');
149+
this.plugin.settings.core = 'smiles-drawer';
150+
await this.plugin.saveSettings();
151+
setFallbackCore(this.plugin.settings);
152+
onSettingsChange();
153+
}).open();
154+
}
155+
);
149156
onSettingsChange();
150157
})
151158
);
@@ -379,5 +386,5 @@ export class ChemSettingTab extends PluginSettingTab {
379386
refreshBlocks();
380387
}
381388

382-
updateCore = async () => await setCore(this.plugin.settings);
389+
updateCore = () => updateCoreSettings(this.plugin.settings);
383390
}

0 commit comments

Comments
 (0)