From ed8caea5b7e3e5f4d17740cfcb6b7b6f08b0b754 Mon Sep 17 00:00:00 2001 From: Luc Patiny Date: Tue, 30 Jul 2024 08:15:08 +0200 Subject: [PATCH 1/5] feat: create a script to build help --- help.html | 327 +++++++++++++++++++++++++++++++++++++++++ package-lock.json | 15 +- package.json | 2 + scripts/build_help.mjs | 67 +++++++++ 4 files changed, 404 insertions(+), 7 deletions(-) create mode 100644 help.html create mode 100644 scripts/build_help.mjs diff --git a/help.html b/help.html new file mode 100644 index 00000000..6d0a9c2d --- /dev/null +++ b/help.html @@ -0,0 +1,327 @@ + + + + + Osiris Structure Editor + + + + + +

Chemical Editor Concepts

+

The structure editor is used to draw chemical molecules, substructure fragments, +reactions and simple drawing objects like text, boxes, etc. Its functionality changes +depending on the kind of object(s) being edited. Thus, the atom mapping tool is only available, +when a reaction is edited. Query feature dialogs only open, when a substructure is edited, etc.

+
+

Atom and Bond Hot-Keys

+

Hot-keys allow to quickly change atoms and bonds or to attach common groups to an atom. +When the mouse pointer is moving near to an atom or bond then the atom or bond is automatically highlighted, +indicating that a mouse click or typing hot-keys will affect the highlighted atom or bond.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Hot-KeyTypeNameExplanation
'Delete'Atoms
Bonds
Mapping
DeleteDeletes highlighted and selected atoms and bonds. When a reaction is edited and +the mapping tool is selected, then pressing the 'Delete' key removes all atom mapping from the reaction. +
'1','2','3', ...AtomAtom ChainAttaches a chain of 'n' carbon atoms to the highlighted atom provided that its valence maximum is not exceeded.
'+', '-'AtomCharge ModifiersIncreases or decreases the charge of the highlighted atom.
'.'AtomRadical State ModifierAdds or removes a single electron to change the atom's radical state.
':'AtomRadical State ModifierAdd or removes two electrons and toggles between singlet and triplet states.
'?'AtomConnection PointConverts an atom into a connection point of a substituent or building block.
'q'Atom,
Bond
Query Feature DialogIf a substructure query is edited then this key opens a query feature dialog, +which allows to specify additional atom/bond conditions for the substructure search.
'1','2','3','0'BondBond OrderChanges the highlighted bond into a single, double, triple, or coordinate (0-order) bond.
'u'BondUp BondChanges the highlighted bond into 'up' stereo bond, if at least one of the associated +atoms is a relative or absolute stereo center.
'd'BondDown BondChanges the highlighted bond into 'down' stereo bond, if at least one of the associated +atoms is a relative or absolute stereo center.
'c'BondCross BondChanges the highlighted bond into a double bond with unknown configuration.
'v','4','5','6','7'BondAnnelate RingAnnellates a new saturated n-membered ring the highlighted bond ('v' adds a 3-membered ring).
'b'BondAnnelate BenzoAnnellates a new benzene ring to the highlighted bond.

+ +
+

Atom labels & Substituents & Protecting Groups

+

While the mouse pointer is near an atom such that the atom is highlighted, you may type one or +multiple keys to change the atom label or to attach common substituent. +The sequence of typed keys is shown in the editor. If it is recognized to represent a valid atom label, +then the key string is shown in black. If it is recognized as a known substituent or protecting group name, +then it is shown in blue. The key string is shown in gray, if it can be extended by adding more keys to +a known group name. Finally, it is shown in red, if no valid completion to a known name is possible. +Once a black or blue color indicates that your key string is recognized, you may move the mouse away or type +Enter to actually convert the atom into the new type or to automatically attach the recognized +substituent.

+

For the sake of convenience atom labels or group names can be typed using lower case letters only. +Dashes in group names may also be omitted. E.g. typing 'n' followed by 'a' converts the atom to a sodium atom +and typing 'otolyl' attaches an ortho-Tolyl group.

+

Known group names are: Ac, Alloc, Allyl, Am, Benzoyl, Benzyl, Boc, BOM, Bn, Bs, Bt, Btm, Bu, Bz, +Bzh, Bzl, BzOM, Cbz, Cy, cyclobutyl, cycloheptyl, cyclooctyl, cyclopentyl, cyclopropyl, Dan, DEAE, DEIPS, +DMIPS, DMPM, DMPS, DMTr, DNP, DNS, DPIPS, DPTBS, DTBMS, Et, Fmoc, i-Am, i-Bu, i-Pr, Im, m-Tolyl, MDIPS, +MDPS, MEM, MMTr, MOM, MPM, MTM, Me, Mes, Ms, N3, n-Am, n-Bu, n-Pr, neo-Am, nitro, NO2, Np, o-Tolyl, p-Tolyl, +PMB, PMBM, PNB, PPi, Ph, Phenyl, Pht, Piv, Poc, Pr, Pv, s-Am, s-Bu, s-Butyl, SEM, SES, t-Am, t-Bu, t-Butyl, +TBDMS, TBDPS, TBMPS, TBS, TDS, TFA, THF, THP, TIPS, TMS, Tf, Thexyl, Tos, Troc, Trt, Ts, Xyl.

+

To be compatible with ChemDraw, 'l' is recognized as chlorine atom. If you are editing a sub-structure, +you may use 'x' to convert an atom into an atom list containing all halogene atoms.

+ +
+

Buttons and Tools

+

Chemical structures are drawn by selecting an appropriate tool and applying it +while moving, clicking and dragging the mouse pointer over the drawing area. While the +selection of a tool has no immidiate effect on the drawn structure, pressing a button +does. Currently available buttons allow to clean up atom coordinates or to revert the +most recent structure change.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Button or ToolNameExplanation
Atom ToolsThe Atom Tools cover atoms frequently used in organic chemistry. After +selecting one of these tools, you may place new atoms by clicking into the +empty space or change existing atom by clicking onto them.
Atom Detail ToolWhen this tool is selected and an atom is clicked with the left mouse button +while pressing the 'Ctrl'-key, then a dialog window open that allows to +specify an unusual/custom atom label, a specific isotop, an abnormal valence +and/or a radical state. When closing the dialog the clicked atom is changed +accordingly. The dialog settings are remembered and applied to any other clicked atom, +as long as the tool is selected.
Charge ToolsThese tools let you assign charges to atoms. Multiple clicks on the +same atom will increase or decrease the charge by 1. The Osiris Structure +Editor assumes the presence of implicit hydrogens at any charged or non +charged atoms to meet the atom's default valence. However, implicit hydrogens +are only shown on hetero atoms.
Ring ToolsAfter selecting one of the predefined rings, one may create a new ring +or attach a ring to an existing bond or atom to create annelated ring systems +or a spiro compound, respectively.
Bond ToolsBond Tools allow you to create new bonds and to change existing bonds +concerning their order or stereo-orientation. By clicking into the open +space you create a new bond of the selected type including two carbon atoms. +Clicking onto an existing atom will attach a new carbon. Dragging the mouse from +one atom to another one will just connect these atoms or increment the bond order +between them, if they were already connected. If the plain single bond tool is +selected, bonds may be multiply clicked to cycle through the possible bond orders. +Where reasonable, it also toggles between double bonds with known or unknown +configuration, the so-called cross bond. +When using stereo bonds please consider that the pointed tip of the bond should +always point to the stereo center.
Chain ToolThe Chain Tool provides the fastest way to draw aliphatic carbon chains in +zig-zag orientation. With the selected Chain Tool click into the empty space or at +an existing atom and drag the mouse to create a new zig-zag chain and flip its +orientation.
ESR ToolsThe editor supports the enhanced stereo recognition (ESR) introduced by +Molecular Design Ltd. with IsisDraw V2.5. The ESR Tools allow to define for every +stereo center, whether it is an absolute one or if it belongs to a group of stereo +centers that have the drawn, but relative configuration. For any group of relative +stereo centers one may define that the drawn and the inverse configurations are present, +i.e. that we have a racemic situation concerning the group members. This is indicated by +green stereo bonds and an ampersand '&' at the stereo centers. Likewise one may +define groups of relative stereo centers that contain either the drawn configuration or +the opposite one. Stereo centers of such a group are indicated by blue stereo bonds and +the word 'or' at the stereo centers. In addition to the '&' or 'or' +indicators group numbers show which stereo centers belong to the same group.
Unknown Configuration ToolThis symbol is used to denote an unknown stereo configuration at a chiral center.
Cleanup ButtonA cleanup will generate new coordinates of an existing molecule. It +rearranges bond and atom positions in order to have a proper 2D structure. +If a part of the molecule or reaction is selected, the cleanup will retain +the relative orientation of the non-selected atoms.
Lasso Pointer ToolThis tools allows you to select multiple atoms and bonds to rearrange, +rotate, resize, delete or duplicate at the same time. Just drag the mouse pointer +around the atoms of interest. Selected atoms and bonds are shown in red. By pressing +the control key you switch to the rectangular selection mode and the shift key lets +you add atoms to the current selection. By clicking onto an atom or bonds and dragging +the mouse one may more it or all selected atoms, if the clicked one is selected. +When pressing the shift key while dragging a selected fragment, an automatically created +copy of the selected fragment is dragged. +Where the editor needs to support multiple molecules, e.g. if a reaction is edited, +then every reactant and product is indicated by a large light gray indicator in +the background ('A', 'B', 'P1', etc.). Where independent fragments shall be treated +as one molecule, e.g. anion and kation of one salt, one must locate those fragments +close enough that there is only one indicator behind them. +If a reaction or molecule query is edited, then one may double click an atom or +a set of selected atoms to open a query feature dialog to specify additional conditions +for a fragment to match in a substructure search. The same applies to individual or +multiple selected bonds. If the edited object is not a query then double clicking of +an atom or bond will just select the entire fragment.
Delete ToolIt allows you to delete individual atoms or bonds or selected parts of the molecule. +Selected areas may also be removed by pressing the delete key from the keyboard.
Zoom and Rotate ToolThis tool allows to zoom or rotate the entire drawing or a selected subset. +Just click at the intended origin for the rotation or zooming and drag the mouse vertically +to zoom or horizontically to rotate.
Mapping ToolThis tool is only available when a reaction is edited. Use this tool to +map reactant atoms to the respective product atoms. This is done by clicking on +a reactant atom and dragging the mouse onto the corresponding product atom. The +editor will acknowledge the link by displaying a red mapping number at both atoms. +Afterwards it tries to map atoms in the vicinity automatically and displays green +numbers at automatically mapped atoms. Please assign manually as many reactant-product +pairs as are needed that the entire reaction is mapped completely and correctly. +Pressing the 'Delete' key while this tool is selected removes all mapping from the reaction.
Text ToolThe text tool may only be selected in certain situations. If it is selected +one may use it to place text elements directly into the drawing. These text elements +will be integral part of the drawing and, thus, move, resize and rotate with the molecules +or reaction.
+ + diff --git a/package-lock.json b/package-lock.json index 363d8f62..814e4669 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "BSD-3-Clause", "devDependencies": { "@types/jest": "^29.5.12", + "@types/node": "^22.0.0", "autoprefixer": "^10.4.19", "benchmark": "^2.1.4", "esbuild": "^0.23.0", @@ -2267,13 +2268,13 @@ } }, "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.0.tgz", + "integrity": "sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.11.1" } }, "node_modules/@types/normalize-package-data": { @@ -7862,9 +7863,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", + "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index bc845958..02fa4cad 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "build-esm-bundle-export": "npm run build-esm-bundle && node scripts/build_esm_bundle_types.js", "build-esm-bundle-types": "node scripts/esm_bundle_types.js", "build-esm-bundle-watch": "npm run build-esm-bundle -- --watch", + "build-help": "node scripts/build_help.mjs", "build-full-pretty": "npm run build:pretty -- -m full", "build-minimal": "npm run build:min -- -m minimal", "eslint": "eslint .", @@ -77,6 +78,7 @@ "homepage": "https://github.com/cheminfo/openchemlib-js", "devDependencies": { "@types/jest": "^29.5.12", + "@types/node": "^22.0.0", "autoprefixer": "^10.4.19", "benchmark": "^2.1.4", "esbuild": "^0.23.0", diff --git a/scripts/build_help.mjs b/scripts/build_help.mjs new file mode 100644 index 00000000..7b765dd8 --- /dev/null +++ b/scripts/build_help.mjs @@ -0,0 +1,67 @@ +import { readdir, readFile, writeFile } from 'fs/promises' + +const homedir = new URL('../openchemlib/src/main/resources/html/', import.meta.url) + +// loading the images +const pngNames = await readdir(new URL('editor', homedir)).then(files => files.filter(file => file.endsWith('.png'))) +const images = {} +for (const pngName of pngNames) { + const blob = await readFile(new URL(`editor/${pngName}`, homedir)) + const encoded = toBase64URL(blob, 'image/png') + images[pngName] = encoded +} + +// loading the html file +let html = await readFile(new URL('editor/editor.html', homedir), 'utf8') +for (const [pngName, encoded] of Object.entries(images)) { + html = html.replaceAll(`src="${pngName}"`, `src="${encoded}"`) +} + +// loading css +let css = await readFile(new URL('styles.css', homedir), 'utf8') +html = html.replace('', ``) + +await writeFile(new URL('../help.html', import.meta.url), html) + +/* + * base64-arraybuffer + * https://github.com/niklasvh/base64-arraybuffer + * + * Copyright (c) 2012 Niklas von Hertzen + * Licensed under the MIT license. + */ + +function encode(bytes) { + + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + + // Use a lookup table to find the index. + const lookup = new Uint8Array(256); + for (let i = 0; i < chars.length; i++) { + lookup[chars.charCodeAt(i)] = i; + } + + let i; + let len = bytes.length; + let base64 = ''; + + for (i = 0; i < len; i += 3) { + base64 += chars[bytes[i] >> 2]; + base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; + base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; + base64 += chars[bytes[i + 2] & 63]; + } + + if (len % 3 === 2) { + base64 = `${base64.substring(0, base64.length - 1)}=`; + } else if (len % 3 === 1) { + base64 = `${base64.substring(0, base64.length - 2)}==`; + } + + return base64; +} + +function toBase64URL(bytes, type) { + const base64 = encode(bytes); + return `data:${type};base64,${base64}`; +} From cfe8b6ceb58c79c8c56b3e0d1edfc148d5a66ceb Mon Sep 17 00:00:00 2001 From: Luc Patiny Date: Tue, 30 Jul 2024 10:43:31 +0200 Subject: [PATCH 2/5] chore: use native base64 --- scripts/build_help.mjs | 43 ++---------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/scripts/build_help.mjs b/scripts/build_help.mjs index 7b765dd8..cb46eff6 100644 --- a/scripts/build_help.mjs +++ b/scripts/build_help.mjs @@ -6,7 +6,7 @@ const homedir = new URL('../openchemlib/src/main/resources/html/', import.meta.u const pngNames = await readdir(new URL('editor', homedir)).then(files => files.filter(file => file.endsWith('.png'))) const images = {} for (const pngName of pngNames) { - const blob = await readFile(new URL(`editor/${pngName}`, homedir)) + const blob = await readFile(new URL(`editor/${pngName}`, homedir), 'base64') const encoded = toBase64URL(blob, 'image/png') images[pngName] = encoded } @@ -23,45 +23,6 @@ html = html.replace('> 2]; - base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; - base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; - base64 += chars[bytes[i + 2] & 63]; - } - - if (len % 3 === 2) { - base64 = `${base64.substring(0, base64.length - 1)}=`; - } else if (len % 3 === 1) { - base64 = `${base64.substring(0, base64.length - 2)}==`; - } - - return base64; -} - -function toBase64URL(bytes, type) { - const base64 = encode(bytes); +function toBase64URL(base64, type) { return `data:${type};base64,${base64}`; } From 382c6616287ebe2ea9f7bfb3b594558d0add397d Mon Sep 17 00:00:00 2001 From: Luc Patiny Date: Wed, 21 Aug 2024 11:21:32 +0200 Subject: [PATCH 3/5] chore: fix eslint --- eslint.config.mjs | 14 +++++++++++++- scripts/build_help.mjs | 3 ++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index a65e6c98..91be68a2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -54,7 +54,7 @@ export default [ }, }, { - files: ['scripts/**'], + files: ['scripts/**/*.js'], languageOptions: { globals: { ...globals.node, @@ -65,6 +65,18 @@ export default [ 'unicorn/no-process-exit': 'off', }, }, + { + files: ['scripts/**/*.mjs'], + languageOptions: { + globals: { + ...globals.node, + }, + sourceType: 'module', + }, + rules: { + 'unicorn/no-process-exit': 'off', + }, + }, { files: ['tests/**'], rules: { diff --git a/scripts/build_help.mjs b/scripts/build_help.mjs index cb46eff6..f74094b7 100644 --- a/scripts/build_help.mjs +++ b/scripts/build_help.mjs @@ -1,4 +1,5 @@ -import { readdir, readFile, writeFile } from 'fs/promises' +/* eslint-disable no-await-in-loop */ +import { readdir, readFile, writeFile } from 'node:fs/promises' const homedir = new URL('../openchemlib/src/main/resources/html/', import.meta.url) From 1d280091ea5b34e5ae68fe09c5c44675fccaa6fe Mon Sep 17 00:00:00 2001 From: Luc Patiny Date: Wed, 21 Aug 2024 13:43:51 +0200 Subject: [PATCH 4/5] chore: fix prettier in build_help --- scripts/build_help.mjs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/scripts/build_help.mjs b/scripts/build_help.mjs index f74094b7..ff42a7bf 100644 --- a/scripts/build_help.mjs +++ b/scripts/build_help.mjs @@ -1,28 +1,36 @@ /* eslint-disable no-await-in-loop */ -import { readdir, readFile, writeFile } from 'node:fs/promises' +import { readdir, readFile, writeFile } from 'node:fs/promises'; -const homedir = new URL('../openchemlib/src/main/resources/html/', import.meta.url) +const homedir = new URL( + '../openchemlib/src/main/resources/html/', + import.meta.url, +); // loading the images -const pngNames = await readdir(new URL('editor', homedir)).then(files => files.filter(file => file.endsWith('.png'))) -const images = {} +const pngNames = await readdir(new URL('editor', homedir)).then((files) => + files.filter((file) => file.endsWith('.png')), +); +const images = {}; for (const pngName of pngNames) { - const blob = await readFile(new URL(`editor/${pngName}`, homedir), 'base64') - const encoded = toBase64URL(blob, 'image/png') - images[pngName] = encoded + const blob = await readFile(new URL(`editor/${pngName}`, homedir), 'base64'); + const encoded = toBase64URL(blob, 'image/png'); + images[pngName] = encoded; } // loading the html file -let html = await readFile(new URL('editor/editor.html', homedir), 'utf8') +let html = await readFile(new URL('editor/editor.html', homedir), 'utf8'); for (const [pngName, encoded] of Object.entries(images)) { - html = html.replaceAll(`src="${pngName}"`, `src="${encoded}"`) + html = html.replaceAll(`src="${pngName}"`, `src="${encoded}"`); } // loading css -let css = await readFile(new URL('styles.css', homedir), 'utf8') -html = html.replace('', ``) +let css = await readFile(new URL('styles.css', homedir), 'utf8'); +html = html.replace( + '', + ``, +); -await writeFile(new URL('../help.html', import.meta.url), html) +await writeFile(new URL('../help.html', import.meta.url), html); function toBase64URL(base64, type) { return `data:${type};base64,${base64}`; From 2f1e4de0ec55108465afc8944668c0e44768f107 Mon Sep 17 00:00:00 2001 From: Luc Patiny Date: Wed, 21 Aug 2024 13:47:36 +0200 Subject: [PATCH 5/5] chore: add help.html in prettier ignore --- .prettierignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.prettierignore b/.prettierignore index d5b09124..957474f5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,4 @@ /lib/canvas_editor/cursors_24px.js /openchemlib /war +/help.html \ No newline at end of file