Skip to content

Commit

Permalink
Merge pull request #90 from imjoy-team/imagej-chat
Browse files Browse the repository at this point in the history
Add Imagej chat
  • Loading branch information
oeway authored Apr 11, 2024
2 parents 352ec10 + ff46598 commit 4fed7f3
Show file tree
Hide file tree
Showing 6 changed files with 465 additions and 46 deletions.
291 changes: 291 additions & 0 deletions src/chat.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="ResourceLoaderDynamicStyles" content="" />
<meta name="generator" content="MediaWiki 1.28.0" />
<meta
name="description"
content="ImageJ is an open source image processing program designed for scientific multidimensional images."
/>
<link rel="shortcut icon" href="assets/img/ij2.ico" />
<meta property="og:type" content="website" />

<meta property="og:title" content="ImageJ.JS" />

<meta property="og:image" content="assets/img/imagej-js-imjoy.png" />

<meta
property="og:description"
content="ImageJ.JS is an open source image processing web application designed for scientific multidimensional images"
/>
<meta
id="index-viewport"
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>ImageJ.JS</title>
<link rel="stylesheet" href="/style.css" />
<script src="https://cjrtnc.leaningtech.com/20201217_2/loader.js"></script>
<link
rel="apple-touch-icon"
sizes="48x48"
href="assets/icons/android/android-launchericon-48-48.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="assets/icons/android/android-launchericon-72-72.png"
/>
<link
rel="apple-touch-icon"
sizes="96x96"
href="assets/icons/android/android-launchericon-96-96.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="assets/icons/android/android-launchericon-144-144.png"
/>
<link
rel="apple-touch-icon"
sizes="192x192"
href="assets/icons/android/android-launchericon-192-192.png"
/>
<link
rel="apple-touch-icon"
sizes="512x512"
href="assets/icons/android/android-launchericon-512-512.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="assets/icons/android/android-launchericon-192-192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="assets/icons/firefox/firefox-general-32-32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="assets/icons/android/android-launchericon-96-96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="assets/icons/chrome/chrome-favicon-16-16.png"
/>
<script
async
src="https://cdn.jsdelivr.net/npm/pwacompat"
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/codemirror.min.js"
integrity="sha512-K8GMktcEqOI28I3f5C6kraFm1F4wVLvnBxGU+imS/zOLT1COAT799Ln4DJyAbpdyNt6LgMIWcwy4ptCYXCIDqA=="
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/codemirror.min.css"
integrity="sha512-/BlxZbYLtYGExexketXsTi47eHp+r2kTeq2OHecQPZlfbc7WFXVrwbVW9HOYjI6c9Ti+P60ASmVLxittZ0EBGw=="
crossorigin="anonymous"
/>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/xml/xml.min.js"
integrity="sha512-k1HnoY9EXahEfPz7kq/lD9DltloKH9OrB9XNKYoUQrNz9epe5F4mQP5PfuIfeRfoXHkNrE0gF3Mx4LhC5BVl9Q=="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/css/css.min.js"
integrity="sha512-DG+5u//fVN9kpDgTGe78IJhJW8e5+tlrPaMgNqcrzyPXsn+GPaF2T62+X3ds7SuhFR9Qeb7XZ6kMD8X09FeJhA=="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/javascript/javascript.min.js"
integrity="sha512-9miXlEjnHTF+nVGdc2IGOLGTFW2wWkWbd1/7Ltlre+dM53ZSCUQ/PNN+jtsmYqr3ndiD5RW6XQJUm/Hz8JvyOQ=="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/python/python.min.js"
integrity="sha512-DS+asaww1mE0V/N6YGVgoNIRj+yXB9hAV68vM6rVeWs0G+OyMd24LKrnS4Z+g26rgghU7qvGeEnRVUArV7nVog=="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/htmlmixed/htmlmixed.min.js"
integrity="sha512-p15qsXPrhaUkH+/RPE6QzCmxUAPkCRw89ityx+tWC1lAYI6Et2L0UpN+iqifxUdt+ss1FQ+9CuzxpBeT9mR3/w=="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/markdown/markdown.min.js"
integrity="sha512-i0WWgBhBBkoMBlFQAsWUqSGwbGx6ZRKHJqGvuLdAl1qu1SDSyxJmwPAiVSPkzGqz1TM64yiH7b8sCUjNeoW8Kw=="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/search/search.min.js"
integrity="sha512-Scy8gOuTrwkguZyL89xZe5MMIdgZGUUQAkkDKtUI4mq2fNVTRg+pSsPcXkRtNmFN8sQ23MZNA0FsqJUPEQ0PUQ=="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/search/searchcursor.min.js"
integrity="sha512-DVIRH6DkN3F/ZpyO69rw0Z4v2KmSXzt281MckBasGKgKfLSi2n4n5L0SByrLFZzZP1cunvJY8xkjhtZKk9k8HA=="
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/dialog/dialog.min.css"
integrity="sha512-Vogm+Cii1SXP5oxWQyPdkA91rHB776209ZVvX4C/i4ypcfBlWVRXZGodoTDAyyZvO36JlTqDqkMhVKAYc7CMjQ=="
crossorigin="anonymous"
/>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/dialog/dialog.min.js"
integrity="sha512-YU8ue8QADzIU/tOodVSM+D74vp1FICLl737eY54IbYYuu+ZsG/JEoZFgfUGOXWWyp3lo02wKTnPHrroWRy+Fgg=="
crossorigin="anonymous"
></script>
</head>

<body>
<div id="site-tips-container" style="display: none;text-align: center;">
中国用户请使用镜像站点
<a href="https://cnij.imjoy.io">https://cnij.imjoy.io</a>
</div>
<div id="drag-overlay">
<input type="file" id="open-file" style="display: none;" />
<span>Drop file here to open</span>
</div>
<div id="ijWindowPlaceholder" style="visibility: hidden;">
<a>ImageJ</a><a class="controls closeButton">+</a>
</div>
<div class="container" id="imagej-container"></div>

<div class="lds-ellipsis" id="loader">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="dialog" id="open-file-dialog">
<div class="dialog-overlay" tabindex="-1" data-a11y-dialog-hide></div>
<dialog
class="dialog-content"
aria-labelledby="dialogTitle"
aria-describedby="dialogDescription"
>
<button
data-a11y-dialog-hide
class="dialog-close"
aria-label="Close this dialog window"
>
&times;
</button>

<h1 id="dialogTitle">Open File</h1>

<p id="dialogDescription">
You can either select a file from your local file system or cached
files in the browser.
</p>

<div class="dialog-button-group">
<button aria-label="Select a local file" id="open-file-modal-select">
Select Local File
</button>
<button
aria-label="Select a cached file"
id="open-file-modal-internal"
>
Select Cached File
</button>
<button
aria-label="Cancel the selection"
id="open-file-modal-cancel"
data-a11y-dialog-hide
>
Cancel
</button>
</div>
</dialog>
</div>

<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
crossorigin="anonymous"
/>
<script>
function unRegisterServiceWorker(){
navigator.serviceWorker.getRegistrations().then(
function(registrations) {
for (let registration of registrations) {
registration.unregister().then(function() {
return self.clients.matchAll();
}).then(function(clients) {
clients.forEach(client => {
if (client.url && "navigate" in client) {
client.navigate(client.url);
}
});
console.log('Service worker unregistered');
});
}
});
}
document.addEventListener(
"DOMContentLoaded",
async function() {
unRegisterServiceWorker();
await startImageJ();
const imjoy = await window.imjoyReady;
await imjoy.api.loadPlugin(window.location.origin + "/imagej-js-chatbot-extension.imjoy.html")
},
false
);
</script>
<script>
const siteTipsContainer = document.getElementById("site-tips-container");
setTimeout(() => {
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const hostnameArr = [
"cnij.imjoy.io",
"0.0.0.0",
"127.0.0.1",
"localhost"
];
if (
timeZone === "Asia/Shanghai" &&
!hostnameArr.includes(window.location.hostname)
) {
siteTipsContainer.style.display = "block";
}
}, 1000);
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=UA-134837258-3"
></script>
<script>
const dnt =
navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
if (dnt != "1" && dnt != "yes") {
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());

gtag("config", "UA-134837258-3");
} else {
console.debug("Respecting Do-Not-Track, not loading analytics.");
}
</script>
</body>
</html>
103 changes: 103 additions & 0 deletions src/imagej-js-chatbot-extension.imjoy.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@

<docs>
[TODO: write documentation for this plugin.]
</docs>

<config lang="json">
{
"name": "ImageJ.JS Chatbot Extension",
"type": "web-worker",
"tags": [],
"ui": "",
"version": "0.1.0",
"cover": "",
"description": "Run ImageJ.JS macro in the chatbot",
"icon": "extension",
"inputs": null,
"outputs": null,
"api_version": "0.1.8",
"env": "",
"permissions": [],
"requirements": [],
"dependencies": []
}
</config>

<script lang="javascript">
class ImJoyPlugin {
async setup() {
if (api.registerChatbotExtension) {
const chatbot = api
await this.registerExtensions(chatbot.registerChatbotExtension)
} else {
let chatbot = await api.getWindow("BioImage.IO Chatbot")
if (chatbot) {
await this.registerExtensions(chatbot.registerExtension)
} else {
chatbot = await api.createWindow({src: "https://bioimage.io/chat", w: 28, h: 20, name: "BioImage.IO Chatbot"})
await this.registerExtensions(chatbot.registerExtension)
}
let ij = await api.getWindow("ImageJ.JS")
if (!ij) {
ij = await api.createWindow({src: "https://ij.imjoy.io/", name: "ImageJ.JS"})
await ij.runMacro('run("Blobs (25K)")')
}
}
}

async registerExtensions(register) {
await register({
_rintf: true,
id: "imagej_js",
type: "bioimageio-chatbot-extension",
name: "ImageJ.JS",
description: "Run ImageJ macro for image analysis; This allows the user to interact with a ImageJ instance running in the chat assitant, the imagej macro will be executed in imagej and the result will be shown to the user; The state of the ImageJ instance will be preserved between different macro executions. Try to fix the macro if an error occurs.",
async get_schema() {
return {
run_macro: {
type: "object",
title: "run_macro",
description: "Run ImageJ macro",
properties: {
macro: {
type: "string",
description: "ImageJ macro (for running inside imagej1, with no plugin support)",
},
},
required: ["macro"]
}
}
},
tools: {
async run_macro(config) {
let ij = await api.getWindow("ImageJ.JS")
if(!ij){
ij = await api.createWindow({src: "https://ij.imjoy.io/", name: "ImageJ.JS"})
}
// patch example images
// replace https://imagej.nih.gov/ij/images/* to https://imagej.net/images/*
config.macro = config.macro.replace(/https:\/\/imagej.nih.gov\/ij\/images\//g, "https://imagej.net/images/")
try{
await ij.runMacro(config.macro)
return "Macro executed successfully."
}
catch(e){
// hack to handle null pointer exception
if(e === "java.lang.NullPointerException"){
return "Macro executed successfully."
}
console.error(e)
return "Failed to execute the macro: "+e
}
},
}
})

api.log('initialized')
}

async run(ctx) { }
}

api.export(new ImJoyPlugin())
</script>
Loading

0 comments on commit 4fed7f3

Please sign in to comment.