update MVS Stories app and deploy scripts (#1574)

* update MVS Stories app and deploy scripts

* reorder changelog
This commit is contained in:
David Sehnal
2025-07-19 09:25:32 +02:00
committed by GitHub
parent 572874f4ae
commit a2e582d4a9
10 changed files with 159 additions and 52 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
build/ build/
deploy/
lib/ lib/
docs/site/ docs/site/

View File

@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file, following t
Note that since we don't clearly distinguish between a public and private interfaces there will be changes in non-major versions that are potentially breaking. If we make breaking changes to less used interfaces we will highlight it in here. Note that since we don't clearly distinguish between a public and private interfaces there will be changes in non-major versions that are potentially breaking. If we make breaking changes to less used interfaces we will highlight it in here.
## [Unreleased] ## [Unreleased]
- [Breaking] Renamed some color schemes ('inferno' -> 'inferno-no-black', 'magma' -> 'magma-no-black', 'turbo' -> 'turbo-no-black', 'rainbow' -> 'simple-rainbow')
- [Breaking] `Box3D.nearestIntersectionWithRay` -> `nearestIntersectionWithRay3D` (use `Ray3D`)
- [Breaking] `Plane3D.distanceToSpher3D` -> `distanceToSphere3D` (fix spelling)
- [Breaking] fix typo `MarchinCubes` -> `MarchingCubes`
- [Breaking] `PluginContext.initViewer/initContainer/mount` are now async and have been renamed to include `Async` postfix
- [Breaking] Add `Volume.instances` support and a `VolumeInstances` transform to dynamically assign it
- This change is breaking because all volume objects require the `instances` field now.
- Update production build to use `esbuild` - Update production build to use `esbuild`
- Emit explicit paths in `import`s in `lib/` - Emit explicit paths in `import`s in `lib/`
- Fix outlines on opaque elements using illumination mode - Fix outlines on opaque elements using illumination mode
@@ -21,7 +28,6 @@ Note that since we don't clearly distinguish between a public and private interf
- Support `matrix` on transform params - Support `matrix` on transform params
- Add `instance` node type - Add `instance` node type
- Support transforming and instancing of structures, components, and volumes - Support transforming and instancing of structures, components, and volumes
- [Breaking] Renamed some color schemes ('inferno' -> 'inferno-no-black', 'magma' -> 'magma-no-black', 'turbo' -> 'turbo-no-black', 'rainbow' -> 'simple-rainbow')
- Added new color schemes, synchronized with D3.js ('inferno', 'magma', 'turbo', 'rainbow', 'sinebow', 'warm', 'cool', 'cubehelix-default', 'category-10', 'observable-10', 'tableau-10') - Added new color schemes, synchronized with D3.js ('inferno', 'magma', 'turbo', 'rainbow', 'sinebow', 'warm', 'cool', 'cubehelix-default', 'category-10', 'observable-10', 'tableau-10')
- Snapshot Markdown improvements - Snapshot Markdown improvements
- Add `MarkdownExtensionManager` (`PluginContext.managers.markdownExtensions`) - Add `MarkdownExtensionManager` (`PluginContext.managers.markdownExtensions`)
@@ -34,21 +40,16 @@ Note that since we don't clearly distinguish between a public and private interf
- Fix isosurface compute shader normals when transformation matrix is applied to volume - Fix isosurface compute shader normals when transformation matrix is applied to volume
- Symmetry operator naming for spacegroup symmetry - parenthesize multi-character indices (1_111-1 -> 1_(11)1(-1)) - Symmetry operator naming for spacegroup symmetry - parenthesize multi-character indices (1_111-1 -> 1_(11)1(-1))
- Add `SymmetryOperator.instanceId` that corresponds to a canonical operator name (e.g. ASM-1, ASM-X0-1 for assemblies, 1_555, 1_(11)1(-1) for crystals) - Add `SymmetryOperator.instanceId` that corresponds to a canonical operator name (e.g. ASM-1, ASM-X0-1 for assemblies, 1_555, 1_(11)1(-1) for crystals)
- [Breaking] `PluginContext.initViewer/initContainer/mount` are now async and have been renamed to include `Async` postfix
- Mol2 Reader - Mol2 Reader
- Fix column count parsing - Fix column count parsing
- Add support for substructure - Add support for substructure
- Fix shader error when clipping flags are set without clip objects present - Fix shader error when clipping flags are set without clip objects present
- [Breaking] Add `Volume.instances` support and a `VolumeInstances` transform to dynamically assign it
- This change is breaking because all volume objects require the `instances` field now.
- Fix wrong group count calculation on geometry update (#1562) - Fix wrong group count calculation on geometry update (#1562)
- Fix wrong instance index in `calcMeshColorSmoothing` - Fix wrong instance index in `calcMeshColorSmoothing`
- Add `Ray3D` object and helpers - Add `Ray3D` object and helpers
- [Breaking] `Box3D.nearestIntersectionWithRay` -> `nearestIntersectionWithRay3D` (use `Ray3D`)
- [Breaking] `Plane3D.distanceToSpher3D` -> `distanceToSphere3D` (fix spelling)
- [Breaking] fix typo `MarchinCubes` -> `MarchingCubes`
- Volume slice representation: add `relativeX/Y/Z` options for dimension - Volume slice representation: add `relativeX/Y/Z` options for dimension
- Add `StructureInstances` transform - Add `StructureInstances` transform
- Add `story-id` URL arg support to `mvs-stories` app
## [v4.18.0] - 2025-06-08 ## [v4.18.0] - 2025-06-08
- MolViewSpec extension: - MolViewSpec extension:

View File

@@ -190,9 +190,14 @@ To get syntax highlighting for shader files add the following to Visual Code's s
npm publish npm publish
## Deploy ## Deploy
To prepare apps and demos for https://molstar.org deploy, run:
npm run test npm run test
npm run build npm run deploy:local
node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer
To commit these changes remotely to the `molstar/molstar.github.io` repo:
npm run deploy:remote
## Contributing ## Contributing
Just open an issue or make a pull request. All contributions are welcome. Just open an issue or make a pull request. All contributions are welcome.

View File

@@ -18,7 +18,8 @@
"lint-fix": "eslint . --fix", "lint-fix": "eslint . --fix",
"test": "npm install --no-save \"gl@^6.0.2\" && npm run lint && jest", "test": "npm install --no-save \"gl@^6.0.2\" && npm run lint && jest",
"jest": "jest", "jest": "jest",
"clean": "node ./scripts/clean.js", "clean": "node ./scripts/clean.js --all",
"clean:build": "node ./scripts/clean.js --build",
"build": "npm run build:apps && npm run build:lib", "build": "npm run build:apps && npm run build:lib",
"build:apps": "node ./scripts/build.mjs -a -e --prd", "build:apps": "node ./scripts/build.mjs -a -e --prd",
"build:lib": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\" && npm run build:lib-extra", "build:lib": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\" && npm run build:lib-extra",
@@ -31,6 +32,8 @@
"dev:examples": "node ./scripts/build.mjs -e", "dev:examples": "node ./scripts/build.mjs -e",
"dev:browser-tests": "node ./scripts/build.mjs -bt", "dev:browser-tests": "node ./scripts/build.mjs -bt",
"serve": "http-server -p 1338 -g", "serve": "http-server -p 1338 -g",
"deploy:local": "npm run clean:build && npm run build:apps && node ./scripts/deploy.js --local",
"deploy:remote": "npm run clean:build && npm run build:apps && node ./scripts/deploy.js",
"model-server": "node lib/commonjs/servers/model/server.js", "model-server": "node lib/commonjs/servers/model/server.js",
"model-server-watch": "nodemon --watch lib lib/commonjs/servers/model/server.js", "model-server-watch": "nodemon --watch lib lib/commonjs/servers/model/server.js",
"volume-server-test": "node lib/commonjs/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336", "volume-server-test": "node lib/commonjs/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",

View File

@@ -6,6 +6,7 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const argparse = require('argparse');
function removeDir(dirPath) { function removeDir(dirPath) {
for (const ent of fs.readdirSync(dirPath)) { for (const ent of fs.readdirSync(dirPath)) {
@@ -24,11 +25,29 @@ function remove(entryPath) {
fs.unlinkSync(entryPath); fs.unlinkSync(entryPath);
} }
const toClean = [ const argParser = new argparse.ArgumentParser({
path.resolve(__dirname, '../build'), add_help: true,
path.resolve(__dirname, '../lib'), description: 'Clean Script'
path.resolve(__dirname, '../tsconfig.tsbuildinfo'), });
]; argParser.add_argument('--build', { required: false, action: 'store_true' });
argParser.add_argument('--lib', { required: false, action: 'store_true' });
argParser.add_argument('--all', { required: false, action: 'store_true' });
const args = argParser.parse_args();
const toClean = [];
if (args.build || args.all) {
toClean.push(path.resolve(__dirname, '../build'));
toClean.push(path.resolve(__dirname, '../deploy/data'));
}
if (args.lib || args.all) {
toClean.push(
path.resolve(__dirname, '../lib'),
path.resolve(__dirname, '../tsconfig.tsbuildinfo'),
);
}
console.log('\n###', 'cleaning', toClean.join(', '));
toClean.forEach(ph => { toClean.forEach(ph => {
if (fs.existsSync(ph)) { if (fs.existsSync(ph)) {

View File

@@ -2,20 +2,24 @@
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/ */
const git = require('simple-git'); const git = require('simple-git');
const path = require('path'); const path = require('path');
const fs = require("fs"); const fs = require("fs");
const fse = require("fs-extra"); const fse = require("fs-extra");
const argparse = require('argparse');
const VERSION = require(path.resolve(__dirname, '../package.json')).version; const VERSION = require(path.resolve(__dirname, '../package.json')).version;
const MVS_STORIES_VERSION = require(path.resolve(__dirname, '../src/apps/mvs-stories/version.ts')).VERSION;
const remoteUrl = "https://github.com/molstar/molstar.github.io.git"; const remoteUrl = "https://github.com/molstar/molstar.github.io.git";
const dataDir = path.resolve(__dirname, '../data/'); const dataDir = path.resolve(__dirname, '../data/');
const buildDir = path.resolve(__dirname, '../build/'); const buildDir = path.resolve(__dirname, '../build/');
const deployDir = path.resolve(buildDir, 'deploy/'); const deployDir = path.resolve(__dirname, '../deploy/');
const localPath = path.resolve(deployDir, 'molstar.github.io/'); const localPath = path.resolve(deployDir, 'data/');
const repositoryPath = path.resolve(deployDir, 'molstar.github.io/');
const analyticsTag = /<!-- __MOLSTAR_ANALYTICS__ -->/g; const analyticsTag = /<!-- __MOLSTAR_ANALYTICS__ -->/g;
const analyticsCode = `<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "c414cbae2d284ea995171a81e4a3e721"}'></script><!-- End Cloudflare Web Analytics --><iframe src="https://web3dsurvey.com/collector-iframe.html" style="width: 1px; height: 1px;"></iframe>`; const analyticsCode = `<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "c414cbae2d284ea995171a81e4a3e721"}'></script><!-- End Cloudflare Web Analytics --><iframe src="https://web3dsurvey.com/collector-iframe.html" style="width: 1px; height: 1px;"></iframe>`;
@@ -80,54 +84,106 @@ function copyMe() {
addAnalytics(path.resolve(meDeployPath, 'index.html')); addAnalytics(path.resolve(meDeployPath, 'index.html'));
} }
function copyMVSStories() {
console.log('\n###', 'copy MVS stories files');
const mvsStoriesBuildPath = path.resolve(buildDir, 'mvs-stories/');
const mvsStoriesDeployPath = path.resolve(localPath, `stories-viewer/v${MVS_STORIES_VERSION}/`);
fse.copySync(mvsStoriesBuildPath, mvsStoriesDeployPath, { overwrite: true });
addAnalytics(path.resolve(mvsStoriesDeployPath, 'index.html'));
// TODO: add PWA
// addManifest(path.resolve(mvsStoriesDeployPath, 'index.html'));
// addPwa(path.resolve(mvsStoriesDeployPath, 'index.html'));
}
function copyDemo(name) {
console.log('\n###', `copy demo files for ${name}`);
const demoBuildPath = path.resolve(buildDir, `examples/${name}/`);
const demoDeployPath = path.resolve(localPath, `demos/${name}/`);
fse.copySync(demoBuildPath, demoDeployPath, { overwrite: true });
addAnalytics(path.resolve(demoDeployPath, 'index.html'));
}
function copyDemos() { function copyDemos() {
console.log('\n###', 'copy demos files'); console.log('\n###', 'copy demos files');
const lightingBuildPath = path.resolve(buildDir, 'examples/lighting/'); copyDemo('lighting');
const lightingDeployPath = path.resolve(localPath, 'demos/lighting/'); copyDemo('alpha-orbitals');
fse.copySync(lightingBuildPath, lightingDeployPath, { overwrite: true }); copyDemo('mvs-stories');
addAnalytics(path.resolve(lightingDeployPath, 'index.html'));
const orbitalsBuildPath = path.resolve(buildDir, 'examples/alpha-orbitals/');
const orbitalsDeployPath = path.resolve(localPath, 'demos/alpha-orbitals/');
fse.copySync(orbitalsBuildPath, orbitalsDeployPath, { overwrite: true });
addAnalytics(path.resolve(orbitalsDeployPath, 'index.html'));
} }
function copyFiles() { function copyFiles() {
try { try {
copyViewer(); copyViewer();
copyMe(); copyMe();
copyMVSStories();
copyDemos(); copyDemos();
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
} }
function copyToRepository() {
console.log('\n###', 'copy repository files');
fse.copySync(localPath, repositoryPath, { overwrite: true });
}
function syncRepository() {
console.log('\n###', 'sync repository');
if (!fs.existsSync(path.resolve(repositoryPath, '.git/'))) {
console.log('\n###', 'clone repository');
git()
.outputHandler(log)
.clone(remoteUrl, repositoryPath)
.fetch(['--all'])
.exec(copyToRepository);
} else {
console.log('\n###', 'update repository');
git()
.outputHandler(log)
.fetch(['--all'])
.reset(['--hard', 'origin/master'])
.exec(copyToRepository);
}
}
function commit() {
console.log('\n###', 'commit changes');
git()
.outputHandler(log)
.add(['-A'])
.commit(`Updated Apps and Demos
- Mol* version: ${VERSION}
- MVS Stories version: ${MVS_STORIES_VERSION}`)
.push();
}
if (!fs.existsSync(localPath)) { if (!fs.existsSync(localPath)) {
console.log('\n###', 'create localPath'); console.log('\n###', 'create localPath');
fs.mkdirSync(localPath, { recursive: true }); fs.mkdirSync(localPath, { recursive: true });
} }
process.chdir(localPath); const argParser = new argparse.ArgumentParser({
add_help: true,
description: 'Mol* Deploy'
});
argParser.add_argument('--local',{
help: 'Do not commit to remote repository.',
required: false,
action: 'store_true',
});
const args = argParser.parse_args();
if (!fs.existsSync(path.resolve(localPath, '.git/'))) { copyFiles();
console.log('\n###', 'clone repository');
git() if (args.local) {
.outputHandler(log) process.exit(0);
.clone(remoteUrl, localPath) }
.fetch(['--all'])
.exec(copyFiles) if (!fs.existsSync(repositoryPath)) {
.add(['-A']) console.log('\n###', 'create repositoryPath');
.commit('updated viewer & demos') fs.mkdirSync(repositoryPath, { recursive: true });
.push(); }
} else {
console.log('\n###', 'update repository'); process.chdir(repositoryPath);
git() syncRepository();
.outputHandler(log) commit();
.fetch(['--all'])
.reset(['--hard', 'origin/master'])
.exec(copyFiles)
.add(['-A'])
.commit('updated viewer & demos')
.push();
}

View File

@@ -76,6 +76,7 @@
<script> <script>
var urlParams = new URLSearchParams(window.location.search); var urlParams = new URLSearchParams(window.location.search);
var storyId = urlParams.get('story-id');
var storyUrl = urlParams.get('story-url'); var storyUrl = urlParams.get('story-url');
var format = urlParams.get('data-format'); var format = urlParams.get('data-format');
@@ -84,11 +85,13 @@
// storyUrl = 'https://raw.githubusercontent.com/molstar/molstar/master/examples/mvs/kinase-story.mvsj'; // storyUrl = 'https://raw.githubusercontent.com/molstar/molstar/master/examples/mvs/kinase-story.mvsj';
// } // }
mvsStories.loadFromURL( if (storyId) {
storyUrl, mvsStories.loadFromID(storyId, { contextName: 'story1' });
{ format: format || 'mvsj', contextName: 'story1' }, } else if (storyUrl) {
); mvsStories.loadFromURL(storyUrl, { format: format || 'mvsj', contextName: 'story1' });
}
</script> </script>
<!-- __MOLSTAR_ANALYTICS__ -->
</body> </body>
</html> </html>

View File

@@ -37,4 +37,15 @@ export function loadFromData(data: MVSData | string | Uint8Array, options?: { fo
}, 0); }, 0);
} }
function getStoryUrlFromId(id: string, format: 'mvsx' | 'mvsj' = 'mvsj') {
return `https://stories.molstar.org/api/story/${id}/data`;
}
export function loadFromID(id: string, options?: { format?: 'mvsx' | 'mvsj', contextName?: string }) {
loadFromURL(
getStoryUrlFromId(id, options?.format),
{ format: options?.format ?? 'mvsj', contextName: options?.contextName },
);
}
export { MVSData }; export { MVSData };

View File

@@ -0,0 +1,7 @@
/**
* Copyright (c) 2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
export const VERSION = 1;

View File

@@ -118,6 +118,7 @@
window.initStories(); window.initStories();
}, 0); }, 0);
</script> </script>
<!-- __MOLSTAR_ANALYTICS__ -->
</body> </body>
</html> </html>