Merge branch 'imjoy-support' into 'master'
Added imjoy support Closes #157 See merge request openflexure/openflexure-microscope-server!120
This commit is contained in:
commit
5b63745ad0
18 changed files with 1656 additions and 95 deletions
|
|
@ -1,3 +1,7 @@
|
|||
# Unreleased
|
||||
* Added support for ImJoy
|
||||
* Added a setting to disable the gallery
|
||||
|
||||
# [v2.10.0b0](https://gitlab.com/openflexure/openflexure-microscope-server/compare/v2.9.3...v2.10.0b0) (2021-05-18)
|
||||
|
||||
## New features
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
NODE_ENV=development
|
||||
VUE_APP_MODE=development
|
||||
VUE_APP_PLATFORM=web
|
||||
VUE_APP_TARGET=web
|
||||
VUE_APP_TARGET=web
|
||||
VUE_APP_ENABLE_IMJOY=true
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
NODE_ENV=production
|
||||
VUE_APP_MODE=production
|
||||
VUE_APP_PLATFORM=web
|
||||
VUE_APP_TARGET=web
|
||||
VUE_APP_TARGET=web
|
||||
VUE_APP_ENABLE_IMJOY=true
|
||||
|
|
|
|||
402
openflexure_microscope/api/static/package-lock.json
generated
402
openflexure_microscope/api/static/package-lock.json
generated
|
|
@ -2276,6 +2276,11 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/component-emitter": {
|
||||
"version": "1.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
|
||||
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||
|
|
@ -3578,6 +3583,11 @@
|
|||
"integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==",
|
||||
"dev": true
|
||||
},
|
||||
"after": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
|
||||
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
||||
},
|
||||
"aggregate-error": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||
|
|
@ -3592,7 +3602,6 @@
|
|||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
|
||||
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
|
|
@ -3705,11 +3714,15 @@
|
|||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"argsarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz",
|
||||
"integrity": "sha1-bnIHtOzbObCviDA/pa4ivajfYcs="
|
||||
},
|
||||
"arr-diff": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||
|
|
@ -3755,6 +3768,11 @@
|
|||
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
|
||||
"dev": true
|
||||
},
|
||||
"arraybuffer.slice": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
|
|
@ -4047,6 +4065,11 @@
|
|||
"resolve": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
|
|
@ -4108,6 +4131,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"base64-arraybuffer": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
|
||||
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
|
|
@ -4163,6 +4191,11 @@
|
|||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"blob": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
|
|
@ -5076,11 +5109,20 @@
|
|||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||
"dev": true
|
||||
},
|
||||
"component-bind": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
||||
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
|
||||
},
|
||||
"component-emitter": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
|
||||
},
|
||||
"component-inherit": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
|
||||
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
|
||||
},
|
||||
"compressible": {
|
||||
"version": "2.0.18",
|
||||
|
|
@ -6091,6 +6133,11 @@
|
|||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.8.tgz",
|
||||
"integrity": "sha512-9H0UL59EkDLgY3dUFjLV6IEUaHm5qp3mxSqWw7Yyx4Zhk2Jn2cmLe+CNPP3xy13zl8Bqg+0NehQzkdMoVhGRww=="
|
||||
},
|
||||
"domutils": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
|
||||
|
|
@ -6224,6 +6271,50 @@
|
|||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.1.tgz",
|
||||
"integrity": "sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==",
|
||||
"requires": {
|
||||
"base64-arraybuffer": "0.1.4",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~4.0.1",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~7.4.2",
|
||||
"yeast": "0.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.4.5",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
|
||||
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz",
|
||||
"integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==",
|
||||
"requires": {
|
||||
"base64-arraybuffer": "0.1.4"
|
||||
}
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
|
||||
|
|
@ -6660,8 +6751,7 @@
|
|||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"esquery": {
|
||||
"version": "1.0.1",
|
||||
|
|
@ -6974,8 +7064,7 @@
|
|||
"fast-deep-equal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
|
||||
"dev": true
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||
},
|
||||
"fast-diff": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -7000,8 +7089,7 @@
|
|||
"fast-json-stable-stringify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
|
||||
"dev": true
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||
},
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
|
|
@ -7089,6 +7177,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"file-saver": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
|
||||
"integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
|
|
@ -7236,8 +7329,7 @@
|
|||
"follow-redirects": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
||||
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -8087,6 +8179,26 @@
|
|||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-binary2": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
|
||||
"requires": {
|
||||
"isarray": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
|
|
@ -8483,6 +8595,77 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"imjoy-core": {
|
||||
"version": "0.13.74",
|
||||
"resolved": "https://registry.npmjs.org/imjoy-core/-/imjoy-core-0.13.74.tgz",
|
||||
"integrity": "sha512-eXKRw4vpK8E8DnKrc1G8dLq8zGQsOddLubVpomIzs5CtOktAhQOqr5kOEucmM4V3uEDd24UGbMydv1MzP2KWBQ==",
|
||||
"requires": {
|
||||
"ajv": "^6.9.1",
|
||||
"axios": "^0.21.1",
|
||||
"dompurify": "^2.0.8",
|
||||
"file-saver": "^1.3.3",
|
||||
"imjoy-rpc": "^0.3.6",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.15",
|
||||
"lz-string": "^1.4.4",
|
||||
"minibus": "^3.1.0",
|
||||
"pouchdb-browser": "^7.0.0",
|
||||
"socket.io-client": "^2.3.0",
|
||||
"spark-md5": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imjoy-rpc": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/imjoy-rpc/-/imjoy-rpc-0.3.9.tgz",
|
||||
"integrity": "sha512-dWyEIFlHKEGkyTLhGvKMnY7Tus26cOgfPMj3plEfcwNIfiatD8kfwdmdu4yoWUTdOnOUY4KtY06c9UnNT04k/Q==",
|
||||
"requires": {
|
||||
"socket.io-client": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.1.1.tgz",
|
||||
"integrity": "sha512-avzRzFZIkmyNxqvhmm5ns0Itq5dgEkesDPB6Tl0Yben47U08MvdFnVXAuFDULQhDXjuYdCb6QUEILYLUKQEuGg==",
|
||||
"requires": {
|
||||
"@types/component-emitter": "^1.2.10",
|
||||
"backo2": "~1.0.2",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-client": "~5.1.1",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"immediate": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
|
||||
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q=="
|
||||
},
|
||||
"import-cwd": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
|
||||
|
|
@ -8539,6 +8722,11 @@
|
|||
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
|
||||
"dev": true
|
||||
},
|
||||
"indexof": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
|
||||
},
|
||||
"infer-owner": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
|
||||
|
|
@ -8558,8 +8746,7 @@
|
|||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "7.3.3",
|
||||
|
|
@ -9154,7 +9341,6 @@
|
|||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
|
|
@ -9193,8 +9379,7 @@
|
|||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
},
|
||||
"json-stable-stringify-without-jsonify": {
|
||||
"version": "1.0.1",
|
||||
|
|
@ -9446,8 +9631,7 @@
|
|||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"lodash.defaultsdeep": {
|
||||
"version": "4.6.1",
|
||||
|
|
@ -9524,6 +9708,11 @@
|
|||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"lz-string": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
|
||||
"integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY="
|
||||
},
|
||||
"make-dir": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||
|
|
@ -9754,6 +9943,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"minibus": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minibus/-/minibus-3.1.0.tgz",
|
||||
"integrity": "sha1-/UKe2XWj/U5VbwxumqyaTqy80ZM="
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
|
|
@ -10607,6 +10801,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"parseqs": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||
},
|
||||
"parseuri": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
|
@ -11409,6 +11613,26 @@
|
|||
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"pouchdb-browser": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-browser/-/pouchdb-browser-7.2.2.tgz",
|
||||
"integrity": "sha512-90pkngk9/F95knUwOhGQ0xuNzG51uIecF5kD5AfNdhqqrV9wbngz+I3QA7u/9eVyZRVyOrTrT3oaKcNqAB7Brg==",
|
||||
"requires": {
|
||||
"argsarray": "0.0.1",
|
||||
"immediate": "3.3.0",
|
||||
"inherits": "2.0.4",
|
||||
"spark-md5": "3.0.1",
|
||||
"uuid": "8.1.0",
|
||||
"vuvuzela": "1.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz",
|
||||
"integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
|
|
@ -11562,8 +11786,7 @@
|
|||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"q": {
|
||||
"version": "1.5.1",
|
||||
|
|
@ -12456,6 +12679,114 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
|
||||
"integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
|
||||
"requires": {
|
||||
"backo2": "1.0.2",
|
||||
"component-bind": "1.0.0",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-client": "~3.5.0",
|
||||
"has-binary2": "~1.0.2",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~3.3.0",
|
||||
"to-array": "0.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz",
|
||||
"integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==",
|
||||
"requires": {
|
||||
"component-emitter": "~1.3.0",
|
||||
"component-inherit": "0.0.3",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-parser": "~2.2.0",
|
||||
"has-cors": "1.1.0",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~7.4.2",
|
||||
"xmlhttprequest-ssl": "~1.6.2",
|
||||
"yeast": "0.1.2"
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
|
||||
"integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
|
||||
"requires": {
|
||||
"after": "0.8.2",
|
||||
"arraybuffer.slice": "~0.0.7",
|
||||
"base64-arraybuffer": "0.1.4",
|
||||
"blob": "0.0.5",
|
||||
"has-binary2": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
|
||||
"integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
|
||||
"requires": {
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~3.1.0",
|
||||
"isarray": "2.0.1"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.4.5",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
|
||||
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
|
||||
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
|
||||
"requires": {
|
||||
"@types/component-emitter": "^1.2.10",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"sockjs": {
|
||||
"version": "0.3.20",
|
||||
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
|
||||
|
|
@ -12567,6 +12898,11 @@
|
|||
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
|
||||
"dev": true
|
||||
},
|
||||
"spark-md5": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.1.tgz",
|
||||
"integrity": "sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig=="
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
|
||||
|
|
@ -12651,8 +12987,7 @@
|
|||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
|
|
@ -13136,6 +13471,11 @@
|
|||
"os-tmpdir": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"to-array": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
|
||||
},
|
||||
"to-arraybuffer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
|
||||
|
|
@ -13464,7 +13804,6 @@
|
|||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
|
|
@ -13848,6 +14187,11 @@
|
|||
"integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ==",
|
||||
"dev": true
|
||||
},
|
||||
"vuvuzela": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vuvuzela/-/vuvuzela-1.0.3.tgz",
|
||||
"integrity": "sha1-O+FF5YJxxzylUnndhR8SpoIRSws="
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
|
||||
|
|
@ -14517,6 +14861,11 @@
|
|||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.2.tgz",
|
||||
"integrity": "sha512-tYOaldF/0BLfKuoA39QMwD4j2m8lq4DIncqj1yuNELX4vz9+z/ieG/vwmctjJce+boFHXstqhWnHSxc4W8f4qg=="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
|
@ -14653,6 +15002,11 @@
|
|||
"decamelize": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
},
|
||||
"yorkie": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@
|
|||
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --mode development",
|
||||
"serve-without-imjoy": "VUE_APP_ENABLE_IMJOY=false vue-cli-service serve --mode development",
|
||||
"build": "vue-cli-service build --mode production",
|
||||
"build-without-imjoy": "VUE_APP_ENABLE_IMJOY=false vue-cli-service build --mode production",
|
||||
"lint": "vue-cli-service lint",
|
||||
"lint:fix": "vue-cli-service lint --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"imjoy-core": "^0.13.74",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"mousetrap": "^1.6.5",
|
||||
"vue-observe-visibility": "^0.4.6"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<docs>
|
||||
[TODO: write documentation for this plugin.]
|
||||
</docs>
|
||||
|
||||
<config lang="json">
|
||||
{
|
||||
"name": "List services",
|
||||
"type": "web-worker",
|
||||
"tags": [],
|
||||
"ui": "",
|
||||
"version": "0.1.0",
|
||||
"cover": "",
|
||||
"description": "List the available ImJoy services in the console",
|
||||
"icon": "extension",
|
||||
"inputs": null,
|
||||
"outputs": null,
|
||||
"api_version": "0.1.8",
|
||||
"env": "",
|
||||
"permissions": [],
|
||||
"requirements": [],
|
||||
"dependencies": []
|
||||
}
|
||||
</config>
|
||||
|
||||
<script lang="javascript">
|
||||
class ImJoyPlugin {
|
||||
async setup() {
|
||||
api.log('initialized')
|
||||
}
|
||||
|
||||
async run(ctx) {
|
||||
// get a list of service
|
||||
const services = await api.getServices({name: "OpenFlexure"});
|
||||
console.log("OpenFlexure services:");
|
||||
console.log(services);
|
||||
const allservices = await api.getServices();
|
||||
console.log("All services:");
|
||||
console.log(allservices);
|
||||
}
|
||||
}
|
||||
|
||||
api.export(new ImJoyPlugin())
|
||||
</script>
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
<config lang="json">
|
||||
{
|
||||
"name": "Script Editor",
|
||||
"type": "web-worker",
|
||||
"tags": [],
|
||||
"ui": "",
|
||||
"version": "0.1.0",
|
||||
"cover": "",
|
||||
"description": "A Script Editor for Controlling OpenFlexure Microscope with ImJoy",
|
||||
"icon": "extension",
|
||||
"inputs": null,
|
||||
"outputs": null,
|
||||
"api_version": "0.1.8",
|
||||
"env": "",
|
||||
"permissions": [],
|
||||
"requirements": [],
|
||||
"dependencies": []
|
||||
}
|
||||
</config>
|
||||
|
||||
<script lang="javascript">
|
||||
class ImJoyPlugin {
|
||||
async setup() {
|
||||
api.log('initialized')
|
||||
}
|
||||
|
||||
async run(ctx) {
|
||||
let pluginInEditor, stopped, editorWindow;
|
||||
const config = {lang: 'javascript'}
|
||||
config.templates = [
|
||||
{
|
||||
name: "New",
|
||||
url: null,
|
||||
lang: 'javascript',
|
||||
},
|
||||
{
|
||||
name: "Snap Image Template",
|
||||
url: "./OpenFlexureSnapImageTemplate.imjoy.html",
|
||||
lang: "html",
|
||||
}
|
||||
]
|
||||
config.ui_elements = {
|
||||
save: {
|
||||
_rintf: true,
|
||||
type: 'button',
|
||||
label: "Save",
|
||||
visible: false,
|
||||
icon: "content-save",
|
||||
callback(content) {
|
||||
console.log(content)
|
||||
}
|
||||
},
|
||||
run: {
|
||||
_rintf: true,
|
||||
type: 'button',
|
||||
label: "Run",
|
||||
icon: "play",
|
||||
visible: true,
|
||||
shortcut: 'Shift-Enter',
|
||||
async callback(content) {
|
||||
try {
|
||||
editorWindow.setLoader(true);
|
||||
editorWindow.updateUIElement('stop', {
|
||||
visible: true
|
||||
})
|
||||
api.showProgress(0);
|
||||
pluginInEditor = await api.getPlugin({src: content, hot_reloading: true})
|
||||
|
||||
if (stopped) {
|
||||
pluginInEditor = null;
|
||||
return;
|
||||
}
|
||||
if (pluginInEditor && pluginInEditor.run) {
|
||||
return await pluginInEditor.run({
|
||||
config: {},
|
||||
data: {}
|
||||
});
|
||||
}
|
||||
if (stopped) {
|
||||
pluginInEditor = null;
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
api.showMessage("Failed to load plugin, error: " + e.toString());
|
||||
} finally {
|
||||
editorWindow.updateUIElement('stop', {
|
||||
visible: false
|
||||
})
|
||||
editorWindow.setLoader(false);
|
||||
api.showProgress(100);
|
||||
}
|
||||
}
|
||||
},
|
||||
stop: {
|
||||
_rintf: true,
|
||||
type: 'button',
|
||||
label: "Stop",
|
||||
style: "color: #ff0080cf;",
|
||||
icon: "stop",
|
||||
visible: false,
|
||||
async callback() {
|
||||
stopped = true;
|
||||
await editorWindow.setLoader(false);
|
||||
await editorWindow.updateUIElement('stop', {
|
||||
visible: false
|
||||
})
|
||||
}
|
||||
},
|
||||
export: {
|
||||
_rintf: true,
|
||||
type: 'button',
|
||||
label: "Export",
|
||||
icon: "file-download-outline",
|
||||
visible: true,
|
||||
async callback(content) {
|
||||
const fileName = (pluginInEditor && pluginInEditor.config.name && pluginInEditor.config.name + '.imjoy.html') || config.name + '.imjoy.html' || "myPlugin.imjoy.html";
|
||||
await api.exportFile(content, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
editorWindow = await api.createWindow({
|
||||
src: 'https://if.imjoy.io',
|
||||
name: (ctx && ctx.data && ctx.data.name) ||'OpenFlexure Script Editor',
|
||||
config,
|
||||
data: {code: ctx && ctx.data && ctx.data.code}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
api.export(new ImJoyPlugin())
|
||||
</script>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<docs>
|
||||
[TODO: write documentation for this plugin.]
|
||||
</docs>
|
||||
|
||||
<config lang="json">
|
||||
{
|
||||
"name": "OpenFlexureSnapImageTemplate",
|
||||
"type": "web-worker",
|
||||
"tags": [],
|
||||
"ui": "",
|
||||
"version": "0.1.0",
|
||||
"cover": "",
|
||||
"description": "[TODO: describe this plugin with one sentence.]",
|
||||
"icon": "extension",
|
||||
"inputs": null,
|
||||
"outputs": null,
|
||||
"api_version": "0.1.8",
|
||||
"env": "",
|
||||
"permissions": [],
|
||||
"requirements": [],
|
||||
"dependencies": []
|
||||
}
|
||||
</config>
|
||||
|
||||
<script lang="javascript">
|
||||
class ImJoyPlugin {
|
||||
async setup() {
|
||||
api.log('initialized')
|
||||
}
|
||||
|
||||
async run(ctx) {
|
||||
// get a list of service
|
||||
const services = await api.getServices({name: "OpenFlexure"});
|
||||
// get the first service
|
||||
const ofm = services[0];
|
||||
// snap an image
|
||||
await ofm.snapImage();
|
||||
console.log("snapped an image");
|
||||
}
|
||||
}
|
||||
|
||||
api.export(new ImJoyPlugin())
|
||||
</script>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<docs>
|
||||
[TODO: write documentation for this plugin.]
|
||||
</docs>
|
||||
|
||||
<config lang="json">
|
||||
{
|
||||
"name": "Test OFM Stage",
|
||||
"type": "web-worker",
|
||||
"tags": [],
|
||||
"ui": "",
|
||||
"version": "0.1.0",
|
||||
"cover": "",
|
||||
"description": "Move the OFM stage to test the API works",
|
||||
"icon": "extension",
|
||||
"inputs": null,
|
||||
"outputs": null,
|
||||
"api_version": "0.1.8",
|
||||
"env": "",
|
||||
"permissions": [],
|
||||
"requirements": [],
|
||||
"dependencies": []
|
||||
}
|
||||
</config>
|
||||
|
||||
<script lang="javascript">
|
||||
class ImJoyPlugin {
|
||||
async setup() {
|
||||
api.log('initialized')
|
||||
}
|
||||
|
||||
async run(ctx) {
|
||||
// get a list of service
|
||||
const services = await api.getServices({name: "OpenFlexure"});
|
||||
// get the first service
|
||||
const ofm = services[0];
|
||||
// snap an image
|
||||
const start = await ofm.getXYZPosition();
|
||||
console.log(`Starting position: ${start}`);
|
||||
// move the Z axis up and down
|
||||
await ofm.setPosition(start[2] + 100);
|
||||
console.log(`Moved Z to ${await ofm.getPosition()}`);
|
||||
await ofm.setPosition(start[2]);
|
||||
console.log(`Moved Z to ${await ofm.getPosition()}`);
|
||||
await ofm.fullFocus();
|
||||
console.log("Ran a full autofocus");
|
||||
}
|
||||
}
|
||||
|
||||
api.export(new ImJoyPlugin())
|
||||
</script>
|
||||
|
|
@ -10,8 +10,8 @@
|
|||
// Colors
|
||||
//
|
||||
|
||||
@global-color: #666;
|
||||
@global-emphasis-color: #333;
|
||||
@global-color: #000;
|
||||
@global-emphasis-color: #C32280;
|
||||
@global-muted-color: #666;
|
||||
|
||||
@global-primary-background: #C32280;
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
@global-medium-font-size: 20px;
|
||||
@global-small-font-size: 14px;
|
||||
|
||||
@global-emphasis-color: #222;
|
||||
|
||||
|
||||
//
|
||||
// Base
|
||||
|
|
|
|||
|
|
@ -28,7 +28,14 @@
|
|||
:class="item.class"
|
||||
@set-tab="setTab"
|
||||
>
|
||||
<i class="material-icons">{{ item.icon }}</i>
|
||||
<img
|
||||
v-if="item.iconURL"
|
||||
style="filter: grayscale(100%);width: 22px;margin-top: 5px;margin-bottom: 8px;"
|
||||
:src="item.iconURL"
|
||||
/>
|
||||
<i v-if="!item.iconURL" class="material-icons">
|
||||
{{ item.icon }}
|
||||
</i>
|
||||
</tabIcon>
|
||||
<!-- Add a divider if item.divide is true -->
|
||||
<hr v-if="item.divide" :key="'tab-divider-' + index" />
|
||||
|
|
@ -48,6 +55,25 @@
|
|||
<i class="material-icons">{{ plugin.icon || "extension" }}</i>
|
||||
</tabIcon>
|
||||
|
||||
<tabIcon
|
||||
v-for="imjoyTab in imjoyTabs"
|
||||
:key="imjoyTab.id"
|
||||
:tab-i-d="'ImJoy-Plugin-' + imjoyTab.id"
|
||||
:title="imjoyTab.name"
|
||||
:require-connection="false"
|
||||
:current-tab="currentTab"
|
||||
@set-tab="setTab"
|
||||
>
|
||||
<img
|
||||
v-if="imjoyTab.iconURL"
|
||||
style="filter: grayscale(100%);width: 22px;margin-top: 5px;margin-bottom: 8px;"
|
||||
:src="imjoyTab.iconURL"
|
||||
/>
|
||||
<i v-if="!imjoyTab.iconURL" class="material-icons">
|
||||
{{ imjoyTab.iconName || "extension" }}
|
||||
</i>
|
||||
</tabIcon>
|
||||
|
||||
<hr id="extension-tab-divider" />
|
||||
|
||||
<!-- For each bottom tab -->
|
||||
|
|
@ -104,6 +130,16 @@
|
|||
/>
|
||||
</tabContent>
|
||||
|
||||
<tabContent
|
||||
v-for="imjoyTab in imjoyTabs"
|
||||
:key="imjoyTab.id"
|
||||
:tab-i-d="'ImJoy-Plugin-' + imjoyTab.id"
|
||||
:require-connection="false"
|
||||
:current-tab="currentTab"
|
||||
>
|
||||
<div :id="imjoyTab.window_id" class="window-container">Loading...</div>
|
||||
</tabContent>
|
||||
|
||||
<!-- For each bottom tab -->
|
||||
<tabContent
|
||||
v-for="item in bottomTabs"
|
||||
|
|
@ -121,6 +157,7 @@
|
|||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
// Import generic components
|
||||
import tabIcon from "./genericComponents/tabIcon";
|
||||
|
|
@ -132,15 +169,22 @@ import captureContent from "./tabContentComponents/captureContent.vue";
|
|||
import slideScanContent from "./tabContentComponents/slideScanContent.vue";
|
||||
import viewContent from "./tabContentComponents/viewContent.vue";
|
||||
import settingsContent from "./tabContentComponents/settingsContent.vue";
|
||||
import galleryContent from "./tabContentComponents/galleryContent.vue";
|
||||
import extensionContent from "./tabContentComponents/extensionContent.vue";
|
||||
import aboutContent from "./tabContentComponents/aboutContent.vue";
|
||||
import loggingContent from "./tabContentComponents/loggingContent.vue";
|
||||
// ImJoy and the gallery are loaded asynchronously to allow them to be disabled if needed
|
||||
const galleryContent = () =>
|
||||
import(
|
||||
/* webpackChunkName: "gallery" */ "./tabContentComponents/galleryContent.vue"
|
||||
);
|
||||
const imjoyContent = () =>
|
||||
import(
|
||||
/* webpackChunkName: "imjoy" */ "./tabContentComponents/imjoyContent.vue"
|
||||
);
|
||||
|
||||
// Import modal components for device initialisation
|
||||
import calibrationModal from "./modalComponents/calibrationModal.vue";
|
||||
|
||||
import { mapState } from "vuex";
|
||||
import TabIcon from "./genericComponents/tabIcon.vue";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
|
|
@ -158,38 +202,14 @@ export default {
|
|||
extensionContent,
|
||||
calibrationModal,
|
||||
aboutContent,
|
||||
loggingContent
|
||||
loggingContent,
|
||||
TabIcon,
|
||||
imjoyContent
|
||||
},
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
plugins: [],
|
||||
currentTab: "view",
|
||||
unwatchStoreFunction: null,
|
||||
topTabs: [
|
||||
{
|
||||
id: "view",
|
||||
icon: "visibility",
|
||||
component: viewContent
|
||||
},
|
||||
{
|
||||
id: "gallery",
|
||||
icon: "photo_library",
|
||||
component: galleryContent,
|
||||
divide: true // Add a divider after this tab icon
|
||||
},
|
||||
{
|
||||
id: "navigate",
|
||||
icon: "gamepad",
|
||||
component: navigateContent
|
||||
},
|
||||
{
|
||||
id: "capture",
|
||||
icon: "camera_alt",
|
||||
component: captureContent,
|
||||
divide: true // Add a divider after this tab icon
|
||||
}
|
||||
],
|
||||
bottomTabs: [
|
||||
{
|
||||
id: "settings",
|
||||
|
|
@ -241,25 +261,64 @@ export default {
|
|||
return ind;
|
||||
},
|
||||
|
||||
topTabs: function() {
|
||||
let tabs = [
|
||||
{
|
||||
id: "view",
|
||||
icon: "visibility",
|
||||
component: viewContent
|
||||
},
|
||||
{
|
||||
id: "gallery",
|
||||
icon: "photo_library",
|
||||
component: galleryContent,
|
||||
divide: true // Add a divider after this tab icon
|
||||
},
|
||||
{
|
||||
id: "navigate",
|
||||
icon: "gamepad",
|
||||
component: navigateContent
|
||||
},
|
||||
{
|
||||
id: "capture",
|
||||
icon: "camera_alt",
|
||||
component: captureContent,
|
||||
divide: true // Add a divider after this tab icon
|
||||
}
|
||||
];
|
||||
if (!this.$store.state.galleryEnabled) {
|
||||
tabs = tabs.filter(tab => tab.id != "gallery");
|
||||
}
|
||||
if (this.$store.state.IHIEnabled) {
|
||||
tabs.push({
|
||||
id: "slidescan",
|
||||
icon: "settings_overscan",
|
||||
component: slideScanContent,
|
||||
divide: true
|
||||
});
|
||||
}
|
||||
if (this.$store.state.imjoyEnabled) {
|
||||
tabs.push({
|
||||
id: "imjoy",
|
||||
iconURL: "https://imjoy.io/static/img/imjoy-icon.svg",
|
||||
component: imjoyContent,
|
||||
divide: true
|
||||
});
|
||||
}
|
||||
return tabs;
|
||||
},
|
||||
|
||||
currentTabIndex: function() {
|
||||
return this.tabOrder.indexOf(this.currentTab);
|
||||
},
|
||||
|
||||
// Map the setting for IHI's interface so we can watch it
|
||||
...mapState(["IHIEnabled"])
|
||||
},
|
||||
|
||||
watch: {
|
||||
// Update the interface when the IHI interface is enabled/disabled
|
||||
IHIEnabled: function(newValue) {
|
||||
this.updateTopTabs(newValue);
|
||||
}
|
||||
// Map the tabs from ImJoy's store module so we can display them
|
||||
...mapState("imjoy", { imjoyTabs: "tabs" }),
|
||||
...mapState({ imjoyEnabled: "imjoyEnabled" })
|
||||
},
|
||||
|
||||
created: function() {
|
||||
if (this.$store.getters.ready) {
|
||||
// Update top tabs
|
||||
this.updateTopTabs(this.$store.state.IHIEnabled);
|
||||
// Update plugins
|
||||
this.updatePlugins().then(() => {
|
||||
// Start initialisation modals
|
||||
|
|
@ -284,19 +343,6 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
updateTopTabs: function(IHIEnabled) {
|
||||
if (IHIEnabled) {
|
||||
this.topTabs.push({
|
||||
id: "slidescan",
|
||||
icon: "settings_overscan",
|
||||
component: slideScanContent,
|
||||
divide: true
|
||||
});
|
||||
} else {
|
||||
// If the connection is now disconnected, empty capture list
|
||||
this.topTabs = this.topTabs.filter(tab => tab.id != "slidescan");
|
||||
}
|
||||
},
|
||||
updatePlugins: function() {
|
||||
return axios
|
||||
.get(this.pluginsUri)
|
||||
|
|
@ -331,6 +377,10 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.window-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#component-left {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,26 @@
|
|||
</div>
|
||||
|
||||
<div class="uk-card-footer uk-padding-small">
|
||||
<button
|
||||
v-if="openInImjoyMenuItems.length != 0"
|
||||
class="uk-icon"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
style="width:25px;"
|
||||
src="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||
/>
|
||||
</button>
|
||||
<div uk-dropdown="pos: top-center">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li v-for="item in openInImjoyMenuItems" :key="item.name">
|
||||
<a href="#" @click="item.callback(name, imgURL)"
|
||||
><i class="material-icons">launch</i>{{ item.title }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-for="tag in tags" :key="tag" class="uk-display-inline">
|
||||
<span
|
||||
v-if="tag === 'temporary'"
|
||||
|
|
@ -147,6 +167,7 @@
|
|||
<script>
|
||||
import UIkit from "uikit";
|
||||
import axios from "axios";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
import keyvalList from "../../fieldComponents/keyvalList";
|
||||
|
||||
|
|
@ -233,7 +254,8 @@ export default {
|
|||
},
|
||||
captureURL: function() {
|
||||
return this.links.self.href;
|
||||
}
|
||||
},
|
||||
...mapState("imjoy", { openInImjoyMenuItems: "openImageMenu" })
|
||||
},
|
||||
|
||||
created: function() {},
|
||||
|
|
|
|||
|
|
@ -40,6 +40,29 @@
|
|||
</div>
|
||||
|
||||
<div class="uk-card-footer uk-padding-small">
|
||||
<button
|
||||
v-if="openInImjoyMenuItems.length != 0"
|
||||
class="uk-icon"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
style="width:25px;"
|
||||
src="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||
/>
|
||||
</button>
|
||||
<div uk-dropdown="pos: top-center">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li v-for="item in openInImjoyMenuItems" :key="item.name">
|
||||
<a
|
||||
href="#"
|
||||
style="color:black"
|
||||
@click="item.callback(name, allURLs)"
|
||||
><i class="material-icons">launch</i>{{ item.title }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
|
|
@ -53,6 +76,7 @@
|
|||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
// Export main app
|
||||
export default {
|
||||
|
|
@ -100,7 +124,8 @@ export default {
|
|||
urls.push(capture.links.self.href);
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
},
|
||||
...mapState("imjoy", { openInImjoyMenuItems: "openScanMenu" })
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div
|
||||
class="uk-card uk-card-hover uk-card-small uk-card-default uk-margin"
|
||||
:class="{ 'uk-card-secondary': $store.state.darkMode }"
|
||||
>
|
||||
<div class="uk-card-body">
|
||||
<div class="uk-grid-small uk-flex-middle" uk-grid>
|
||||
<div class="uk-width-auto">
|
||||
<img
|
||||
v-if="iconURL"
|
||||
style="height:50px"
|
||||
:data-src="iconURL"
|
||||
:alt="name + ' icon'"
|
||||
uk-img
|
||||
/>
|
||||
<i v-if="!iconURL" class="material-icons" style="font-size:50px">
|
||||
{{ iconName }}
|
||||
</i>
|
||||
</div>
|
||||
<div class="uk-width-expand">
|
||||
<h3 class="uk-card-title uk-margin-remove-bottom">{{ name }}</h3>
|
||||
<p class="uk-text-meta uk-margin-remove-top">
|
||||
<slot name="subtitle"></slot>
|
||||
</p>
|
||||
</div>
|
||||
<div class="uk-width-auto">
|
||||
<button class="uk-button" @click="$emit('click')">
|
||||
<slot name="buttonText">Launch</slot>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ImjoyPluginCard",
|
||||
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
iconURL: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => ""
|
||||
},
|
||||
iconName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => "extension"
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,644 @@
|
|||
<template>
|
||||
<!-- Grid managing tab content -->
|
||||
<div class="uk-height-1-1 view-component">
|
||||
<progress
|
||||
ref="imjoyProgressbar"
|
||||
style="height: 4px; margin-bottom:0!important"
|
||||
class="uk-progress"
|
||||
max="100"
|
||||
></progress>
|
||||
|
||||
<div
|
||||
class="uk-flex uk-flex-column uk-margin uk-margin-left uk-margin-right"
|
||||
>
|
||||
<imjoy-plugin-card
|
||||
name="Load From URL"
|
||||
icon-u-r-l="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||
@click="loadPluginDialog()"
|
||||
>
|
||||
<template v-slot:buttonText>Open</template>
|
||||
</imjoy-plugin-card>
|
||||
<imjoy-plugin-card
|
||||
name="ImageJ.JS"
|
||||
icon-u-r-l="https://ij.imjoy.io/assets/icons/chrome/chrome-installprocess-128-128.png"
|
||||
@click="openImageJ()"
|
||||
/>
|
||||
<imjoy-plugin-card
|
||||
name="Kaibu"
|
||||
icon-u-r-l="https://kaibu.org/static/img/kaibu-icon.svg"
|
||||
@click="startKaibu()"
|
||||
/>
|
||||
<imjoy-plugin-card
|
||||
v-for="(p, name) in loadedPlugins"
|
||||
:key="p.id"
|
||||
:name="name"
|
||||
icon-name="extension"
|
||||
@click="runPlugin(p)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="loading" class="progress uk-margin-small">
|
||||
<div class="indeterminate"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as imjoyCore from "imjoy-core";
|
||||
import axios from "axios";
|
||||
import UIkit from "uikit";
|
||||
import { mapState } from "vuex";
|
||||
import ImjoyPluginCard from "./imjoyComponents/imjoyPluginCard.vue";
|
||||
|
||||
// TODO: move this to a module or something...
|
||||
async function pollAction(response) {
|
||||
// Poll an action until its status is completed
|
||||
// TODO: raise an error if it fails or is cancelled
|
||||
// For ease, this accepts and returns a response object from
|
||||
// axios.get
|
||||
// FIXME: this could be an infinite loop if the task is cancelled/errored
|
||||
let r = response;
|
||||
while ((r.data.status == "running") | (r.data.status == "pending")) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
r = await axios.get(r.data.href);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Image element and wait for it to load.
|
||||
*
|
||||
* This returns a Promise, so you can "await" the image
|
||||
* element rather than needing to add a callback.
|
||||
*/
|
||||
function loadImageFromUrl(src) {
|
||||
// Create an Image, and return it once it has loaded
|
||||
// see https://stackoverflow.com/a/66180709
|
||||
// no need to declare async, as it returns a Promise
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
// We must set crossOrigin="anonymous" in order
|
||||
// to avoid security errors when we get the pixel
|
||||
// data
|
||||
img.crossOrigin = "anonymous";
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = reject;
|
||||
img.src = src;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an image from a URL, and return the RGB data
|
||||
* in a format that can be cast to an ndarray
|
||||
*
|
||||
* This follows a slightly convoluted recipe, where we
|
||||
* "draw" the image, and then extract the pixels from the
|
||||
* 2D canvas.
|
||||
*/
|
||||
async function imageUrlToNdarray(url) {
|
||||
// see https://stackoverflow.com/questions/934012/get-image-data-url-in-javascript
|
||||
const img = await loadImageFromUrl(url);
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
||||
const rgbaData = imageData.data; // This gets the RGBA values as an ArrayBuffer
|
||||
// convert RGBA to RGB
|
||||
const rgbData = new Uint8Array(new ArrayBuffer(img.height * img.width * 3));
|
||||
for (let i = 0; i < img.height; i++) {
|
||||
for (let j = 0; j < img.width; j++) {
|
||||
const pos = i * img.width + j;
|
||||
rgbData[pos * 3] = rgbaData[pos * 4];
|
||||
rgbData[pos * 3 + 1] = rgbaData[pos * 4 + 1];
|
||||
rgbData[pos * 3 + 2] = rgbaData[pos * 4 + 2];
|
||||
}
|
||||
}
|
||||
return {
|
||||
_rtype: "ndarray",
|
||||
_rdtype: "uint8",
|
||||
_rshape: [img.height, img.width, 3],
|
||||
_rvalue: rgbData.buffer
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "ImJoyContent",
|
||||
|
||||
components: { ImjoyPluginCard },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
loadedPlugins: {},
|
||||
viewers: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
captureActionUri: function() {
|
||||
return `${this.$store.getters.baseUri}/api/v2/actions/camera/capture`;
|
||||
},
|
||||
snapshotStreamUri: function() {
|
||||
return `${this.$store.getters.baseUri}/api/v2/streams/snapshot`;
|
||||
},
|
||||
getPositionUri: function() {
|
||||
return `${this.$store.getters.baseUri}/api/v2/instrument/state/stage/position`;
|
||||
},
|
||||
moveActionUri: function() {
|
||||
return `${this.$store.getters.baseUri}/api/v2/actions/stage/move`;
|
||||
},
|
||||
baseUri: function() {
|
||||
return this.$store.getters.baseUri;
|
||||
},
|
||||
...mapState("imjoy", { windows: "tabs" })
|
||||
},
|
||||
mounted() {
|
||||
const self = this;
|
||||
const imjoy = new imjoyCore.ImJoy({
|
||||
// We will load the ImJoy plugin base frame via http (instead of https)
|
||||
// such that imjoy plugins can access insecured urls
|
||||
// however, some browser features won't work (e.g. webrtc)
|
||||
// unless we explicitly set `base_frame` in the plugin to
|
||||
// https://lib.imjoy.io/default_base_frame.html
|
||||
default_base_frame: "http://lib.imjoy.io/default_base_frame.html",
|
||||
imjoy_api: {
|
||||
async showDialog(plugin, config, exta_config) {
|
||||
return await imjoy.pm.createWindow(plugin, config, exta_config);
|
||||
},
|
||||
async showSnackbar(plugin, msg, duration) {
|
||||
duration = duration || 5;
|
||||
UIkit.notification.closeAll();
|
||||
UIkit.notification({
|
||||
message: msg.slice(0, 100),
|
||||
status: "primary",
|
||||
pos: "bottom-center",
|
||||
timeout: duration * 1000
|
||||
});
|
||||
},
|
||||
async showMessage(plugin, msg) {
|
||||
imjoy.imjoy_api.showSnackbar(plugin, msg, 5);
|
||||
},
|
||||
async showStatus(plugin, status) {
|
||||
imjoy.imjoy_api.showSnackbar(plugin, status, 5);
|
||||
},
|
||||
async showProgress(plugin, p) {
|
||||
p = p || 0;
|
||||
if (p < 1.0) p = p * 100;
|
||||
if (p > 100) p = 100;
|
||||
if (p) {
|
||||
self.$refs.imjoyProgressbar.style.display = "block";
|
||||
self.$refs.imjoyProgressbar.value = parseInt(p);
|
||||
} else {
|
||||
self.$refs.imjoyProgressbar.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
imjoy.start({ workspace: "default" }).then(async () => {
|
||||
console.log("ImJoy started");
|
||||
imjoy.event_bus.on("add_window", w => {
|
||||
this.addWindow(w);
|
||||
});
|
||||
this.setupMicroscopeAPI();
|
||||
this.setupViewers();
|
||||
const origin = window.location.origin;
|
||||
const localPlugins = [
|
||||
"OpenFlexureSnapImageTemplate",
|
||||
"OpenFlexureScriptEditor",
|
||||
"OpenFlexureTestMoveStage",
|
||||
"ListServices"
|
||||
];
|
||||
for (let fname of localPlugins) {
|
||||
await this.loadPlugin(`${origin}/${fname}.imjoy.html`);
|
||||
}
|
||||
this.setupPluginEngine();
|
||||
});
|
||||
this.imjoy = imjoy;
|
||||
},
|
||||
methods: {
|
||||
setupPluginEngine() {
|
||||
// setup a plugin engine for Python plugins
|
||||
this.imjoy.pm
|
||||
.reloadPluginRecursively({
|
||||
uri:
|
||||
"https://imjoy-team.github.io/jupyter-engine-manager/Jupyter-Engine-Manager.imjoy.html"
|
||||
})
|
||||
.then(enginePlugin => {
|
||||
// TODO: currently the url query are not preserved for ImJoy
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
const engine = urlParams.get("engine");
|
||||
const spec = urlParams.get("spec");
|
||||
if (engine) {
|
||||
enginePlugin.api
|
||||
.createEngine({
|
||||
name: "MyCustomEngine",
|
||||
nbUrl: engine,
|
||||
url: engine.split("?")[0]
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Jupyter Engine connected!");
|
||||
})
|
||||
.catch(e => {
|
||||
console.error("Failed to connect to Jupyter Engine", e);
|
||||
});
|
||||
} else {
|
||||
enginePlugin.api
|
||||
.createEngine({
|
||||
name: "MyBinder Engine",
|
||||
url: "https://mybinder.org",
|
||||
spec: spec || "oeway/imjoy-binder-image/master"
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Binder Engine connected!");
|
||||
})
|
||||
.catch(e => {
|
||||
console.error("Failed to connect to MyBinder Engine", e);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
async setupMicroscopeAPI() {
|
||||
// Here we register a set of API for controlling the microscope as a service
|
||||
// For interoperatability, it might be good to reuse a subset of the function names defined in MicroManager
|
||||
// See here: https://valelab4.ucsf.edu/~MM/doc/MMCore/html/class_c_m_m_core.html
|
||||
const snapshotUri = this.snapshotStreamUri;
|
||||
const captureActionUri = this.captureActionUri;
|
||||
const modalError = this.modalError;
|
||||
const getPositionAsObject = this.getStagePosition;
|
||||
const setPositionAsObject = this.setStagePosition;
|
||||
const getInstrumentSettings = this.getInstrumentSettings;
|
||||
const setInstrumentSettings = this.setInstrumentSettings;
|
||||
const runAutofocus = this.runAutofocus;
|
||||
|
||||
const service = {
|
||||
_rintf: true, // this will make sure the function can be called multiple times
|
||||
type: "#microscope-control",
|
||||
name: "OpenFlexure",
|
||||
lastSnapResponse: null,
|
||||
snapPreviewImage() {
|
||||
// The "proper" capture method is more involved - this one pulls a frame out of the preview stream
|
||||
return axios
|
||||
.get(snapshotUri)
|
||||
.then(response => {
|
||||
return response.data;
|
||||
})
|
||||
.catch(modalError);
|
||||
},
|
||||
snapImage() {
|
||||
// Do capture
|
||||
return axios.post(captureActionUri, {}).then(response => {
|
||||
service.lastSnapResponse = response;
|
||||
// TODO: send the events to flash the stream and update captures
|
||||
// Flash the stream (capture animation)
|
||||
//this.$root.$emit("globalFlashStream");
|
||||
// Update the global capture list
|
||||
//this.$root.$emit("globalUpdateCaptures");
|
||||
});
|
||||
//.catch(modalError);
|
||||
},
|
||||
async getImage() {
|
||||
// FIXME: handle nicely getImage() being called before snapImage()
|
||||
// Wait until the capture has completed and we can retrieve it
|
||||
service.lastSnapResponse = await pollAction(service.lastSnapResponse);
|
||||
let capture = service.lastSnapResponse.data.output;
|
||||
// TODO: check links.download.href exists and is JPEG
|
||||
let jpeg = await axios
|
||||
.get(capture.links.download.href, {
|
||||
responseType: "arraybuffer",
|
||||
headers: {
|
||||
"Content-Type": "image/jpeg"
|
||||
}
|
||||
})
|
||||
.then(r => r.data);
|
||||
console.log(
|
||||
"Downloaded ",
|
||||
jpeg.byteLength,
|
||||
" bytes of JPEG image data"
|
||||
);
|
||||
return jpeg; //TODO: what is the expected output format???
|
||||
},
|
||||
setPosition(z) {
|
||||
// The C api suggests this defaults to just Z, and can accept a "label" to
|
||||
// select the stage.
|
||||
return setPositionAsObject({
|
||||
z: z,
|
||||
absolute: true
|
||||
});
|
||||
},
|
||||
async getPosition() {
|
||||
const { z } = await getPositionAsObject();
|
||||
return z;
|
||||
},
|
||||
async setXYPosition(x, y) {
|
||||
return setPositionAsObject({
|
||||
x: x,
|
||||
y: y,
|
||||
absolute: true
|
||||
});
|
||||
},
|
||||
async getXYPosition() {
|
||||
const { x, y } = await getPositionAsObject();
|
||||
return [x, y];
|
||||
},
|
||||
async getXYZPosition() {
|
||||
const { x, y, z } = await getPositionAsObject();
|
||||
return [x, y, z];
|
||||
},
|
||||
async setXYZPosition(x, y, z) {
|
||||
return setPositionAsObject({
|
||||
x: x,
|
||||
y: y,
|
||||
z: z,
|
||||
absolute: true
|
||||
});
|
||||
},
|
||||
async getExposure() {
|
||||
const settings = await getInstrumentSettings();
|
||||
if (settings.camera.picamera) {
|
||||
if (settings.camera.picamera.shutter_speed) {
|
||||
return settings.camera.picamera.shutter_speed / 1000;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
async setExposure(exposure) {
|
||||
setInstrumentSettings({
|
||||
camera: {
|
||||
picamera: {
|
||||
shutter_speed: exposure * 1000
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
async fullFocus() {
|
||||
return runAutofocus();
|
||||
}
|
||||
};
|
||||
const ref = await this.imjoy.pm.registerService(
|
||||
{ type: "#microscope-control", name: "OpenFlexure" },
|
||||
service
|
||||
);
|
||||
console.log("registered service");
|
||||
console.log(ref);
|
||||
},
|
||||
closeWindow(w) {
|
||||
if (w) {
|
||||
w.api.close();
|
||||
this.$store.commit("imjoy/removeTab", w);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Activate a window - you can pass the window object, its name, or its id.
|
||||
*/
|
||||
selectWindow(window) {
|
||||
for (let w of this.windows) {
|
||||
if ((w === window) | (w.name === window) | (w.id === window)) {
|
||||
// Make the relevant window visible by switching to its tab
|
||||
this.$root.$emit("globalSwitchTab", `ImJoy-Plugin-${w.id}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
setWindowIconUrl(window, iconURL) {
|
||||
this.$store.commit("imjoy/setTabProperty", {
|
||||
tab: window,
|
||||
key: "iconURL",
|
||||
value: iconURL
|
||||
});
|
||||
},
|
||||
async startKaibu() {
|
||||
this.viewers.kaibu = await this.imjoy.pm.createWindow(null, {
|
||||
name: "Kaibu",
|
||||
src: "https://kaibu.org/#/app"
|
||||
});
|
||||
this.viewers.kaibu.on("close", () => {
|
||||
delete this.viewers.kaibu;
|
||||
});
|
||||
this.setWindowIconUrl(
|
||||
this.viewers.kaibu.config.id,
|
||||
"https://kaibu.org/static/img/kaibu-icon.svg"
|
||||
);
|
||||
this.viewers.kaibu.set_mode("full");
|
||||
},
|
||||
async startImageJ() {
|
||||
this.viewers.imagej = await this.imjoy.pm.createWindow(null, {
|
||||
name: "ImageJ.JS",
|
||||
src: "https://ij.imjoy.io"
|
||||
});
|
||||
this.viewers.imagej.on("close", () => {
|
||||
delete this.viewers.imagej;
|
||||
});
|
||||
this.setWindowIconUrl(
|
||||
this.viewers.imagej.config.id,
|
||||
"https://ij.imjoy.io/assets/icons/chrome/chrome-installprocess-128-128.png"
|
||||
);
|
||||
// load the ITK/VTK viewer for imagej.js
|
||||
this.loadPlugin(
|
||||
"https://gist.githubusercontent.com/oeway/e5c980fbf6582f25fde795262a7e33ec/raw/itk-vtk-viewer-imagej.imjoy.html"
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Switch to the ImageJ window, starting it if necessary.
|
||||
*/
|
||||
async openImageJ() {
|
||||
if (!this.viewers.imagej) {
|
||||
await this.startImageJ();
|
||||
}
|
||||
this.selectWindow("ImageJ.JS");
|
||||
return this.viewers.imagej;
|
||||
},
|
||||
/**
|
||||
* Switch to the Kaibu window, starting it if necessary.
|
||||
*/
|
||||
async openKaibu() {
|
||||
if (!this.viewers.kaibu) {
|
||||
await this.startKaibu();
|
||||
}
|
||||
this.selectWindow("Kaibu"); // TODO: make this behave more nicely when we have multiple Kaibu windows
|
||||
return this.viewers.kaibu;
|
||||
},
|
||||
async setupViewers() {
|
||||
this.loading = true;
|
||||
// TODO: improve this using the ImJoy `api.getServices`
|
||||
// See here: https://imjoy.io/docs/#/api?id=apigetservices
|
||||
// The idea is that plugins can register custom services via `api.registerService`
|
||||
// For example, we can have a image visualization service
|
||||
// Then other plugins can use `api.getServices` to get a list of services
|
||||
// And this can be used for render our "Open In ImJoy" menu
|
||||
try {
|
||||
const self = this;
|
||||
|
||||
this.$store.commit("imjoy/addOpenImageItem", {
|
||||
title: "Kaibu",
|
||||
async callback(name, imageUrl) {
|
||||
const viewer = await self.openKaibu();
|
||||
await viewer.view_image(imageUrl, { type: "2d-image", name });
|
||||
await viewer.add_shapes([]);
|
||||
}
|
||||
});
|
||||
|
||||
this.$store.commit("imjoy/addOpenImageItem", {
|
||||
title: "ImageJ.JS",
|
||||
async callback(name, imageUrl) {
|
||||
const viewer = await self.openImageJ();
|
||||
const response = await fetch(imageUrl);
|
||||
const buffer = await response.arrayBuffer();
|
||||
await viewer.viewImage(buffer, {
|
||||
name: name.replace(".jpeg", ".jpg")
|
||||
});
|
||||
}
|
||||
});
|
||||
this.$store.commit("imjoy/addOpenImageItem", {
|
||||
title: "ImageJ.JS [ndarray]",
|
||||
async callback(name, imageUrl) {
|
||||
const viewer = await self.openImageJ();
|
||||
const ndarray = await imageUrlToNdarray(imageUrl);
|
||||
await viewer.viewImage(ndarray, {
|
||||
name: name.replace(".jpeg", "")
|
||||
});
|
||||
}
|
||||
});
|
||||
this.$store.commit("imjoy/addOpenScanItem", {
|
||||
title: "ImageJ.JS [stack]",
|
||||
async callback(name, allURLs) {
|
||||
const viewer = await self.openImageJ();
|
||||
// allURLs is a list of URLs for *capture objects*, so
|
||||
// first we need to retrieve them, then extract image URLs
|
||||
// and finally turn image URLs into "numpy arrays" (RGB data).
|
||||
const imagesAsNdarrays = await Promise.all(
|
||||
allURLs.map(async url => {
|
||||
const r = await axios.get(url);
|
||||
return await imageUrlToNdarray(r.data.links.download.href);
|
||||
})
|
||||
);
|
||||
// Construct an array to hold all the images, based on the first image
|
||||
// TODO: assert that _rdtype is uint8, _rshape is the same, _rtype is ndarray
|
||||
const shape = imagesAsNdarrays[0]._rshape;
|
||||
const totalSize = imagesAsNdarrays.reduce(
|
||||
(total, image) => total + image._rvalue.byteLength,
|
||||
0
|
||||
);
|
||||
const stackData = new Uint8Array(new ArrayBuffer(totalSize));
|
||||
const transferredBytes = imagesAsNdarrays.reduce((total, image) => {
|
||||
console.log(`inserting image at ${total} bytes`);
|
||||
// TODO: is it bad to populate stackData as a "side effect"?
|
||||
stackData.set(new Uint8Array(image._rvalue), total);
|
||||
//assert image._rshape == shape;
|
||||
return total + image._rvalue.byteLength;
|
||||
}, 0);
|
||||
const stackNdarray = {
|
||||
_rtype: "ndarray",
|
||||
_rdtype: "uint8",
|
||||
_rshape: [imagesAsNdarrays.length, shape[0], shape[1], shape[2]],
|
||||
_rvalue: stackData.buffer
|
||||
};
|
||||
console.log(
|
||||
`Stack is ${transferredBytes} and will have shape: ${stackNdarray._rshape}`
|
||||
);
|
||||
await viewer.viewImage(stackNdarray, {
|
||||
name
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
alert(`Failed to setup viewers, error: ${e}`);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async addWindow(w) {
|
||||
// Here we can add check w.standalone if we want to allow the plugin to decide whether to open in a new tab.
|
||||
this.$store.commit("imjoy/addTab", w);
|
||||
await this.$nextTick();
|
||||
//this.$forceUpdate();
|
||||
this.selectWindow(w);
|
||||
},
|
||||
loadPlugin(uri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.loading = true;
|
||||
this.imjoy.pm
|
||||
.reloadPluginRecursively({
|
||||
uri
|
||||
})
|
||||
.then(async plugin => {
|
||||
this.loadedPlugins[plugin.name] = plugin;
|
||||
this.loading = false;
|
||||
resolve(plugin);
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
this.loading = false;
|
||||
reject(e);
|
||||
alert(`failed to load the plugin, error: ${e}`);
|
||||
});
|
||||
});
|
||||
},
|
||||
async runPlugin(plugin) {
|
||||
await plugin.api.run();
|
||||
},
|
||||
loadPluginDialog() {
|
||||
const uri = prompt(
|
||||
"Paste the ImJoy plugin url here",
|
||||
"imjoy-team/imjoy-plugins:Welcome"
|
||||
);
|
||||
if (uri) {
|
||||
this.loadPlugin(uri);
|
||||
}
|
||||
},
|
||||
async getStagePosition() {
|
||||
return axios.get(this.getPositionUri).then(r => r.data);
|
||||
},
|
||||
async setStagePosition(payload) {
|
||||
return axios.post(this.moveActionUri, payload).then(pollAction);
|
||||
},
|
||||
async getInstrumentSettings() {
|
||||
return axios
|
||||
.get(`${this.baseUri}/api/v2/instrument/settings/`)
|
||||
.then(r => r.data);
|
||||
},
|
||||
async setInstrumentSettings(payload) {
|
||||
return axios.put(`${this.baseUri}/api/v2/instrument/settings/`, payload);
|
||||
},
|
||||
runAutofocus() {
|
||||
// This mechanism is also used by the keybinding for autofocus.
|
||||
// It means we don't have a way to poll, but that's not a problem.
|
||||
this.$root.$emit("globalFastAutofocusEvent");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.window-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.imjoy-container-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.window-title {
|
||||
height: 26px;
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
background-color: #efefef;
|
||||
color: #482727;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.plugins-button {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
height: 26px;
|
||||
line-height: 11px;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
position: absolute;
|
||||
right: 100px;
|
||||
height: 26px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -14,6 +14,36 @@
|
|||
for use in clinics acquiring blood smear samples using a 100x
|
||||
oil-immersion objective, with a Raspberry Pi Camera v2.
|
||||
</p>
|
||||
<p class="uk-margin-small" :class="{ 'uk-text-muted': !imjoyPermitted }">
|
||||
<label :disabled="!imjoyPermitted">
|
||||
<input
|
||||
v-model="imjoyEnabled"
|
||||
class="uk-checkbox"
|
||||
type="checkbox"
|
||||
:disabled="!imjoyPermitted"
|
||||
/>
|
||||
Enable ImJoy plugin engine
|
||||
</label>
|
||||
</p>
|
||||
<p v-if="imjoyPermitted" class="uk-margin-small">
|
||||
<a href="https://imjoy.io/">ImJoy</a> enables integration with a wide
|
||||
variety of microscopy and image analysis applications, including ImageJ.JS
|
||||
and Kaibu. Support for ImJoy within the OFM software is currently
|
||||
experimental, and it may slow down loading of the application.
|
||||
</p>
|
||||
<p v-if="!imjoyPermitted" class="uk-margin-small uk-text-muted">
|
||||
ImJoy plugins are disabled in this build of the OpenFlexure software.
|
||||
</p>
|
||||
<p class="uk-margin-small">
|
||||
<label
|
||||
><input v-model="galleryEnabled" class="uk-checkbox" type="checkbox" />
|
||||
Enable Gallery</label
|
||||
>
|
||||
</p>
|
||||
<p class="uk-margin-small">
|
||||
The "gallery" tab can cause performance issues; un-tick the box above to
|
||||
disable it.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -34,18 +64,50 @@ export default {
|
|||
set(value) {
|
||||
this.$store.commit("changeIHIEnabled", value);
|
||||
}
|
||||
},
|
||||
imjoyEnabled: {
|
||||
get() {
|
||||
return this.$store.state.imjoyEnabled;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("changeImjoyEnabled", value);
|
||||
}
|
||||
},
|
||||
galleryEnabled: {
|
||||
get() {
|
||||
return this.$store.state.galleryEnabled;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("changeGalleryEnabled", value);
|
||||
}
|
||||
},
|
||||
imjoyPermitted: function() {
|
||||
// ImJoy may be disabled using an environment variable.
|
||||
// If this function returns false, it is never possible to
|
||||
// use ImJoy, so we should gray out the option.
|
||||
return process.env.VUE_APP_ENABLE_IMJOY === "true";
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
IHIEnabled: function() {
|
||||
this.setLocalStorageObj("IHIEnabled", this.IHIEnabled);
|
||||
},
|
||||
imjoyEnabled: function() {
|
||||
this.setLocalStorageObj("imjoyEnabled", this.imjoyEnabled);
|
||||
},
|
||||
galleryEnabled: function() {
|
||||
this.setLocalStorageObj("galleryEnabled", this.galleryEnabled);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Try loading settings from localStorage. If null, don't change.
|
||||
this.IHIEnabled = this.getLocalStorageObj("IHIEnabled") || this.IHIEnabled;
|
||||
this.imjoyEnabled =
|
||||
this.getLocalStorageObj("imjoyEnabled") || this.imjoyEnabled;
|
||||
this.galleryEnabled =
|
||||
this.getLocalStorageObj("galleryEnabled") || this.galleryEnabled;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -95,21 +95,12 @@ export default {
|
|||
// Cache the stream settings to local storage for persistence
|
||||
// (the next 3 functions all relate to this)
|
||||
disableStream: function(newValue) {
|
||||
console.log(
|
||||
`disableStream updated to ${newValue} and saved in local storage`
|
||||
);
|
||||
this.setLocalStorageObj("disableStream", newValue);
|
||||
},
|
||||
autoGpuPreview: function(newValue) {
|
||||
console.log(
|
||||
`GPU preview updated to ${newValue} and saved in local storage`
|
||||
);
|
||||
this.setLocalStorageObj("autoGpuPreview", newValue);
|
||||
},
|
||||
trackWindow: function(newValue) {
|
||||
console.log(
|
||||
`trackWindow updated to ${newValue} and saved in local storage`
|
||||
);
|
||||
this.setLocalStorageObj("trackWindow", newValue);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,64 @@ import Vuex from "vuex";
|
|||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const moduleImjoy = {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
tabs: [],
|
||||
openImageMenu: [],
|
||||
openScanMenu: []
|
||||
}),
|
||||
mutations: {
|
||||
addOpenImageItem(state, newItem) {
|
||||
state.openImageMenu.push(newItem);
|
||||
}, // TODO: add a mutation to remove items when plugins are unloaded
|
||||
addOpenScanItem(state, newItem) {
|
||||
state.openScanMenu.push(newItem);
|
||||
}, // TODO: add a mutation to remove items when plugins are unloaded
|
||||
clearMenus(state) {
|
||||
// This is primarily useful to reset the state in hot-reloads of
|
||||
// imjoyContent.vue
|
||||
state.openImageMenu = [];
|
||||
state.openScanMenu = [];
|
||||
},
|
||||
addTab(state, newItem) {
|
||||
// Add a tab to the list of ImJoy tabs
|
||||
state.tabs.push(newItem);
|
||||
},
|
||||
removeTab(state, tabToRemove) {
|
||||
const index = state.tabs.indexOf(tabToRemove);
|
||||
if (index > -1) {
|
||||
state.tabs.splice(index, 1);
|
||||
} else {
|
||||
console.warn(
|
||||
`Attempted to remove a non-existing ImJoy tab ${tabToRemove}`
|
||||
);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Set a parameter on the tab with a given id
|
||||
*
|
||||
* Payload should contain:
|
||||
* tab: ID of the tab to modify (not window_id)
|
||||
* key: name of the property to add/change
|
||||
* value: value of the property to add/change
|
||||
*/
|
||||
setTabProperty(state, payload) {
|
||||
let tab = payload.tab;
|
||||
let key = payload.key;
|
||||
let value = payload.value;
|
||||
state.tabs = state.tabs.map(t => {
|
||||
if (t.id === tab) {
|
||||
t[key] = value;
|
||||
}
|
||||
return t;
|
||||
});
|
||||
}
|
||||
},
|
||||
actions: {},
|
||||
getters: {}
|
||||
};
|
||||
|
||||
function getOriginFromLocation() {
|
||||
// This will default to the same origin that's serving
|
||||
// the web app - but can be overridden by the URL.
|
||||
|
|
@ -17,6 +75,9 @@ function getOriginFromLocation() {
|
|||
}
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
imjoy: moduleImjoy
|
||||
},
|
||||
state: {
|
||||
origin: getOriginFromLocation(),
|
||||
available: false,
|
||||
|
|
@ -26,6 +87,8 @@ export default new Vuex.Store({
|
|||
autoGpuPreview: false,
|
||||
trackWindow: true,
|
||||
IHIEnabled: false,
|
||||
imjoyEnabled: false,
|
||||
galleryEnabled: true,
|
||||
appTheme: "system",
|
||||
activeStreams: {}
|
||||
},
|
||||
|
|
@ -52,6 +115,20 @@ export default new Vuex.Store({
|
|||
changeIHIEnabled(state, enabled) {
|
||||
state.IHIEnabled = enabled;
|
||||
},
|
||||
changeImjoyEnabled(state, enabled) {
|
||||
if (process.env.VUE_APP_ENABLE_IMJOY === "true") {
|
||||
state.imjoyEnabled = enabled;
|
||||
} else {
|
||||
state.imjoyEnabled = false;
|
||||
if (enabled)
|
||||
console.warn(
|
||||
"Attempted to enable ImJoy, but it's disabled by VUE_APP_ENABLE_IMJOY."
|
||||
);
|
||||
}
|
||||
},
|
||||
changeGalleryEnabled(state, enabled) {
|
||||
state.galleryEnabled = enabled;
|
||||
},
|
||||
resetState(state) {
|
||||
state.waiting = false;
|
||||
state.available = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue