diff --git a/web/next.config.js b/web/next.config.js
index 2877a6469..1a8d7a53a 100644
--- a/web/next.config.js
+++ b/web/next.config.js
@@ -13,7 +13,6 @@ const cspHeader = `
     object-src 'none';
     base-uri 'self';
     form-action 'self';
-    frame-ancestors 'none';
     ${
       process.env.NEXT_PUBLIC_CLOUD_ENABLED === "true"
         ? "upgrade-insecure-requests;"
@@ -27,6 +26,17 @@ const nextConfig = {
   publicRuntimeConfig: {
     version,
   },
+  images: {
+    // Used to fetch favicons
+    remotePatterns: [
+      {
+        protocol: "https",
+        hostname: "www.google.com",
+        port: "",
+        pathname: "/s2/favicons/**",
+      },
+    ],
+  },
   async headers() {
     return [
       {
@@ -44,17 +54,12 @@ const nextConfig = {
             key: "Referrer-Policy",
             value: "strict-origin-when-cross-origin",
           },
-          {
-            key: "X-Frame-Options",
-            value: "DENY",
-          },
           {
             key: "X-Content-Type-Options",
             value: "nosniff",
           },
           {
             key: "Permissions-Policy",
-            // Deny all permissions by default
             value:
               "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()",
           },
diff --git a/web/package-lock.json b/web/package-lock.json
index d32345fe6..0d1eb688f 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -17,7 +17,9 @@
         "@phosphor-icons/react": "^2.0.8",
         "@radix-ui/react-checkbox": "^1.1.2",
         "@radix-ui/react-dialog": "^1.1.2",
+        "@radix-ui/react-label": "^2.1.1",
         "@radix-ui/react-popover": "^1.1.2",
+        "@radix-ui/react-radio-group": "^1.2.2",
         "@radix-ui/react-select": "^2.1.2",
         "@radix-ui/react-separator": "^1.1.0",
         "@radix-ui/react-slot": "^1.1.0",
@@ -77,6 +79,7 @@
       "devDependencies": {
         "@chromatic-com/playwright": "^0.10.0",
         "@tailwindcss/typography": "^0.5.10",
+        "@types/chrome": "^0.0.287",
         "chromatic": "^11.18.1",
         "eslint": "^8.48.0",
         "eslint-config-next": "^14.1.0",
@@ -2912,6 +2915,85 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-label": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz",
+      "integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-primitive": "2.0.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-compose-refs": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
+      "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/react": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
+      "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-slot": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-slot": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
+      "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-compose-refs": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-popover": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz",
@@ -3063,6 +3145,196 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-radio-group": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.2.2.tgz",
+      "integrity": "sha512-E0MLLGfOP0l8P/NxgVzfXJ8w3Ch8cdO6UDzJfDChu4EJDy+/WdO5LqpdY8PYnCErkmZH3gZhDL1K7kQ41fAHuQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/primitive": "1.1.1",
+        "@radix-ui/react-compose-refs": "1.1.1",
+        "@radix-ui/react-context": "1.1.1",
+        "@radix-ui/react-direction": "1.1.0",
+        "@radix-ui/react-presence": "1.1.2",
+        "@radix-ui/react-primitive": "2.0.1",
+        "@radix-ui/react-roving-focus": "1.1.1",
+        "@radix-ui/react-use-controllable-state": "1.1.0",
+        "@radix-ui/react-use-previous": "1.1.0",
+        "@radix-ui/react-use-size": "1.1.0"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/primitive": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
+      "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
+      "license": "MIT"
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-collection": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz",
+      "integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-compose-refs": "1.1.1",
+        "@radix-ui/react-context": "1.1.1",
+        "@radix-ui/react-primitive": "2.0.1",
+        "@radix-ui/react-slot": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-compose-refs": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
+      "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/react": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-context": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
+      "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/react": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-presence": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
+      "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-compose-refs": "1.1.1",
+        "@radix-ui/react-use-layout-effect": "1.1.0"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-primitive": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
+      "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-slot": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-roving-focus": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz",
+      "integrity": "sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/primitive": "1.1.1",
+        "@radix-ui/react-collection": "1.1.1",
+        "@radix-ui/react-compose-refs": "1.1.1",
+        "@radix-ui/react-context": "1.1.1",
+        "@radix-ui/react-direction": "1.1.0",
+        "@radix-ui/react-id": "1.1.0",
+        "@radix-ui/react-primitive": "2.0.1",
+        "@radix-ui/react-use-callback-ref": "1.1.0",
+        "@radix-ui/react-use-controllable-state": "1.1.0"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-slot": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
+      "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-compose-refs": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-roving-focus": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
@@ -4655,6 +4927,17 @@
         "url": "https://github.com/sponsors/tannerlinsley"
       }
     },
+    "node_modules/@types/chrome": {
+      "version": "0.0.287",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.287.tgz",
+      "integrity": "sha512-wWhBNPNXZHwycHKNYnexUcpSbrihVZu++0rdp6GEk5ZgAglenLx+RwdEouh6FrHS0XQiOxSd62yaujM1OoQlZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/filesystem": "*",
+        "@types/har-format": "*"
+      }
+    },
     "node_modules/@types/connect": {
       "version": "3.4.36",
       "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
@@ -4738,6 +5021,30 @@
         "@types/estree": "*"
       }
     },
+    "node_modules/@types/filesystem": {
+      "version": "0.0.36",
+      "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
+      "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/filewriter": "*"
+      }
+    },
+    "node_modules/@types/filewriter": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
+      "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/har-format": {
+      "version": "1.2.16",
+      "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
+      "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/hast": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
diff --git a/web/package.json b/web/package.json
index 4a52f11c9..3fdf30f85 100644
--- a/web/package.json
+++ b/web/package.json
@@ -19,7 +19,9 @@
     "@phosphor-icons/react": "^2.0.8",
     "@radix-ui/react-checkbox": "^1.1.2",
     "@radix-ui/react-dialog": "^1.1.2",
+    "@radix-ui/react-label": "^2.1.1",
     "@radix-ui/react-popover": "^1.1.2",
+    "@radix-ui/react-radio-group": "^1.2.2",
     "@radix-ui/react-select": "^2.1.2",
     "@radix-ui/react-separator": "^1.1.0",
     "@radix-ui/react-slot": "^1.1.0",
@@ -79,6 +81,7 @@
   "devDependencies": {
     "@chromatic-com/playwright": "^0.10.0",
     "@tailwindcss/typography": "^0.5.10",
+    "@types/chrome": "^0.0.287",
     "chromatic": "^11.18.1",
     "eslint": "^8.48.0",
     "eslint-config-next": "^14.1.0",
diff --git a/web/src/app/admin/settings/AnonymousUserPath.tsx b/web/src/app/admin/settings/AnonymousUserPath.tsx
index 699b70f25..b35a8d9aa 100644
--- a/web/src/app/admin/settings/AnonymousUserPath.tsx
+++ b/web/src/app/admin/settings/AnonymousUserPath.tsx
@@ -5,7 +5,7 @@ import { useState } from "react";
 
 import { PopupSpec } from "@/components/admin/connectors/Popup";
 import { Button } from "@/components/ui/button";
-import { NEXT_PUBLIC_CLOUD_DOMAIN } from "@/lib/constants";
+import { NEXT_PUBLIC_WEB_DOMAIN } from "@/lib/constants";
 import { ClipboardIcon } from "@/components/icons/icons";
 import { Input } from "@/components/ui/input";
 import { ThreeDotsLoader } from "@/components/Loading";
@@ -118,7 +118,7 @@ export function AnonymousUserPath({
         <div className="flex flex-col gap-2 justify-center items-start">
           <div className="w-full flex-grow  flex items-center rounded-md shadow-sm">
             <span className="inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm h-10">
-              {NEXT_PUBLIC_CLOUD_DOMAIN}/anonymous/
+              {NEXT_PUBLIC_WEB_DOMAIN}/anonymous/
             </span>
             <Input
               type="text"
@@ -143,7 +143,7 @@ export function AnonymousUserPath({
               className="h-10 px-4"
               onClick={() => {
                 navigator.clipboard.writeText(
-                  `${NEXT_PUBLIC_CLOUD_DOMAIN}/anonymous/${anonymousUserPath}`
+                  `${NEXT_PUBLIC_WEB_DOMAIN}/anonymous/${anonymousUserPath}`
                 );
                 setPopup({
                   message: "Invite link copied!",
diff --git a/web/src/app/auth/login/LoginPage.tsx b/web/src/app/auth/login/LoginPage.tsx
new file mode 100644
index 000000000..cc10e5b74
--- /dev/null
+++ b/web/src/app/auth/login/LoginPage.tsx
@@ -0,0 +1,106 @@
+"use client";
+
+import { AuthTypeMetadata } from "@/lib/userSS";
+import { LoginText } from "./LoginText";
+import Link from "next/link";
+import { SignInButton } from "./SignInButton";
+import { EmailPasswordForm } from "./EmailPasswordForm";
+import { NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED } from "@/lib/constants";
+import Title from "@/components/ui/title";
+import { useSendAuthRequiredMessage } from "@/lib/extension/utils";
+
+export default function LoginPage({
+  authUrl,
+  authTypeMetadata,
+  nextUrl,
+  searchParams,
+  showPageRedirect,
+}: {
+  authUrl: string | null;
+  authTypeMetadata: AuthTypeMetadata | null;
+  nextUrl: string | null;
+  searchParams:
+    | {
+        [key: string]: string | string[] | undefined;
+      }
+    | undefined;
+  showPageRedirect?: boolean;
+}) {
+  useSendAuthRequiredMessage();
+  return (
+    <div className="flex flex-col w-full justify-center">
+      {authUrl && authTypeMetadata && (
+        <>
+          <h2 className="text-center text-xl text-strong font-bold">
+            <LoginText />
+          </h2>
+
+          <SignInButton
+            authorizeUrl={authUrl}
+            authType={authTypeMetadata?.authType}
+          />
+        </>
+      )}
+
+      {authTypeMetadata?.authType === "cloud" && (
+        <div className="mt-4 w-full justify-center">
+          <div className="flex items-center w-full my-4">
+            <div className="flex-grow border-t border-gray-300"></div>
+            <span className="px-4 text-gray-500">or</span>
+            <div className="flex-grow border-t border-gray-300"></div>
+          </div>
+          <EmailPasswordForm shouldVerify={true} nextUrl={nextUrl} />
+
+          <div className="flex mt-4 justify-between">
+            <Link
+              href={`/auth/signup${
+                searchParams?.next ? `?next=${searchParams.next}` : ""
+              }`}
+              className="text-link font-medium"
+            >
+              Create an account
+            </Link>
+
+            {NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED && (
+              <Link
+                href="/auth/forgot-password"
+                className="text-link font-medium"
+              >
+                Reset Password
+              </Link>
+            )}
+          </div>
+        </div>
+      )}
+
+      {authTypeMetadata?.authType === "basic" && (
+        <>
+          <div className="flex">
+            <Title className="mb-2 mx-auto text-xl text-strong font-bold">
+              <LoginText />
+            </Title>
+          </div>
+          <EmailPasswordForm nextUrl={nextUrl} />
+          <div className="flex flex-col gap-y-2 items-center"></div>
+        </>
+      )}
+      {showPageRedirect && (
+        <p className="text-center mt-4">
+          Don&apos;t have an account?{" "}
+          <span
+            onClick={() => {
+              if (typeof window !== "undefined" && window.top) {
+                window.top.location.href = "/auth/signup";
+              } else {
+                window.location.href = "/auth/signup";
+              }
+            }}
+            className="text-link font-medium cursor-pointer"
+          >
+            Create an account
+          </span>
+        </p>
+      )}
+    </div>
+  );
+}
diff --git a/web/src/app/auth/login/SignInButton.tsx b/web/src/app/auth/login/SignInButton.tsx
index b06f9bad7..b2d1a15be 100644
--- a/web/src/app/auth/login/SignInButton.tsx
+++ b/web/src/app/auth/login/SignInButton.tsx
@@ -46,7 +46,7 @@ export function SignInButton({
 
   return (
     <a
-      className="mx-auto mt-6 py-3 w-full text-text-100 bg-accent flex rounded cursor-pointer hover:bg-indigo-800"
+      className="mx-auto mb-4 mt-6 py-3 w-full text-text-100 bg-accent flex rounded cursor-pointer hover:bg-indigo-800"
       href={finalAuthorizeUrl}
     >
       {button}
diff --git a/web/src/app/auth/login/page.tsx b/web/src/app/auth/login/page.tsx
index 32ea1aa37..1d30a8242 100644
--- a/web/src/app/auth/login/page.tsx
+++ b/web/src/app/auth/login/page.tsx
@@ -7,18 +7,8 @@ import {
   AuthTypeMetadata,
 } from "@/lib/userSS";
 import { redirect } from "next/navigation";
-import { SignInButton } from "./SignInButton";
-import { EmailPasswordForm } from "./EmailPasswordForm";
-import Title from "@/components/ui/title";
-import Text from "@/components/ui/text";
-import Link from "next/link";
-import { LoginText } from "./LoginText";
-import { getSecondsUntilExpiration } from "@/lib/time";
 import AuthFlowContainer from "@/components/auth/AuthFlowContainer";
-import CardSection from "@/components/admin/CardSection";
-import { NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED } from "@/lib/constants";
-import { SettingsContext } from "@/components/settings/SettingsProvider";
-import { useContext } from "react";
+import LoginPage from "./LoginPage";
 
 const Page = async (props: {
   searchParams?: Promise<{ [key: string]: string | string[] | undefined }>;
@@ -49,13 +39,7 @@ const Page = async (props: {
   }
 
   // if user is already logged in, take them to the main app page
-  const secondsTillExpiration = getSecondsUntilExpiration(currentUser);
-  if (
-    currentUser &&
-    currentUser.is_active &&
-    !currentUser.is_anonymous_user &&
-    (secondsTillExpiration === null || secondsTillExpiration > 0)
-  ) {
+  if (currentUser && currentUser.is_active && !currentUser.is_anonymous_user) {
     if (authTypeMetadata?.requiresVerification && !currentUser.is_verified) {
       return redirect("/auth/waiting-on-verification");
     }
@@ -83,55 +67,12 @@ const Page = async (props: {
           <HealthCheckBanner />
         </div>
 
-        <div className="flex flex-col w-full justify-center">
-          {authUrl && authTypeMetadata && (
-            <>
-              <h2 className="text-center text-xl text-strong font-bold">
-                <LoginText />
-              </h2>
-
-              <SignInButton
-                authorizeUrl={authUrl}
-                authType={authTypeMetadata?.authType}
-              />
-            </>
-          )}
-
-          {authTypeMetadata?.authType === "cloud" && (
-            <div className="mt-4 w-full justify-center">
-              <div className="flex items-center w-full my-4">
-                <div className="flex-grow border-t border-gray-300"></div>
-                <span className="px-4 text-gray-500">or</span>
-                <div className="flex-grow border-t border-gray-300"></div>
-              </div>
-
-              <EmailPasswordForm shouldVerify={true} nextUrl={nextUrl} />
-
-              <div className="flex mt-4 justify-between">
-                {NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED && (
-                  <Link
-                    href="/auth/forgot-password"
-                    className="text-link font-medium"
-                  >
-                    Reset Password
-                  </Link>
-                )}
-              </div>
-            </div>
-          )}
-
-          {authTypeMetadata?.authType === "basic" && (
-            <>
-              <div className="flex">
-                <Title className="mb-2 mx-auto text-xl text-strong font-bold">
-                  <LoginText />
-                </Title>
-              </div>
-              <EmailPasswordForm nextUrl={nextUrl} />
-              <div className="flex flex-col gap-y-2 items-center"></div>
-            </>
-          )}
-        </div>
+        <LoginPage
+          authUrl={authUrl}
+          authTypeMetadata={authTypeMetadata}
+          nextUrl={nextUrl!}
+          searchParams={searchParams}
+        />
       </AuthFlowContainer>
     </div>
   );
diff --git a/web/src/app/chat/ChatIntro.tsx b/web/src/app/chat/ChatIntro.tsx
index d86eb2315..1d9ef4353 100644
--- a/web/src/app/chat/ChatIntro.tsx
+++ b/web/src/app/chat/ChatIntro.tsx
@@ -14,7 +14,7 @@ export function ChatIntro({ selectedPersona }: { selectedPersona: Persona }) {
             <div
               onMouseEnter={() => setHoveredAssistant(true)}
               onMouseLeave={() => setHoveredAssistant(false)}
-              className="p-4 scale-[.7] cursor-pointer border-dashed rounded-full flex border border-gray-300 border-2 border-dashed"
+              className="mobile:hidden p-4 scale-[.7] cursor-pointer border-dashed rounded-full flex border border-gray-300 border-2 border-dashed"
             >
               <AssistantIcon
                 disableToolip
@@ -22,7 +22,7 @@ export function ChatIntro({ selectedPersona }: { selectedPersona: Persona }) {
                 assistant={selectedPersona}
               />
             </div>
-            <div className="absolute right-full mr-1 w-[300px] top-0">
+            <div className="absolute right-full mr-1 mobile:mr-0 w-[300px] top-0">
               {hoveredAssistant && (
                 <DisplayAssistantCard selectedPersona={selectedPersona} />
               )}
diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx
index 0548bc3c5..af944f0b9 100644
--- a/web/src/app/chat/ChatPage.tsx
+++ b/web/src/app/chat/ChatPage.tsx
@@ -1,6 +1,6 @@
 "use client";
 
-import { useRouter, useSearchParams } from "next/navigation";
+import { redirect, useRouter, useSearchParams } from "next/navigation";
 import {
   BackendChatSession,
   BackendMessage,
@@ -52,13 +52,11 @@ import {
   useLayoutEffect,
   useRef,
   useState,
-  useMemo,
 } from "react";
 import { usePopup } from "@/components/admin/connectors/Popup";
 import { SEARCH_PARAM_NAMES, shouldSubmitOnLoad } from "./searchParams";
 import { useDocumentSelection } from "./useDocumentSelection";
 import { LlmOverride, useFilters, useLlmOverride } from "@/lib/hooks";
-import { computeAvailableFilters } from "@/lib/filters";
 import { ChatState, FeedbackType, RegenerationState } from "./types";
 import { ChatFilters } from "./documentSidebar/ChatFilters";
 import { OnyxInitializingLoader } from "@/components/OnyxInitializingLoader";
@@ -110,7 +108,11 @@ import AssistantBanner from "../../components/assistants/AssistantBanner";
 import TextView from "@/components/chat_search/TextView";
 import AssistantSelector from "@/components/chat_search/AssistantSelector";
 import { Modal } from "@/components/Modal";
-import { createPostponedAbortSignal } from "next/dist/server/app-render/dynamic-rendering";
+import { useSendMessageToParent } from "@/lib/extension/utils";
+import {
+  CHROME_MESSAGE,
+  SUBMIT_MESSAGE_TYPES,
+} from "@/lib/extension/constants";
 
 const TEMP_USER_MESSAGE_ID = -1;
 const TEMP_ASSISTANT_MESSAGE_ID = -2;
@@ -120,10 +122,12 @@ export function ChatPage({
   toggle,
   documentSidebarInitialWidth,
   toggledSidebar,
+  firstMessage,
 }: {
   toggle: (toggled?: boolean) => void;
   documentSidebarInitialWidth?: number;
   toggledSidebar: boolean;
+  firstMessage?: string;
 }) {
   const router = useRouter();
   const searchParams = useSearchParams();
@@ -140,6 +144,7 @@ export function ChatPage({
     shouldShowWelcomeModal,
     refreshChatSessions,
   } = useChatContext();
+
   function useScreenSize() {
     const [screenSize, setScreenSize] = useState({
       width: typeof window !== "undefined" ? window.innerWidth : 0,
@@ -192,9 +197,6 @@ export function ChatPage({
   const { user, isAdmin } = useUser();
   const slackChatId = searchParams.get("slackChatId");
   const existingChatIdRaw = searchParams.get("chatId");
-  const [sendOnLoad, setSendOnLoad] = useState<string | null>(
-    searchParams.get(SEARCH_PARAM_NAMES.SEND_ON_LOAD)
-  );
 
   const modelVersionFromSearchParams = searchParams.get(
     SEARCH_PARAM_NAMES.STRUCTURED_MODEL
@@ -210,24 +212,34 @@ export function ChatPage({
       toggle(false);
     }
   }, [user]);
-  // Effect to handle sendOnLoad
-  useEffect(() => {
-    if (sendOnLoad) {
-      const newSearchParams = new URLSearchParams(searchParams.toString());
-      newSearchParams.delete(SEARCH_PARAM_NAMES.SEND_ON_LOAD);
 
-      // Update the URL without the send-on-load parameter
-      router.replace(`?${newSearchParams.toString()}`, { scroll: false });
+  const processSearchParamsAndSubmitMessage = (searchParamsString: string) => {
+    const newSearchParams = new URLSearchParams(searchParamsString);
+    const message = newSearchParams.get("user-prompt");
 
-      // Update our local state to reflect the change
-      setSendOnLoad(null);
+    filterManager.buildFiltersFromQueryString(
+      newSearchParams.toString(),
+      availableSources,
+      documentSets.map((ds) => ds.name),
+      tags
+    );
 
-      // If there's a message, submit it
-      if (message) {
-        onSubmit({ messageOverride: message });
-      }
+    const fileDescriptorString = newSearchParams.get(SEARCH_PARAM_NAMES.FILES);
+    const overrideFileDescriptors: FileDescriptor[] = fileDescriptorString
+      ? JSON.parse(decodeURIComponent(fileDescriptorString))
+      : [];
+
+    newSearchParams.delete(SEARCH_PARAM_NAMES.SEND_ON_LOAD);
+
+    router.replace(`?${newSearchParams.toString()}`, { scroll: false });
+
+    // If there's a message, submit it
+    if (message) {
+      console.log("SUBMITTING MESSAGE");
+      setSubmittedMessage(message);
+      onSubmit({ messageOverride: message, overrideFileDescriptors });
     }
-  }, [sendOnLoad, searchParams, router]);
+  };
 
   const existingChatSessionId = existingChatIdRaw ? existingChatIdRaw : null;
 
@@ -312,14 +324,6 @@ export function ChatPage({
   const noAssistants = liveAssistant == null || liveAssistant == undefined;
 
   const availableSources = ccPairs.map((ccPair) => ccPair.source);
-  const [finalAvailableSources, finalAvailableDocumentSets] =
-    computeAvailableFilters({
-      selectedPersona: availableAssistants.find(
-        (assistant) => assistant.id === liveAssistant?.id
-      ),
-      availableSources: availableSources,
-      availableDocumentSets: documentSets,
-    });
 
   // always set the model override for the chat session, when an assistant, llm provider, or user preference exists
   useEffect(() => {
@@ -399,8 +403,6 @@ export function ChatPage({
     setIsReady(true);
   }, []);
 
-  // this is triggered every time the user switches which chat
-  // session they are using
   useEffect(() => {
     const priorChatSessionId = chatSessionIdRef.current;
     const loadedSessionId = loadedIdSessionRef.current;
@@ -456,7 +458,6 @@ export function ChatPage({
         }
         return;
       }
-      setIsReady(true);
       const shouldScrollToBottom =
         visibleRange.get(existingChatSessionId) === undefined ||
         visibleRange.get(existingChatSessionId)?.end == 0;
@@ -651,10 +652,10 @@ export function ChatPage({
     currentMessageMap(completeMessageDetail)
   );
 
-  const [submittedMessage, setSubmittedMessage] = useState("");
+  const [submittedMessage, setSubmittedMessage] = useState(firstMessage || "");
 
   const [chatState, setChatState] = useState<Map<string | null, ChatState>>(
-    new Map([[chatSessionIdRef.current, "input"]])
+    new Map([[chatSessionIdRef.current, firstMessage ? "loading" : "input"]])
   );
 
   const [regenerationState, setRegenerationState] = useState<
@@ -798,6 +799,19 @@ export function ChatPage({
     }
   }, [defaultAssistantId, availableAssistants, messageHistory.length]);
 
+  useEffect(() => {
+    if (
+      submittedMessage &&
+      currentSessionChatState === "loading" &&
+      messageHistory.length == 0
+    ) {
+      window.parent.postMessage(
+        { type: CHROME_MESSAGE.LOAD_NEW_CHAT_PAGE },
+        "*"
+      );
+    }
+  }, [submittedMessage, currentSessionChatState]);
+
   const [
     selectedDocuments,
     toggleDocumentSelection,
@@ -997,12 +1011,32 @@ export function ChatPage({
     }
   }, [chatSessionIdRef.current]);
 
+  const loadNewPageLogic = (event: MessageEvent) => {
+    if (event.data.type === SUBMIT_MESSAGE_TYPES.PAGE_CHANGE) {
+      try {
+        const url = new URL(event.data.href);
+        processSearchParamsAndSubmitMessage(url.searchParams.toString());
+      } catch (error) {
+        console.error("Error parsing URL:", error);
+      }
+    }
+  };
+
+  // Equivalent to `loadNewPageLogic`
   useEffect(() => {
-    adjustDocumentSidebarWidth(); // Adjust the width on initial render
-    window.addEventListener("resize", adjustDocumentSidebarWidth); // Add resize event listener
+    if (searchParams.get(SEARCH_PARAM_NAMES.SEND_ON_LOAD)) {
+      processSearchParamsAndSubmitMessage(searchParams.toString());
+    }
+  }, [searchParams, router]);
+
+  useEffect(() => {
+    adjustDocumentSidebarWidth();
+    window.addEventListener("resize", adjustDocumentSidebarWidth);
+    window.addEventListener("message", loadNewPageLogic);
 
     return () => {
-      window.removeEventListener("resize", adjustDocumentSidebarWidth); // Cleanup the event listener
+      window.removeEventListener("message", loadNewPageLogic);
+      window.removeEventListener("resize", adjustDocumentSidebarWidth);
     };
   }, []);
 
@@ -1078,6 +1112,7 @@ export function ChatPage({
     alternativeAssistantOverride = null,
     modelOverRide,
     regenerationRequest,
+    overrideFileDescriptors,
   }: {
     messageIdToResend?: number;
     messageOverride?: string;
@@ -1087,6 +1122,7 @@ export function ChatPage({
     alternativeAssistantOverride?: Persona | null;
     modelOverRide?: LlmOverride;
     regenerationRequest?: RegenerationRequest | null;
+    overrideFileDescriptors?: FileDescriptor[];
   } = {}) => {
     let frozenSessionId = currentSessionId();
     updateCanContinue(false, frozenSessionId);
@@ -1113,6 +1149,7 @@ export function ChatPage({
 
     let currChatSessionId: string;
     const isNewSession = chatSessionIdRef.current === null;
+
     const searchParamBasedChatSessionName =
       searchParams.get(SEARCH_PARAM_NAMES.TITLE) || null;
 
@@ -1228,7 +1265,7 @@ export function ChatPage({
         signal: controller.signal, // Add this line
         message: currMessage,
         alternateAssistantId: currentAssistantId,
-        fileDescriptors: currentMessageFiles,
+        fileDescriptors: overrideFileDescriptors || currentMessageFiles,
         parentMessageId:
           regenerationRequest?.parentMessage.messageId ||
           lastSuccessfulMessageId,
@@ -1815,6 +1852,7 @@ export function ChatPage({
     end: 0,
     mostVisibleMessageId: null,
   };
+  useSendMessageToParent();
 
   useEffect(() => {
     if (noAssistants) {
@@ -1889,6 +1927,7 @@ export function ChatPage({
 
     handleSlackChatRedirect();
   }, [searchParams, router]);
+
   useEffect(() => {
     const handleKeyDown = (event: KeyboardEvent) => {
       if (event.metaKey || event.ctrlKey) {
@@ -1957,6 +1996,10 @@ export function ChatPage({
       });
     };
   }
+  if (!user) {
+    redirect("/auth/login");
+  }
+
   if (noAssistants)
     return (
       <>
@@ -2039,7 +2082,11 @@ export function ChatPage({
 
       {retrievalEnabled && documentSidebarToggled && settings?.isMobile && (
         <div className="md:hidden">
-          <Modal noPadding noScroll>
+          <Modal
+            onOutsideClick={() => setDocumentSidebarToggled(false)}
+            noPadding
+            noScroll
+          >
             <ChatFilters
               setPresentingDocument={setPresentingDocument}
               modal={true}
@@ -2247,7 +2294,11 @@ export function ChatPage({
               )}
 
               {documentSidebarInitialWidth !== undefined && isReady ? (
-                <Dropzone onDrop={handleImageUpload} noClick>
+                <Dropzone
+                  key={currentSessionId()}
+                  onDrop={handleImageUpload}
+                  noClick
+                >
                   {({ getRootProps }) => (
                     <div className="flex h-full w-full">
                       {!settings?.isMobile && (
@@ -2327,7 +2378,8 @@ export function ChatPage({
                           {messageHistory.length === 0 &&
                             !isFetchingChatMessages &&
                             currentSessionChatState == "input" &&
-                            !loadingError && (
+                            !loadingError &&
+                            !submittedMessage && (
                               <div className="h-full w-[95%] mx-auto mt-12 flex flex-col justify-center items-center">
                                 <ChatIntro selectedPersona={liveAssistant} />
 
@@ -2344,7 +2396,7 @@ export function ChatPage({
                                   currentSessionChatState == "input" &&
                                   !loadingError &&
                                   allAssistants.length > 1 && (
-                                    <div className="mx-auto px-4 w-full max-w-[750px] flex flex-col items-center">
+                                    <div className="mobile:hidden mx-auto px-4 w-full max-w-[750px] flex flex-col items-center">
                                       <Separator className="mx-2 w-full my-12" />
                                       <div className="text-sm text-black font-medium mb-4">
                                         Recent Assistants
@@ -2362,8 +2414,9 @@ export function ChatPage({
                             )}
 
                           <div
+                            key={currentSessionId()}
                             className={
-                              "-ml-4 w-full mx-auto " +
+                              "desktop:-ml-4 w-full mx-auto " +
                               "absolute mobile:top-0 desktop:top-12 left-0 " +
                               (settings?.enterpriseSettings
                                 ?.two_lines_for_chat_header
@@ -2774,6 +2827,7 @@ export function ChatPage({
                               </div>
                             )}
                             <ChatInputBar
+                              llmOverrideManager={llmOverrideManager}
                               removeDocs={() => {
                                 clearSelectedDocuments();
                               }}
@@ -2786,7 +2840,6 @@ export function ChatPage({
                               }
                               chatState={currentSessionChatState}
                               stopGenerating={stopGenerating}
-                              openModelSettings={() => setSettingsToggled(true)}
                               selectedDocuments={selectedDocuments}
                               // assistant stuff
                               selectedAssistant={liveAssistant}
@@ -2796,7 +2849,6 @@ export function ChatPage({
                               message={message}
                               setMessage={setMessage}
                               onSubmit={onSubmit}
-                              filterManager={filterManager}
                               files={currentMessageFiles}
                               setFiles={setCurrentMessageFiles}
                               toggleFilters={
diff --git a/web/src/app/chat/WrappedChat.tsx b/web/src/app/chat/WrappedChat.tsx
index 6b48e4421..55d0f91c6 100644
--- a/web/src/app/chat/WrappedChat.tsx
+++ b/web/src/app/chat/WrappedChat.tsx
@@ -4,14 +4,20 @@ import FunctionalWrapper from "./shared_chat_search/FunctionalWrapper";
 
 export default function WrappedChat({
   initiallyToggled,
+  firstMessage,
 }: {
   initiallyToggled: boolean;
+  firstMessage?: string;
 }) {
   return (
     <FunctionalWrapper
       initiallyToggled={initiallyToggled}
       content={(toggledSidebar, toggle) => (
-        <ChatPage toggle={toggle} toggledSidebar={toggledSidebar} />
+        <ChatPage
+          toggle={toggle}
+          toggledSidebar={toggledSidebar}
+          firstMessage={firstMessage}
+        />
       )}
     />
   );
diff --git a/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx b/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx
index 8ee3c6ead..13c1e1b0d 100644
--- a/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx
+++ b/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx
@@ -79,7 +79,9 @@ export function ChatDocumentDisplay({
     document.updated_at || Object.keys(document.metadata).length > 0;
   return (
     <div
-      className={`max-w-[400px] opacity-100 ${modal ? "w-[90vw]" : "w-full"}`}
+      className={`desktop:max-w-[400px] opacity-100 ${
+        modal ? "w-[90vw]" : "w-full"
+      }`}
     >
       <div
         className={`flex relative flex-col gap-0.5  rounded-xl mx-2 my-1 ${
diff --git a/web/src/app/chat/input/ChatInputBar.tsx b/web/src/app/chat/input/ChatInputBar.tsx
index c5c21c440..c07d6a142 100644
--- a/web/src/app/chat/input/ChatInputBar.tsx
+++ b/web/src/app/chat/input/ChatInputBar.tsx
@@ -3,7 +3,7 @@ import { FiPlusCircle, FiPlus, FiInfo, FiX, FiSearch } from "react-icons/fi";
 import { ChatInputOption } from "./ChatInputOption";
 import { Persona } from "@/app/admin/assistants/interfaces";
 
-import { FilterManager } from "@/lib/hooks";
+import { FilterManager, LlmOverrideManager } from "@/lib/hooks";
 import { useChatContext } from "@/components/context/ChatContext";
 import { getFinalLLM } from "@/lib/llm/utils";
 import { ChatFileType, FileDescriptor } from "../interfaces";
@@ -31,13 +31,73 @@ import { ChatState } from "../types";
 import UnconfiguredProviderText from "@/components/chat_search/UnconfiguredProviderText";
 import { useAssistants } from "@/components/context/AssistantsContext";
 import { XIcon } from "lucide-react";
-import FiltersDisplay from "./FilterDisplay";
+import { fetchTitleFromUrl } from "@/lib/sources";
 
 const MAX_INPUT_HEIGHT = 200;
 
+const SelectedUrlChip = ({
+  url,
+  onRemove,
+}: {
+  url: string;
+  onRemove: (url: string) => void;
+}) => (
+  <div className="bg-white border border-gray-200 shadow-sm rounded-lg p-2 flex items-center space-x-2">
+    <img
+      src={`https://www.google.com/s2/favicons?domain=${new URL(url).hostname}`}
+      alt="Website favicon"
+      className="w-4 h-4"
+    />
+    <p className="text-sm font-medium text-gray-700 truncate">
+      {new URL(url).hostname}
+    </p>
+    <XIcon
+      onClick={() => onRemove(url)}
+      size={16}
+      className="text-text-400 hover:text-text-600 ml-auto cursor-pointer"
+    />
+  </div>
+);
+
+const SentUrlChip = ({
+  url,
+  onRemove,
+  onClick,
+  title,
+}: {
+  url: string;
+  onRemove: (url: string) => void;
+  onClick: () => void;
+  title: string;
+}) => {
+  return (
+    <button
+      className="bg-white/80 opacity-50 group-hover:opacity-100 border border-gray-200/50 shadow-sm rounded-lg p-2 flex items-center space-x-2  hover:bg-white hover:border-gray-200 transition-all duration-200"
+      onClick={onClick}
+    >
+      <img
+        src={`https://www.google.com/s2/favicons?domain=${
+          new URL(url).hostname
+        }`}
+        alt="Website favicon"
+        className="w-4 h-4 "
+      />
+      <p className="text-sm font-medium text-gray-600 truncate group-hover:text-gray-700">
+        {title}
+      </p>
+      <XIcon
+        onClick={(e) => {
+          onRemove(url);
+        }}
+        size={16}
+        className="text-text-300 hover:text-text-500 ml-auto transition-colors duration-200"
+      />
+    </button>
+  );
+};
+
 interface ChatInputBarProps {
   removeDocs: () => void;
-  openModelSettings: () => void;
   showDocs: () => void;
   showConfigureAPIKey: () => void;
   selectedDocuments: OnyxDocument[];
@@ -45,7 +105,7 @@ interface ChatInputBarProps {
   setMessage: (message: string) => void;
   stopGenerating: () => void;
   onSubmit: () => void;
-  filterManager: FilterManager;
+  llmOverrideManager: LlmOverrideManager;
   chatState: ChatState;
   alternativeAssistant: Persona | null;
   // assistants
@@ -61,7 +121,6 @@ interface ChatInputBarProps {
 
 export function ChatInputBar({
   removeDocs,
-  openModelSettings,
   showDocs,
   showConfigureAPIKey,
   selectedDocuments,
@@ -69,7 +128,6 @@ export function ChatInputBar({
   setMessage,
   stopGenerating,
   onSubmit,
-  filterManager,
   chatState,
 
   // assistants
@@ -408,7 +466,7 @@ export function ChatInputBar({
               style={{ scrollbarWidth: "thin" }}
               role="textarea"
               aria-multiline
-              placeholder="Ask me anything.."
+              placeholder="Ask me anything..."
               value={message}
               onKeyDown={(event) => {
                 if (
@@ -453,16 +511,6 @@ export function ChatInputBar({
                   onClick={toggleFilters}
                 />
               )}
-              {(filterManager.selectedSources.length > 0 ||
-                filterManager.selectedDocumentSets.length > 0 ||
-                filterManager.selectedTags.length > 0 ||
-                filterManager.timeRange) &&
-                toggleFilters && (
-                  <FiltersDisplay
-                    filterManager={filterManager}
-                    toggleFilters={toggleFilters}
-                  />
-                )}
             </div>
 
             <div className="absolute bottom-2.5 mobile:right-4 desktop:right-10">
diff --git a/web/src/app/chat/input/SimplifiedChatInputBar.tsx b/web/src/app/chat/input/SimplifiedChatInputBar.tsx
new file mode 100644
index 000000000..20a5dae80
--- /dev/null
+++ b/web/src/app/chat/input/SimplifiedChatInputBar.tsx
@@ -0,0 +1,244 @@
+import React, { useEffect } from "react";
+import { FiPlusCircle } from "react-icons/fi";
+import { ChatInputOption } from "./ChatInputOption";
+import { FilterManager } from "@/lib/hooks";
+import { ChatFileType, FileDescriptor } from "../interfaces";
+import {
+  InputBarPreview,
+  InputBarPreviewImageProvider,
+} from "../files/InputBarPreview";
+import { SendIcon } from "@/components/icons/icons";
+import { HorizontalSourceSelector } from "@/components/search/filtering/HorizontalSourceSelector";
+import { Tag } from "@/lib/types";
+
+const MAX_INPUT_HEIGHT = 200;
+
+interface ChatInputBarProps {
+  message: string;
+  setMessage: (message: string) => void;
+  onSubmit: () => void;
+  files: FileDescriptor[];
+  setFiles: (files: FileDescriptor[]) => void;
+  handleFileUpload: (files: File[]) => void;
+  textAreaRef: React.RefObject<HTMLTextAreaElement>;
+  filterManager?: FilterManager;
+  existingSources: string[];
+  availableDocumentSets: { name: string }[];
+  availableTags: Tag[];
+}
+
+export function SimplifiedChatInputBar({
+  message,
+  setMessage,
+  onSubmit,
+  files,
+  setFiles,
+  handleFileUpload,
+  textAreaRef,
+  filterManager,
+  existingSources,
+  availableDocumentSets,
+  availableTags,
+}: ChatInputBarProps) {
+  useEffect(() => {
+    const textarea = textAreaRef.current;
+    if (textarea) {
+      textarea.style.height = "0px";
+      textarea.style.height = `${Math.min(
+        textarea.scrollHeight,
+        MAX_INPUT_HEIGHT
+      )}px`;
+    }
+  }, [message, textAreaRef]);
+
+  const handlePaste = (event: React.ClipboardEvent) => {
+    const items = event.clipboardData?.items;
+    if (items) {
+      const pastedFiles = [];
+      for (let i = 0; i < items.length; i++) {
+        if (items[i].kind === "file") {
+          const file = items[i].getAsFile();
+          if (file) pastedFiles.push(file);
+        }
+      }
+      if (pastedFiles.length > 0) {
+        event.preventDefault();
+        handleFileUpload(pastedFiles);
+      }
+    }
+  };
+
+  const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
+    const text = event.target.value;
+    setMessage(text);
+  };
+
+  return (
+    <div
+      id="onyx-chat-input"
+      className="
+            w-full
+            relative
+            mx-auto
+          "
+    >
+      <div
+        className="
+              opacity-100
+              w-full
+              h-fit
+              flex
+              flex-col
+              border
+              border-[#E5E7EB]
+              rounded-lg
+              relative
+              text-text-chatbar
+              bg-background-chatbar
+              [&:has(textarea:focus)]::ring-1
+              [&:has(textarea:focus)]::ring-black
+            "
+      >
+        {files.length > 0 && (
+          <div className="flex gap-x-2 px-2 pt-2">
+            <div className="flex gap-x-1 px-2 overflow-visible overflow-x-scroll items-end miniscroll">
+              {files.map((file) => (
+                <div className="flex-none" key={file.id}>
+                  {file.type === ChatFileType.IMAGE ? (
+                    <InputBarPreviewImageProvider
+                      file={file}
+                      onDelete={() => {
+                        setFiles(
+                          files.filter(
+                            (fileInFilter) => fileInFilter.id !== file.id
+                          )
+                        );
+                      }}
+                      isUploading={file.isUploading || false}
+                    />
+                  ) : (
+                    <InputBarPreview
+                      file={file}
+                      onDelete={() => {
+                        setFiles(
+                          files.filter(
+                            (fileInFilter) => fileInFilter.id !== file.id
+                          )
+                        );
+                      }}
+                      isUploading={file.isUploading || false}
+                    />
+                  )}
+                </div>
+              ))}
+            </div>
+          </div>
+        )}
+
+        <textarea
+          onPaste={handlePaste}
+          onChange={handleInputChange}
+          ref={textAreaRef}
+          className={`
+                m-0
+                w-full
+                shrink
+                resize-none
+                rounded-lg
+                border-0
+                bg-background-chatbar
+                placeholder:text-text-chatbar-subtle
+                ${
+                  textAreaRef.current &&
+                  textAreaRef.current.scrollHeight > MAX_INPUT_HEIGHT
+                    ? "overflow-y-auto mt-2"
+                    : ""
+                }
+                whitespace-normal
+                break-word
+                overscroll-contain
+                outline-none
+                placeholder-subtle
+                resize-none
+                px-5
+                py-4
+                h-14
+              `}
+          autoFocus
+          style={{ scrollbarWidth: "thin" }}
+          role="textarea"
+          aria-multiline
+          placeholder="Ask me anything..."
+          value={message}
+          onKeyDown={(event) => {
+            if (
+              event.key === "Enter" &&
+              !event.shiftKey &&
+              !(event.nativeEvent as any).isComposing
+            ) {
+              event.preventDefault();
+              if (message) {
+                onSubmit();
+              }
+            }
+          }}
+          suppressContentEditableWarning={true}
+        />
+        <div className="flex items-center space-x-3 mr-12 px-4 pb-2">
+          <ChatInputOption
+            flexPriority="stiff"
+            name="File"
+            Icon={FiPlusCircle}
+            onClick={() => {
+              const input = document.createElement("input");
+              input.type = "file";
+              input.multiple = true; // Allow multiple files
+              input.onchange = (event: any) => {
+                const selectedFiles = Array.from(
+                  event?.target?.files || []
+                ) as File[];
+                if (selectedFiles.length > 0) {
+                  handleFileUpload(selectedFiles);
+                }
+              };
+              input.click();
+            }}
+          />
+
+          {filterManager && (
+            <HorizontalSourceSelector
+              timeRange={filterManager.timeRange}
+              setTimeRange={filterManager.setTimeRange}
+              selectedSources={filterManager.selectedSources}
+              setSelectedSources={filterManager.setSelectedSources}
+              selectedDocumentSets={filterManager.selectedDocumentSets}
+              setSelectedDocumentSets={filterManager.setSelectedDocumentSets}
+              selectedTags={filterManager.selectedTags}
+              setSelectedTags={filterManager.setSelectedTags}
+              existingSources={existingSources}
+              availableDocumentSets={availableDocumentSets}
+              availableTags={availableTags}
+            />
+          )}
+        </div>
+      </div>
+      <div className="absolute bottom-2 mobile:right-4 desktop:right-4">
+        <button
+          className="cursor-pointer"
+          onClick={() => {
+            if (message) {
+              onSubmit();
+            }
+          }}
+        >
+          <SendIcon
+            size={28}
+            className={`text-emphasis text-white p-1 rounded-full  ${
+              message ? "bg-submit-background" : "bg-disabled-submit-background"
+            } `}
+          />
+        </button>
+      </div>
+    </div>
+  );
+}
diff --git a/web/src/app/chat/layout.tsx b/web/src/app/chat/layout.tsx
new file mode 100644
index 000000000..4023ba8c4
--- /dev/null
+++ b/web/src/app/chat/layout.tsx
@@ -0,0 +1,62 @@
+import { redirect } from "next/navigation";
+import { unstable_noStore as noStore } from "next/cache";
+import { fetchChatData } from "@/lib/chat/fetchChatData";
+import { ChatProvider } from "@/components/context/ChatContext";
+import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
+
+export default async function Layout({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  noStore();
+
+  // Ensure searchParams is an object, even if it's empty
+  const safeSearchParams = {};
+
+  const data = await fetchChatData(
+    safeSearchParams as { [key: string]: string }
+  );
+
+  if ("redirect" in data) {
+    redirect(data.redirect);
+  }
+
+  const {
+    chatSessions,
+    availableSources,
+    user,
+    documentSets,
+    tags,
+    llmProviders,
+    folders,
+    openedFolders,
+    defaultAssistantId,
+    shouldShowWelcomeModal,
+    ccPairs,
+  } = data;
+
+  return (
+    <>
+      <InstantSSRAutoRefresh />
+      <ChatProvider
+        value={{
+          chatSessions,
+          availableSources,
+          ccPairs,
+          documentSets,
+          tags,
+          availableDocumentSets: documentSets,
+          availableTags: tags,
+          llmProviders,
+          folders,
+          openedFolders,
+          shouldShowWelcomeModal,
+          defaultAssistantId,
+        }}
+      >
+        {children}
+      </ChatProvider>
+    </>
+  );
+}
diff --git a/web/src/app/chat/message/MemoizedTextComponents.tsx b/web/src/app/chat/message/MemoizedTextComponents.tsx
index a29038e62..3ded90770 100644
--- a/web/src/app/chat/message/MemoizedTextComponents.tsx
+++ b/web/src/app/chat/message/MemoizedTextComponents.tsx
@@ -1,11 +1,7 @@
 import { Citation } from "@/components/search/results/Citation";
-import { WebResultIcon } from "@/components/WebResultIcon";
 import { LoadedOnyxDocument, OnyxDocument } from "@/lib/search/interfaces";
-import { getSourceMetadata, SOURCE_METADATA_MAP } from "@/lib/sources";
-import { ValidSources } from "@/lib/types";
 import React, { memo } from "react";
 import isEqual from "lodash/isEqual";
-import { SlackIcon } from "@/components/icons/icons";
 import { SourceIcon } from "@/components/SourceIcon";
 
 export const MemoizedAnchor = memo(
@@ -66,7 +62,6 @@ export const MemoizedLink = memo((props: any) => {
       <Citation
         url={document?.url}
         icon={document?.icon as React.ReactNode}
-        link={rest?.href}
         document={document as LoadedOnyxDocument}
         updatePresentingDocument={updatePresentingDocument}
       >
diff --git a/web/src/app/chat/message/Messages.tsx b/web/src/app/chat/message/Messages.tsx
index c3d404f41..4d3bbbd1f 100644
--- a/web/src/app/chat/message/Messages.tsx
+++ b/web/src/app/chat/message/Messages.tsx
@@ -383,14 +383,14 @@ export const AIMessage = ({
     <div
       id="onyx-ai-message"
       ref={trackedElementRef}
-      className={`py-5 ml-4 px-5 relative flex `}
+      className={`py-5 ml-4 lg:px-5 relative flex `}
     >
       <div
         className={`mx-auto ${
           shared ? "w-full" : "w-[90%]"
         }  max-w-message-max`}
       >
-        <div className={`desktop:mr-12 ${!shared && "mobile:ml-0 md:ml-8"}`}>
+        <div className={`lg:mr-12 ${!shared && "mobile:ml-0 md:ml-8"}`}>
           <div className="flex">
             <AssistantIcon
               size="small"
@@ -399,7 +399,7 @@ export const AIMessage = ({
 
             <div className="w-full">
               <div className="max-w-message-max break-words">
-                <div className="w-full ml-4">
+                <div className="w-full lg:ml-4">
                   <div className="max-w-message-max break-words">
                     {!toolCall || toolCall.tool_name === SEARCH_TOOL_NAME ? (
                       <>
@@ -410,6 +410,8 @@ export const AIMessage = ({
                               query={query}
                               finished={toolCall?.tool_result != undefined}
                               handleSearchQueryEdit={handleSearchQueryEdit}
+                              docs={docs || []}
+                              toggleDocumentSelection={toggleDocumentSelection!}
                             />
                           </div>
                         )}
@@ -465,7 +467,7 @@ export const AIMessage = ({
                       )}
 
                     {docs && docs.length > 0 && (
-                      <div className="mt-2 -mx-8 w-full mb-4 flex relative">
+                      <div className="mobile:hidden mt-2 -mx-8 w-full mb-4 flex relative">
                         <div className="w-full">
                           <div className="px-8 flex gap-x-2">
                             {!settings?.isMobile &&
@@ -768,7 +770,7 @@ export const HumanMessage = ({
   return (
     <div
       id="onyx-human-message"
-      className="pt-5 pb-1 px-2 lg:px-5 flex -mr-6 relative"
+      className="pt-5 pb-1 w-full lg:px-5 flex -mr-6 relative"
       onMouseEnter={() => setIsHovered(true)}
       onMouseLeave={() => setIsHovered(false)}
     >
@@ -778,7 +780,7 @@ export const HumanMessage = ({
         } max-w-[790px]`}
       >
         <div className="xl:ml-8">
-          <div className="flex flex-col mr-4">
+          <div className="flex flex-col desktop:mr-4">
             <FileDisplay alignBubble files={files || []} />
 
             <div className="flex justify-end">
diff --git a/web/src/app/chat/message/SearchSummary.tsx b/web/src/app/chat/message/SearchSummary.tsx
index 9c22f81d3..2b3392c98 100644
--- a/web/src/app/chat/message/SearchSummary.tsx
+++ b/web/src/app/chat/message/SearchSummary.tsx
@@ -4,12 +4,15 @@ import {
 } from "@/components/BasicClickable";
 import { HoverPopup } from "@/components/HoverPopup";
 import { Hoverable } from "@/components/Hoverable";
+import { SourceIcon } from "@/components/SourceIcon";
 import {
   Tooltip,
   TooltipContent,
   TooltipProvider,
   TooltipTrigger,
 } from "@/components/ui/tooltip";
+import { OnyxDocument } from "@/lib/search/interfaces";
+import { ValidSources } from "@/lib/types";
 import { useEffect, useRef, useState } from "react";
 import { FiCheck, FiEdit2, FiSearch, FiX } from "react-icons/fi";
 
@@ -45,11 +48,15 @@ export function SearchSummary({
   query,
   finished,
   handleSearchQueryEdit,
+  docs,
+  toggleDocumentSelection,
 }: {
   index: number;
   finished: boolean;
   query: string;
   handleSearchQueryEdit?: (query: string) => void;
+  docs: OnyxDocument[];
+  toggleDocumentSelection: () => void;
 }) {
   const [isEditing, setIsEditing] = useState(false);
   const [finalQuery, setFinalQuery] = useState(query);
@@ -87,27 +94,63 @@ export function SearchSummary({
   }, [query, isEditing]);
 
   const searchingForDisplay = (
-    <div className={`flex p-1 rounded ${isOverflowed && "cursor-default"}`}>
-      <FiSearch className="flex-none mr-2 my-auto" size={14} />
+    <div className="flex flex-col gap-y-1">
       <div
-        className={`${!finished && "loading-text"} 
-        !text-sm !line-clamp-1 !break-all px-0.5`}
-        ref={searchingForRef}
+        className={`flex items-center w-full rounded ${
+          isOverflowed && "cursor-default"
+        }`}
       >
-        {finished ? "Searched" : "Searching"} for:{" "}
-        <i>
-          {index === 1
-            ? finalQuery.length > 50
-              ? `${finalQuery.slice(0, 50)}...`
-              : finalQuery
-            : finalQuery}
-        </i>
+        <FiSearch className="mobile:hidden flex-none mr-2" size={14} />
+        <div
+          className={`${
+            !finished && "loading-text"
+          } text-xs desktop:text-sm mobile:ml-auto !line-clamp-1 !break-all px-0.5 flex-grow`}
+          ref={searchingForRef}
+        >
+          {finished ? "Searched" : "Searching"} for:{" "}
+          <i>
+            {index === 1
+              ? finalQuery.length > 50
+                ? `${finalQuery.slice(0, 50)}...`
+                : finalQuery
+              : finalQuery}
+          </i>
+        </div>
+      </div>
+
+      <div className="desktop:hidden">
+        {" "}
+        {docs && (
+          <button
+            className="cursor-pointer mr-2 flex items-center gap-0.5"
+            onClick={() => toggleDocumentSelection()}
+          >
+            {Array.from(new Set(docs.map((doc) => doc.source_type)))
+              .slice(0, 3)
+              .map((sourceType, idx) => (
+                <div key={idx} className="rounded-full">
+                  <SourceIcon sourceType={sourceType} iconSize={14} />
+                </div>
+              ))}
+            {Array.from(new Set(docs.map((doc) => doc.source_type))).length >
+              3 && (
+              <div className="rounded-full bg-gray-200 w-3.5 h-3.5 flex items-center justify-center">
+                <span className="text-[8px]">
+                  +
+                  {Array.from(new Set(docs.map((doc) => doc.source_type)))
+                    .length - 3}
+                </span>
+              </div>
+            )}
+            <span className="text-xs underline">View sources</span>
+          </button>
+        )}
       </div>
     </div>
   );
 
   const editInput = handleSearchQueryEdit ? (
-    <div className="flex w-full mr-3">
+    <div className="mobile:hidden flex w-full mr-3">
       <div className="my-2 w-full">
         <input
           ref={editQueryRef}
@@ -155,12 +198,12 @@ export function SearchSummary({
   ) : null;
 
   return (
-    <div className="flex">
+    <div className="flex items-center">
       {isEditing ? (
         editInput
       ) : (
         <>
-          <div className="text-sm">
+          <div className="mobile:w-full mobile:mr-2 text-sm mobile:flex-grow">
             {isOverflowed ? (
               <HoverPopup
                 mainContent={searchingForDisplay}
@@ -176,12 +219,13 @@ export function SearchSummary({
               searchingForDisplay
             )}
           </div>
+
           {handleSearchQueryEdit && (
             <TooltipProvider delayDuration={1000}>
               <Tooltip>
                 <TooltipTrigger asChild>
                   <button
-                    className="my-auto hover:bg-hover p-1.5 rounded"
+                    className="ml-2 mobile:hidden hover:bg-hover p-1 rounded flex-shrink-0"
                     onClick={() => {
                       setIsEditing(true);
                     }}
diff --git a/web/src/app/chat/message/SkippedSearch.tsx b/web/src/app/chat/message/SkippedSearch.tsx
index 27a50d4f6..33bbd352e 100644
--- a/web/src/app/chat/message/SkippedSearch.tsx
+++ b/web/src/app/chat/message/SkippedSearch.tsx
@@ -1,4 +1,4 @@
-import { EmphasizedClickable } from "@/components/BasicClickable";
+import { CustomTooltip } from "@/components/tooltip/CustomTooltip";
 import { FiBook } from "react-icons/fi";
 
 export function SkippedSearch({
@@ -7,22 +7,29 @@ export function SkippedSearch({
   handleForceSearch: () => void;
 }) {
   return (
-    <div className="flex text-sm !pt-0 p-1">
-      <div className="flex mb-auto">
-        <FiBook className="my-auto flex-none mr-2" size={14} />
-        <div className="my-auto cursor-default">
+    <div className="flex w-full text-sm !pt-0 p-1">
+      <div className="flex w-full mb-auto">
+        <FiBook className="mobile:hidden my-auto flex-none mr-2" size={14} />
+        <div className="my-auto flex w-full items-center justify-between cursor-default">
           <span className="mobile:hidden">
             The AI decided this query didn&apos;t need a search
           </span>
-          <span className="desktop:hidden">No search</span>
+          <p className="text-xs desktop:hidden">No search performed</p>
+          <CustomTooltip
+            content="Perform a search for this query"
+            showTick
+            line
+            wrap
+          >
+            <button
+              onClick={handleForceSearch}
+              className="ml-auto mr-4 text-xs desktop:hidden underline-dotted decoration-dotted underline cursor-pointer"
+            >
+              Force search?
+            </button>
+          </CustomTooltip>
         </div>
       </div>
-
-      <div className="ml-auto my-auto" onClick={handleForceSearch}>
-        <EmphasizedClickable size="sm">
-          <div className="w-24 text-xs">Force Search</div>
-        </EmphasizedClickable>
-      </div>
     </div>
   );
 }
diff --git a/web/src/app/chat/nrf/NRFPage.tsx b/web/src/app/chat/nrf/NRFPage.tsx
new file mode 100644
index 000000000..c2433f56a
--- /dev/null
+++ b/web/src/app/chat/nrf/NRFPage.tsx
@@ -0,0 +1,384 @@
+"use client";
+import React, { useState, useEffect, useRef } from "react";
+import { useUser } from "@/components/user/UserProvider";
+import { usePopup } from "@/components/admin/connectors/Popup";
+import {
+  Dialog,
+  DialogContent,
+  DialogHeader,
+  DialogTitle,
+  DialogDescription,
+  DialogFooter,
+} from "@/components/ui/dialog";
+import { v4 as uuidv4 } from "uuid";
+import { Button } from "@/components/ui/button";
+import { SimplifiedChatInputBar } from "../input/SimplifiedChatInputBar";
+import { Menu } from "lucide-react";
+import { Shortcut } from "./interfaces";
+import {
+  MaxShortcutsReachedModal,
+  NewShortCutModal,
+} from "@/components/extension/Shortcuts";
+import { Modal } from "@/components/Modal";
+import { useNightTime } from "@/lib/dateUtils";
+import { useFilters } from "@/lib/hooks";
+import { uploadFilesForChat } from "../lib";
+import { ChatFileType, FileDescriptor } from "../interfaces";
+import { useChatContext } from "@/components/context/ChatContext";
+import Dropzone from "react-dropzone";
+import { useSendMessageToParent } from "@/lib/extension/utils";
+import { useNRFPreferences } from "@/components/context/NRFPreferencesContext";
+import { SettingsPanel } from "../../components/nrf/SettingsPanel";
+import { ShortcutsDisplay } from "../../components/nrf/ShortcutsDisplay";
+import LoginPage from "../../auth/login/LoginPage";
+import { AuthType, NEXT_PUBLIC_WEB_DOMAIN } from "@/lib/constants";
+import { sendSetDefaultNewTabMessage } from "@/lib/extension/utils";
+import { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies";
+import { CHROME_MESSAGE } from "@/lib/extension/constants";
+import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
+
+export default function NRFPage({
+  requestCookies,
+}: {
+  requestCookies: ReadonlyRequestCookies;
+}) {
+  const {
+    theme,
+    defaultLightBackgroundUrl,
+    defaultDarkBackgroundUrl,
+    shortcuts: shortCuts,
+    setShortcuts: setShortCuts,
+    setUseOnyxAsNewTab,
+    showShortcuts,
+  } = useNRFPreferences();
+
+  const filterManager = useFilters();
+  const { isNight } = useNightTime();
+  const { user } = useUser();
+  const { ccPairs, documentSets, tags, llmProviders } = useChatContext();
+
+  const { popup, setPopup } = usePopup();
+
+  // State
+  const [message, setMessage] = useState("");
+  const [settingsOpen, setSettingsOpen] = useState<boolean>(false);
+  const [editingShortcut, setEditingShortcut] = useState<Shortcut | null>(null);
+  const [backgroundUrl, setBackgroundUrl] = useState<string>(
+    theme === "light" ? defaultLightBackgroundUrl : defaultDarkBackgroundUrl
+  );
+
+  // Modals
+  const [showTurnOffModal, setShowTurnOffModal] = useState<boolean>(false);
+  const [showShortCutModal, setShowShortCutModal] = useState(false);
+  const [showMaxShortcutsModal, setShowMaxShortcutsModal] = useState(false);
+  const [showLoginModal, setShowLoginModal] = useState<boolean>(!user);
+
+  // Refs
+  const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
+  const inputRef = useRef<HTMLInputElement>(null);
+
+  useEffect(() => {
+    setBackgroundUrl(
+      theme === "light" ? defaultLightBackgroundUrl : defaultDarkBackgroundUrl
+    );
+  }, [theme, defaultLightBackgroundUrl, defaultDarkBackgroundUrl]);
+
+  useSendMessageToParent();
+  useEffect(() => {
+    if (inputRef.current) {
+      inputRef.current.focus();
+    }
+  }, []);
+
+  const toggleSettings = () => {
+    setSettingsOpen((prev) => !prev);
+  };
+
+  // If user toggles the "Use Onyx" switch to off, prompt a modal
+  const handleUseOnyxToggle = (checked: boolean) => {
+    if (!checked) {
+      setShowTurnOffModal(true);
+    } else {
+      setUseOnyxAsNewTab(true);
+      sendSetDefaultNewTabMessage(true);
+    }
+  };
+
+  const availableSources = ccPairs.map((ccPair) => ccPair.source);
+
+  const [currentMessageFiles, setCurrentMessageFiles] = useState<
+    FileDescriptor[]
+  >([]);
+
+  const handleImageUpload = async (acceptedFiles: File[]) => {
+    const tempFileDescriptors = acceptedFiles.map((file) => ({
+      id: uuidv4(),
+      type: file.type.startsWith("image/")
+        ? ChatFileType.IMAGE
+        : ChatFileType.DOCUMENT,
+      isUploading: true,
+    }));
+
+    // only show loading spinner for reasonably large files
+    const totalSize = acceptedFiles.reduce((sum, file) => sum + file.size, 0);
+    if (totalSize > 50 * 1024) {
+      setCurrentMessageFiles((prev) => [...prev, ...tempFileDescriptors]);
+    }
+
+    const removeTempFiles = (prev: FileDescriptor[]) => {
+      return prev.filter(
+        (file) => !tempFileDescriptors.some((newFile) => newFile.id === file.id)
+      );
+    };
+
+    await uploadFilesForChat(acceptedFiles).then(([files, error]) => {
+      if (error) {
+        setCurrentMessageFiles((prev) => removeTempFiles(prev));
+        setPopup({
+          type: "error",
+          message: error,
+        });
+      } else {
+        setCurrentMessageFiles((prev) => [...removeTempFiles(prev), ...files]);
+      }
+    });
+  };
+
+  const confirmTurnOff = () => {
+    setUseOnyxAsNewTab(false);
+    setShowTurnOffModal(false);
+    sendSetDefaultNewTabMessage(false);
+  };
+
+  // Auth related
+  const [authType, setAuthType] = useState<AuthType | null>(null);
+  const [fetchingAuth, setFetchingAuth] = useState(false);
+  useEffect(() => {
+    // If user is already logged in, no need to fetch auth data
+    if (user) return;
+
+    async function fetchAuthData() {
+      setFetchingAuth(true);
+
+      try {
+        const res = await fetch("/api/auth/type", {
+          method: "GET",
+          credentials: "include",
+        });
+        if (!res.ok) {
+          throw new Error(`Failed to fetch auth type: ${res.statusText}`);
+        }
+
+        const data = await res.json();
+        setAuthType(data.auth_type);
+      } catch (err) {
+        console.error("Error fetching auth data:", err);
+      } finally {
+        setFetchingAuth(false);
+      }
+    }
+
+    fetchAuthData();
+  }, [user]);
+
+  const onSubmit = async ({
+    messageOverride,
+  }: {
+    messageOverride?: string;
+  } = {}) => {
+    const userMessage = messageOverride || message;
+
+    let filterString = filterManager?.getFilterString();
+
+    if (currentMessageFiles.length > 0) {
+      filterString +=
+        "&files=" + encodeURIComponent(JSON.stringify(currentMessageFiles));
+    }
+
+    const newHref =
+      `${NEXT_PUBLIC_WEB_DOMAIN}/chat?send-on-load=true&user-prompt=` +
+      encodeURIComponent(userMessage) +
+      filterString;
+
+    if (typeof window !== "undefined" && window.parent) {
+      window.parent.postMessage(
+        { type: CHROME_MESSAGE.LOAD_NEW_PAGE, href: newHref },
+        "*"
+      );
+    } else {
+      window.location.href = newHref;
+    }
+  };
+
+  return (
+    <div
+      className="relative w-full h-full flex flex-col min-h-screen bg-cover bg-center bg-no-repeat overflow-hidden transition-[background-image] duration-300 ease-in-out"
+      style={{
+        backgroundImage: `url(${backgroundUrl})`,
+      }}
+    >
+      <div className="absolute top-0 right-0 p-4 z-10">
+        <button
+          aria-label="Open settings"
+          onClick={toggleSettings}
+          className="bg-white bg-opacity-70 rounded-full p-2.5 cursor-pointer hover:bg-opacity-80 transition-colors duration-200"
+        >
+          <Menu size={12} className="text-neutral-900" />
+        </button>
+      </div>
+
+      <Dropzone onDrop={handleImageUpload} noClick>
+        {({ getRootProps }) => (
+          <div
+            {...getRootProps()}
+            className="absolute top-20 left-0 w-full h-full flex flex-col"
+          >
+            <div className="pointer-events-auto absolute top-[40%] left-1/2 -translate-x-1/2 -translate-y-1/2 text-center w-[90%] lg:max-w-3xl">
+              <h1
+                className={`pl-2 text-xl text-left w-full mb-4 ${
+                  theme === "light" ? "text-neutral-800" : "text-white"
+                }`}
+              >
+                {isNight
+                  ? "End your day with Onyx"
+                  : "Start your day with Onyx"}
+              </h1>
+
+              <SimplifiedChatInputBar
+                onSubmit={onSubmit}
+                handleFileUpload={handleImageUpload}
+                message={message}
+                setMessage={setMessage}
+                files={currentMessageFiles}
+                setFiles={setCurrentMessageFiles}
+                filterManager={filterManager}
+                textAreaRef={textAreaRef}
+                existingSources={availableSources}
+                availableDocumentSets={documentSets}
+                availableTags={tags}
+              />
+
+              <ShortcutsDisplay
+                shortCuts={shortCuts}
+                showShortcuts={showShortcuts}
+                setEditingShortcut={setEditingShortcut}
+                setShowShortCutModal={setShowShortCutModal}
+                openShortCutModal={() => {
+                  if (shortCuts.length >= 6) {
+                    setShowMaxShortcutsModal(true);
+                  } else {
+                    setEditingShortcut(null);
+                    setShowShortCutModal(true);
+                  }
+                }}
+              />
+            </div>
+          </div>
+        )}
+      </Dropzone>
+      {showMaxShortcutsModal && (
+        <MaxShortcutsReachedModal
+          onClose={() => setShowMaxShortcutsModal(false)}
+        />
+      )}
+      {showShortCutModal && (
+        <NewShortCutModal
+          setPopup={setPopup}
+          onDelete={(shortcut: Shortcut) => {
+            setShortCuts(
+              shortCuts.filter((s: Shortcut) => s.name !== shortcut.name)
+            );
+            setShowShortCutModal(false);
+          }}
+          isOpen={showShortCutModal}
+          onClose={() => {
+            setEditingShortcut(null);
+            setShowShortCutModal(false);
+          }}
+          onAdd={(shortCut: Shortcut) => {
+            if (editingShortcut) {
+              setShortCuts(
+                shortCuts
+                  .filter((s) => s.name !== editingShortcut.name)
+                  .concat(shortCut)
+              );
+            } else {
+              setShortCuts([...shortCuts, shortCut]);
+            }
+            setShowShortCutModal(false);
+          }}
+          editingShortcut={editingShortcut}
+        />
+      )}
+      <SettingsPanel
+        settingsOpen={settingsOpen}
+        toggleSettings={toggleSettings}
+        handleUseOnyxToggle={handleUseOnyxToggle}
+      />
+
+      <Dialog open={showTurnOffModal} onOpenChange={setShowTurnOffModal}>
+        <DialogContent className="w-fit max-w-[95%]">
+          <DialogHeader>
+            <DialogTitle>Turn off Onyx new tab page?</DialogTitle>
+            <DialogDescription>
+              You&apos;ll see your browser&apos;s default new tab page instead.
+              <br />
+              You can turn it back on anytime in your Onyx settings.
+            </DialogDescription>
+          </DialogHeader>
+          <DialogFooter className="flex gap-2 justify-center">
+            <Button
+              variant="outline"
+              onClick={() => setShowTurnOffModal(false)}
+            >
+              Cancel
+            </Button>
+            <Button variant="destructive" onClick={confirmTurnOff}>
+              Turn off
+            </Button>
+          </DialogFooter>
+        </DialogContent>
+      </Dialog>
+      {!user && authType !== "disabled" && showLoginModal ? (
+        <Modal className="max-w-md mx-auto">
+          {fetchingAuth ? (
+            <p className="p-4">Loading login info…</p>
+          ) : authType == "basic" ? (
+            <LoginPage
+              showPageRedirect
+              authUrl={null}
+              authTypeMetadata={{
+                authType: authType as AuthType,
+                autoRedirect: false,
+                requiresVerification: false,
+                anonymousUserEnabled: null,
+              }}
+              nextUrl="/nrf"
+              searchParams={{}}
+            />
+          ) : (
+            <div className="flex flex-col items-center">
+              <h2 className="text-center text-xl text-strong font-bold mb-4">
+                Welcome to Onyx
+              </h2>
+              <Button
+                className="bg-accent w-full hover:bg-accent-hover text-white"
+                onClick={() => {
+                  if (window.top) {
+                    window.top.location.href = "/auth/login";
+                  } else {
+                    window.location.href = "/auth/login";
+                  }
+                }}
+              >
+                Log in
+              </Button>
+            </div>
+          )}
+        </Modal>
+      ) : (
+        llmProviders.length == 0 && <ApiKeyModal setPopup={setPopup} />
+      )}
+    </div>
+  );
+}
diff --git a/web/src/app/chat/nrf/interfaces.ts b/web/src/app/chat/nrf/interfaces.ts
new file mode 100644
index 000000000..ad750cc71
--- /dev/null
+++ b/web/src/app/chat/nrf/interfaces.ts
@@ -0,0 +1,5 @@
+export interface Shortcut {
+  name: string;
+  url: string;
+  favicon?: string;
+}
diff --git a/web/src/app/chat/nrf/page.tsx b/web/src/app/chat/nrf/page.tsx
new file mode 100644
index 000000000..d3682286c
--- /dev/null
+++ b/web/src/app/chat/nrf/page.tsx
@@ -0,0 +1,20 @@
+import { unstable_noStore as noStore } from "next/cache";
+import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
+import { cookies } from "next/headers";
+import NRFPage from "./NRFPage";
+import { NRFPreferencesProvider } from "../../../components/context/NRFPreferencesContext";
+
+export default async function Page() {
+  noStore();
+  const requestCookies = await cookies();
+
+  return (
+    <div className="w-full h-full bg-black">
+      <InstantSSRAutoRefresh />
+
+      <NRFPreferencesProvider>
+        <NRFPage requestCookies={requestCookies} />
+      </NRFPreferencesProvider>
+    </div>
+  );
+}
diff --git a/web/src/app/chat/page.tsx b/web/src/app/chat/page.tsx
index e742c54a5..adb0234b8 100644
--- a/web/src/app/chat/page.tsx
+++ b/web/src/app/chat/page.tsx
@@ -1,65 +1,10 @@
-import { redirect } from "next/navigation";
-import { unstable_noStore as noStore } from "next/cache";
-import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
-import { WelcomeModal } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
-import { ChatProvider } from "@/components/context/ChatContext";
-import { fetchChatData } from "@/lib/chat/fetchChatData";
 import WrappedChat from "./WrappedChat";
-import { cookies } from "next/headers";
 
 export default async function Page(props: {
   searchParams: Promise<{ [key: string]: string }>;
 }) {
   const searchParams = await props.searchParams;
-  noStore();
-  const requestCookies = await cookies();
-  const data = await fetchChatData(searchParams);
+  const firstMessage = searchParams.firstMessage;
 
-  if ("redirect" in data) {
-    redirect(data.redirect);
-  }
-
-  const {
-    user,
-    chatSessions,
-    availableSources,
-    documentSets,
-    tags,
-    llmProviders,
-    folders,
-    toggleSidebar,
-    openedFolders,
-    defaultAssistantId,
-    shouldShowWelcomeModal,
-    ccPairs,
-  } = data;
-
-  return (
-    <>
-      <InstantSSRAutoRefresh />
-      {shouldShowWelcomeModal && (
-        <WelcomeModal user={user} requestCookies={requestCookies} />
-      )}
-      <ChatProvider
-        value={{
-          chatSessions,
-          availableSources,
-          ccPairs,
-          documentSets,
-          tags,
-          availableDocumentSets: documentSets,
-          availableTags: tags,
-          llmProviders,
-          folders,
-          openedFolders,
-          shouldShowWelcomeModal,
-          defaultAssistantId,
-        }}
-      >
-        <WrappedChat
-          initiallyToggled={toggleSidebar && !user?.is_anonymous_user}
-        />
-      </ChatProvider>
-    </>
-  );
+  return <WrappedChat firstMessage={firstMessage} initiallyToggled={false} />;
 }
diff --git a/web/src/app/chat/searchParams.ts b/web/src/app/chat/searchParams.ts
index 7b3dc44fe..b32390aa4 100644
--- a/web/src/app/chat/searchParams.ts
+++ b/web/src/app/chat/searchParams.ts
@@ -15,6 +15,7 @@ export const SEARCH_PARAM_NAMES = {
   SUBMIT_ON_LOAD: "submit-on-load",
   // chat title
   TITLE: "title",
+  FILES: "files",
   // for seeding chats
   SEEDED: "seeded",
   SEND_ON_LOAD: "send-on-load",
diff --git a/web/src/app/components/nrf/SettingsPanel.tsx b/web/src/app/components/nrf/SettingsPanel.tsx
new file mode 100644
index 000000000..c61c35271
--- /dev/null
+++ b/web/src/app/components/nrf/SettingsPanel.tsx
@@ -0,0 +1,179 @@
+import React from "react";
+import { Switch } from "@/components/ui/switch";
+import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
+import { Label } from "@/components/ui/label";
+import { useNRFPreferences } from "../../../components/context/NRFPreferencesContext";
+import {
+  darkExtensionImages,
+  lightExtensionImages,
+} from "@/lib/extension/constants";
+
+const SidebarSwitch = ({
+  checked,
+  onCheckedChange,
+  label,
+}: {
+  checked: boolean;
+  onCheckedChange: (checked: boolean) => void;
+  label: string;
+}) => (
+  <div className="flex justify-between items-center py-2">
+    <span className="text-sm text-gray-300">{label}</span>
+    <Switch
+      checked={checked}
+      onCheckedChange={onCheckedChange}
+      className="data-[state=checked]:bg-white data-[state=checked]:border-neutral-200 data-[state=unchecked]:bg-gray-600"
+      circleClassName="data-[state=checked]:bg-neutral-200"
+    />
+  </div>
+);
+
+const RadioOption = ({
+  value,
+  label,
+  description,
+  groupValue,
+  onChange,
+}: {
+  value: string;
+  label: string;
+  description: string;
+  groupValue: string;
+  onChange: (value: string) => void;
+}) => (
+  <div className="flex items-start space-x-2 mb-2">
+    <RadioGroupItem
+      value={value}
+      id={value}
+      className="mt-1 border border-gray-600 data-[state=checked]:border-white data-[state=checked]:bg-white"
+    />
+    <Label htmlFor={value} className="flex flex-col">
+      <span className="text-sm text-gray-300">{label}</span>
+      {description && (
+        <span className="text-xs text-gray-500">{description}</span>
+      )}
+    </Label>
+  </div>
+);
+
+export const SettingsPanel = ({
+  settingsOpen,
+  toggleSettings,
+  handleUseOnyxToggle,
+}: {
+  settingsOpen: boolean;
+  toggleSettings: () => void;
+  handleUseOnyxToggle: (checked: boolean) => void;
+}) => {
+  const {
+    theme,
+    setTheme,
+    defaultLightBackgroundUrl,
+    setDefaultLightBackgroundUrl,
+    defaultDarkBackgroundUrl,
+    setDefaultDarkBackgroundUrl,
+    useOnyxAsNewTab,
+    showShortcuts,
+    setShowShortcuts,
+  } = useNRFPreferences();
+
+  const toggleTheme = (newTheme: string) => {
+    setTheme(newTheme);
+  };
+
+  const updateBackgroundUrl = (url: string) => {
+    if (theme === "light") {
+      setDefaultLightBackgroundUrl(url);
+    } else {
+      setDefaultDarkBackgroundUrl(url);
+    }
+  };
+
+  return (
+    <div
+      className="fixed top-0 right-0 w-[360px] h-full bg-[#202124] text-gray-300 overflow-y-auto z-20 transition-transform duration-300 ease-in-out transform"
+      style={{
+        transform: settingsOpen ? "translateX(0)" : "translateX(100%)",
+        boxShadow: "-2px 0 10px rgba(0,0,0,0.3)",
+      }}
+    >
+      <div className="p-6">
+        <div className="flex justify-between items-center mb-6">
+          <h2 className="text-xl font-semibold text-white">
+            Home page settings
+          </h2>
+          <button
+            aria-label="Close"
+            onClick={toggleSettings}
+            className="text-gray-400 hover:text-white"
+          >
+            ✕
+          </button>
+        </div>
+
+        <h3 className="text-sm font-semibold mb-2">General</h3>
+        <SidebarSwitch
+          checked={useOnyxAsNewTab}
+          onCheckedChange={handleUseOnyxToggle}
+          label="Use Onyx as new tab page"
+        />
+
+        <SidebarSwitch
+          checked={showShortcuts}
+          onCheckedChange={setShowShortcuts}
+          label="Show bookmarks"
+        />
+
+        <h3 className="text-sm font-semibold mt-6 mb-2">Theme</h3>
+        <RadioGroup
+          value={theme}
+          onValueChange={toggleTheme}
+          className="space-y-2"
+        >
+          <RadioOption
+            value="light"
+            label="Light theme"
+            description="Light theme"
+            groupValue={theme}
+            onChange={toggleTheme}
+          />
+          <RadioOption
+            value="dark"
+            label="Dark theme"
+            description="Dark theme"
+            groupValue={theme}
+            onChange={toggleTheme}
+          />
+        </RadioGroup>
+
+        <h3 className="text-sm font-semibold mt-6 mb-2">Background</h3>
+        <div className="grid grid-cols-4 gap-2">
+          {(theme === "dark" ? darkExtensionImages : lightExtensionImages).map(
+            (bg: string, index: number) => (
+              <div
+                key={bg}
+                onClick={() => updateBackgroundUrl(bg)}
+                className={`relative ${
+                  index === 0 ? "col-span-2 row-span-2" : ""
+                } cursor-pointer rounded-sm overflow-hidden`}
+                style={{
+                  paddingBottom: index === 0 ? "100%" : "50%",
+                }}
+              >
+                <div
+                  className="absolute inset-0 bg-cover bg-center"
+                  style={{ backgroundImage: `url(${bg})` }}
+                />
+                {(theme === "light"
+                  ? defaultLightBackgroundUrl
+                  : defaultDarkBackgroundUrl) === bg && (
+                  <div className="absolute inset-0 border-2 border-blue-400 rounded" />
+                )}
+              </div>
+            )
+          )}
+        </div>
+      </div>
+    </div>
+  );
+};
diff --git a/web/src/app/components/nrf/ShortcutsDisplay.tsx b/web/src/app/components/nrf/ShortcutsDisplay.tsx
new file mode 100644
index 000000000..ff0d0b64d
--- /dev/null
+++ b/web/src/app/components/nrf/ShortcutsDisplay.tsx
@@ -0,0 +1,46 @@
+"use client";
+import React from "react";
+import { ShortCut, AddShortCut } from "@/components/extension/Shortcuts";
+import { Shortcut } from "@/app/chat/nrf/interfaces";
+
+interface ShortcutsDisplayProps {
+  shortCuts: Shortcut[];
+  showShortcuts: boolean;
+  setEditingShortcut: (shortcut: Shortcut | null) => void;
+  setShowShortCutModal: (show: boolean) => void;
+  openShortCutModal: () => void;
+}
+
+export const ShortcutsDisplay: React.FC<ShortcutsDisplayProps> = ({
+  shortCuts,
+  showShortcuts,
+  setEditingShortcut,
+  setShowShortCutModal,
+  openShortCutModal,
+}) => {
+  return (
+    <div
+      className={`
+        mx-auto flex flex-wrap justify-center gap-x-6 gap-y-4 mt-12
+        transition-all duration-700 ease-in-out
+        ${
+          showShortcuts
+            ? "opacity-100 max-h-[500px]"
+            : "opacity-0 max-h-0 overflow-hidden pointer-events-none"
+        }
+      `}
+    >
+      {shortCuts.map((shortCut: Shortcut, index: number) => (
+        <ShortCut
+          key={index}
+          onEdit={() => {
+            setEditingShortcut(shortCut);
+            setShowShortCutModal(true);
+          }}
+          shortCut={shortCut}
+        />
+      ))}
+      <AddShortCut openShortCutModal={openShortCutModal} />
+    </div>
+  );
+};
diff --git a/web/src/app/ee/Hori b/web/src/app/ee/Hori
new file mode 100644
index 000000000..e69de29bb
diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx
index c8d15792f..b5ff9d70a 100644
--- a/web/src/app/layout.tsx
+++ b/web/src/app/layout.tsx
@@ -13,7 +13,6 @@ import { Metadata } from "next";
 import { buildClientUrl } from "@/lib/utilsSS";
 import { Inter } from "next/font/google";
 import { EnterpriseSettings, GatingType } from "./admin/settings/interfaces";
-import { HeaderTitle } from "@/components/header/HeaderTitle";
 import { fetchAssistantData } from "@/lib/chat/fetchAssistantdata";
 import { AppProvider } from "@/components/context/AppProvider";
 import { PHProvider } from "./providers";
diff --git a/web/src/components/assistants/AssistantIcon.tsx b/web/src/components/assistants/AssistantIcon.tsx
index df1ac725b..b753ebd1c 100644
--- a/web/src/components/assistants/AssistantIcon.tsx
+++ b/web/src/components/assistants/AssistantIcon.tsx
@@ -33,6 +33,7 @@ export function AssistantIcon({
 
   return (
     <CustomTooltip
+      className="hidden lg:block"
       disabled={disableToolip || !assistant.description}
       showTick
       line
diff --git a/web/src/components/chat_search/AssistantSelector.tsx b/web/src/components/chat_search/AssistantSelector.tsx
index 6192a93f7..4949fb363 100644
--- a/web/src/components/chat_search/AssistantSelector.tsx
+++ b/web/src/components/chat_search/AssistantSelector.tsx
@@ -66,10 +66,12 @@ const AssistantSelector = ({
   const [isTemperatureExpanded, setIsTemperatureExpanded] = useState(false);
 
   // Initialize selectedTab from localStorage
-  const [selectedTab, setSelectedTab] = useState<number>(() => {
+  const [selectedTab, setSelectedTab] = useState<number | undefined>();
+  useEffect(() => {
     const storedTab = localStorage.getItem("assistantSelectorSelectedTab");
-    return storedTab !== null ? Number(storedTab) : 0;
-  });
+    const tab = storedTab !== null ? Number(storedTab) : 0;
+    setSelectedTab(tab);
+  }, [localStorage]);
 
   const sensors = useSensors(
     useSensor(PointerSensor, {
diff --git a/web/src/components/chat_search/hooks.ts b/web/src/components/chat_search/hooks.ts
index e1cb683f8..e60a99617 100644
--- a/web/src/components/chat_search/hooks.ts
+++ b/web/src/components/chat_search/hooks.ts
@@ -72,15 +72,20 @@ export const useSidebarVisibility = ({
     };
 
     const handleMouseLeave = () => {
-      setShowDocSidebar(false);
+      if (!mobile) {
+        setShowDocSidebar(false);
+      }
     };
-
-    document.addEventListener("mousemove", handleEvent);
-    document.addEventListener("mouseleave", handleMouseLeave);
+    if (!mobile) {
+      document.addEventListener("mousemove", handleEvent);
+      document.addEventListener("mouseleave", handleMouseLeave);
+    }
 
     return () => {
-      document.removeEventListener("mousemove", handleEvent);
-      document.removeEventListener("mouseleave", handleMouseLeave);
+      if (!mobile) {
+        document.removeEventListener("mousemove", handleEvent);
+        document.removeEventListener("mouseleave", handleMouseLeave);
+      }
     };
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [showDocSidebar, toggledSidebar, sidebarElementRef, mobile]);
diff --git a/web/src/components/context/NRFPreferencesContext.tsx b/web/src/components/context/NRFPreferencesContext.tsx
new file mode 100644
index 000000000..a78f5bfde
--- /dev/null
+++ b/web/src/components/context/NRFPreferencesContext.tsx
@@ -0,0 +1,123 @@
+"use client";
+
+import React, { createContext, useContext, useState, useEffect } from "react";
+import { Shortcut } from "@/app/chat/nrf/interfaces";
+import { notifyExtensionOfThemeChange } from "@/lib/extension/utils";
+import {
+  darkExtensionImages,
+  lightExtensionImages,
+  LocalStorageKeys,
+} from "@/lib/extension/constants";
+
+interface NRFPreferencesContextValue {
+  theme: string;
+  setTheme: (t: string) => void;
+  defaultLightBackgroundUrl: string;
+  setDefaultLightBackgroundUrl: (val: string) => void;
+  defaultDarkBackgroundUrl: string;
+  setDefaultDarkBackgroundUrl: (val: string) => void;
+  shortcuts: Shortcut[];
+  setShortcuts: (s: Shortcut[]) => void;
+  useOnyxAsNewTab: boolean;
+  setUseOnyxAsNewTab: (v: boolean) => void;
+  showShortcuts: boolean;
+  setShowShortcuts: (v: boolean) => void;
+}
+
+const NRFPreferencesContext = createContext<
+  NRFPreferencesContextValue | undefined
+>(undefined);
+
+function useLocalStorageState<T>(
+  key: string,
+  defaultValue: T
+): [T, (value: T) => void] {
+  const [state, setState] = useState<T>(() => {
+    if (typeof window !== "undefined") {
+      const storedValue = localStorage.getItem(key);
+      return storedValue ? JSON.parse(storedValue) : defaultValue;
+    }
+    return undefined;
+  });
+
+  const setValue = (value: T) => {
+    setState(value);
+    if (typeof window !== "undefined") {
+      localStorage.setItem(key, JSON.stringify(value));
+    }
+  };
+
+  return [state, setValue];
+}
+
+export function NRFPreferencesProvider({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  const [theme, setTheme] = useLocalStorageState<string>(
+    LocalStorageKeys.THEME,
+    "dark"
+  );
+  const [defaultLightBackgroundUrl, setDefaultLightBackgroundUrl] =
+    useLocalStorageState<string>(
+      LocalStorageKeys.LIGHT_BG_URL,
+      lightExtensionImages[0]
+    );
+  const [defaultDarkBackgroundUrl, setDefaultDarkBackgroundUrl] =
+    useLocalStorageState<string>(
+      LocalStorageKeys.DARK_BG_URL,
+      darkExtensionImages[0]
+    );
+  const [shortcuts, setShortcuts] = useLocalStorageState<Shortcut[]>(
+    LocalStorageKeys.SHORTCUTS,
+    []
+  );
+  const [showShortcuts, setShowShortcuts] = useLocalStorageState<boolean>(
+    LocalStorageKeys.SHOW_SHORTCUTS,
+    false
+  );
+  const [useOnyxAsNewTab, setUseOnyxAsNewTab] = useLocalStorageState<boolean>(
+    LocalStorageKeys.USE_ONYX_AS_NEW_TAB,
+    true
+  );
+
+  useEffect(() => {
+    if (theme === "dark") {
+      notifyExtensionOfThemeChange(theme, defaultDarkBackgroundUrl);
+    } else {
+      notifyExtensionOfThemeChange(theme, defaultLightBackgroundUrl);
+    }
+  }, [theme, defaultLightBackgroundUrl, defaultDarkBackgroundUrl]);
+
+  return (
+    <NRFPreferencesContext.Provider
+      value={{
+        theme,
+        setTheme,
+        defaultLightBackgroundUrl,
+        setDefaultLightBackgroundUrl,
+        defaultDarkBackgroundUrl,
+        setDefaultDarkBackgroundUrl,
+        shortcuts,
+        setShortcuts,
+        useOnyxAsNewTab,
+        setUseOnyxAsNewTab,
+        showShortcuts,
+        setShowShortcuts,
+      }}
+    >
+      {children}
+    </NRFPreferencesContext.Provider>
+  );
+}
+
+export function useNRFPreferences() {
+  const context = useContext(NRFPreferencesContext);
+  if (!context) {
+    throw new Error(
+      "useNRFPreferences must be used within an NRFPreferencesProvider"
+    );
+  }
+  return context;
+}
diff --git a/web/src/components/extension/Shortcuts.tsx b/web/src/components/extension/Shortcuts.tsx
new file mode 100644
index 000000000..a1581c792
--- /dev/null
+++ b/web/src/components/extension/Shortcuts.tsx
@@ -0,0 +1,257 @@
+import { useState, useEffect } from "react";
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import { Label } from "@/components/ui/label";
+import { Shortcut } from "@/app/chat/nrf/interfaces";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { PencilIcon, PlusIcon } from "lucide-react";
+import Image from "next/image";
+import { PopupSpec } from "@/components/admin/connectors/Popup";
+import { Modal } from "../Modal";
+import { QuestionMarkIcon } from "../icons/icons";
+
+export const validateUrl = (input: string) => {
+  try {
+    new URL(input);
+    return true;
+  } catch {
+    return false;
+  }
+};
+
+export const ShortCut = ({
+  shortCut,
+  onEdit,
+}: {
+  shortCut: Shortcut;
+  onEdit: (shortcut: Shortcut) => void;
+}) => {
+  const [faviconError, setFaviconError] = useState(false);
+
+  return (
+    <div className="w-24 h-24 flex-none rounded-xl shadow-lg relative group transition-all duration-300 ease-in-out hover:scale-105 bg-white/10 backdrop-blur-sm">
+      <button
+        onClick={(e) => {
+          e.stopPropagation();
+          onEdit(shortCut);
+        }}
+        className="absolute top-1 right-1 p-1 bg-white/20 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-200"
+      >
+        <PencilIcon className="w-3 h-3 text-white" />
+      </button>
+      <div
+        onClick={() => window.open(shortCut.url, "_blank")}
+        className="w-full h-full flex flex-col items-center justify-center cursor-pointer"
+      >
+        <div className="w-8 h-8 mb-2 relative">
+          {shortCut.favicon && !faviconError ? (
+            <Image
+              src={shortCut.favicon}
+              alt={shortCut.name}
+              width={40}
+              height={40}
+              className="rounded-sm"
+              onError={() => setFaviconError(true)}
+            />
+          ) : (
+            <QuestionMarkIcon size={32} className="text-white w-full h-full" />
+          )}
+        </div>
+        <h1 className="text-white w-full text-center font-semibold text-sm truncate px-2">
+          {shortCut.name}
+        </h1>
+      </div>
+    </div>
+  );
+};
+
+export const AddShortCut = ({
+  openShortCutModal,
+}: {
+  openShortCutModal: () => void;
+}) => {
+  return (
+    <button
+      onClick={openShortCutModal}
+      className="w-24 h-24 flex-none rounded-xl bg-white/10 hover:bg-white/20 backdrop-blur-sm transition-all duration-300 ease-in-out flex flex-col items-center justify-center"
+    >
+      <PlusIcon className="w-8 h-8 text-white mb-2" />
+      <h1 className="text-white text-xs font-medium">New Bookmark</h1>
+    </button>
+  );
+};
+
+export const NewShortCutModal = ({
+  isOpen,
+  onClose,
+  onAdd,
+  editingShortcut,
+  onDelete,
+  setPopup,
+}: {
+  isOpen: boolean;
+  onClose: () => void;
+  onDelete: (shortcut: Shortcut) => void;
+  onAdd: (shortcut: Shortcut) => void;
+  editingShortcut?: Shortcut | null;
+  setPopup: (popup: PopupSpec) => void;
+}) => {
+  const [name, setName] = useState(editingShortcut?.name || "");
+  const [url, setUrl] = useState(editingShortcut?.url || "");
+  const [faviconError, setFaviconError] = useState(false);
+  const [isValidUrl, setIsValidUrl] = useState(false);
+
+  const handleSubmit = (e: React.FormEvent) => {
+    e.preventDefault();
+    if (isValidUrl) {
+      const faviconUrl = `https://www.google.com/s2/favicons?domain=${
+        new URL(url).hostname
+      }&sz=64`;
+      onAdd({ name, url, favicon: faviconUrl });
+      onClose();
+    } else {
+      console.error("Invalid URL submitted");
+      setPopup({
+        type: "error",
+        message: "Please enter a valid URL",
+      });
+    }
+  };
+
+  const handleUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const newUrl = e.target.value;
+    setUrl(newUrl);
+    setIsValidUrl(validateUrl(newUrl));
+    setFaviconError(false);
+  };
+
+  useEffect(() => {
+    setIsValidUrl(validateUrl(url));
+  }, [url]);
+
+  const faviconUrl = isValidUrl
+    ? `https://www.google.com/s2/favicons?domain=${new URL(url).hostname}&sz=64`
+    : "";
+
+  return (
+    <Dialog open={isOpen} onOpenChange={onClose}>
+      <DialogContent className="max-w-[95%] sm:max-w-[425px] bg-neutral-900 border-none text-white">
+        <DialogHeader>
+          <DialogTitle>
+            {editingShortcut ? "Edit Shortcut" : "Add New Shortcut"}
+          </DialogTitle>
+          <DialogDescription>
+            {editingShortcut
+              ? "Modify your existing shortcut."
+              : "Create a new shortcut for quick access to your favorite websites."}
+          </DialogDescription>
+        </DialogHeader>
+        <form onSubmit={handleSubmit} className="w-full space-y-6">
+          <div className="space-y-4 w-full">
+            <div className="flex flex-col space-y-2">
+              <Label
+                htmlFor="name"
+                className="text-sm font-medium text-neutral-300"
+              >
+                Name
+              </Label>
+              <Input
+                id="name"
+                value={name}
+                onChange={(e) => setName(e.target.value)}
+                className="w-full bg-neutral-800 border-neutral-700 text-white"
+                placeholder="Enter shortcut name"
+              />
+            </div>
+            <div className="flex flex-col space-y-2">
+              <Label
+                htmlFor="url"
+                className="text-sm font-medium text-neutral-300"
+              >
+                URL
+              </Label>
+              <Input
+                id="url"
+                value={url}
+                onChange={handleUrlChange}
+                className={`bg-neutral-800 border-neutral-700 text-white ${
+                  !isValidUrl && url ? "border-red-500" : ""
+                }`}
+                placeholder="https://example.com"
+              />
+              {!isValidUrl && url && (
+                <p className="text-red-500 text-sm">Please enter a valid URL</p>
+              )}
+            </div>
+            <div className="flex items-center space-x-2">
+              <Label className="text-sm font-medium text-neutral-300">
+                Favicon Preview:
+              </Label>
+              <div className="w-8 h-8 relative flex items-center justify-center">
+                {isValidUrl && !faviconError ? (
+                  <Image
+                    src={faviconUrl}
+                    alt="Favicon"
+                    width={32}
+                    height={32}
+                    className="w-full h-full rounded-sm"
+                    onError={() => setFaviconError(true)}
+                  />
+                ) : (
+                  <QuestionMarkIcon size={32} className="w-full h-full" />
+                )}
+              </div>
+            </div>
+          </div>
+          <DialogFooter>
+            <Button
+              type="submit"
+              className="bg-blue-600 hover:bg-blue-700 text-white"
+              disabled={!isValidUrl || !name}
+            >
+              {editingShortcut ? "Save Changes" : "Add Shortcut"}
+            </Button>
+            {editingShortcut && (
+              <Button
+                type="button"
+                variant="destructive"
+                onClick={() => onDelete(editingShortcut)}
+              >
+                Delete
+              </Button>
+            )}
+          </DialogFooter>
+        </form>
+      </DialogContent>
+    </Dialog>
+  );
+};
+
+export const MaxShortcutsReachedModal = ({
+  onClose,
+}: {
+  onClose: () => void;
+}) => {
+  return (
+    <Modal
+      width="max-w-md"
+      title="Maximum Shortcuts Reached"
+      onOutsideClick={onClose}
+    >
+      <div className="flex flex-col gap-4">
+        <p className="text-left text-neutral-900">
+          You&apos;ve reached the maximum limit of 8 shortcuts. To add a new
+          shortcut, please remove an existing one.
+        </p>
+        <Button onClick={onClose}>Close</Button>
+      </div>
+    </Modal>
+  );
+};
diff --git a/web/src/components/header/LogoWithText.tsx b/web/src/components/header/LogoWithText.tsx
index 90417cf67..c469f231e 100644
--- a/web/src/components/header/LogoWithText.tsx
+++ b/web/src/components/header/LogoWithText.tsx
@@ -119,7 +119,7 @@ export default function LogoWithText({
           <Tooltip>
             <TooltipTrigger asChild>
               <button
-                className="mr-3 my-auto ml-auto"
+                className="mr-3  my-auto ml-auto"
                 onClick={() => {
                   toggleSidebar();
                   if (toggled) {
@@ -138,7 +138,7 @@ export default function LogoWithText({
                 />
               </button>
             </TooltipTrigger>
-            <TooltipContent>
+            <TooltipContent className="!border-none">
               {toggled ? `Unpin sidebar` : "Pin sidebar"}
             </TooltipContent>
           </Tooltip>
diff --git a/web/src/components/icons/icons.tsx b/web/src/components/icons/icons.tsx
index 15320d170..dd8b425ca 100644
--- a/web/src/components/icons/icons.tsx
+++ b/web/src/components/icons/icons.tsx
@@ -2799,3 +2799,24 @@ export const AirtableIcon = ({
     </div>
   );
 };
+
+export const QuestionMarkIcon = ({
+  size = 16,
+  className = defaultTailwindCSS,
+}: IconProps) => (
+  <svg
+    style={{ width: `${size}px`, height: `${size}px` }}
+    className={`w-[${size}px] h-[${size}px] ` + className}
+    xmlns="http://www.w3.org/2000/svg"
+    viewBox="0 0 24 24"
+    fill="none"
+    stroke="currentColor"
+    strokeWidth="2"
+    strokeLinecap="round"
+    strokeLinejoin="round"
+  >
+    <circle cx="12" cy="12" r="10" />
+    <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
+    <line x1="12" y1="17" x2="12.01" y2="17" />
+  </svg>
+);
diff --git a/web/src/components/initialSetup/welcome/WelcomeModalWrapper.tsx b/web/src/components/initialSetup/welcome/WelcomeModalWrapper.tsx
index 3deaab065..5ca97fb6a 100644
--- a/web/src/components/initialSetup/welcome/WelcomeModalWrapper.tsx
+++ b/web/src/components/initialSetup/welcome/WelcomeModalWrapper.tsx
@@ -1,4 +1,3 @@
-import { cookies } from "next/headers";
 import {
   _CompletedWelcomeFlowDummyComponent,
   _WelcomeModal,
diff --git a/web/src/components/llm/ApiKeyModal.tsx b/web/src/components/llm/ApiKeyModal.tsx
index 775c6ac9c..af37098ea 100644
--- a/web/src/components/llm/ApiKeyModal.tsx
+++ b/web/src/components/llm/ApiKeyModal.tsx
@@ -10,7 +10,7 @@ export const ApiKeyModal = ({
   hide,
   setPopup,
 }: {
-  hide: () => void;
+  hide?: () => void;
   setPopup: (popup: PopupSpec) => void;
 }) => {
   const router = useRouter();
@@ -28,18 +28,25 @@ export const ApiKeyModal = ({
     <Modal
       title="Configure a Generative AI Model"
       width="max-w-3xl w-full"
-      onOutsideClick={() => hide()}
+      onOutsideClick={hide ? () => hide() : undefined}
     >
       <>
         <div className="mb-5 text-sm text-gray-700">
           Please provide an API Key – you can always change this or switch
           models later.
           <br />
-          If you would rather look around first, you can{" "}
-          <strong onClick={() => hide()} className="text-link cursor-pointer">
-            skip this step
-          </strong>
-          .
+          {hide && (
+            <>
+              If you would rather look around first, you can{" "}
+              <strong
+                onClick={() => hide()}
+                className="text-link cursor-pointer"
+              >
+                skip this step
+              </strong>
+              .
+            </>
+          )}
         </div>
 
         <ApiKeyForm
@@ -47,7 +54,7 @@ export const ApiKeyModal = ({
           onSuccess={() => {
             router.refresh();
             refreshProviderInfo();
-            hide();
+            hide?.();
           }}
           providerOptions={providerOptions}
         />
diff --git a/web/src/components/popup/PopupFromQuery.tsx b/web/src/components/popup/PopupFromQuery.tsx
index 71834f699..6c3cde6fc 100644
--- a/web/src/components/popup/PopupFromQuery.tsx
+++ b/web/src/components/popup/PopupFromQuery.tsx
@@ -14,8 +14,10 @@ export const usePopupFromQuery = (messages: PopupMessages) => {
 
   useEffect(() => {
     const searchParams = new URLSearchParams(window.location.search);
+
     // Get the value for search param with key "message"
     const messageValue = searchParams.get("message");
+
     // Check if any key from messages object is present in search params
     if (messageValue && messageValue in messages) {
       const popupMessage = messages[messageValue];
diff --git a/web/src/components/search/filtering/FilterDropdown.tsx b/web/src/components/search/filtering/FilterDropdown.tsx
index 431c92fbb..a7f95420d 100644
--- a/web/src/components/search/filtering/FilterDropdown.tsx
+++ b/web/src/components/search/filtering/FilterDropdown.tsx
@@ -5,6 +5,7 @@ interface Option {
   key: string;
   display: string | JSX.Element;
   displayName?: string;
+  icon?: JSX.Element;
 }
 export function FilterDropdown({
   options,
@@ -65,6 +66,7 @@ export function FilterDropdown({
                       flex-none
                       w-fit
                       text-emphasis
+                      items-center
                       gap-x-1
                       ${dropdownColor || "bg-background"}
                       hover:bg-hover
@@ -80,6 +82,7 @@ export function FilterDropdown({
                     event.stopPropagation();
                   }}
                 >
+                  {option.icon}
                   {option.display}
                   {isSelected && (
                     <div className="ml-auto my-auto mr-1">
diff --git a/web/src/components/search/filtering/HorizontalSourceSelector.tsx b/web/src/components/search/filtering/HorizontalSourceSelector.tsx
new file mode 100644
index 000000000..da59fd01e
--- /dev/null
+++ b/web/src/components/search/filtering/HorizontalSourceSelector.tsx
@@ -0,0 +1,226 @@
+import React from "react";
+import {
+  Popover,
+  PopoverTrigger,
+  PopoverContent,
+} from "@/components/ui/popover"; // shadcn popover
+import { FiBook, FiMap, FiTag, FiCalendar } from "react-icons/fi";
+import { SourceMetadata } from "@/lib/search/interfaces";
+import { Calendar } from "@/components/ui/calendar"; // or wherever your Calendar component lives
+import { FilterDropdown } from "@/components/search/filtering/FilterDropdown";
+import { listSourceMetadata } from "@/lib/sources";
+import { getDateRangeString } from "@/lib/dateUtils";
+import { DateRangePickerValue } from "../../../app/ee/admin/performance/DateRangeSelector";
+import { Tag } from "@/lib/types";
+import { SourceIcon } from "@/components/SourceIcon";
+export interface SourceSelectorProps {
+  timeRange: DateRangePickerValue | null;
+  setTimeRange: React.Dispatch<
+    React.SetStateAction<DateRangePickerValue | null>
+  >;
+  selectedSources: SourceMetadata[];
+  setSelectedSources: React.Dispatch<React.SetStateAction<SourceMetadata[]>>;
+  selectedDocumentSets: string[];
+  setSelectedDocumentSets: React.Dispatch<React.SetStateAction<string[]>>;
+  selectedTags: Tag[];
+  setSelectedTags: React.Dispatch<React.SetStateAction<Tag[]>>;
+  existingSources: string[]; // e.g. list of internalName that exist
+  availableDocumentSets: { name: string }[];
+  availableTags: Tag[];
+}
+
+export function HorizontalSourceSelector({
+  timeRange,
+  setTimeRange,
+  selectedSources,
+  setSelectedSources,
+  selectedDocumentSets,
+  setSelectedDocumentSets,
+  selectedTags,
+  setSelectedTags,
+  existingSources,
+  availableDocumentSets,
+  availableTags,
+}: SourceSelectorProps) {
+  const handleSourceSelect = (source: SourceMetadata) => {
+    setSelectedSources((prev: SourceMetadata[]) => {
+      if (prev.map((s) => s.internalName).includes(source.internalName)) {
+        return prev.filter((s) => s.internalName !== source.internalName);
+      } else {
+        return [...prev, source];
+      }
+    });
+  };
+
+  const handleDocumentSetSelect = (documentSetName: string) => {
+    setSelectedDocumentSets((prev: string[]) => {
+      if (prev.includes(documentSetName)) {
+        return prev.filter((s) => s !== documentSetName);
+      } else {
+        return [...prev, documentSetName];
+      }
+    });
+  };
+
+  const handleTagSelect = (tag: Tag) => {
+    setSelectedTags((prev: Tag[]) => {
+      if (
+        prev.some(
+          (t) => t.tag_key === tag.tag_key && t.tag_value === tag.tag_value
+        )
+      ) {
+        return prev.filter(
+          (t) => !(t.tag_key === tag.tag_key && t.tag_value === tag.tag_value)
+        );
+      } else {
+        return [...prev, tag];
+      }
+    });
+  };
+
+  const resetSources = () => {
+    setSelectedSources([]);
+  };
+
+  const resetDocuments = () => {
+    setSelectedDocumentSets([]);
+  };
+
+  const resetTags = () => {
+    setSelectedTags([]);
+  };
+
+  return (
+    <div className="flex flex-row flex-wrap items-center space-x-2">
+      {/* Date Range Popover */}
+      <Popover>
+        <PopoverTrigger asChild>
+          <button
+            className="
+              flex items-center space-x-1 border 
+              border-border rounded-lg px-3 py-1.5 
+              hover:bg-hover text-sm cursor-pointer
+              bg-background-search-filter
+            "
+          >
+            <FiCalendar size={14} />
+            <span>
+              {timeRange?.from
+                ? getDateRangeString(timeRange.from, timeRange.to)
+                : "Date Range"}
+            </span>
+          </button>
+        </PopoverTrigger>
+        <PopoverContent
+          className="bg-background-search-filter border border-border rounded-md z-[200] p-2"
+          align="start"
+        >
+          <Calendar
+            mode="range"
+            selected={
+              timeRange
+                ? { from: new Date(timeRange.from), to: new Date(timeRange.to) }
+                : undefined
+            }
+            onSelect={(daterange) => {
+              const initialDate = daterange?.from || new Date();
+              const endDate = daterange?.to || new Date();
+              setTimeRange({
+                from: initialDate,
+                to: endDate,
+                selectValue: timeRange?.selectValue || "",
+              });
+            }}
+            className="rounded-md"
+          />
+        </PopoverContent>
+      </Popover>
+
+      {/* Sources Popover */}
+      {existingSources.length > 0 && (
+        <FilterDropdown
+          icon={<FiMap size={14} />}
+          backgroundColor="bg-background-search-filter"
+          dropdownColor="bg-background-search-filter-dropdown"
+          dropdownWidth="w-40"
+          defaultDisplay="Sources"
+          resetValues={resetSources}
+          width="w-fit"
+          options={listSourceMetadata()
+            .filter((source) => existingSources.includes(source.internalName))
+            .map((source) => ({
+              icon: (
+                <SourceIcon sourceType={source.internalName} iconSize={14} />
+              ),
+              key: source.internalName,
+              display: (
+                <span className="flex items-center space-x-2">
+                  <span>{source.displayName}</span>
+                </span>
+              ),
+            }))}
+          optionClassName="truncate w-full break-all"
+          selected={selectedSources.map((src) => src.internalName)}
+          handleSelect={(option) => {
+            const s = listSourceMetadata().find(
+              (m) => m.internalName === option.key
+            );
+            if (s) handleSourceSelect(s);
+          }}
+        />
+      )}
+
+      {/* Document Sets Popover */}
+      {availableDocumentSets.length > 0 && (
+        <FilterDropdown
+          icon={<FiBook size={14} />}
+          backgroundColor="bg-background-search-filter"
+          dropdownColor="bg-background-search-filter-dropdown"
+          dropdownWidth="w-40"
+          defaultDisplay="Sets"
+          resetValues={resetDocuments}
+          width="w-fit"
+          options={availableDocumentSets.map((docSet) => ({
+            key: docSet.name,
+            display: <>{docSet.name}</>,
+          }))}
+          optionClassName="truncate w-full break-all"
+          selected={selectedDocumentSets}
+          handleSelect={(option) => handleDocumentSetSelect(option.key)}
+        />
+      )}
+
+      {/* Tags Popover */}
+      {availableTags.length > 0 && (
+        <FilterDropdown
+          icon={<FiTag size={14} />}
+          backgroundColor="bg-background-search-filter"
+          dropdownColor="bg-background-search-filter-dropdown"
+          dropdownWidth="w-64"
+          defaultDisplay="Tags"
+          resetValues={resetTags}
+          width="w-fit"
+          options={availableTags.map((tag) => ({
+            key: `${tag.tag_key}=${tag.tag_value}`,
+            display: (
+              <span className="text-sm">
+                {tag.tag_key}
+                <b>=</b>
+                {tag.tag_value}
+              </span>
+            ),
+          }))}
+          optionClassName="truncate w-full break-all"
+          selected={selectedTags.map((t) => `${t.tag_key}=${t.tag_value}`)}
+          handleSelect={(option) => {
+            const [tKey, tValue] = option.key.split("=");
+            const foundTag = availableTags.find(
+              (tg) => tg.tag_key === tKey && tg.tag_value === tValue
+            );
+            if (foundTag) handleTagSelect(foundTag);
+          }}
+        />
+      )}
+    </div>
+  );
+}
diff --git a/web/src/components/search/results/Citation.tsx b/web/src/components/search/results/Citation.tsx
index 941136863..79d969a0d 100644
--- a/web/src/components/search/results/Citation.tsx
+++ b/web/src/components/search/results/Citation.tsx
@@ -12,14 +12,12 @@ import { openDocument } from "@/lib/search/utils";
 
 export function Citation({
   children,
-  link,
   document,
   index,
   updatePresentingDocument,
   icon,
   url,
 }: {
-  link?: string;
   children?: JSX.Element | string | null | ReactNode;
   index?: number;
   updatePresentingDocument: (document: OnyxDocument) => void;
diff --git a/web/src/components/tooltip/CustomTooltip.tsx b/web/src/components/tooltip/CustomTooltip.tsx
index 001b9fdf5..669fe811a 100644
--- a/web/src/components/tooltip/CustomTooltip.tsx
+++ b/web/src/components/tooltip/CustomTooltip.tsx
@@ -48,6 +48,7 @@ export const CustomTooltip = ({
   delay = 500,
   position = "bottom",
   disabled = false,
+  className,
 }: {
   medium?: boolean;
   content: string | ReactNode;
@@ -61,6 +62,7 @@ export const CustomTooltip = ({
   citation?: boolean;
   position?: "top" | "bottom";
   disabled?: boolean;
+  className?: string;
 }) => {
   const [isVisible, setIsVisible] = useState(false);
   const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
@@ -115,7 +117,7 @@ export const CustomTooltip = ({
     <>
       <span
         ref={triggerRef}
-        className="relative inline-block"
+        className={`relative inline-block ${className}`}
         onMouseEnter={showTooltip}
         onMouseLeave={hideTooltip}
       >
@@ -125,9 +127,11 @@ export const CustomTooltip = ({
         !disabled &&
         createPortal(
           <div
-            className={`min-w-8 fixed z-[1000] ${
-              citation ? "max-w-[350px]" : "w-40"
-            } ${large ? (medium ? "w-88" : "w-96") : line && "max-w-64 w-auto"} 
+            className={`min-w-8 fixed z-[1000]
+              ${className}
+              ${citation ? "max-w-[350px]" : "w-40"} ${
+                large ? (medium ? "w-88" : "w-96") : line && "max-w-64 w-auto"
+              } 
             transform -translate-x-1/2 text-sm 
             ${
               light
diff --git a/web/src/components/ui/label.tsx b/web/src/components/ui/label.tsx
new file mode 100644
index 000000000..40378d43a
--- /dev/null
+++ b/web/src/components/ui/label.tsx
@@ -0,0 +1,26 @@
+"use client";
+
+import * as React from "react";
+import * as LabelPrimitive from "@radix-ui/react-label";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const labelVariants = cva(
+  "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+);
+
+const Label = React.forwardRef<
+  React.ElementRef<typeof LabelPrimitive.Root>,
+  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
+    VariantProps<typeof labelVariants>
+>(({ className, ...props }, ref) => (
+  <LabelPrimitive.Root
+    ref={ref}
+    className={cn(labelVariants(), className)}
+    {...props}
+  />
+));
+Label.displayName = LabelPrimitive.Root.displayName;
+
+export { Label };
diff --git a/web/src/components/ui/radio-group.tsx b/web/src/components/ui/radio-group.tsx
new file mode 100644
index 000000000..62bfe37f4
--- /dev/null
+++ b/web/src/components/ui/radio-group.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import * as React from "react";
+import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
+import { Circle } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+
+const RadioGroup = React.forwardRef<
+  React.ElementRef<typeof RadioGroupPrimitive.Root>,
+  React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
+>(({ className, ...props }, ref) => {
+  return (
+    <RadioGroupPrimitive.Root
+      className={cn("grid gap-2", className)}
+      {...props}
+      ref={ref}
+    />
+  );
+});
+RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
+
+const RadioGroupItem = React.forwardRef<
+  React.ElementRef<typeof RadioGroupPrimitive.Item>,
+  React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
+>(({ className, ...props }, ref) => {
+  return (
+    <RadioGroupPrimitive.Item
+      ref={ref}
+      className={cn(
+        "aspect-square h-4 w-4 rounded-full border border-neutral-200 border-neutral-900 text-neutral-900 ring-offset-white focus:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-neutral-800 dark:border-neutral-50 dark:text-neutral-50 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300",
+        className
+      )}
+      {...props}
+    >
+      <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
+        <Circle className="h-2.5 w-2.5 fill-current text-current" />
+      </RadioGroupPrimitive.Indicator>
+    </RadioGroupPrimitive.Item>
+  );
+});
+RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
+
+export { RadioGroup, RadioGroupItem };
diff --git a/web/src/components/ui/switch.tsx b/web/src/components/ui/switch.tsx
index 3a9298a76..0ff41a528 100644
--- a/web/src/components/ui/switch.tsx
+++ b/web/src/components/ui/switch.tsx
@@ -7,8 +7,10 @@ import { cn } from "@/lib/utils";
 
 const Switch = React.forwardRef<
   React.ElementRef<typeof SwitchPrimitives.Root>,
-  React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
->(({ className, ...props }, ref) => (
+  React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> & {
+    circleClassName?: string;
+  }
+>(({ circleClassName, className, ...props }, ref) => (
   <SwitchPrimitives.Root
     className={cn(
       "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-neutral-900 data-[state=unchecked]:bg-neutral-200 dark:focus-visible:ring-neutral-300 dark:focus-visible:ring-offset-neutral-950 dark:data-[state=checked]:bg-neutral-50 dark:data-[state=unchecked]:bg-neutral-800",
@@ -19,7 +21,8 @@ const Switch = React.forwardRef<
   >
     <SwitchPrimitives.Thumb
       className={cn(
-        "pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0 dark:bg-neutral-950"
+        "pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0 dark:bg-neutral-950",
+        circleClassName
       )}
     />
   </SwitchPrimitives.Root>
diff --git a/web/src/lib/chat/fetchChatData.ts b/web/src/lib/chat/fetchChatData.ts
index 1306f9c02..27596ed86 100644
--- a/web/src/lib/chat/fetchChatData.ts
+++ b/web/src/lib/chat/fetchChatData.ts
@@ -24,7 +24,10 @@ import {
   DOCUMENT_SIDEBAR_WIDTH_COOKIE_NAME,
 } from "@/components/resizable/constants";
 import { hasCompletedWelcomeFlowSS } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
-import { NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN } from "../constants";
+import {
+  NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN,
+  NEXT_PUBLIC_ENABLE_CHROME_EXTENSION,
+} from "../constants";
 import { redirect } from "next/navigation";
 
 interface FetchChatDataResult {
@@ -98,7 +101,9 @@ export async function fetchChatData(searchParams: {
       ? `${fullUrl}?${searchParamsString}`
       : fullUrl;
 
-    return redirect(`/auth/login?next=${encodeURIComponent(redirectUrl)}`);
+    if (!NEXT_PUBLIC_ENABLE_CHROME_EXTENSION) {
+      return redirect(`/auth/login?next=${encodeURIComponent(redirectUrl)}`);
+    }
   }
 
   if (user && !user.is_verified && authTypeMetadata?.requiresVerification) {
diff --git a/web/src/lib/chat/fetchSomeChatData.ts b/web/src/lib/chat/fetchSomeChatData.ts
index b363e233b..fb1dc5b40 100644
--- a/web/src/lib/chat/fetchSomeChatData.ts
+++ b/web/src/lib/chat/fetchSomeChatData.ts
@@ -91,6 +91,7 @@ export async function fetchSomeChatData(
   const authDisabled = authTypeMetadata?.authType === "disabled";
 
   let user: User | null = null;
+
   if (fetchOptions.includes("user")) {
     user = results.shift();
     if (!authDisabled && !user) {
diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts
index 20751fef0..d9b3f60b9 100644
--- a/web/src/lib/constants.ts
+++ b/web/src/lib/constants.ts
@@ -87,5 +87,8 @@ export const NEXT_PUBLIC_TEST_ENV =
 export const NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED =
   process.env.NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED?.toLowerCase() === "true";
 
-export const NEXT_PUBLIC_CLOUD_DOMAIN =
-  process.env.NEXT_PUBLIC_CLOUD_DOMAIN || "http://127.0.0.1:3000";
+export const NEXT_PUBLIC_ENABLE_CHROME_EXTENSION =
+  process.env.NEXT_PUBLIC_ENABLE_CHROME_EXTENSION?.toLowerCase() === "true";
+
+export const NEXT_PUBLIC_WEB_DOMAIN =
+  process.env.NEXT_PUBLIC_WEB_DOMAIN || "http://127.0.0.1:3000";
diff --git a/web/src/lib/dateUtils.ts b/web/src/lib/dateUtils.ts
index 57ea060e0..2f2de7038 100644
--- a/web/src/lib/dateUtils.ts
+++ b/web/src/lib/dateUtils.ts
@@ -1,4 +1,23 @@
-import { DateRangePickerValue } from "@/app/ee/admin/performance/DateRangeSelector";
+import { useEffect } from "react";
+import { useState } from "react";
+
+export const useNightTime = () => {
+  const [isNight, setIsNight] = useState(false);
+
+  useEffect(() => {
+    const checkNightTime = () => {
+      const currentHour = new Date().getHours();
+      setIsNight(currentHour >= 18 || currentHour < 6);
+    };
+
+    checkNightTime();
+    const interval = setInterval(checkNightTime, 60000); // Check every minute
+
+    return () => clearInterval(interval);
+  }, []);
+
+  return { isNight };
+};
 
 export function getXDaysAgo(daysAgo: number) {
   const today = new Date();
diff --git a/web/src/lib/extension/constants.ts b/web/src/lib/extension/constants.ts
new file mode 100644
index 000000000..2be72db1c
--- /dev/null
+++ b/web/src/lib/extension/constants.ts
@@ -0,0 +1,33 @@
+export const darkExtensionImages = [
+  "https://images.unsplash.com/photo-1692520883599-d543cfe6d43d?q=80&w=2666&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
+  "https://images.unsplash.com/photo-1520330461350-508fab483d6a?q=80&w=2723&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
+];
+
+export const lightExtensionImages = [
+  "https://images.unsplash.com/photo-1473830439578-14e9a9e61d55?q=80&w=2670&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
+  "https://images.unsplash.com/photo-1500964757637-c85e8a162699?q=80&w=2703&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
+  "https://images.unsplash.com/photo-1475924156734-496f6cac6ec1?q=80&w=2670&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
+];
+
+// Chrome message types
+export const CHROME_MESSAGE = {
+  PREFERENCES_UPDATED: "PREFERENCES_UPDATED",
+  ONYX_APP_LOADED: "ONYX_APP_LOADED",
+  SET_DEFAULT_NEW_TAB: "SET_DEFAULT_NEW_TAB",
+  LOAD_NEW_CHAT_PAGE: "LOAD_NEW_CHAT_PAGE",
+  LOAD_NEW_PAGE: "LOAD_NEW_PAGE",
+  AUTH_REQUIRED: "AUTH_REQUIRED",
+};
+
+export const SUBMIT_MESSAGE_TYPES = {
+  PAGE_CHANGE: "PAGE_CHANGE",
+};
+
+export const LocalStorageKeys = {
+  THEME: "onyxTheme",
+  LIGHT_BG_URL: "lightBgUrl",
+  DARK_BG_URL: "darkBgUrl",
+  SHORTCUTS: "shortCuts",
+  SHOW_SHORTCUTS: "showShortcuts",
+  USE_ONYX_AS_NEW_TAB: "useOnyxAsDefaultNewTab",
+};
diff --git a/web/src/lib/extension/utils.ts b/web/src/lib/extension/utils.ts
new file mode 100644
index 000000000..1e5cea2be
--- /dev/null
+++ b/web/src/lib/extension/utils.ts
@@ -0,0 +1,51 @@
+import { useEffect } from "react";
+import { CHROME_MESSAGE } from "./constants";
+export function sendSetDefaultNewTabMessage(value: boolean) {
+  if (typeof window !== "undefined" && window.parent) {
+    window.parent.postMessage(
+      { type: CHROME_MESSAGE.SET_DEFAULT_NEW_TAB, value },
+      "*"
+    );
+  }
+}
+
+export const sendAuthRequiredMessage = () => {
+  if (typeof window !== "undefined" && window.parent) {
+    window.parent.postMessage({ type: CHROME_MESSAGE.AUTH_REQUIRED }, "*");
+  }
+};
+
+export const useSendAuthRequiredMessage = () => {
+  useEffect(() => {
+    sendAuthRequiredMessage();
+  }, []);
+};
+
+export const sendMessageToParent = () => {
+  if (typeof window !== "undefined" && window.parent) {
+    window.parent.postMessage({ type: CHROME_MESSAGE.ONYX_APP_LOADED }, "*");
+  }
+};
+export const useSendMessageToParent = () => {
+  useEffect(() => {
+    sendMessageToParent();
+  }, []);
+};
+
+export function notifyExtensionOfThemeChange(
+  newTheme: string,
+  newBgUrl: string
+) {
+  if (typeof window !== "undefined" && window.parent) {
+    window.parent.postMessage(
+      {
+        type: CHROME_MESSAGE.PREFERENCES_UPDATED,
+        payload: {
+          theme: newTheme,
+          backgroundUrl: newBgUrl,
+        },
+      },
+      "*"
+    );
+  }
+}
diff --git a/web/src/lib/hooks.ts b/web/src/lib/hooks.ts
index 81f425f0d..caad9cb64 100644
--- a/web/src/lib/hooks.ts
+++ b/web/src/lib/hooks.ts
@@ -7,12 +7,13 @@ import {
   UserGroup,
   ConnectorStatus,
   CCPairBasicInfo,
+  ValidSources,
 } from "@/lib/types";
 import useSWR, { mutate, useSWRConfig } from "swr";
 import { errorHandlingFetcher } from "./fetcher";
 import { useContext, useEffect, useState } from "react";
 import { DateRangePickerValue } from "@/app/ee/admin/performance/DateRangeSelector";
-import { SourceMetadata } from "./search/interfaces";
+import { Filters, SourceMetadata } from "./search/interfaces";
 import { destructureValue, structureValue } from "./llm/utils";
 import { ChatSession } from "@/app/chat/interfaces";
 import { AllUsersResponse } from "./types";
@@ -24,6 +25,8 @@ import {
   LLMProviderDescriptor,
 } from "@/app/admin/configuration/llm/interfaces";
 import { isAnthropic } from "@/app/admin/configuration/llm/interfaces";
+import { getSourceMetadata } from "./sources";
+import { buildFilters } from "./search/utils";
 
 const CREDENTIAL_URL = "/api/manage/admin/credential";
 
@@ -147,6 +150,13 @@ export interface FilterManager {
   setSelectedDocumentSets: React.Dispatch<React.SetStateAction<string[]>>;
   selectedTags: Tag[];
   setSelectedTags: React.Dispatch<React.SetStateAction<Tag[]>>;
+  getFilterString: () => string;
+  buildFiltersFromQueryString: (
+    filterString: string,
+    availableSources: ValidSources[],
+    availableDocumentSets: string[],
+    availableTags: Tag[]
+  ) => void;
 }
 
 export function useFilters(): FilterManager {
@@ -157,6 +167,97 @@ export function useFilters(): FilterManager {
   );
   const [selectedTags, setSelectedTags] = useState<Tag[]>([]);
 
+  const getFilterString = () => {
+    const params = new URLSearchParams();
+
+    if (timeRange) {
+      params.set("from", timeRange.from.toISOString());
+      params.set("to", timeRange.to.toISOString());
+    }
+
+    if (selectedSources.length > 0) {
+      const sourcesParam = selectedSources
+        .map((source) => encodeURIComponent(source.internalName))
+        .join(",");
+      params.set("sources", sourcesParam);
+    }
+
+    if (selectedDocumentSets.length > 0) {
+      const docSetsParam = selectedDocumentSets
+        .map((ds) => encodeURIComponent(ds))
+        .join(",");
+      params.set("documentSets", docSetsParam);
+    }
+
+    if (selectedTags.length > 0) {
+      const tagsParam = selectedTags
+        .map((tag) => encodeURIComponent(tag.tag_value))
+        .join(",");
+      params.set("tags", tagsParam);
+    }
+
+    const queryString = params.toString();
+    return queryString ? `&${queryString}` : "";
+  };
+
+  function buildFiltersFromQueryString(
+    filterString: string,
+    availableSources: ValidSources[],
+    availableDocumentSets: string[],
+    availableTags: Tag[]
+  ): void {
+    const params = new URLSearchParams(filterString);
+
+    // Parse the "from" parameter as a DateRangePickerValue
+    let newTimeRange: DateRangePickerValue | null = null;
+    const fromParam = params.get("from");
+    const toParam = params.get("to");
+    if (fromParam && toParam) {
+      const fromDate = new Date(fromParam);
+      const toDate = new Date(toParam);
+      if (!isNaN(fromDate.getTime()) && !isNaN(toDate.getTime())) {
+        newTimeRange = { from: fromDate, to: toDate, selectValue: "" };
+      }
+    }
+
+    // Parse sources
+    const availableSourcesMetadata = availableSources.map(getSourceMetadata);
+    let newSelectedSources: SourceMetadata[] = [];
+    const sourcesParam = params.get("sources");
+    if (sourcesParam) {
+      const sourceNames = sourcesParam.split(",").map(decodeURIComponent);
+      newSelectedSources = availableSourcesMetadata.filter((source) =>
+        sourceNames.includes(source.internalName)
+      );
+    }
+
+    // Parse document sets
+    let newSelectedDocSets: string[] = [];
+    const docSetsParam = params.get("documentSets");
+    if (docSetsParam) {
+      const docSetNames = docSetsParam.split(",").map(decodeURIComponent);
+      newSelectedDocSets = availableDocumentSets.filter((ds) =>
+        docSetNames.includes(ds)
+      );
+    }
+
+    // Parse tags
+    let newSelectedTags: Tag[] = [];
+    const tagsParam = params.get("tags");
+    if (tagsParam) {
+      const tagValues = tagsParam.split(",").map(decodeURIComponent);
+      newSelectedTags = availableTags.filter((tag) =>
+        tagValues.includes(tag.tag_value)
+      );
+    }
+
+    // Update filter manager's values instead of returning
+    setTimeRange(newTimeRange);
+    setSelectedSources(newSelectedSources);
+    setSelectedDocumentSets(newSelectedDocSets);
+    setSelectedTags(newSelectedTags);
+  }
+
   return {
     timeRange,
     setTimeRange,
@@ -166,6 +267,8 @@ export function useFilters(): FilterManager {
     setSelectedDocumentSets,
     selectedTags,
     setSelectedTags,
+    getFilterString,
+    buildFiltersFromQueryString,
   };
 }
 
diff --git a/web/src/lib/search/utils.ts b/web/src/lib/search/utils.ts
index 1d428c0cb..55601f9f8 100644
--- a/web/src/lib/search/utils.ts
+++ b/web/src/lib/search/utils.ts
@@ -1,10 +1,5 @@
 import { Tag, ValidSources } from "../types";
-import {
-  Filters,
-  LoadedOnyxDocument,
-  OnyxDocument,
-  SourceMetadata,
-} from "./interfaces";
+import { Filters, OnyxDocument, SourceMetadata } from "./interfaces";
 import { DateRangePickerValue } from "@/app/ee/admin/performance/DateRangeSelector";
 
 export const buildFilters = (
diff --git a/web/src/lib/sources.ts b/web/src/lib/sources.ts
index a9a323c09..425c8da28 100644
--- a/web/src/lib/sources.ts
+++ b/web/src/lib/sources.ts
@@ -388,3 +388,26 @@ export function getSourcesForPersona(persona: Persona): ValidSources[] {
   });
   return personaSources;
 }
+
+export async function fetchTitleFromUrl(url: string): Promise<string | null> {
+  try {
+    const response = await fetch(url, {
+      method: "GET",
+      // If the remote site has no CORS header, this may fail in the browser
+      mode: "cors",
+    });
+    if (!response.ok) {
+      // Non-200 response, treat as a failure
+      return null;
+    }
+    const html = await response.text();
+    const parser = new DOMParser();
+    const doc = parser.parseFromString(html, "text/html");
+    // If the site has <title>My Demo Page</title>, we retrieve "My Demo Page"
+    const pageTitle = doc.querySelector("title")?.innerText.trim() ?? null;
+    return pageTitle;
+  } catch (error) {
+    console.error("Error fetching page title:", error);
+    return null;
+  }
+}