From a2e582d4a9593d818d13d888226a3d1744283d29 Mon Sep 17 00:00:00 2001 From: David Sehnal Date: Sat, 19 Jul 2025 09:25:32 +0200 Subject: [PATCH] update MVS Stories app and deploy scripts (#1574) * update MVS Stories app and deploy scripts * reorder changelog --- .gitignore | 1 + CHANGELOG.md | 15 ++-- README.md | 9 +- package.json | 5 +- scripts/clean.js | 29 +++++-- scripts/deploy.js | 122 ++++++++++++++++++++-------- src/apps/mvs-stories/index.html | 11 ++- src/apps/mvs-stories/index.tsx | 11 +++ src/apps/mvs-stories/version.ts | 7 ++ src/examples/mvs-stories/index.html | 1 + 10 files changed, 159 insertions(+), 52 deletions(-) create mode 100644 src/apps/mvs-stories/version.ts diff --git a/.gitignore b/.gitignore index cbb436d59..2cba57c9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ +deploy/ lib/ docs/site/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f3f0d473f..0dc04f7c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. ## [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` - Emit explicit paths in `import`s in `lib/` - 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 - Add `instance` node type - 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') - Snapshot Markdown improvements - 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 - 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) -- [Breaking] `PluginContext.initViewer/initContainer/mount` are now async and have been renamed to include `Async` postfix - Mol2 Reader - Fix column count parsing - Add support for substructure - 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 instance index in `calcMeshColorSmoothing` - 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 - Add `StructureInstances` transform +- Add `story-id` URL arg support to `mvs-stories` app ## [v4.18.0] - 2025-06-08 - MolViewSpec extension: diff --git a/README.md b/README.md index fefb46801..3a3fc8617 100644 --- a/README.md +++ b/README.md @@ -190,9 +190,14 @@ To get syntax highlighting for shader files add the following to Visual Code's s npm publish ## Deploy +To prepare apps and demos for https://molstar.org deploy, run: + npm run test - npm run build - node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer + npm run deploy:local + +To commit these changes remotely to the `molstar/molstar.github.io` repo: + + npm run deploy:remote ## Contributing Just open an issue or make a pull request. All contributions are welcome. diff --git a/package.json b/package.json index 3fc84d75f..fb9734dcb 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "lint-fix": "eslint . --fix", "test": "npm install --no-save \"gl@^6.0.2\" && npm run lint && 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:apps": "node ./scripts/build.mjs -a -e --prd", "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:browser-tests": "node ./scripts/build.mjs -bt", "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-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", diff --git a/scripts/clean.js b/scripts/clean.js index 1dbb4cd1b..cf493dd88 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -6,6 +6,7 @@ const fs = require('fs'); const path = require('path'); +const argparse = require('argparse'); function removeDir(dirPath) { for (const ent of fs.readdirSync(dirPath)) { @@ -24,11 +25,29 @@ function remove(entryPath) { fs.unlinkSync(entryPath); } -const toClean = [ - path.resolve(__dirname, '../build'), - path.resolve(__dirname, '../lib'), - path.resolve(__dirname, '../tsconfig.tsbuildinfo'), -]; +const argParser = new argparse.ArgumentParser({ + add_help: true, + description: 'Clean Script' +}); +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 => { if (fs.existsSync(ph)) { diff --git a/scripts/deploy.js b/scripts/deploy.js index c284055f2..6d2a53893 100644 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -2,20 +2,24 @@ * Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose + * @author David Sehnal */ const git = require('simple-git'); const path = require('path'); const fs = require("fs"); const fse = require("fs-extra"); +const argparse = require('argparse'); 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 dataDir = path.resolve(__dirname, '../data/'); const buildDir = path.resolve(__dirname, '../build/'); -const deployDir = path.resolve(buildDir, 'deploy/'); -const localPath = path.resolve(deployDir, 'molstar.github.io/'); +const deployDir = path.resolve(__dirname, '../deploy/'); +const localPath = path.resolve(deployDir, 'data/'); +const repositoryPath = path.resolve(deployDir, 'molstar.github.io/'); const analyticsTag = //g; const analyticsCode = ``; @@ -80,54 +84,106 @@ function copyMe() { 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() { console.log('\n###', 'copy demos files'); - const lightingBuildPath = path.resolve(buildDir, 'examples/lighting/'); - const lightingDeployPath = path.resolve(localPath, 'demos/lighting/'); - fse.copySync(lightingBuildPath, lightingDeployPath, { overwrite: true }); - 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')); + copyDemo('lighting'); + copyDemo('alpha-orbitals'); + copyDemo('mvs-stories'); } function copyFiles() { try { copyViewer(); copyMe(); + copyMVSStories(); copyDemos(); } catch (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)) { console.log('\n###', 'create localPath'); 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/'))) { - console.log('\n###', 'clone repository'); - git() - .outputHandler(log) - .clone(remoteUrl, localPath) - .fetch(['--all']) - .exec(copyFiles) - .add(['-A']) - .commit('updated viewer & demos') - .push(); -} else { - console.log('\n###', 'update repository'); - git() - .outputHandler(log) - .fetch(['--all']) - .reset(['--hard', 'origin/master']) - .exec(copyFiles) - .add(['-A']) - .commit('updated viewer & demos') - .push(); -} \ No newline at end of file +copyFiles(); + +if (args.local) { + process.exit(0); +} + +if (!fs.existsSync(repositoryPath)) { + console.log('\n###', 'create repositoryPath'); + fs.mkdirSync(repositoryPath, { recursive: true }); +} + +process.chdir(repositoryPath); +syncRepository(); +commit(); \ No newline at end of file diff --git a/src/apps/mvs-stories/index.html b/src/apps/mvs-stories/index.html index 739eb9f95..4741c1d66 100644 --- a/src/apps/mvs-stories/index.html +++ b/src/apps/mvs-stories/index.html @@ -76,6 +76,7 @@ + \ No newline at end of file diff --git a/src/apps/mvs-stories/index.tsx b/src/apps/mvs-stories/index.tsx index 32893a9d5..0fe1fae50 100644 --- a/src/apps/mvs-stories/index.tsx +++ b/src/apps/mvs-stories/index.tsx @@ -37,4 +37,15 @@ export function loadFromData(data: MVSData | string | Uint8Array, options?: { fo }, 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 }; \ No newline at end of file diff --git a/src/apps/mvs-stories/version.ts b/src/apps/mvs-stories/version.ts new file mode 100644 index 000000000..c095c5493 --- /dev/null +++ b/src/apps/mvs-stories/version.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2025 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal + */ + +export const VERSION = 1; \ No newline at end of file diff --git a/src/examples/mvs-stories/index.html b/src/examples/mvs-stories/index.html index 776842e02..15e7af958 100644 --- a/src/examples/mvs-stories/index.html +++ b/src/examples/mvs-stories/index.html @@ -118,6 +118,7 @@ window.initStories(); }, 0); + \ No newline at end of file