Skip to content

Commit

Permalink
#5190 - Include indigo without render module into ketcher-standalone …
Browse files Browse the repository at this point in the history
…build process (#5212)

- added ketcher-standalone builds for indigo without render module
- added structService reinitialization
  • Loading branch information
rrodionov91 authored and Guch1g0v committed Oct 17, 2024
1 parent fb230c2 commit 51b0bba
Show file tree
Hide file tree
Showing 19 changed files with 333 additions and 54 deletions.
4 changes: 4 additions & 0 deletions example/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ export default defineConfig({
find: 'web-worker:./indigoWorker',
replacement: './indigoWorker?worker',
},
{
find: '_indigo-ketcher-import-alias_',
replacement: 'indigo-ketcher',
},
],
},
customLogger: logger,
Expand Down
59 changes: 59 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 19 additions & 14 deletions packages/ketcher-core/src/application/ketcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ const allowedApiSettings = {

export class Ketcher {
logging: LogSettings;
#structService: StructService;
structService: StructService;
#formatterFactory: FormatterFactory;
#editor: Editor;
#indigo: Indigo;
_indigo: Indigo;
#eventBus: EventEmitter;

get editor(): Editor {
Expand All @@ -79,9 +79,9 @@ export class Ketcher {
assert(formatterFactory != null);

this.#editor = editor;
this.#structService = structService;
this.structService = structService;
this.#formatterFactory = formatterFactory;
this.#indigo = new Indigo(this.#structService);
this._indigo = new Indigo(this.structService);
this.#eventBus = new EventEmitter();
this.logging = {
enabled: false,
Expand All @@ -95,7 +95,7 @@ export class Ketcher {
}

get indigo() {
return this.#indigo;
return this._indigo;
}

// TEMP.: getting only dearomatize-on-load setting
Expand Down Expand Up @@ -300,7 +300,7 @@ export class Ketcher {
this.#editor.struct(),
);

return this.#structService.getInChIKey(struct);
return this.structService.getInChIKey(struct);
}

containsReaction(): boolean {
Expand Down Expand Up @@ -354,11 +354,11 @@ export class Ketcher {

if (window.isPolymerEditorTurnedOn) {
deleteAllEntitiesOnCanvas();
await parseAndAddMacromoleculesOnCanvas(structStr, this.#structService);
await parseAndAddMacromoleculesOnCanvas(structStr, this.structService);
} else {
const struct: Struct = await prepareStructToRender(
structStr,
this.#structService,
this.structService,
this,
);

Expand All @@ -375,7 +375,7 @@ export class Ketcher {
assert(typeof helmStr === 'string');
const struct: Struct = await prepareStructToRender(
helmStr,
this.#structService,
this.structService,
this,
);
struct.rescale();
Expand All @@ -393,11 +393,11 @@ export class Ketcher {
assert(typeof structStr === 'string');

if (window.isPolymerEditorTurnedOn) {
await parseAndAddMacromoleculesOnCanvas(structStr, this.#structService);
await parseAndAddMacromoleculesOnCanvas(structStr, this.structService);
} else {
const struct: Struct = await prepareStructToRender(
structStr,
this.#structService,
this.structService,
this,
);

Expand All @@ -413,7 +413,7 @@ export class Ketcher {
}

runAsyncAction<void>(async () => {
const struct = await this.#indigo.layout(this.#editor.struct());
const struct = await this._indigo.layout(this.#editor.struct());
const ketSerializer = new KetSerializer();
this.setMolecule(ketSerializer.serialize(struct));
}, this.eventBus);
Expand Down Expand Up @@ -458,7 +458,7 @@ export class Ketcher {
if (window.isPolymerEditorTurnedOn) {
throw new Error('Recognize is not available in macro mode');
}
return this.#indigo.recognize(image, { version });
return this._indigo.recognize(image, { version });
}

async generateImage(
Expand All @@ -481,7 +481,7 @@ export class Ketcher {
options.outputFormat = 'png';
}

const base64 = await this.#structService.generateImageAsBase64(
const base64 = await this.structService.generateImageAsBase64(
data,
options,
);
Expand All @@ -494,4 +494,9 @@ export class Ketcher {
const blob = new Blob([byteArray], { type: meta });
return blob;
}

public reinitializeIndigo(structService: StructService) {
this.structService = structService;
this._indigo = new Indigo(structService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,5 @@ export interface StructService {
data: ExplicitHydrogensData,
options?: StructServiceOptions,
) => Promise<ExplicitHydrogensResult>;
destroy?: () => void;
}
34 changes: 25 additions & 9 deletions packages/ketcher-react/src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ import init, { Config } from './script';
import { useEffect, useRef } from 'react';
import { createRoot, Root } from 'react-dom/client';

import { Ketcher } from 'ketcher-core';
import { Ketcher, StructService } from 'ketcher-core';
import classes from './Editor.module.less';
import clsx from 'clsx';
import { useResizeObserver } from './hooks';
import {
ketcherInitEventName,
KETCHER_ROOT_NODE_CLASS_NAME,
} from './constants';
import { KetcherBuilder } from './script/builders';

const mediaSizes = {
smallWidth: 1040,
Expand All @@ -47,13 +48,24 @@ function Editor(props: EditorProps) {
const initPromiseRef = useRef<ReturnType<typeof init> | null>(null);
const appRootRef = useRef<Root | null>(null);
const cleanupRef = useRef<(() => unknown) | null>(null);
const ketcherBuilderRef = useRef<KetcherBuilder | null>(null);
// eslint-disable-next-line @typescript-eslint/no-empty-function
const setServerRef = useRef<(structService: StructService) => void>(() => {});
const structServiceProvider = props.structServiceProvider;

const rootElRef = useRef<HTMLDivElement>(null);

const { height, width } = useResizeObserver<HTMLDivElement>({
ref: rootElRef,
});

useEffect(() => {
ketcherBuilderRef.current?.reinitializeApi(
props.structServiceProvider,
setServerRef.current,
);
}, [structServiceProvider]);

const initKetcher = () => {
appRootRef.current = createRoot(rootElRef.current as HTMLDivElement);

Expand All @@ -63,15 +75,19 @@ function Editor(props: EditorProps) {
appRoot: appRootRef.current,
});

initPromiseRef.current?.then(({ ketcher, ketcherId, cleanup }) => {
cleanupRef.current = cleanup;
initPromiseRef.current?.then(
({ ketcher, ketcherId, cleanup, builder, setServer }) => {
cleanupRef.current = cleanup;
ketcherBuilderRef.current = builder;
setServerRef.current = setServer;

if (typeof props.onInit === 'function' && ketcher) {
props.onInit(ketcher);
const ketcherInitEvent = new Event(ketcherInitEventName(ketcherId));
window.dispatchEvent(ketcherInitEvent);
}
});
if (typeof props.onInit === 'function' && ketcher) {
props.onInit(ketcher);
const ketcherInitEvent = new Event(ketcherInitEventName(ketcherId));
window.dispatchEvent(ketcherInitEvent);
}
},
);
};
useEffect(() => {
if (initPromiseRef.current === null) {
Expand Down
3 changes: 3 additions & 0 deletions packages/ketcher-react/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ export const KETCHER_ROOT_NODE_CSS_SELECTOR = `.${KETCHER_ROOT_NODE_CLASS_NAME}`

export const EditorClassName = 'Ketcher-polymer-editor-root';
export const KETCHER_MACROMOLECULES_ROOT_NODE_SELECTOR = `.${EditorClassName}`;
export const STRUCT_SERVICE_NO_RENDER_INITIALIZED_EVENT =
'struct-service-no-render-initialized';
export const STRUCT_SERVICE_INITIALIZED_EVENT = 'struct-service-initialized';
1 change: 1 addition & 0 deletions packages/ketcher-react/src/script/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function createApi(
getInChIKey: structService.getInChIKey.bind(structService),
toggleExplicitHydrogens:
structService.toggleExplicitHydrogens.bind(structService),
destroy: structService.destroy?.bind(structService),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
StructService,
StructServiceProvider,
ketcherProvider,
KetcherLogger,
} from 'ketcher-core';

import { ButtonsConfig } from './ButtonsConfig';
Expand All @@ -30,6 +31,7 @@ import createApi from '../../api';
import { initApp } from '../../ui';
import { Root } from 'react-dom/client';
import { IndigoProvider } from 'src/script/providers';
import { STRUCT_SERVICE_INITIALIZED_EVENT } from '../../../constants';

class KetcherBuilder {
private structService: StructService | null;
Expand All @@ -51,6 +53,39 @@ class KetcherBuilder {
);
}

reinitializeApi(
structServiceProvider: StructServiceProvider,
setStructServiceToStore: (structService: StructService) => void,
) {
const oldStructService = this.structService;

this.structService = createApi(
structServiceProvider,
DefaultStructServiceOptions,
);

window.addEventListener(
STRUCT_SERVICE_INITIALIZED_EVENT,
() => {
oldStructService?.destroy?.();

if (!this.structService) {
KetcherLogger.warn('Structure service is not reinitialized');

return;
}

const ketcher = ketcherProvider.getKetcher();
ketcher.reinitializeIndigo(this.structService);
IndigoProvider.setIndigo(this.structService);
setStructServiceToStore(this.structService);
},
{ once: true },
);

return this.structService;
}

appendServiceMode(mode: ServiceMode) {
this.serviceMode = mode;
}
Expand All @@ -66,14 +101,16 @@ class KetcherBuilder {
setKetcher: (ketcher: Ketcher) => void;
ketcherId: string;
cleanup: ReturnType<typeof initApp> | null;
setServer: (structService: StructService) => void;
}> {
const { structService } = this;
let cleanup: ReturnType<typeof initApp> | null = null;

const { editor, setKetcher, ketcherId } = await new Promise<{
const { editor, setKetcher, ketcherId, setServer } = await new Promise<{
editor: Editor;
setKetcher: (ketcher: Ketcher) => void;
ketcherId: string;
setServer: (structService: StructService) => void;
}>((resolve) => {
cleanup = initApp(
element,
Expand All @@ -100,7 +137,7 @@ class KetcherBuilder {
() => {};
this.formatterFactory = new FormatterFactory(structService!);

return { setKetcher, ketcherId, cleanup };
return { setKetcher, ketcherId, cleanup, setServer };
}

build() {
Expand Down
Loading

0 comments on commit 51b0bba

Please sign in to comment.