From d7da27ac5c183806beae96f4d4bf99410879551e Mon Sep 17 00:00:00 2001 From: Brianna Major Date: Tue, 22 Apr 2025 09:19:04 -0400 Subject: [PATCH 1/6] Replace webpack with vite --- .../web_client/package.json | 39 ++++++++----------- .../web_client/tsconfig.js | 8 ++++ .../web_client/vite-env.d.ts | 6 +++ .../web_client/vite.config.ts | 38 ++++++++++++++++++ .../web_client/webpack.helper.js | 19 --------- 5 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 wsi_superpixel_guided_labeling/web_client/tsconfig.js create mode 100644 wsi_superpixel_guided_labeling/web_client/vite-env.d.ts create mode 100644 wsi_superpixel_guided_labeling/web_client/vite.config.ts delete mode 100644 wsi_superpixel_guided_labeling/web_client/webpack.helper.js diff --git a/wsi_superpixel_guided_labeling/web_client/package.json b/wsi_superpixel_guided_labeling/web_client/package.json index ef4bb0d9..9f1f3ed8 100644 --- a/wsi_superpixel_guided_labeling/web_client/package.json +++ b/wsi_superpixel_guided_labeling/web_client/package.json @@ -3,44 +3,34 @@ "version": "0.1.0", "private": true, "description": "Perform active learning with superpixel classification.", - "homepage": "https://github.com/DigitalSlideArchive/wsi_superpixel_guided_labeling", + "homepage": "https://github.com/DigitalSlideArchive/wsi-superpixel-guided-labeling", "license": "Apache-2.0", - "peerDependencies": { - "@girder/core": "*", - "@girder/histomicsui": "*" - }, - "girderPlugin": { - "name": "wsi_superpixel_guided_labeling", - "main": "./main.js", - "dependencies": [ - "histomicsui", - "large_image", - "large_image_annotation", - "slicer_cli_web" - ], - "webpack": "webpack.helper" - }, + "main": "./main.js", "scripts": { "lint": "eslint . && pug-lint . && stylint", - "format": "eslint --cache --fix ." + "format": "eslint --cache --fix .", + "dev": "vite", + "build": "vite build" }, "dependencies": { + "@girder/core": "^5.0.0-beta.2", + "@girder/histomicsui": "file:../../../HistomicsUI/histomicsui/web_client", "@vue/compiler-sfc": "^3.2.33", "backbone.localstorage": "^2.0.2", "bootstrap-submenu": "^2.0.4", - "copy-webpack-plugin": "^4.5.2", + "js-yaml": "^4.1.0", "petite-vue": "^0.4.1", "sinon": "^2.1.0", "tinycolor2": "^1.4.1", "url-search-params-polyfill": "^8.1.1", - "vue": "~2.6.14", - "vue-template-compiler": "~2.6.14", - "vue-loader": "~15.9.8", - "webpack": "^3" + "vue": "^2.7.16", + "vue-template-compiler": "~2.6.14" }, "devDependencies": { "@girder/eslint-config": "*", "@girder/pug-lint-config": "*", + "@types/node": "^20.11.24", + "@vitejs/plugin-vue2": "^2.2.0", "eslint": "^8.20.0", "eslint-config-semistandard": "17.0.0", "eslint-config-standard": "^17.0.0", @@ -49,8 +39,11 @@ "eslint-plugin-n": "^15.2.4", "eslint-plugin-promise": "^6.0.0", "eslint-plugin-vue": "^9.10.0", + "pug": "^3.0.3", "pug-lint": "^2", - "stylint": "^2" + "stylint": "^2", + "stylus": "^0.64.0", + "vite": "^5.0.0" }, "eslintConfig": { "extends": [ diff --git a/wsi_superpixel_guided_labeling/web_client/tsconfig.js b/wsi_superpixel_guided_labeling/web_client/tsconfig.js new file mode 100644 index 00000000..3298d7a9 --- /dev/null +++ b/wsi_superpixel_guided_labeling/web_client/tsconfig.js @@ -0,0 +1,8 @@ +{ + "include": ["./vite-env.d.ts", "./**/*"], + "exclude": ["./dist/**/*"], + "compilerOptions": { + "allowJs": true, + "outDir": "./dist", + } +} \ No newline at end of file diff --git a/wsi_superpixel_guided_labeling/web_client/vite-env.d.ts b/wsi_superpixel_guided_labeling/web_client/vite-env.d.ts new file mode 100644 index 00000000..40c880e7 --- /dev/null +++ b/wsi_superpixel_guided_labeling/web_client/vite-env.d.ts @@ -0,0 +1,6 @@ +/// +import { type Girder } from '@girder/core'; + +declare global { + const girder: Girder; +} diff --git a/wsi_superpixel_guided_labeling/web_client/vite.config.ts b/wsi_superpixel_guided_labeling/web_client/vite.config.ts new file mode 100644 index 00000000..9a09a5b2 --- /dev/null +++ b/wsi_superpixel_guided_labeling/web_client/vite.config.ts @@ -0,0 +1,38 @@ +import { resolve } from 'path'; + +import { defineConfig } from 'vite'; +import { compileClient } from 'pug'; +import vue from '@vitejs/plugin-vue2'; + +function pugPlugin() { + return { + name: 'pug', + transform(src: string, id: string) { + if (id.endsWith('.pug')) { + return { + code: `${compileClient(src, {filename: id})}\nexport default template`, + map: null, + }; + } + }, + }; +} + +export default defineConfig({ + plugins: [ + pugPlugin(), + vue() + ], + build: { + sourcemap: !process.env.SKIP_SOURCE_MAPS, + lib: { + entry: resolve(__dirname, 'main.js'), + name: 'GirderPluginWSISuperpixelGuidedLabeling', + fileName: 'girder-plugin-wsi-superpixel-guided-labeling', + }, + }, + define: { + __BUILD_TIMESTAMP__: `${+new Date()}`, + 'process.env': {}, // for legacy Vue 2 + }, +}); diff --git a/wsi_superpixel_guided_labeling/web_client/webpack.helper.js b/wsi_superpixel_guided_labeling/web_client/webpack.helper.js deleted file mode 100644 index 8853253c..00000000 --- a/wsi_superpixel_guided_labeling/web_client/webpack.helper.js +++ /dev/null @@ -1,19 +0,0 @@ -const { VueLoaderPlugin } = require('vue-loader'); - -module.exports = function (config) { - config.module.rules.push({ - resource: { - test: /\.vue$/ - }, - use: [ - require.resolve('vue-loader') - ] - }); - config.resolve = { - alias: { - vue: process.env.NODE_ENV === 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js' - } - }; - config.plugins.push(new VueLoaderPlugin()); - return config; -}; From 7a8657250035c5adaba94d80206e55859d08bbf4 Mon Sep 17 00:00:00 2001 From: Brianna Major Date: Tue, 22 Apr 2025 09:19:25 -0400 Subject: [PATCH 2/6] Update setup.py --- setup.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 4a454f29..c4bf94cf 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ readme = readme_file.read() requirements = [ - 'girder>=3.0.0a1' + 'girder>=5.0.0a2' ] setup( @@ -22,10 +22,10 @@ ], description='Perform active learning with superpixel classification.', install_requires=[ - 'histomicsui', - 'large_image[tiff,openslide,memcached,openjpeg,converter]>=1.20.6', - 'girder-large-image-annotation>=1.20.6', - 'girder-slicer-cli-web>=1.2.3', + 'histomicsui @ git+https://github.com/DigitalSlideArchive/HistomicsUI.git@girder-5', + 'large_image[tiff,openslide,memcached,openjpeg,converter]==1.32.2a115', + 'girder-large-image-annotation>=1.32.2a115', + # 'girder-slicer-cli-web>=1.2.3', ], license='Apache Software License 2.0', long_description=readme, @@ -33,12 +33,12 @@ include_package_data=True, keywords='girder-plugin, wsi_superpixel_guided_labeling', packages=find_packages(exclude=['test', 'test.*']), - url='https://github.com/DigitalSlideArchive/wsi_superpixel_guided_labeling', + url='https://github.com/DigitalSlideArchive/wsi-superpixel-guided-labeling', version='0.1.0', zip_safe=False, entry_points={ 'girder.plugin': [ - 'wsi_superpixel_guided_labeling = wsi_superpixel_guided_labeling:GirderPlugin' + 'wsi_superpixel_guided_labeling = wsi_superpixel_guided_labeling:WSISuperpixelGuidedLabelingPlugin' ] } ) From 7b110495861cc9ced772c7fec4c5f53009a06f54 Mon Sep 17 00:00:00 2001 From: Brianna Major Date: Tue, 22 Apr 2025 09:20:04 -0400 Subject: [PATCH 3/6] Use registerPluginStaticContent --- wsi_superpixel_guided_labeling/__init__.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/wsi_superpixel_guided_labeling/__init__.py b/wsi_superpixel_guided_labeling/__init__.py index b672dfed..114b95dd 100644 --- a/wsi_superpixel_guided_labeling/__init__.py +++ b/wsi_superpixel_guided_labeling/__init__.py @@ -1,10 +1,17 @@ -from girder import plugin +from pathlib import Path +from girder import plugin -class GirderPlugin(plugin.GirderPlugin): +class WSISuperpixelGuidedLabelingPlugin(plugin.GirderPlugin): DISPLAY_NAME = 'WSI Superpixel Guided Labeling' - CLIENT_SOURCE_PATH = 'web_client' def load(self, info): - # add plugin loading logic here plugin.getPlugin('histomicsui').load(info) + # add plugin loading logic here + plugin.registerPluginStaticContent( + plugin='wsi_superpixel_guided_labeling', + css=['/style.css'], + js=['/girder-plugin-wsi-superpixel-guided-labeling.umd.js'], + staticDir=Path(__file__).parent / 'web_client' / 'dist', + tree=info['serverRoot'], + ) From d0aedc6300c8897b94c47200a286a5de84b7d7de Mon Sep 17 00:00:00 2001 From: Brianna Major Date: Tue, 22 Apr 2025 10:25:15 -0400 Subject: [PATCH 4/6] Update imports --- .../web_client/main.js | 16 +++-- .../web_client/views/HeaderImageView.js | 4 +- .../web_client/views/HeaderView.js | 4 +- .../views/body/ActiveLearningView.js | 71 ++++++++----------- .../web_client/views/itemAndFolderList.js | 11 +-- .../ActiveLearningFilmStrip.vue | 2 +- .../ActiveLearningFilmStripCard.vue | 2 +- .../vue/components/ActiveLearningLabeling.vue | 6 +- .../ActiveLearningReviewCard.vue | 2 +- .../ActiveLearningReviewContainer.vue | 2 +- .../ActiveLearningMergeConfirmation.vue | 4 +- .../components/ActiveLearningSlideViewer.vue | 6 +- .../components/AnnotationOpacityControl.vue | 2 +- .../web_client/views/vue/components/store.js | 2 +- .../web_client/views/vue/components/utils.js | 3 +- 15 files changed, 65 insertions(+), 72 deletions(-) diff --git a/wsi_superpixel_guided_labeling/web_client/main.js b/wsi_superpixel_guided_labeling/web_client/main.js index 989889a8..d205ee94 100644 --- a/wsi_superpixel_guided_labeling/web_client/main.js +++ b/wsi_superpixel_guided_labeling/web_client/main.js @@ -1,7 +1,6 @@ -import router from '@girder/histomicsui/router'; -import { registerPluginNamespace } from '@girder/core/pluginUtils'; -import { exposePluginConfig } from '@girder/core/utilities/PluginUtils'; -import girderEvents from '@girder/core/events'; +const events = girder.events; +const { registerPluginNamespace } = girder.pluginUtils; +const { exposePluginConfig } = girder.utilities.PluginUtils; import ActiveLearningView from './views/body/ActiveLearningView'; import './views/itemAndFolderList'; @@ -15,6 +14,11 @@ const configRoute = `plugins/${pluginName}/config`; registerPluginNamespace(pluginName, WSISuperpixelGuidedLabeling); exposePluginConfig(pluginName, configRoute); -router.route('active-learning', 'active-learning', function () { - girderEvents.trigger('g:navigateTo', ActiveLearningView, {}); +// g:appload.before runs after all plugin static files have been loaded +events.on('g:appload.before', () => { + console.log("g:appload.before event triggered", girder); + const router = girder.plugins.histomicsui.router; + router.route('active-learning', 'active-learning', function () { + events.trigger('g:navigateTo', ActiveLearningView, {}); + }); }); diff --git a/wsi_superpixel_guided_labeling/web_client/views/HeaderImageView.js b/wsi_superpixel_guided_labeling/web_client/views/HeaderImageView.js index 7d2304d9..96284b45 100644 --- a/wsi_superpixel_guided_labeling/web_client/views/HeaderImageView.js +++ b/wsi_superpixel_guided_labeling/web_client/views/HeaderImageView.js @@ -1,5 +1,5 @@ -import { wrap } from '@girder/core/utilities/PluginUtils'; -import HeaderImageView from '@girder/histomicsui/views/layout/HeaderImageView.js'; +const { wrap } = girder.utilities.PluginUtils; +const HeaderImageView = girder.plugins.histomicsui.views.layout.HeaderImageView; wrap(HeaderImageView, 'render', function (render) { render.call(this); diff --git a/wsi_superpixel_guided_labeling/web_client/views/HeaderView.js b/wsi_superpixel_guided_labeling/web_client/views/HeaderView.js index cc61339c..d4914d70 100644 --- a/wsi_superpixel_guided_labeling/web_client/views/HeaderView.js +++ b/wsi_superpixel_guided_labeling/web_client/views/HeaderView.js @@ -1,5 +1,5 @@ -import { wrap } from '@girder/core/utilities/PluginUtils'; -import HeaderView from '@girder/histomicsui/views/layout/HeaderView'; +const { wrap } = girder.utilities.PluginUtils; +const HeaderView = girder.plugins.histomicsui.views.layout.HeaderView; wrap(HeaderView, 'render', function (render) { const isActiveLearning = window.location.href.includes('active-learning'); diff --git a/wsi_superpixel_guided_labeling/web_client/views/body/ActiveLearningView.js b/wsi_superpixel_guided_labeling/web_client/views/body/ActiveLearningView.js index d09f42ef..c66220cf 100644 --- a/wsi_superpixel_guided_labeling/web_client/views/body/ActiveLearningView.js +++ b/wsi_superpixel_guided_labeling/web_client/views/body/ActiveLearningView.js @@ -1,15 +1,15 @@ -/* global $, __webpack_public_path__ */ -import View from '@girder/core/views/View'; -import { restRequest, getApiRoot } from '@girder/core/rest'; -import { confirm } from '@girder/core/dialog'; -import _ from 'underscore'; - -import router from '@girder/histomicsui/router'; -import FolderCollection from '@girder/core/collections/FolderCollection'; -import AnnotationModel from '@girder/large_image_annotation/models/AnnotationModel'; -import ItemCollection from '@girder/core/collections/ItemCollection'; -import JobStatus from '@girder/jobs/JobStatus.js'; -import { parse } from '@girder/slicer_cli_web/parser'; +const $ = girder.$; +const View = girder.views.View; +const { restRequest, getApiRoot } = girder.rest; +const { confirm } = girder.dialog; +const _ = girder._; + +const router = girder.plugins.histomicsui.router; +const FolderCollection = girder.collections.FolderCollection; +const AnnotationModel = girder.plugins.large_image_annotation.models.AnnotationModel; +const ItemCollection = girder.collections.ItemCollection; +const JobStatus = girder.plugins.jobs.JobStatus; +const { parse } = girder.plugins.slicer_cli_web.parser; import learningTemplate from '../../templates/body/activeLearningView.pug'; import ActiveLearningGlobalContainer from '../vue/components/ActiveLearningGlobalContainer.vue'; @@ -20,10 +20,9 @@ import { debounce, isValidNumber } from '../vue/components/utils.js'; import '../../stylesheets/body/learning.styl'; -const yaml = require('js-yaml'); +import * as yaml from 'js-yaml'; // Only necessary until we have native support for Promises with es6. // Used for Promise.all() and Promise.resolve() support. -const Promise = require('bluebird'); const epochRegex = /epoch (\d+)/i; @@ -269,33 +268,23 @@ const ActiveLearningView = View.extend({ this.vueApp.$destroy(); } const el = this.$('.h-active-learning-container').get(0); - // eslint-disable-next-line - const root = (__webpack_public_path__ || '/status/built').replace(/\/$/, ''); - const geojsUrl = root + '/plugins/large_image/extra/geojs.js'; - // Make sure geojs is available, as it required by the image viewer widgets - $.ajax({ - url: geojsUrl, - dataType: 'script', - cache: true - }).done(() => { - const imageNamesById = {}; - _.forEach(Object.keys(this.imageItemsById), (imageId) => { - imageNamesById[imageId] = this.imageItemsById[imageId].name; - }); - this.vueApp = new ActiveLearningGlobalContainer({ - el, - propsData: { - backboneParent: this, - imageNamesById, - annotationsByImageId: this.annotationsByImageId, - certaintyMetrics: this.certaintyMetrics, - featureShapes: this.featureShapes, - apiRoot: getApiRoot(), - currentAverageCertainty: this.currentAverageCertainty, - availableImages: this.availableImages, - categoryMap: this.categoryMap - } - }); + const imageNamesById = {}; + _.forEach(Object.keys(this.imageItemsById), (imageId) => { + imageNamesById[imageId] = this.imageItemsById[imageId].name; + }); + this.vueApp = new ActiveLearningGlobalContainer({ + el, + propsData: { + backboneParent: this, + imageNamesById, + annotationsByImageId: this.annotationsByImageId, + certaintyMetrics: this.certaintyMetrics, + featureShapes: this.featureShapes, + apiRoot: getApiRoot(), + currentAverageCertainty: this.currentAverageCertainty, + availableImages: this.availableImages, + categoryMap: this.categoryMap + } }); }, diff --git a/wsi_superpixel_guided_labeling/web_client/views/itemAndFolderList.js b/wsi_superpixel_guided_labeling/web_client/views/itemAndFolderList.js index 4ce605b1..28143dc3 100644 --- a/wsi_superpixel_guided_labeling/web_client/views/itemAndFolderList.js +++ b/wsi_superpixel_guided_labeling/web_client/views/itemAndFolderList.js @@ -1,8 +1,9 @@ -import { wrap } from '@girder/core/utilities/PluginUtils'; -import ItemListWidget from '@girder/core/views/widgets/ItemListWidget'; -import FolderListWidget from '@girder/core/views/widgets/FolderListWidget'; -import _ from 'underscore'; -import { restRequest } from '@girder/core/rest'; +const {wrap} = girder.utilities.PluginUtils; +const ItemListWidget = girder.views.widgets.ItemListWidget; +const FolderListWidget = girder.views.widgets.FolderListWidget; +const {restRequest} = girder.rest; + +const _ = girder._; const specialFolders = ['Annotations', 'Models', 'Features']; diff --git a/wsi_superpixel_guided_labeling/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue b/wsi_superpixel_guided_labeling/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue index f2951fd5..dfd4f24a 100644 --- a/wsi_superpixel_guided_labeling/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue +++ b/wsi_superpixel_guided_labeling/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue @@ -1,5 +1,5 @@ diff --git a/wsi_superpixel_guided_labeling/web_client/vite.config.ts b/wsi_superpixel_guided_labeling/web_client/vite.config.ts index 9a09a5b2..84c1a66b 100644 --- a/wsi_superpixel_guided_labeling/web_client/vite.config.ts +++ b/wsi_superpixel_guided_labeling/web_client/vite.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ vue() ], build: { - sourcemap: !process.env.SKIP_SOURCE_MAPS, + sourcemap: true, lib: { entry: resolve(__dirname, 'main.js'), name: 'GirderPluginWSISuperpixelGuidedLabeling',