@datagrok/bio 2.12.17 → 2.12.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,33 +3,51 @@ import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
- import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
6
+ import {delay} from '@datagrok-libraries/utils/src/test';
7
+ import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
7
8
  import {
8
9
  getUserLibSettings, setUserLibSettings, LIB_PATH
9
10
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
10
11
  import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
11
12
  import {
12
- IMonomerLibHelper,
13
+ IMonomerLibFileEventManager, IMonomerLibHelper,
13
14
  } from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
15
+
14
16
  import {MonomerLib} from './monomer-lib';
15
17
  import {MonomerLibFileManager} from './library-file-manager/file-manager';
16
18
  import {MonomerLibFileEventManager} from './library-file-manager/event-manager';
19
+
17
20
  import {_package} from '../../package';
18
21
 
19
- type MonomerLibWindowType = Window & { $monomerLibHelper: MonomerLibManager };
22
+ type MonomerLibWindowType = Window & { $monomerLibHelperPromise?: Promise<MonomerLibManager> };
20
23
  declare const window: MonomerLibWindowType;
21
24
 
22
- export async function getLibFileNameList(): Promise<string[]> {
23
- const fileEventManager = MonomerLibFileEventManager.getInstance();
24
- const fileManager = await MonomerLibFileManager.getInstance(fileEventManager);
25
- return fileManager.getValidLibraryPaths();
26
- }
27
-
28
25
  /** Singleton wrapper for MonomerLib, provides API for managing libraries on
29
26
  * the platform */
30
27
  export class MonomerLibManager implements IMonomerLibHelper {
31
28
  private readonly _monomerLib = new MonomerLib({}, 'MAIN');
32
29
 
30
+ private _eventManager: MonomerLibFileEventManager;
31
+
32
+ public get eventManager(): IMonomerLibFileEventManager { return this._eventManager; }
33
+
34
+ public async awaitLoaded(timeout: number = 3000): Promise<void> {
35
+ return await Promise.race([
36
+ (async () => {
37
+ const fileManager = await this.getFileManager();
38
+ await fileManager.filesPromise;
39
+ return true;
40
+ })(),
41
+ (async () => {
42
+ await delay(timeout);
43
+ return false;
44
+ })(),
45
+ ]).then((res) => {
46
+ if (!res)
47
+ throw new Error(`Loading monomer libraries is timeout ${timeout} ms.`);
48
+ });
49
+ }
50
+
33
51
  /** Protect constructor to prevent multiple instantiation. */
34
52
  protected constructor() {}
35
53
 
@@ -40,6 +58,19 @@ export class MonomerLibManager implements IMonomerLibHelper {
40
58
  return this._monomerLib;
41
59
  }
42
60
 
61
+ /** Instance promise of {@link getFileManager} */
62
+ private _fileManagerPromise?: Promise<MonomerLibFileManager>;
63
+
64
+ async getFileManager(): Promise<MonomerLibFileManager> {
65
+ if (this._fileManagerPromise === undefined) {
66
+ this._fileManagerPromise = (async () => {
67
+ const fileManager: MonomerLibFileManager = await MonomerLibFileManager.create(this, this._eventManager);
68
+ return fileManager;
69
+ })();
70
+ }
71
+ return this._fileManagerPromise;
72
+ }
73
+
43
74
  /** Allows syncing with managing settings/loading libraries */
44
75
  public loadLibrariesPromise: Promise<void> = Promise.resolve();
45
76
 
@@ -53,7 +84,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
53
84
  // through blocking this.loadLibrariesPromise
54
85
  try {
55
86
  const [libFileNameList, settings]: [string[], UserLibSettings] = await Promise.all([
56
- getLibFileNameList(),
87
+ (await this.getFileManager()).getValidLibraryPaths(),
57
88
  getUserLibSettings(),
58
89
  ]);
59
90
 
@@ -91,9 +122,8 @@ export class MonomerLibManager implements IMonomerLibHelper {
91
122
  * @return {Promise<IMonomerLib>} Promise of IMonomerLib
92
123
  */
93
124
  async readLibrary(path: string, fileName: string): Promise<IMonomerLib> {
94
- const eventManager = MonomerLibFileEventManager.getInstance();
95
- const libFileManager = await MonomerLibFileManager.getInstance(eventManager);
96
- const lib: IMonomerLib = await libFileManager.loadLibraryFromFile(path, fileName);
125
+ const fileManager = await this.getFileManager();
126
+ const lib: IMonomerLib = await fileManager.loadLibraryFromFile(path, fileName);
97
127
  return lib;
98
128
  }
99
129
 
@@ -103,19 +133,27 @@ export class MonomerLibManager implements IMonomerLibHelper {
103
133
  if (invalidNames.length > 0)
104
134
  throw new Error(`Cannot select libraries ${invalidNames}: no such library in the list`);
105
135
  const settings = await getUserLibSettings();
106
- settings.exclude = (await getLibFileNameList()).filter((fileName) => !libFileNameList.includes(fileName));
136
+ settings.exclude = ((await this.getFileManager()).getValidLibraryPaths())
137
+ .filter((fileName) => !libFileNameList.includes(fileName));
107
138
  await setUserLibSettings(settings);
108
139
  }
109
140
 
110
141
  private async getInvalidFileNames(libFileNameList: string[]): Promise<string[]> {
111
- const availableFileNames = await getLibFileNameList();
142
+ const availableFileNames = (await this.getFileManager()).getValidLibraryPaths();
112
143
  const invalidNames = libFileNameList.filter((fileName) => !availableFileNames.includes(fileName));
113
144
  return invalidNames;
114
145
  }
115
146
 
116
147
  // -- Instance singleton --
117
- public static get instance(): MonomerLibManager {
118
- if (!window.$monomerLibHelper) window.$monomerLibHelper = new MonomerLibManager();
119
- return window.$monomerLibHelper;
148
+ public static async getInstance(): Promise<MonomerLibManager> {
149
+ let res = window.$monomerLibHelperPromise;
150
+ if (res === undefined) {
151
+ res = window.$monomerLibHelperPromise = (async () => {
152
+ const instance = new MonomerLibManager();
153
+ instance._eventManager = MonomerLibFileEventManager.getInstance();
154
+ return instance;
155
+ })();
156
+ }
157
+ return res;
120
158
  }
121
159
  }
@@ -1,7 +1,13 @@
1
- import * as rxjs from 'rxjs';
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {BehaviorSubject, Observable, Subject} from 'rxjs';
2
6
  import {debounceTime, tap, skip} from 'rxjs/operators';
3
7
 
4
- export class MonomerLibFileEventManager {
8
+ import {IMonomerLibFileEventManager} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
9
+
10
+ export class MonomerLibFileEventManager implements IMonomerLibFileEventManager {
5
11
  // WARNING: this must be a singleton because it manages the unique state
6
12
  private constructor() {}
7
13
 
@@ -14,9 +20,9 @@ export class MonomerLibFileEventManager {
14
20
  return MonomerLibFileEventManager._instance;
15
21
  }
16
22
 
17
- private _libraryFilesUpdateSubject$ = new rxjs.BehaviorSubject<string[]>([]);
18
- private _addLibraryFilesSubject$ = new rxjs.Subject<void>();
19
- private _librarySelectionSubject$ = new rxjs.Subject<[string, boolean]>();
23
+ private _libraryFilesUpdateSubject$ = new BehaviorSubject<string[]>([]);
24
+ private _addLibraryFilesSubject$ = new Subject<void>();
25
+ private _librarySelectionSubject$ = new Subject<[string, boolean]>();
20
26
 
21
27
  getValidFilesPathList(): string[] {
22
28
  return this._libraryFilesUpdateSubject$.getValue();
@@ -38,19 +44,19 @@ export class MonomerLibFileEventManager {
38
44
  this._libraryFilesUpdateSubject$.next(newList);
39
45
  }
40
46
 
41
- get updateUIControlsRequested$(): rxjs.Observable<string[]> {
47
+ get updateUIControlsRequested$(): Observable<string[]> {
42
48
  return this._libraryFilesUpdateSubject$.pipe(
43
49
  // debounceTime(1000)
44
50
  );
45
51
  }
46
52
 
47
- get updateValidLibraryFileListRequested$(): rxjs.Observable<string[]> {
53
+ get updateValidLibraryFileListRequested$(): Observable<string[]> {
48
54
  return this._libraryFilesUpdateSubject$.pipe(
49
55
  // debounceTime(3000)
50
56
  );
51
57
  }
52
58
 
53
- get addLibraryFileRequested$(): rxjs.Observable<void> {
59
+ get addLibraryFileRequested$(): Observable<void> {
54
60
  return this._addLibraryFilesSubject$.pipe(
55
61
  // debounceTime(1000)
56
62
  );
@@ -60,7 +66,7 @@ export class MonomerLibFileEventManager {
60
66
  this._addLibraryFilesSubject$.next();
61
67
  }
62
68
 
63
- get librarySelectionRequested$(): rxjs.Observable<[string, boolean]> {
69
+ get librarySelectionRequested$(): Observable<[string, boolean]> {
64
70
  return this._librarySelectionSubject$;
65
71
  }
66
72
 
@@ -3,47 +3,56 @@ import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
- import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types/index';
6
+ import {JSONSchemaType} from 'ajv';
7
+
8
+ import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
7
9
  import {LIB_PATH} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
8
- import {MonomerLib} from '../monomer-lib';
9
10
  import {
10
11
  HELM_REQUIRED_FIELD as REQ,
11
12
  } from '@datagrok-libraries/bio/src/utils/const';
12
- import {JSONSchemaType} from 'ajv';
13
+ import {
14
+ IMonomerLibHelper, IMonomerLibFileManager
15
+ } from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
16
+
17
+ import {MonomerLib} from '../monomer-lib';
13
18
  import {HELM_JSON_SCHEMA_PATH} from './consts';
14
19
  import {MonomerLibFileEventManager} from './event-manager';
15
-
16
20
  import {MonomerLibFileValidator} from './file-validator';
17
- import {MonomerLibManager} from '../lib-manager';
21
+
22
+ import {_package} from '../../../package';
23
+
18
24
 
19
25
  /** Singleton for adding, validation and reading of monomer library files.
20
26
  * All files **must** be aligned to the HELM standard before adding. */
21
- export class MonomerLibFileManager {
27
+ export class MonomerLibFileManager implements IMonomerLibFileManager {
28
+ public filesPromise: Promise<void> = Promise.resolve();
29
+
22
30
  private constructor(
23
- private libraryFileValidator: MonomerLibFileValidator,
24
- private libraryEventManager: MonomerLibFileEventManager,
31
+ private readonly fileValidator: MonomerLibFileValidator,
32
+ private readonly libHelper: IMonomerLibHelper,
33
+ public readonly eventManager: MonomerLibFileEventManager,
25
34
  ) {
26
- this.libraryEventManager.updateValidLibraryFileListRequested$.subscribe(async () => {
35
+ this.eventManager.updateValidLibraryFileListRequested$.subscribe(async () => {
27
36
  await this.updateValidLibraryList();
28
37
  });
29
38
  }
30
39
 
31
- private static instancePromise: Promise<MonomerLibFileManager> | undefined;
40
+ private static objCounter: number = -1;
41
+ private readonly objId: number = ++MonomerLibFileManager.objCounter;
42
+
43
+ protected toLog(): string {
44
+ return `MonomerLibFileManager<${this.objId}>`;
45
+ }
32
46
 
33
- static async getInstance(
34
- libraryEventManager: MonomerLibFileEventManager,
47
+ /** For internal use only, get {@link IMonomerLibHelper.getFileManager} */
48
+ public static async create(
49
+ libHelper: IMonomerLibHelper, eventManager: MonomerLibFileEventManager
35
50
  ): Promise<MonomerLibFileManager> {
36
- if (!MonomerLibFileManager.instancePromise) {
37
- MonomerLibFileManager.instancePromise = (async () => {
38
- const helmSchemaRaw = await grok.dapi.files.readAsText(HELM_JSON_SCHEMA_PATH);
39
- const helmSchema = JSON.parse(helmSchemaRaw) as JSONSchemaType<any>;
40
-
41
- const fileValidator = new MonomerLibFileValidator(helmSchema);
42
- return new MonomerLibFileManager(fileValidator, libraryEventManager);
43
- })();
44
- }
51
+ const helmSchemaRaw = await grok.dapi.files.readAsText(HELM_JSON_SCHEMA_PATH);
52
+ const helmSchema = JSON.parse(helmSchemaRaw) as JSONSchemaType<any>;
45
53
 
46
- return MonomerLibFileManager.instancePromise;
54
+ const fileValidator = new MonomerLibFileValidator(helmSchema);
55
+ return new MonomerLibFileManager(fileValidator, libHelper, eventManager);
47
56
  }
48
57
 
49
58
  /** Add standard .json monomer library */
@@ -91,72 +100,82 @@ export class MonomerLibFileManager {
91
100
  const monomers: { [polymerType: string]: { [monomerSymbol: string]: Monomer } } = {};
92
101
  const polymerTypes: string[] = [];
93
102
  rawLibData.forEach((monomer) => {
94
- if (!polymerTypes.includes(monomer[REQ.POLYMER_TYPE])) {
95
- monomers[monomer[REQ.POLYMER_TYPE]] = {};
96
- polymerTypes.push(monomer[REQ.POLYMER_TYPE]);
103
+ const polymerType = monomer[REQ.POLYMER_TYPE];
104
+ const monomerSymbol = monomer[REQ.SYMBOL];
105
+ if (!polymerTypes.includes(polymerType)) {
106
+ monomers[polymerType] = {};
107
+ polymerTypes.push(polymerType);
97
108
  }
98
- monomers[monomer[REQ.POLYMER_TYPE]][monomer[REQ.SYMBOL]] = monomer as Monomer;
109
+ monomers[polymerType][monomerSymbol] = monomer as Monomer;
99
110
  });
100
111
 
101
112
  return new MonomerLib(monomers, fileName);
102
113
  }
103
114
 
104
115
  getValidLibraryPaths(): string[] {
105
- return this.libraryEventManager.getValidFilesPathList();
116
+ return this.eventManager.getValidFilesPathList();
106
117
  }
107
118
 
108
119
  // TODO: remove after adding init from user data storage
109
120
  // WARNING: a temporary solution
110
121
  async getValidLibraryPathsAsynchronously(): Promise<string[]> {
111
- return await this.libraryEventManager.getValidLibraryPathsAsynchronously();
122
+ return await this.eventManager.getValidLibraryPathsAsynchronously();
112
123
  }
113
124
 
114
-
115
125
  private async libraryFileExists(fileName: string): Promise<boolean> {
116
126
  return await grok.dapi.files.exists(LIB_PATH + `${fileName}`);
117
127
  }
118
128
 
119
129
  private async updateValidLibraryList(): Promise<void> {
120
- const invalidFiles = [] as string[];
121
- // console.log(`files before validation:`, this.libraryEventManager.getValidFilesPathList());
122
- const filePaths = await this.getFilePathsAtDefaultLocation();
130
+ const logPrefix: string = `${this.toLog()}.updateValidLibraryList()`;
131
+ _package.logger.debug(`${logPrefix}, start`);
132
+ return this.filesPromise = this.filesPromise.then(async () => {
133
+ _package.logger.debug(`${logPrefix}, IN`);
134
+ const invalidFiles = [] as string[];
135
+ // console.log(`files before validation:`, this.libraryEventManager.getValidFilesPathList());
136
+ const filePaths = await this.getFilePathsAtDefaultLocation();
137
+
138
+ if (!this.fileListHasChanged(filePaths)) {
139
+ _package.logger.debug(`${logPrefix}, end, not changed`);
140
+ return;
141
+ }
123
142
 
124
- if (!this.fileListHasChanged(filePaths))
125
- return;
143
+ for (const path of filePaths) {
144
+ if (!path.endsWith('.json')) {
145
+ invalidFiles.push(path);
146
+ continue;
147
+ }
126
148
 
127
- for (const path of filePaths) {
128
- if (!path.endsWith('.json')) {
129
- invalidFiles.push(path);
130
- continue;
149
+ const fileContent = await grok.dapi.files.readAsText(LIB_PATH + `${path}`);
150
+ if (!this.isValidHELMLibrary(fileContent, path))
151
+ invalidFiles.push(path);
131
152
  }
132
153
 
133
- const fileContent = await grok.dapi.files.readAsText(LIB_PATH + `${path}`);
134
- if (!this.isValidHELMLibrary(fileContent, path))
135
- invalidFiles.push(path);
136
- }
137
-
138
- const validLibraryPaths = filePaths.filter((path) => !invalidFiles.includes(path));
154
+ const validLibraryPaths = filePaths.filter((path) => !invalidFiles.includes(path));
139
155
 
140
- if (this.fileListHasChanged(validLibraryPaths)) {
141
- this.libraryEventManager.changeValidFilesPathList(validLibraryPaths);
142
- MonomerLibManager.instance.loadLibraries(true);
143
- }
144
- // console.log(`files after validation:`, this.libraryEventManager.getValidFilesPathList());
156
+ if (this.fileListHasChanged(validLibraryPaths)) {
157
+ this.eventManager.changeValidFilesPathList(validLibraryPaths);
158
+ this.libHelper.loadLibraries(true);
159
+ }
160
+ // console.log(`files after validation:`, this.libraryEventManager.getValidFilesPathList());
145
161
 
146
- if (validLibraryPaths.some((el) => !el.endsWith('.json')))
147
- console.warn(`Wrong validation: ${validLibraryPaths}`);
162
+ if (validLibraryPaths.some((el) => !el.endsWith('.json')))
163
+ _package.logger.warning(`Wrong validation: ${validLibraryPaths}`);
148
164
 
149
- if (invalidFiles.length > 0) {
150
- const message = `Invalid monomer library files in ${LIB_PATH}` +
151
- `, consider fixing or removing them: ${invalidFiles.join(', ')}`;
165
+ if (invalidFiles.length > 0) {
166
+ const message = `Invalid monomer library files in ${LIB_PATH}` +
167
+ `, consider fixing or removing them: ${invalidFiles.join(', ')}`;
152
168
 
153
- console.warn(message);
154
- // grok.shell.warning(message);
155
- }
169
+ _package.logger.warning(message);
170
+ // grok.shell.warning(message);
171
+ }
172
+ _package.logger.debug(`${logPrefix}, OUT`);
173
+ });
174
+ _package.logger.debug(`${logPrefix}, end`);
156
175
  }
157
176
 
158
177
  private fileListHasChanged(newList: string[]): boolean {
159
- const currentList = this.libraryEventManager.getValidFilesPathList();
178
+ const currentList = this.eventManager.getValidFilesPathList();
160
179
  return newList.length !== currentList.length || newList.some((el, i) => el !== currentList[i]);
161
180
  }
162
181
 
@@ -167,7 +186,7 @@ export class MonomerLibFileManager {
167
186
  }
168
187
 
169
188
  private isValidHELMLibrary(fileContent: string, fileName: string): boolean {
170
- return this.libraryFileValidator.validateFile(fileContent, fileName);
189
+ return this.fileValidator.validateFile(fileContent, fileName);
171
190
  }
172
191
 
173
192
  /** Get relative paths for files in LIB_PATH */
@@ -5,7 +5,9 @@ import addErrors from 'ajv-errors';
5
5
  export class MonomerLibFileValidator {
6
6
  private validateMonomerSchema: ValidateFunction<any>;
7
7
 
8
- constructor(private helmMonomerSchema: JSONSchemaType<any>) {
8
+ constructor(
9
+ private helmMonomerSchema: JSONSchemaType<any>
10
+ ) {
9
11
  const ajv = new Ajv2020({allErrors: true, strictTuples: false});
10
12
  addErrors(ajv);
11
13
  this.validateMonomerSchema = ajv.compile(this.helmMonomerSchema);
@@ -4,16 +4,15 @@ import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import $ from 'cash-dom';
7
- import * as rxjs from 'rxjs';
7
+ import {Subject} from 'rxjs';
8
8
  import './style.css';
9
9
 
10
10
  import {
11
11
  getUserLibSettings, setUserLibSettings
12
12
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
13
13
  import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
14
- import {MonomerLibManager} from '../lib-manager';
14
+ import {getMonomerLibHelper, IMonomerLibFileManager} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
15
15
 
16
- import {MonomerLibFileManager} from './file-manager';
17
16
  import {MonomerLibFileEventManager} from './event-manager';
18
17
 
19
18
  export async function showManageLibrariesDialog(): Promise<void> {
@@ -30,37 +29,39 @@ export async function getMonomerLibraryManagerLink(): Promise<DG.Widget> {
30
29
  }
31
30
 
32
31
  class MonomerLibraryManagerWidget {
33
- private constructor(
34
- private eventManager: MonomerLibFileEventManager
35
- ) { }
32
+ private _fileManager: IMonomerLibFileManager;
36
33
 
37
- private static _instance: MonomerLibraryManagerWidget;
34
+ private _widget: DG.Widget;
35
+ public get widget(): DG.Widget { return this._widget; }
38
36
 
39
- static async getContent(eventManager: MonomerLibFileEventManager): Promise<DG.Widget> {
40
- if (!MonomerLibraryManagerWidget._instance)
41
- MonomerLibraryManagerWidget._instance = new MonomerLibraryManagerWidget(eventManager);
37
+ private constructor() {}
42
38
 
43
- if (!MonomerLibraryManagerWidget._instance.widget)
44
- MonomerLibraryManagerWidget._instance.widget = await MonomerLibraryManagerWidget._instance.createWidget();
39
+ private static instancePromise?: Promise<MonomerLibraryManagerWidget>;
45
40
 
46
- return MonomerLibraryManagerWidget._instance.widget;
41
+ static async getInstance(): Promise<MonomerLibraryManagerWidget> {
42
+ if (MonomerLibraryManagerWidget.instancePromise === undefined) {
43
+ MonomerLibraryManagerWidget.instancePromise = (async () => {
44
+ const instance = new MonomerLibraryManagerWidget();
45
+ const libHelper = await getMonomerLibHelper();
46
+ instance._fileManager = await libHelper.getFileManager();
47
+ instance._widget = await instance.createWidget();
48
+ return instance;
49
+ })();
50
+ }
51
+ return MonomerLibraryManagerWidget.instancePromise;
47
52
  }
48
53
 
49
- private monomerLibFileManager: MonomerLibFileManager;
50
- private widget: DG.Widget | undefined;
51
-
52
54
  private async createWidget() {
53
- this.monomerLibFileManager = await MonomerLibFileManager.getInstance(this.eventManager);
54
55
  const content = await this.getWidgetContent();
55
- this.eventManager.addLibraryFileRequested$.subscribe(
56
+ const monomerLibHelper = await getMonomerLibHelper();
57
+ monomerLibHelper.eventManager.addLibraryFileRequested$.subscribe(
56
58
  async () => await this.promptToAddLibraryFiles()
57
59
  );
58
60
  return new DG.Widget(content);
59
61
  }
60
62
 
61
63
  private async getWidgetContent(): Promise<HTMLElement> {
62
- this.monomerLibFileManager = await MonomerLibFileManager.getInstance(this.eventManager);
63
- const libControlsForm = await LibraryControlsManager.createControlsForm(this.eventManager);
64
+ const libControlsForm = await LibraryControlsManager.createControlsForm();
64
65
  $(libControlsForm).addClass('monomer-lib-controls-form');
65
66
  const widgetContent = ui.divV([libControlsForm]);
66
67
  return widgetContent;
@@ -74,7 +75,7 @@ class MonomerLibraryManagerWidget {
74
75
  const name = selectedFile.name;
75
76
  const progressIndicator = DG.TaskBarProgressIndicator.create(`Adding ${name} as a monomer library`);
76
77
  try {
77
- await this.monomerLibFileManager.addLibraryFile(content, name);
78
+ await this._fileManager.addLibraryFile(content, name);
78
79
  // this.eventManager.updateLibrarySelectionStatus(name, true);
79
80
  } catch (e) {
80
81
  grok.shell.error(`File ${name} is not a valid monomer library, verify it is aligned to HELM JSON schema.`);
@@ -87,27 +88,30 @@ class MonomerLibraryManagerWidget {
87
88
  }
88
89
 
89
90
  class LibraryControlsManager {
90
- private constructor(private eventManager: MonomerLibFileEventManager) {
91
- this.eventManager.updateUIControlsRequested$.subscribe(
92
- async () => await this.updateControlsForm()
93
- );
94
- this.eventManager.librarySelectionRequested$.subscribe(
95
- async ([fileName, isSelected]) => await this.updateLibrarySelectionStatus(isSelected, fileName)
96
- );
91
+ private constructor(
92
+ private fileManager: IMonomerLibFileManager
93
+ ) {
94
+ this.fileManager.eventManager.updateUIControlsRequested$.subscribe(() => {
95
+ this.updateControlsForm();
96
+ });
97
+ this.fileManager.eventManager.librarySelectionRequested$.subscribe(async ([fileName, isSelected]) => {
98
+ await this.updateLibrarySelectionStatus(isSelected, fileName);
99
+ });
97
100
  }
98
- private monomerLibFileManager: MonomerLibFileManager;
101
+
99
102
  private userLibSettings: UserLibSettings;
100
103
 
101
- static async createControlsForm(eventManager: MonomerLibFileEventManager): Promise<HTMLElement> {
102
- const manager = new LibraryControlsManager(eventManager);
104
+ static async createControlsForm(): Promise<HTMLElement> {
105
+ const libHelper = await getMonomerLibHelper();
106
+ const fileManager = await libHelper.getFileManager();
107
+ const manager = new LibraryControlsManager(fileManager);
103
108
  await manager.initialize();
104
109
 
105
- return await manager._createControlsForm();
110
+ return manager._createControlsForm();
106
111
  }
107
112
 
108
- private async _createControlsForm(): Promise<HTMLElement> {
109
- this.monomerLibFileManager = await MonomerLibFileManager.getInstance(this.eventManager);
110
- const libraryControls = await this.createLibraryControls();
113
+ private _createControlsForm(): HTMLElement {
114
+ const libraryControls = this.createLibraryControls();
111
115
  const inputsForm = ui.form(libraryControls);
112
116
  $(inputsForm).addClass('monomer-lib-controls-form');
113
117
 
@@ -118,14 +122,13 @@ class LibraryControlsManager {
118
122
  this.userLibSettings = await getUserLibSettings();
119
123
  };
120
124
 
121
- private async updateControlsForm(): Promise<void> {
122
- const updatedForm = await this._createControlsForm();
125
+ private updateControlsForm(): void {
126
+ const updatedForm = this._createControlsForm();
123
127
  $('.monomer-lib-controls-form').replaceWith(updatedForm);
124
128
  }
125
129
 
126
- private async createLibraryControls(): Promise<DG.InputBase<boolean | null>[]> {
127
- const fileManager = await MonomerLibFileManager.getInstance(this.eventManager);
128
- const libFileNameList: string[] = fileManager.getValidLibraryPaths();
130
+ private createLibraryControls(): DG.InputBase<boolean | null>[] {
131
+ const libFileNameList: string[] = this.fileManager.getValidLibraryPaths();
129
132
  return libFileNameList.map((libFileName) => this.createLibInput(libFileName));
130
133
  }
131
134
 
@@ -134,8 +137,9 @@ class LibraryControlsManager {
134
137
  const libInput = ui.boolInput(
135
138
  libFileName,
136
139
  isMonomerLibrarySelected,
137
- (isSelected: boolean) => this.eventManager.updateLibrarySelectionStatus(libFileName, isSelected)
138
- );
140
+ (isSelected: boolean) => {
141
+ this.fileManager.eventManager.updateLibrarySelectionStatus(libFileName, isSelected);
142
+ });
139
143
  ui.tooltip.bind(libInput.root, `Include monomers from ${libFileName}`);
140
144
  const deleteIcon = ui.iconFA('trash-alt', () => this.promptForLibraryDeletion(libFileName));
141
145
  ui.tooltip.bind(deleteIcon, `Delete ${libFileName}`);
@@ -149,7 +153,8 @@ class LibraryControlsManager {
149
153
  ): Promise<void> {
150
154
  this.updateLibrarySettings(isMonomerLibrarySelected, libFileName);
151
155
  await setUserLibSettings(this.userLibSettings);
152
- await MonomerLibManager.instance.loadLibraries(true);
156
+ const monomerLibHelper = await getMonomerLibHelper();
157
+ await monomerLibHelper.loadLibraries(true);
153
158
  grok.shell.info('Monomer library user settings saved');
154
159
  }
155
160
 
@@ -172,8 +177,8 @@ class LibraryControlsManager {
172
177
  .onOK(async () => {
173
178
  try {
174
179
  const progressIndicator = DG.TaskBarProgressIndicator.create(`Deleting ${fileName} library`);
175
- this.updateLibrarySelectionStatus(false, fileName);
176
- await this.monomerLibFileManager.deleteLibraryFile(fileName);
180
+ await this.updateLibrarySelectionStatus(false, fileName);
181
+ await this.fileManager.deleteLibraryFile(fileName);
177
182
  progressIndicator.close();
178
183
  } catch (e) {
179
184
  console.error(e);
@@ -189,7 +194,7 @@ class DialogWrapper {
189
194
 
190
195
  private static _instance: DialogWrapper;
191
196
  private dialog?: DG.Dialog;
192
- private closeDialogSubject$ = new rxjs.Subject<void>();
197
+ private closeDialogSubject$ = new Subject<void>();
193
198
 
194
199
  static async showDialog(): Promise<void> {
195
200
  if (!DialogWrapper._instance) {
@@ -207,7 +212,7 @@ class DialogWrapper {
207
212
 
208
213
  private async getDialog(): Promise<DG.Dialog> {
209
214
  const eventManager = MonomerLibFileEventManager.getInstance();
210
- const widget = await MonomerLibraryManagerWidget.getContent(eventManager);
215
+ const widget = (await MonomerLibraryManagerWidget.getInstance()).widget;
211
216
  const dialog = ui.dialog(
212
217
  {
213
218
  title: 'Manage monomer libraries',