--- lib/python3.10/site-packages/tensorboard/notebook.py.orig	2022-04-04 15:13:24.242666542 +0200
+++ lib/python3.10/site-packages/tensorboard/notebook.py	2022-04-04 15:22:16.791830780 +0200
@@ -34,6 +34,7 @@
 # details).
 _CONTEXT_COLAB = "_CONTEXT_COLAB"
 _CONTEXT_IPYTHON = "_CONTEXT_IPYTHON"
+_CONTEXT_JUPYTERHUB = "_CONTEXT_JUPYTERHUB"
 _CONTEXT_NONE = "_CONTEXT_NONE"
 
 
@@ -70,11 +71,27 @@
     else:
         ipython = IPython.get_ipython()
         if ipython is not None and ipython.has_trait("kernel"):
+            if os.environ.get("JUPYTERHUB_SERVICE_PREFIX") is not None:
+                return _CONTEXT_JUPYTERHUB
             return _CONTEXT_IPYTHON
 
     # Otherwise, we're not in a known notebook context.
     return _CONTEXT_NONE
 
+def _prefix_jupyterhub(port):
+    prefix = os.path.join(os.environ["JUPYTERHUB_SERVICE_PREFIX"], 'proxy/absolute')
+    return "%s/%d/" % (prefix, port)
+
+
+def _patch_args_jupyterhub(parsed_args):
+    if "--port" in parsed_args:
+        arg_idx = parsed_args.index("--port")
+        port = int(parsed_args[arg_idx+1])
+    else:
+        port = 6006
+        parsed_args += ["--port", str(port)]
+    return parsed_args + ["--path_prefix", _prefix_jupyterhub(port)]
+
 
 def load_ipython_extension(ipython):
     """Deprecated: use `%load_ext tensorboard` instead.
@@ -149,6 +166,9 @@
             handle.update(IPython.display.Pretty(message))
 
     parsed_args = shlex.split(args_string, comments=True, posix=True)
+    if context == _CONTEXT_JUPYTERHUB:
+        parsed_args = _patch_args_jupyterhub(parsed_args)
+
     start_result = manager.start(parsed_args)
 
     if isinstance(start_result, manager.StartLaunched):
@@ -305,6 +325,7 @@
     fn = {
         _CONTEXT_COLAB: _display_colab,
         _CONTEXT_IPYTHON: _display_ipython,
+        _CONTEXT_JUPYTERHUB: _display_jupyterhub,
         _CONTEXT_NONE: _display_cli,
     }[_get_context()]
     return fn(port=port, height=height, display_handle=display_handle)
@@ -401,6 +422,36 @@
     for (k, v) in replacements:
         shell = shell.replace(k, v)
     iframe = IPython.display.HTML(shell)
+    if display_handle:
+        display_handle.update(iframe)
+    else:
+        IPython.display.display(iframe)
+
+
+def _display_jupyterhub(port, height, display_handle):
+    import IPython.display
+
+    frame_id = "tensorboard-frame-{:08x}".format(random.getrandbits(64))
+    shell = """
+      <iframe id="%HTML_ID%" width="100%" height="%HEIGHT%" frameborder="0">
+      </iframe>
+      <script>
+        (function() {
+          const frame = document.getElementById(%JSON_ID%);
+          const url = new URL("%PREFIX%", window.location);
+          frame.src = url;
+        })();
+      </script>
+  """
+    replacements = [
+        ("%HTML_ID%", html.escape(frame_id, quote=True)),
+        ("%JSON_ID%", json.dumps(frame_id)),
+        ("%PREFIX%", _prefix_jupyterhub(port)),
+        ("%HEIGHT%", "%d" % height),
+    ]
+    for (k, v) in replacements:
+        shell = shell.replace(k, v)
+    iframe = IPython.display.HTML(shell)
     if display_handle:
         display_handle.update(iframe)
     else: