diff --git a/components/clp-package-utils/clp_package_utils/scripts/start_clp.py b/components/clp-package-utils/clp_package_utils/scripts/start_clp.py
index 5e5965bf5..4d0d461ba 100755
--- a/components/clp-package-utils/clp_package_utils/scripts/start_clp.py
+++ b/components/clp-package-utils/clp_package_utils/scripts/start_clp.py
@@ -786,6 +786,9 @@ def start_webui(instance_id: str, clp_config: CLPConfig, mounts: CLPDockerMounts
         },
         "public": {
             "ClpStorageEngine": clp_config.package.storage_engine,
+            "LogViewerWebuiUrl": (
+                f"http://{clp_config.log_viewer_webui.host}:{clp_config.log_viewer_webui.port}",
+            ),
         },
     }
     meteor_settings = read_and_update_settings_json(settings_json_path, meteor_settings_updates)
diff --git a/components/log-viewer-webui/client/package-lock.json b/components/log-viewer-webui/client/package-lock.json
index bf75b0710..4141c89aa 100644
--- a/components/log-viewer-webui/client/package-lock.json
+++ b/components/log-viewer-webui/client/package-lock.json
@@ -9,6 +9,11 @@
       "version": "0.1.0",
       "license": "Apache-2.0",
       "dependencies": {
+        "@emotion/react": "^11.13.0",
+        "@emotion/styled": "^11.13.0",
+        "@mui/joy": "^5.0.0-beta.48",
+        "@types/react": "^18.3.3",
+        "axios": "^1.7.2",
         "react": "^18.3.1",
         "react-dom": "^18.3.1"
       },
@@ -46,7 +51,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
       "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
-      "dev": true,
       "dependencies": {
         "@babel/highlight": "^7.24.7",
         "picocolors": "^1.0.0"
@@ -98,7 +102,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz",
       "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==",
-      "dev": true,
       "dependencies": {
         "@babel/types": "^7.24.7",
         "@jridgewell/gen-mapping": "^0.3.5",
@@ -210,7 +213,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
       "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
-      "dev": true,
       "dependencies": {
         "@babel/types": "^7.24.7"
       },
@@ -222,7 +224,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
       "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
-      "dev": true,
       "dependencies": {
         "@babel/template": "^7.24.7",
         "@babel/types": "^7.24.7"
@@ -235,7 +236,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
       "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
-      "dev": true,
       "dependencies": {
         "@babel/types": "^7.24.7"
       },
@@ -260,7 +260,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
       "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
-      "dev": true,
       "dependencies": {
         "@babel/traverse": "^7.24.7",
         "@babel/types": "^7.24.7"
@@ -373,7 +372,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
       "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
-      "dev": true,
       "dependencies": {
         "@babel/types": "^7.24.7"
       },
@@ -385,7 +383,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz",
       "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==",
-      "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
@@ -394,7 +391,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
       "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
-      "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
@@ -440,7 +436,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
       "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
-      "dev": true,
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.24.7",
         "chalk": "^2.4.2",
@@ -455,7 +450,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
       "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==",
-      "dev": true,
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -1764,7 +1758,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz",
       "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==",
-      "dev": true,
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -1776,7 +1769,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
       "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
-      "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.24.7",
         "@babel/parser": "^7.24.7",
@@ -1790,7 +1782,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz",
       "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==",
-      "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.24.7",
         "@babel/generator": "^7.24.7",
@@ -1811,7 +1802,6 @@
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz",
       "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==",
-      "dev": true,
       "dependencies": {
         "@babel/helper-string-parser": "^7.24.7",
         "@babel/helper-validator-identifier": "^7.24.7",
@@ -1830,6 +1820,152 @@
         "node": ">=10.0.0"
       }
     },
+    "node_modules/@emotion/babel-plugin": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
+      "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/runtime": "^7.18.3",
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/serialize": "^1.2.0",
+        "babel-plugin-macros": "^3.1.0",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.2.0"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/source-map": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+      "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/@emotion/cache": {
+      "version": "11.13.0",
+      "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.0.tgz",
+      "integrity": "sha512-hPV345J/tH0Cwk2wnU/3PBzORQ9HeX+kQSbwI+jslzpRCHE6fSGTohswksA/Ensr8znPzwfzKZCmAM9Lmlhp7g==",
+      "dependencies": {
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/sheet": "^1.4.0",
+        "@emotion/utils": "^1.4.0",
+        "@emotion/weak-memoize": "^0.4.0",
+        "stylis": "4.2.0"
+      }
+    },
+    "node_modules/@emotion/hash": {
+      "version": "0.9.2",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+      "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+    },
+    "node_modules/@emotion/is-prop-valid": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz",
+      "integrity": "sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==",
+      "dependencies": {
+        "@emotion/memoize": "^0.9.0"
+      }
+    },
+    "node_modules/@emotion/memoize": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+      "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+    },
+    "node_modules/@emotion/react": {
+      "version": "11.13.0",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.0.tgz",
+      "integrity": "sha512-WkL+bw1REC2VNV1goQyfxjx1GYJkcc23CRQkXX+vZNLINyfI7o+uUn/rTGPt/xJ3bJHd5GcljgnxHf4wRw5VWQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.12.0",
+        "@emotion/cache": "^11.13.0",
+        "@emotion/serialize": "^1.3.0",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+        "@emotion/utils": "^1.4.0",
+        "@emotion/weak-memoize": "^0.4.0",
+        "hoist-non-react-statics": "^3.3.1"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@emotion/serialize": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.0.tgz",
+      "integrity": "sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==",
+      "dependencies": {
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/unitless": "^0.9.0",
+        "@emotion/utils": "^1.4.0",
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/@emotion/sheet": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+      "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+    },
+    "node_modules/@emotion/styled": {
+      "version": "11.13.0",
+      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz",
+      "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==",
+      "dependencies": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.12.0",
+        "@emotion/is-prop-valid": "^1.3.0",
+        "@emotion/serialize": "^1.3.0",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+        "@emotion/utils": "^1.4.0"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.0.0-rc.0",
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@emotion/unitless": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.9.0.tgz",
+      "integrity": "sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ=="
+    },
+    "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz",
+      "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==",
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      }
+    },
+    "node_modules/@emotion/utils": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz",
+      "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ=="
+    },
+    "node_modules/@emotion/weak-memoize": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+      "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+    },
     "node_modules/@es-joy/jsdoccomment": {
       "version": "0.43.1",
       "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.43.1.tgz",
@@ -1962,6 +2098,40 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
+    "node_modules/@floating-ui/core": {
+      "version": "1.6.5",
+      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.5.tgz",
+      "integrity": "sha512-8GrTWmoFhm5BsMZOTHeGD2/0FLKLQQHvO/ZmQga4tKempYRLz8aqJGqXVuQgisnMObq2YZ2SgkwctN1LOOxcqA==",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.5"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.6.8",
+      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.8.tgz",
+      "integrity": "sha512-kx62rP19VZ767Q653wsP1XZCGIirkE09E0QUGNYTM/ttbbQHqcGPdSfWFxUyyNLc/W6aoJRBajOSXhP6GXjC0Q==",
+      "dependencies": {
+        "@floating-ui/core": "^1.6.0",
+        "@floating-ui/utils": "^0.2.5"
+      }
+    },
+    "node_modules/@floating-ui/react-dom": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz",
+      "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==",
+      "dependencies": {
+        "@floating-ui/dom": "^1.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0",
+        "react-dom": ">=16.8.0"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz",
+      "integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ=="
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.14",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -2072,7 +2242,6 @@
       "version": "0.3.5",
       "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
       "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
-      "dev": true,
       "dependencies": {
         "@jridgewell/set-array": "^1.2.1",
         "@jridgewell/sourcemap-codec": "^1.4.10",
@@ -2086,7 +2255,6 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
       "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
-      "dev": true,
       "engines": {
         "node": ">=6.0.0"
       }
@@ -2095,7 +2263,6 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
       "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
-      "dev": true,
       "engines": {
         "node": ">=6.0.0"
       }
@@ -2113,14 +2280,12 @@
     "node_modules/@jridgewell/sourcemap-codec": {
       "version": "1.4.15",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
-      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
-      "dev": true
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
     },
     "node_modules/@jridgewell/trace-mapping": {
       "version": "0.3.25",
       "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
       "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
-      "dev": true,
       "dependencies": {
         "@jridgewell/resolve-uri": "^3.1.0",
         "@jridgewell/sourcemap-codec": "^1.4.14"
@@ -2186,6 +2351,228 @@
       "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
       "dev": true
     },
+    "node_modules/@mui/base": {
+      "version": "5.0.0-beta.40",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
+      "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.23.9",
+        "@floating-ui/react-dom": "^2.0.8",
+        "@mui/types": "^7.2.14",
+        "@mui/utils": "^5.15.14",
+        "@popperjs/core": "^2.11.8",
+        "clsx": "^2.1.0",
+        "prop-types": "^15.8.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui-org"
+      },
+      "peerDependencies": {
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0",
+        "react-dom": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/core-downloads-tracker": {
+      "version": "5.16.4",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.4.tgz",
+      "integrity": "sha512-rNdHXhclwjEZnK+//3SR43YRx0VtjdHnUFhMSGYmAMJve+KiwEja/41EYh8V3pZKqF2geKyfcFUenTfDTYUR4w==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui-org"
+      }
+    },
+    "node_modules/@mui/joy": {
+      "version": "5.0.0-beta.48",
+      "resolved": "https://registry.npmjs.org/@mui/joy/-/joy-5.0.0-beta.48.tgz",
+      "integrity": "sha512-OhTvjuGl9I5IvpBr0BQyDehIW/xb2yteW6YglHJMdOb/279nItn76X1NBtPV9ImldNlBjReGwvpOXmBTTGER9w==",
+      "dependencies": {
+        "@babel/runtime": "^7.23.9",
+        "@mui/base": "5.0.0-beta.40",
+        "@mui/core-downloads-tracker": "^5.16.1",
+        "@mui/system": "^5.16.1",
+        "@mui/types": "^7.2.15",
+        "@mui/utils": "^5.16.1",
+        "clsx": "^2.1.0",
+        "prop-types": "^15.8.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui-org"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.5.0",
+        "@emotion/styled": "^11.3.0",
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0",
+        "react-dom": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/react": {
+          "optional": true
+        },
+        "@emotion/styled": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/private-theming": {
+      "version": "5.16.4",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.4.tgz",
+      "integrity": "sha512-ZsAm8cq31SJ37SVWLRlu02v9SRthxnfQofaiv14L5Bht51B0dz6yQEoVU/V8UduZDCCIrWkBHuReVfKhE/UuXA==",
+      "dependencies": {
+        "@babel/runtime": "^7.23.9",
+        "@mui/utils": "^5.16.4",
+        "prop-types": "^15.8.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui-org"
+      },
+      "peerDependencies": {
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/styled-engine": {
+      "version": "5.16.4",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.4.tgz",
+      "integrity": "sha512-0+mnkf+UiAmTVB8PZFqOhqf729Yh0Cxq29/5cA3VAyDVTRIUUQ8FXQhiAhUIbijFmM72rY80ahFPXIm4WDbzcA==",
+      "dependencies": {
+        "@babel/runtime": "^7.23.9",
+        "@emotion/cache": "^11.11.0",
+        "csstype": "^3.1.3",
+        "prop-types": "^15.8.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui-org"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.4.1",
+        "@emotion/styled": "^11.3.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/react": {
+          "optional": true
+        },
+        "@emotion/styled": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/system": {
+      "version": "5.16.4",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.4.tgz",
+      "integrity": "sha512-ET1Ujl2/8hbsD611/mqUuNArMCGv/fIWO/f8B3ZqF5iyPHM2aS74vhTNyjytncc4i6dYwGxNk+tLa7GwjNS0/w==",
+      "dependencies": {
+        "@babel/runtime": "^7.23.9",
+        "@mui/private-theming": "^5.16.4",
+        "@mui/styled-engine": "^5.16.4",
+        "@mui/types": "^7.2.15",
+        "@mui/utils": "^5.16.4",
+        "clsx": "^2.1.0",
+        "csstype": "^3.1.3",
+        "prop-types": "^15.8.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui-org"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.5.0",
+        "@emotion/styled": "^11.3.0",
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/react": {
+          "optional": true
+        },
+        "@emotion/styled": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/types": {
+      "version": "7.2.15",
+      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz",
+      "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==",
+      "peerDependencies": {
+        "@types/react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/utils": {
+      "version": "5.16.4",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.4.tgz",
+      "integrity": "sha512-nlppYwq10TBIFqp7qxY0SvbACOXeOjeVL3pOcDsK0FT8XjrEXh9/+lkg8AEIzD16z7YfiJDQjaJG2OLkE7BxNg==",
+      "dependencies": {
+        "@babel/runtime": "^7.23.9",
+        "@types/prop-types": "^15.7.12",
+        "clsx": "^2.1.1",
+        "prop-types": "^15.8.1",
+        "react-is": "^18.3.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui-org"
+      },
+      "peerDependencies": {
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/utils/node_modules/react-is": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2304,6 +2691,15 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.8",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+      "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
     "node_modules/@stylistic/eslint-plugin-js": {
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.8.1.tgz",
@@ -2503,6 +2899,16 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/parse-json": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+      "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+    },
+    "node_modules/@types/prop-types": {
+      "version": "15.7.12",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+      "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
+    },
     "node_modules/@types/qs": {
       "version": "6.9.15",
       "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
@@ -2515,6 +2921,15 @@
       "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
       "dev": true
     },
+    "node_modules/@types/react": {
+      "version": "18.3.3",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
+      "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
+      "dependencies": {
+        "@types/prop-types": "*",
+        "csstype": "^3.0.2"
+      }
+    },
     "node_modules/@types/retry": {
       "version": "0.12.2",
       "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
@@ -3061,7 +3476,6 @@
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
       "dependencies": {
         "color-convert": "^1.9.0"
       },
@@ -3298,6 +3712,11 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
     "node_modules/available-typed-arrays": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -3314,6 +3733,16 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/axios": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
+      "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
     "node_modules/babel-loader": {
       "version": "9.1.3",
       "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz",
@@ -3331,6 +3760,20 @@
         "webpack": ">=5"
       }
     },
+    "node_modules/babel-plugin-macros": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+      "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "cosmiconfig": "^7.0.0",
+        "resolve": "^1.19.0"
+      },
+      "engines": {
+        "node": ">=10",
+        "npm": ">=6"
+      }
+    },
     "node_modules/babel-plugin-polyfill-corejs2": {
       "version": "0.4.11",
       "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz",
@@ -3573,8 +4016,6 @@
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
       "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "peer": true,
       "engines": {
         "node": ">=6"
       }
@@ -3613,7 +4054,6 @@
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
       "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
       "dependencies": {
         "ansi-styles": "^3.2.1",
         "escape-string-regexp": "^1.0.5",
@@ -3627,7 +4067,6 @@
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
       "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true,
       "engines": {
         "node": ">=0.8.0"
       }
@@ -3703,11 +4142,18 @@
         "node": ">=6"
       }
     },
+    "node_modules/clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
       "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
       "dependencies": {
         "color-name": "1.1.3"
       }
@@ -3715,8 +4161,7 @@
     "node_modules/color-name": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
     },
     "node_modules/colorette": {
       "version": "2.0.20",
@@ -3724,6 +4169,17 @@
       "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
       "dev": true
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/commander": {
       "version": "8.3.0",
       "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
@@ -3888,6 +4344,21 @@
       "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
       "dev": true
     },
+    "node_modules/cosmiconfig": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3989,6 +4460,11 @@
         "node": ">=4"
       }
     },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
     "node_modules/data-view-buffer": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
@@ -4047,7 +4523,6 @@
       "version": "4.3.5",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
       "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
-      "dev": true,
       "dependencies": {
         "ms": "2.1.2"
       },
@@ -4154,6 +4629,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -4367,6 +4850,14 @@
         "node": ">=4"
       }
     },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
     "node_modules/error-stack-parser": {
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
@@ -4565,8 +5056,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
       "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
-      "dev": true,
-      "peer": true,
       "engines": {
         "node": ">=10"
       },
@@ -5435,6 +5924,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/find-root": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+    },
     "node_modules/find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -5487,7 +5981,6 @@
       "version": "1.15.6",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
       "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
-      "dev": true,
       "funding": [
         {
           "type": "individual",
@@ -5541,6 +6034,19 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -5584,7 +6090,6 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
       "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
-      "dev": true,
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -5745,7 +6250,6 @@
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
       "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
-      "dev": true,
       "engines": {
         "node": ">=4"
       }
@@ -5833,7 +6337,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
       "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
       "engines": {
         "node": ">=4"
       }
@@ -5894,7 +6397,6 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
       "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
-      "dev": true,
       "dependencies": {
         "function-bind": "^1.1.2"
       },
@@ -5911,6 +6413,14 @@
         "he": "bin/he"
       }
     },
+    "node_modules/hoist-non-react-statics": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+      "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+      "dependencies": {
+        "react-is": "^16.7.0"
+      }
+    },
     "node_modules/hpack.js": {
       "version": "2.1.6",
       "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
@@ -6169,8 +6679,6 @@
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
       "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
-      "dev": true,
-      "peer": true,
       "dependencies": {
         "parent-module": "^1.0.0",
         "resolve-from": "^4.0.0"
@@ -6343,6 +6851,11 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+    },
     "node_modules/is-async-function": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
@@ -6418,7 +6931,6 @@
       "version": "2.14.0",
       "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz",
       "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==",
-      "dev": true,
       "dependencies": {
         "hasown": "^2.0.2"
       },
@@ -6938,7 +7450,6 @@
       "version": "2.5.2",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
       "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
-      "dev": true,
       "bin": {
         "jsesc": "bin/jsesc"
       },
@@ -6956,8 +7467,7 @@
     "node_modules/json-parse-even-better-errors": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "node_modules/json-schema-traverse": {
       "version": "0.4.1",
@@ -7043,6 +7553,11 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+    },
     "node_modules/loader-runner": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@@ -7230,7 +7745,6 @@
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
-      "dev": true,
       "engines": {
         "node": ">= 0.6"
       }
@@ -7239,7 +7753,6 @@
       "version": "2.1.35",
       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "dev": true,
       "dependencies": {
         "mime-db": "1.52.0"
       },
@@ -7320,8 +7833,7 @@
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "dev": true
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
     "node_modules/multicast-dns": {
       "version": "7.2.5",
@@ -7438,8 +7950,6 @@
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
       "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "dev": true,
-      "peer": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -7736,8 +8246,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
       "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "peer": true,
       "dependencies": {
         "callsites": "^3.0.0"
       },
@@ -7759,6 +8267,23 @@
         "node": ">= 18"
       }
     },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/parseurl": {
       "version": "1.3.3",
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -7809,8 +8334,7 @@
     "node_modules/path-parse": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
     },
     "node_modules/path-scurry": {
       "version": "1.11.1",
@@ -7847,8 +8371,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
-      "dev": true,
-      "peer": true,
       "engines": {
         "node": ">=8"
       }
@@ -7856,8 +8378,7 @@
     "node_modules/picocolors": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
-      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
-      "dev": true
+      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
     },
     "node_modules/picomatch": {
       "version": "4.0.2",
@@ -8115,8 +8636,6 @@
       "version": "15.8.1",
       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
       "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
-      "dev": true,
-      "peer": true,
       "dependencies": {
         "loose-envify": "^1.4.0",
         "object-assign": "^4.1.1",
@@ -8145,6 +8664,11 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "node_modules/punycode": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -8258,9 +8782,7 @@
     "node_modules/react-is": {
       "version": "16.13.1",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
-      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
-      "dev": true,
-      "peer": true
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
     },
     "node_modules/react-refresh": {
       "version": "0.14.2",
@@ -8364,8 +8886,7 @@
     "node_modules/regenerator-runtime": {
       "version": "0.14.1",
       "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
-      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
-      "dev": true
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
     },
     "node_modules/regenerator-transform": {
       "version": "0.15.2",
@@ -8474,7 +8995,6 @@
       "version": "1.22.8",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
       "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
-      "dev": true,
       "dependencies": {
         "is-core-module": "^2.13.0",
         "path-parse": "^1.0.7",
@@ -8512,8 +9032,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
       "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "peer": true,
       "engines": {
         "node": ">=4"
       }
@@ -9347,11 +9865,15 @@
         "webpack": "^5.27.0"
       }
     },
+    "node_modules/stylis": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+      "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+    },
     "node_modules/supports-color": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
       "dependencies": {
         "has-flag": "^3.0.0"
       },
@@ -9363,7 +9885,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
       "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true,
       "engines": {
         "node": ">= 0.4"
       },
@@ -9502,7 +10023,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
       "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-      "dev": true,
       "engines": {
         "node": ">=4"
       }
@@ -10496,6 +11016,14 @@
       "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
       "dev": true
     },
+    "node_modules/yaml": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/components/log-viewer-webui/client/package.json b/components/log-viewer-webui/client/package.json
index 47b2a7af3..8978b8aee 100644
--- a/components/log-viewer-webui/client/package.json
+++ b/components/log-viewer-webui/client/package.json
@@ -34,6 +34,11 @@
     ]
   },
   "dependencies": {
+    "@emotion/react": "^11.13.0",
+    "@emotion/styled": "^11.13.0",
+    "@mui/joy": "^5.0.0-beta.48",
+    "@types/react": "^18.3.3",
+    "axios": "^1.7.2",
     "react": "^18.3.1",
     "react-dom": "^18.3.1"
   }
diff --git a/components/log-viewer-webui/client/public/index.html b/components/log-viewer-webui/client/public/index.html
index e0646892c..c2e5d6b78 100644
--- a/components/log-viewer-webui/client/public/index.html
+++ b/components/log-viewer-webui/client/public/index.html
@@ -5,6 +5,11 @@
     <meta charset="utf-8"/>
     <meta name="description" content="YScope Log Viewer">
     <meta name="viewport" content="initial-scale=1, maximum-scale=1">
+    <link rel="preconnect" href="https://fonts.googleapis.com"/>
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
+    <link rel="stylesheet"
+          href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
+      />
 </head>
 <body>
 <div id="root"></div>
diff --git a/components/log-viewer-webui/client/src/App.jsx b/components/log-viewer-webui/client/src/App.jsx
index 3a6f57fff..6d7a65e45 100644
--- a/components/log-viewer-webui/client/src/App.jsx
+++ b/components/log-viewer-webui/client/src/App.jsx
@@ -1,3 +1,9 @@
+import {CssVarsProvider} from "@mui/joy/styles/CssVarsProvider";
+
+import LOCAL_STORAGE_KEY from "./typings/LOCAL_STORAGE_KEY.js";
+import QueryStatus from "./ui/QueryStatus.jsx";
+
+
 /**
  * Renders the main application.
  *
@@ -5,7 +11,9 @@
  */
 const App = () => {
     return (
-        <h1>Hello world!</h1>
+        <CssVarsProvider modeStorageKey={LOCAL_STORAGE_KEY.UI_THEME}>
+            <QueryStatus/>
+        </CssVarsProvider>
     );
 };
 
diff --git a/components/log-viewer-webui/client/src/api/query.js b/components/log-viewer-webui/client/src/api/query.js
new file mode 100644
index 000000000..be17d0fc8
--- /dev/null
+++ b/components/log-viewer-webui/client/src/api/query.js
@@ -0,0 +1,32 @@
+import axios from "axios";
+
+
+/**
+ * @typedef {object} ExtractIrResp
+ * @property {number} begin_msg_ix
+ * @property {number} end_msg_ix
+ * @property {string} file_split_id
+ * @property {boolean} is_last_ir_chunk
+ * @property {string} orig_file_id
+ * @property {string} path
+ * @property {string} _id
+ */
+
+/**
+ * Submits a job to extract the split of an original file that contains a given log event. The file
+ * is extracted as a CLP IR file.
+ *
+ * @param {number|string} origFileId The ID of the original file
+ * @param {number} logEventIdx The index of the log event
+ * @param {Function} onUploadProgress Callback to handle upload progress events.
+ * @return {Promise<axios.AxiosResponse<ExtractIrResp>>}
+ */
+const submitExtractIrJob = async (origFileId, logEventIdx, onUploadProgress) => {
+    return await axios.post(
+        "/query/extract-ir",
+        {logEventIdx, origFileId},
+        {onUploadProgress}
+    );
+};
+
+export {submitExtractIrJob};
diff --git a/components/log-viewer-webui/client/src/typings/LOCAL_STORAGE_KEY.js b/components/log-viewer-webui/client/src/typings/LOCAL_STORAGE_KEY.js
new file mode 100644
index 000000000..c1dc19979
--- /dev/null
+++ b/components/log-viewer-webui/client/src/typings/LOCAL_STORAGE_KEY.js
@@ -0,0 +1,8 @@
+/**
+ * Enum of `window.localStorage` keys.
+ */
+const LOCAL_STORAGE_KEY = Object.freeze({
+    UI_THEME: "uiTheme",
+});
+
+export default LOCAL_STORAGE_KEY;
diff --git a/components/log-viewer-webui/client/src/typings/query.js b/components/log-viewer-webui/client/src/typings/query.js
new file mode 100644
index 000000000..b91a814f1
--- /dev/null
+++ b/components/log-viewer-webui/client/src/typings/query.js
@@ -0,0 +1,37 @@
+/**
+ * @typedef {number} QueryLoadingState
+ */
+let enumQueryLoadingState;
+/**
+ * Enum of query loading state.
+ *
+ * @enum {QueryLoadingState}
+ */
+const QUERY_LOADING_STATES = Object.freeze({
+    SUBMITTING: (enumQueryLoadingState = 0),
+    WAITING: ++enumQueryLoadingState,
+    LOADING: ++enumQueryLoadingState,
+});
+
+/**
+ * Descriptions for query loading states.
+ */
+const QUERY_LOADING_STATE_DESCRIPTIONS = Object.freeze({
+    [QUERY_LOADING_STATES.SUBMITTING]: {
+        label: "Submitting query Job",
+        description: "Parsing arguments and submitting job to the server.",
+    },
+    [QUERY_LOADING_STATES.WAITING]: {
+        label: "Waiting for job to finish",
+        description: "The job is running. Waiting for the job to finish.",
+    },
+    [QUERY_LOADING_STATES.LOADING]: {
+        label: "Loading Log Viewer",
+        description: "The query has been completed and the results are being loaded.",
+    },
+});
+
+export {
+    QUERY_LOADING_STATE_DESCRIPTIONS,
+    QUERY_LOADING_STATES,
+};
diff --git a/components/log-viewer-webui/client/src/ui/Loading.css b/components/log-viewer-webui/client/src/ui/Loading.css
new file mode 100644
index 000000000..d8bec1842
--- /dev/null
+++ b/components/log-viewer-webui/client/src/ui/Loading.css
@@ -0,0 +1,24 @@
+.loading-sheet {
+    height: 100%;
+
+    display: flex;
+    flex-direction: column;
+
+    align-items: center;
+    justify-content: center;
+}
+
+.loading-progress-container {
+    width: 100%;
+}
+
+.loading-stepper-container {
+    display: flex;
+    flex-grow: 1;
+
+    align-items: center;
+}
+
+.loading-stepper {
+    --Stepper-verticalGap: 2rem !important;
+}
diff --git a/components/log-viewer-webui/client/src/ui/Loading.jsx b/components/log-viewer-webui/client/src/ui/Loading.jsx
new file mode 100644
index 000000000..e157d1224
--- /dev/null
+++ b/components/log-viewer-webui/client/src/ui/Loading.jsx
@@ -0,0 +1,130 @@
+import {
+    Box,
+    LinearProgress,
+    Sheet,
+    Step,
+    StepIndicator,
+    Stepper,
+    Typography,
+} from "@mui/joy";
+
+import {
+    QUERY_LOADING_STATE_DESCRIPTIONS,
+    QUERY_LOADING_STATES,
+} from "../typings/query.js";
+
+import "./Loading.css";
+
+
+/**
+ * Renders a step with a label and description.
+ *
+ * @param {object} props
+ * @param {string} props.description
+ * @param {boolean} props.isActive
+ * @param {boolean} props.isError
+ * @param {string} props.label
+ * @param {number | string} props.stepIndicatorText
+ * @return {React.ReactElement}
+ */
+const LoadingStep = ({
+    description,
+    isActive,
+    isError,
+    label,
+    stepIndicatorText,
+}) => {
+    let color = isActive ?
+        "primary" :
+        "neutral";
+
+    if (isError) {
+        color = "danger";
+    }
+
+    return (
+        <Step
+            indicator={
+                <StepIndicator
+                    color={color}
+                    variant={isActive ?
+                        "solid" :
+                        "outlined"}
+                >
+                    {stepIndicatorText}
+                </StepIndicator>
+            }
+        >
+            <Typography
+                color={color}
+                level={"title-lg"}
+            >
+                {label}
+            </Typography>
+            <Typography level={"body-sm"}>
+                {description}
+            </Typography>
+        </Step>
+    );
+};
+
+/**
+ * Displays status of a pending query job.
+ *
+ * @param {object} props
+ * @param {QueryLoadState} props.currentState
+ * @param {string} props.errorMsg
+ * @return {React.ReactElement}
+ */
+const Loading = ({currentState, errorMsg}) => {
+    const steps = [];
+    Object.values(QUERY_LOADING_STATES).forEach((state) => {
+        const isActive = (currentState === state);
+        const stateDescription = QUERY_LOADING_STATE_DESCRIPTIONS[state];
+        steps.push(
+            <LoadingStep
+                description={stateDescription.description}
+                isActive={isActive}
+                isError={false}
+                key={state}
+                label={stateDescription.label}
+                stepIndicatorText={state + 1}/>
+        );
+        if (isActive && null !== errorMsg) {
+            steps.push(
+                <LoadingStep
+                    description={errorMsg}
+                    isActive={isActive}
+                    isError={true}
+                    key={`${state}-error`}
+                    label={"Error"}
+                    stepIndicatorText={"X"}/>
+            );
+        }
+    });
+
+    return (
+        <>
+            <Sheet className={"loading-sheet"}>
+                <Box className={"loading-progress-container"}>
+                    <LinearProgress
+                        determinate={null !== errorMsg}
+                        color={null === errorMsg ?
+                            "primary" :
+                            "danger"}/>
+                </Box>
+                <Box className={"loading-stepper-container"}>
+                    <Stepper
+                        className={"loading-stepper"}
+                        orientation={"vertical"}
+                        size={"lg"}
+                    >
+                        {steps}
+                    </Stepper>
+                </Box>
+            </Sheet>
+        </>
+    );
+};
+
+export default Loading;
diff --git a/components/log-viewer-webui/client/src/ui/QueryStatus.jsx b/components/log-viewer-webui/client/src/ui/QueryStatus.jsx
new file mode 100644
index 000000000..d46e6b885
--- /dev/null
+++ b/components/log-viewer-webui/client/src/ui/QueryStatus.jsx
@@ -0,0 +1,80 @@
+import {
+    useEffect,
+    useRef,
+    useState,
+} from "react";
+
+import {AxiosError} from "axios";
+
+import {submitExtractIrJob} from "../api/query.js";
+import {QUERY_LOADING_STATES} from "../typings/query.js";
+import Loading from "./Loading.jsx";
+
+
+/**
+ * Submits queries and renders the query states.
+ *
+ * @return {React.ReactElement}
+ */
+const QueryStatus = () => {
+    const [queryState, setQueryState] = useState(QUERY_LOADING_STATES.SUBMITTING);
+    const [errorMsg, setErrorMsg] = useState(null);
+    const isFirstRun = useRef(true);
+
+    useEffect(() => {
+        if (false === isFirstRun.current) {
+            return;
+        }
+        isFirstRun.current = false;
+
+        const searchParams = new URLSearchParams(window.location.search);
+        const origFileId = searchParams.get("origFileId");
+        const logEventIdx = searchParams.get("logEventIdx");
+        if (null === origFileId || null === logEventIdx) {
+            const error = "Either `origFileId` or `logEventIdx` are missing from the URL " +
+            "parameters. Note that non-IR-extraction queries are not supported at the moment.";
+
+            console.error(error);
+            setErrorMsg(error);
+            return;
+        }
+
+        submitExtractIrJob(
+            origFileId,
+            Number(logEventIdx),
+            () => {
+                setQueryState(QUERY_LOADING_STATES.WAITING);
+            }
+        )
+            .then(({data}) => {
+                setQueryState(QUERY_LOADING_STATES.LOADING);
+
+                const innerLogEventNum = logEventIdx - data.begin_msg_ix + 1;
+                window.location = `/log-viewer/index.html?filePath=/ir/${data.path}` +
+                    `#logEventIdx=${innerLogEventNum}`;
+            })
+            .catch((e) => {
+                let msg = "Unknown error.";
+                if (e instanceof AxiosError) {
+                    msg = e.message;
+                    if ("undefined" !== typeof e.response) {
+                        if ("undefined" !== typeof e.response.data.message) {
+                            msg = e.response.data.message;
+                        } else {
+                            msg = e.response.statusText;
+                        }
+                    }
+                }
+                console.error(msg, e);
+                setErrorMsg(msg);
+            });
+    }, []);
+
+    return (
+        <Loading
+            currentState={queryState}
+            errorMsg={errorMsg}/>
+    );
+};
+
+export default QueryStatus;
diff --git a/components/log-viewer-webui/client/webpack.config.js b/components/log-viewer-webui/client/webpack.config.js
index 2829b3c10..61ec64bb6 100644
--- a/components/log-viewer-webui/client/webpack.config.js
+++ b/components/log-viewer-webui/client/webpack.config.js
@@ -22,6 +22,14 @@ const plugins = [
 ];
 
 const config = {
+    devServer: {
+        proxy: [
+            {
+                context: ["/"],
+                target: "http://localhost:3000",
+            },
+        ],
+    },
     devtool: isProduction ?
         "source-map" :
         "eval-source-map",
diff --git a/components/log-viewer-webui/server/src/DbManager.js b/components/log-viewer-webui/server/src/DbManager.js
index ad71518a5..2cb7ef3e0 100644
--- a/components/log-viewer-webui/server/src/DbManager.js
+++ b/components/log-viewer-webui/server/src/DbManager.js
@@ -131,17 +131,17 @@ class DbManager {
 
     /**
      * Gets the metadata for an IR file extracted from part of an original file, where the original
-     * file has the given ID and the extracted part contains the given message index.
+     * file has the given ID and the extracted part contains the given log event index.
      *
      * @param {string} origFileId
-     * @param {number} msgIdx
+     * @param {number} logEventIdx
      * @return {Promise<object>} A promise that resolves to the extracted IR file's metadata.
      */
-    async getExtractedIrFileMetadata (origFileId, msgIdx) {
+    async getExtractedIrFileMetadata (origFileId, logEventIdx) {
         return await this.#irFilesCollection.findOne({
             orig_file_id: origFileId,
-            begin_msg_ix: {$lte: msgIdx},
-            end_msg_ix: {$gt: msgIdx},
+            begin_msg_ix: {$lte: logEventIdx},
+            end_msg_ix: {$gt: logEventIdx},
         });
     }
 
diff --git a/components/log-viewer-webui/server/src/routes/query.js b/components/log-viewer-webui/server/src/routes/query.js
index 2f790ebc3..628bcd053 100644
--- a/components/log-viewer-webui/server/src/routes/query.js
+++ b/components/log-viewer-webui/server/src/routes/query.js
@@ -1,3 +1,6 @@
+// eslint-disable-next-line no-magic-numbers
+const EXTRACT_IR_TARGET_UNCOMPRESSED_SIZE = 128 * 1024 * 1024;
+
 /**
  * Creates query routes.
  *
@@ -7,30 +10,33 @@
  */
 const routes = async (fastify, options) => {
     fastify.post("/query/extract-ir", async (req, resp) => {
-        const {orig_file_id: origFileId, msg_ix: msgIdx} = req.body;
-        const sanitizedMsgIdx = Number(msgIdx);
+        const {origFileId, logEventIdx} = req.body;
+        const sanitizedLogEventIdx = Number(logEventIdx);
 
-        let irMetadata =
-            await fastify.dbManager.getExtractedIrFileMetadata(origFileId, sanitizedMsgIdx);
+        let irMetadata = await fastify.dbManager.getExtractedIrFileMetadata(
+            origFileId,
+            sanitizedLogEventIdx
+        );
 
         if (null === irMetadata) {
             const extractResult = await fastify.dbManager.submitAndWaitForExtractIrJob({
                 file_split_id: null,
-                msg_ix: sanitizedMsgIdx,
+                msg_ix: sanitizedLogEventIdx,
                 orig_file_id: origFileId,
-                // eslint-disable-next-line no-magic-numbers
-                target_uncompressed_size: 128 * 1024 * 1024,
+                target_uncompressed_size: EXTRACT_IR_TARGET_UNCOMPRESSED_SIZE,
             });
 
             if (null === extractResult) {
                 const err = new Error("Unable to extract IR for file with " +
-                    `orig_file_id=${origFileId} at msg_ix=${sanitizedMsgIdx}`);
+                    `origFileId=${origFileId} at logEventIdx=${sanitizedLogEventIdx}`);
 
                 err.statusCode = 400;
                 throw err;
             }
-            irMetadata =
-                await fastify.dbManager.getExtractedIrFileMetadata(origFileId, sanitizedMsgIdx);
+            irMetadata = await fastify.dbManager.getExtractedIrFileMetadata(
+                origFileId,
+                sanitizedLogEventIdx
+            );
         }
 
         return irMetadata;
diff --git a/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/SearchResultsTable.scss b/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/SearchResultsTable.scss
index 6c24ec2da..db2f38117 100644
--- a/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/SearchResultsTable.scss
+++ b/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/SearchResultsTable.scss
@@ -27,6 +27,8 @@
 }
 
 .search-results-content {
+  overflow: auto;
+
   font-size: 0.875rem;
   line-height: var(--search-results-message-line-height);
 }
diff --git a/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/index.jsx b/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/index.jsx
index 77d6af8a8..28a331bb8 100644
--- a/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/index.jsx
+++ b/components/webui/imports/ui/SearchView/SearchResults/SearchResultsTable/index.jsx
@@ -1,3 +1,4 @@
+import {Meteor} from "meteor/meteor";
 import {useEffect} from "react";
 import Table from "react-bootstrap/Table";
 
@@ -7,6 +8,7 @@ import {
     faSort,
     faSortDown,
     faSortUp,
+    faSquareUpRight,
 } from "@fortawesome/free-solid-svg-icons";
 import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
 
@@ -35,7 +37,7 @@ const SEARCH_RESULT_MESSAGE_LINE_HEIGHT = 1.5;
  * @param {boolean} props.hasMoreResultsInTotal
  * @param {number} props.maxLinesPerResult
  * @param {Function} props.onLoadMoreResults
- * @param {object} props.searchResults
+ * @param {object[]} props.searchResults
  * @param {Function} props.setFieldToSortBy
  * @return {React.ReactElement}
  */
@@ -91,24 +93,9 @@ const SearchResultsTable = ({
         );
     }, [maxLinesPerResult]);
 
-    const rows = [];
-    for (let i = 0; i < searchResults.length; ++i) {
-        const searchResult = searchResults[i];
-        rows.push(
-            <tr key={searchResult._id}>
-                <td className={"search-results-content search-results-timestamp"}>
-                    {searchResult.timestamp ?
-                        dayjs.utc(searchResult.timestamp).format(DATETIME_FORMAT_TEMPLATE) :
-                        "N/A"}
-                </td>
-                <td>
-                    <pre className={"search-results-content search-results-message"}>
-                        {searchResult.message}
-                    </pre>
-                </td>
-            </tr>
-        );
-    }
+    // eslint-disable-next-line no-warning-comments
+    // TODO: remove this flag once "Extract IR" support is added for ClpStorageEngine "clp-s"
+    const isExtractIrSupported = ("clp" === Meteor.settings.public.ClpStorageEngine);
 
     return (
         <div className={"search-results-container"}>
@@ -123,7 +110,6 @@ const SearchResultsTable = ({
                         <th
                             className={"search-results-th search-results-th-sortable"}
                             data-column-name={SEARCH_RESULTS_FIELDS.TIMESTAMP}
-                            key={SEARCH_RESULTS_FIELDS.TIMESTAMP}
                             onClick={toggleSortDirection}
                         >
                             <div className={"search-results-table-header"}>
@@ -134,16 +120,45 @@ const SearchResultsTable = ({
                         </th>
                         <th
                             className={"search-results-th"}
-                            key={"message"}
                         >
                             <div className={"search-results-table-header"}>
                                 Log message
                             </div>
                         </th>
+                        {isExtractIrSupported &&
+                            <th className={"search-results-th"}>
+                                <div className={"search-results-table-header"}>&nbsp;</div>
+                            </th>}
                     </tr>
                 </thead>
                 <tbody>
-                    {rows}
+                    {searchResults.map((result) => (
+                        <tr key={result._id}>
+                            <td className={"search-results-content search-results-timestamp"}>
+                                {result.timestamp ?
+                                    dayjs.utc(result.timestamp).format(DATETIME_FORMAT_TEMPLATE) :
+                                    "N/A"}
+                            </td>
+                            <td>
+                                <pre className={"search-results-content search-results-message"}>
+                                    {result.message}
+                                </pre>
+                            </td>
+                            {isExtractIrSupported &&
+                                <td>
+                                    <a
+                                        rel={"noopener noreferrer"}
+                                        target={"_blank"}
+                                        title={"View log event in context"}
+                                        href={`${Meteor.settings.public.LogViewerWebuiUrl
+                                        }?origFileId=${result.orig_file_id}` +
+                                        `&logEventIdx=${result.log_event_ix}`}
+                                    >
+                                        <FontAwesomeIcon icon={faSquareUpRight}/>
+                                    </a>
+                                </td>}
+                        </tr>
+                    ))}
                 </tbody>
             </Table>
             <SearchResultsLoadSensor
diff --git a/components/webui/settings.json b/components/webui/settings.json
index 335ae0b51..f959dac8e 100644
--- a/components/webui/settings.json
+++ b/components/webui/settings.json
@@ -13,6 +13,7 @@
     "AggregationResultsCollectionName": "aggregation-results",
     "ClpStorageEngine": "clp",
     "CompressionJobsCollectionName": "compression-jobs",
+    "LogViewerWebuiUrl": "http://localhost:8080",
     "SearchResultsCollectionName": "search-results",
     "SearchResultsMetadataCollectionName": "results-metadata",
     "StatsCollectionName": "stats",