From 7e8c21c807c32a10b2d89093eed819158b0f8ad3 Mon Sep 17 00:00:00 2001 From: pablodanswer Date: Tue, 7 Jan 2025 14:14:45 -0800 Subject: [PATCH 01/86] add chrome extension minor clean up additional handling post rebase fixes nit quick bump finalize minor cleanup organizational Revert changes in backend directory Revert changes in deployment directory push misc changes improve shortcut display + general nrf page layout minor clean up quick nit update chrome k build fix k update k --- web/next.config.js | 16 +- web/package-lock.json | 307 +++++++++++++ web/package.json | 3 + web/src/app/auth/login/LoginPage.tsx | 102 +++++ web/src/app/auth/login/SignInButton.tsx | 2 +- web/src/app/auth/login/page.tsx | 71 +-- web/src/app/chat/ChatIntro.tsx | 4 +- web/src/app/chat/ChatPage.tsx | 179 ++++++-- web/src/app/chat/WrappedChat.tsx | 8 +- .../documentSidebar/ChatDocumentDisplay.tsx | 4 +- web/src/app/chat/input/ChatInputBar.tsx | 137 +++++- .../app/chat/input/SimplifiedChatInputBar.tsx | 249 +++++++++++ web/src/app/chat/layout.tsx | 71 +++ .../chat/message/MemoizedTextComponents.tsx | 5 - web/src/app/chat/message/Messages.tsx | 14 +- web/src/app/chat/message/SearchSummary.tsx | 78 +++- web/src/app/chat/message/SkippedSearch.tsx | 35 +- web/src/app/chat/nrf/NRFPage.tsx | 404 ++++++++++++++++++ web/src/app/chat/nrf/interfaces.ts | 67 +++ web/src/app/chat/nrf/page.tsx | 20 + web/src/app/chat/page.tsx | 95 ++-- web/src/app/components/nrf/SettingsPanel.tsx | 176 ++++++++ .../app/components/nrf/ShortcutsDisplay.tsx | 46 ++ web/src/app/ee/Hori | 0 web/src/app/layout.tsx | 1 - .../components/assistants/AssistantIcon.tsx | 1 + .../chat_search/AssistantSelector.tsx | 8 +- web/src/components/chat_search/hooks.ts | 17 +- .../context/NRFPreferencesContext.tsx | 123 ++++++ web/src/components/extension/Shortcuts.tsx | 274 ++++++++++++ web/src/components/header/LogoWithText.tsx | 4 +- .../search/filtering/FilterDropdown.tsx | 3 + .../filtering/HorizontalSourceSelector.tsx | 226 ++++++++++ .../components/search/results/Citation.tsx | 2 - web/src/components/tooltip/CustomTooltip.tsx | 12 +- web/src/components/ui/label.tsx | 26 ++ web/src/components/ui/radio-group.tsx | 44 ++ web/src/components/ui/switch.tsx | 9 +- web/src/lib/chat/fetchChatData.ts | 9 +- web/src/lib/chat/fetchSomeChatData.ts | 1 + web/src/lib/constants.ts | 3 + web/src/lib/dateUtils.ts | 21 +- web/src/lib/extension/utils.ts | 17 + web/src/lib/hooks.ts | 105 ++++- web/src/lib/search/utils.ts | 3 +- web/src/lib/sources.ts | 23 + 46 files changed, 2792 insertions(+), 233 deletions(-) create mode 100644 web/src/app/auth/login/LoginPage.tsx create mode 100644 web/src/app/chat/input/SimplifiedChatInputBar.tsx create mode 100644 web/src/app/chat/layout.tsx create mode 100644 web/src/app/chat/nrf/NRFPage.tsx create mode 100644 web/src/app/chat/nrf/interfaces.ts create mode 100644 web/src/app/chat/nrf/page.tsx create mode 100644 web/src/app/components/nrf/SettingsPanel.tsx create mode 100644 web/src/app/components/nrf/ShortcutsDisplay.tsx create mode 100644 web/src/app/ee/Hori create mode 100644 web/src/components/context/NRFPreferencesContext.tsx create mode 100644 web/src/components/extension/Shortcuts.tsx create mode 100644 web/src/components/search/filtering/HorizontalSourceSelector.tsx create mode 100644 web/src/components/ui/label.tsx create mode 100644 web/src/components/ui/radio-group.tsx create mode 100644 web/src/lib/extension/utils.ts diff --git a/web/next.config.js b/web/next.config.js index 2877a646911..8d22b6f32ef 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,16 @@ const nextConfig = { publicRuntimeConfig: { version, }, + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "www.google.com", + port: "", + pathname: "/s2/favicons/**", + }, + ], + }, async headers() { return [ { @@ -44,17 +53,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 d32345fe64f..0d1eb688f30 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 4a52f11c9bc..3fdf30f8535 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/auth/login/LoginPage.tsx b/web/src/app/auth/login/LoginPage.tsx new file mode 100644 index 00000000000..a3ba7d33cd3 --- /dev/null +++ b/web/src/app/auth/login/LoginPage.tsx @@ -0,0 +1,102 @@ +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"; + +export default function LoginPanel({ + authUrl, + authTypeMetadata, + nextUrl, + searchParams, + showPageRedirect, +}: { + authUrl: string | null; + authTypeMetadata: AuthTypeMetadata | null; + nextUrl: string | null; + searchParams: + | { + [key: string]: string | string[] | undefined; + } + | undefined; + showPageRedirect?: boolean; +}) { + return ( +
+ {authUrl && authTypeMetadata && ( + <> +

+ +

+ + + + )} + + {authTypeMetadata?.authType === "cloud" && ( +
+
+
+ or +
+
+ + +
+ + Create an account + + + {NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED && ( + + Reset Password + + )} +
+
+ )} + + {authTypeMetadata?.authType === "basic" && ( + <> +
+ + <LoginText /> + +
+ +
+ + )} + {showPageRedirect && ( +

+ Don't have an account?{" "} + { + if (typeof window !== "undefined" && window.top) { + window.top.location.href = "/auth/register"; + } else { + window.location.href = "/auth/register"; + } + }} + className="text-link font-medium cursor-pointer" + > + Create an account + +

+ )} +
+ ); +} diff --git a/web/src/app/auth/login/SignInButton.tsx b/web/src/app/auth/login/SignInButton.tsx index b06f9bad79b..b2d1a15be4f 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 ( {button} diff --git a/web/src/app/auth/login/page.tsx b/web/src/app/auth/login/page.tsx index 32ea1aa3734..bb4c47acf1d 100644 --- a/web/src/app/auth/login/page.tsx +++ b/web/src/app/auth/login/page.tsx @@ -7,20 +7,11 @@ 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 LoginPanel from "./LoginPage"; -const Page = async (props: { +const LoginPage = async (props: { searchParams?: Promise<{ [key: string]: string | string[] | undefined }>; }) => { const searchParams = await props.searchParams; @@ -59,6 +50,7 @@ const Page = async (props: { if (authTypeMetadata?.requiresVerification && !currentUser.is_verified) { return redirect("/auth/waiting-on-verification"); } + return redirect("/chat"); } @@ -83,58 +75,15 @@ const Page = async (props: { -
- {authUrl && authTypeMetadata && ( - <> -

- -

- - - - )} - - {authTypeMetadata?.authType === "cloud" && ( -
-
-
- or -
-
- - - -
- {NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED && ( - - Reset Password - - )} -
-
- )} - - {authTypeMetadata?.authType === "basic" && ( - <> -
- - <LoginText /> - -
- -
- - )} -
+ ); }; -export default Page; +export default LoginPage; diff --git a/web/src/app/chat/ChatIntro.tsx b/web/src/app/chat/ChatIntro.tsx index d86eb2315e9..1d9ef435377 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 }) {
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" >
-
+
{hoveredAssistant && ( )} diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index 0548bc3c558..59e49095096 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, @@ -75,6 +75,7 @@ import { StreamStopInfo, StreamStopReason, } from "@/lib/search/interfaces"; +import { Filters } from "@/lib/search/interfaces"; import { buildFilters } from "@/lib/search/utils"; import { SettingsContext } from "@/components/settings/SettingsProvider"; import Dropzone from "react-dropzone"; @@ -110,7 +111,7 @@ 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"; const TEMP_USER_MESSAGE_ID = -1; const TEMP_ASSISTANT_MESSAGE_ID = -2; @@ -120,10 +121,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 +143,7 @@ export function ChatPage({ shouldShowWelcomeModal, refreshChatSessions, } = useChatContext(); + function useScreenSize() { const [screenSize, setScreenSize] = useState({ width: typeof window !== "undefined" ? window.innerWidth : 0, @@ -210,22 +214,48 @@ 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); + const submittingLogic = (searchParamsString: string) => { + const newSearchParams = new URLSearchParams(searchParamsString); + console.log("newSearchParams", newSearchParams); + console.log("searchParamsString", searchParamsString); + const message = newSearchParams.get("user-prompt"); + newSearchParams.delete(SEARCH_PARAM_NAMES.SEND_ON_LOAD); + + filterManager.buildFiltersFromQueryString( + newSearchParams.toString(), + availableSources, + documentSets.map((ds) => ds.name), + tags + ); + const fileDescriptorString = newSearchParams.get("files"); + let overrideFileDescriptors: FileDescriptor[] = []; + if (fileDescriptorString) { + try { + overrideFileDescriptors = JSON.parse( + decodeURIComponent(fileDescriptorString) + ); + } catch (error) { + console.error("Error parsing file descriptors:", error); + } + } - // Update the URL without the send-on-load parameter - router.replace(`?${newSearchParams.toString()}`, { scroll: false }); + // Update the URL without the send-on-load parameter + router.replace(`?${newSearchParams.toString()}`, { scroll: false }); - // Update our local state to reflect the change - setSendOnLoad(null); + // Update our local state to reflect the change + setSendOnLoad(null); - // If there's a message, submit it - if (message) { - onSubmit({ messageOverride: message }); - } + // If there's a message, submit it + if (message) { + setSubmittedMessage(message); + onSubmit({ messageOverride: message, overrideFileDescriptors }); + } + }; + + // Effect to handle sendOnLoad + useEffect(() => { + if (sendOnLoad) { + submittingLogic(sendOnLoad); } }, [sendOnLoad, searchParams, router]); @@ -312,14 +342,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(() => { @@ -401,6 +423,9 @@ export function ChatPage({ // this is triggered every time the user switches which chat // session they are using + const [chromSentUrls, setchromSentUrls] = useState([]); + const [selectedChromeUrls, setSelectedChromeUrls] = useState([]); + useEffect(() => { const priorChatSessionId = chatSessionIdRef.current; const loadedSessionId = loadedIdSessionRef.current; @@ -456,7 +481,7 @@ export function ChatPage({ } return; } - setIsReady(true); + // setIsReady(true); const shouldScrollToBottom = visibleRange.get(existingChatSessionId) === undefined || visibleRange.get(existingChatSessionId)?.end == 0; @@ -651,10 +676,10 @@ export function ChatPage({ currentMessageMap(completeMessageDetail) ); - const [submittedMessage, setSubmittedMessage] = useState(""); + const [submittedMessage, setSubmittedMessage] = useState(firstMessage || ""); const [chatState, setChatState] = useState>( - new Map([[chatSessionIdRef.current, "input"]]) + new Map([[chatSessionIdRef.current, firstMessage ? "loading" : "input"]]) ); const [regenerationState, setRegenerationState] = useState< @@ -798,6 +823,17 @@ export function ChatPage({ } }, [defaultAssistantId, availableAssistants, messageHistory.length]); + useEffect(() => { + if ( + submittedMessage && + currentSessionChatState === "loading" && + messageHistory.length == 0 + ) { + console.log("TRYING TO LOAD NEW CHAT PAGE", submittedMessage); + window.parent.postMessage({ type: "LOAD_NEW_CHAT_PAGE" }, "*"); + } + }, [submittedMessage, currentSessionChatState]); + const [ selectedDocuments, toggleDocumentSelection, @@ -1001,6 +1037,34 @@ export function ChatPage({ adjustDocumentSidebarWidth(); // Adjust the width on initial render window.addEventListener("resize", adjustDocumentSidebarWidth); // Add resize event listener + window.addEventListener("message", (event) => { + if (event.data.type === "LOAD_NEW_PAGE") { + console.log("Received LOAD_NEW_PAGE event:", event.data); + const { href } = event.data; + const url = new URL(href); + // const userPrompt = url.searchParams.get("user-prompt"); + + // { + // "type": "LOAD_NEW_PAGE", + // "href": "http://localhost:3000/chat?send-on-load=true&user-prompt=hi" + // } + + console.log(event.data); + console.log("url.searchParams", url.searchParams); + // if (userPrompt) { + submittingLogic(url.searchParams.toString()); + // setSubmittedMessage(userPrompt); + // updateChatState("loading"); + // } + + // Handle the new page load + // This might involve updating the application's route or state + // console.log("Loading new page:", href); + // Implement your page loading logic here, e.g., updating the URL + // router.push(href, undefined, { shallow: true }); + } + }); + return () => { window.removeEventListener("resize", adjustDocumentSidebarWidth); // Cleanup the event listener }; @@ -1078,6 +1142,7 @@ export function ChatPage({ alternativeAssistantOverride = null, modelOverRide, regenerationRequest, + overrideFileDescriptors, }: { messageIdToResend?: number; messageOverride?: string; @@ -1087,6 +1152,7 @@ export function ChatPage({ alternativeAssistantOverride?: Persona | null; modelOverRide?: LlmOverride; regenerationRequest?: RegenerationRequest | null; + overrideFileDescriptors?: FileDescriptor[]; } = {}) => { let frozenSessionId = currentSessionId(); updateCanContinue(false, frozenSessionId); @@ -1113,6 +1179,7 @@ export function ChatPage({ let currChatSessionId: string; const isNewSession = chatSessionIdRef.current === null; + const searchParamBasedChatSessionName = searchParams.get(SEARCH_PARAM_NAMES.TITLE) || null; @@ -1228,7 +1295,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 +1882,7 @@ export function ChatPage({ end: 0, mostVisibleMessageId: null, }; + useSendMessageToParent(); useEffect(() => { if (noAssistants) { @@ -1889,6 +1957,7 @@ export function ChatPage({ handleSlackChatRedirect(); }, [searchParams, router]); + useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.metaKey || event.ctrlKey) { @@ -1957,6 +2026,10 @@ export function ChatPage({ }); }; } + if (!user) { + redirect("/auth/login"); + } + if (noAssistants) return ( <> @@ -2039,7 +2112,11 @@ export function ChatPage({ {retrievalEnabled && documentSidebarToggled && settings?.isMobile && (
- + setDocumentSidebarToggled(false)} + noPadding + noScroll + > )} - {documentSidebarInitialWidth !== undefined && isReady ? ( - + {true ? ( + {({ getRootProps }) => ( -
+
{!settings?.isMobile && (
@@ -2344,7 +2426,7 @@ export function ChatPage({ currentSessionChatState == "input" && !loadingError && allAssistants.length > 1 && ( -
+
Recent Assistants @@ -2362,8 +2444,9 @@ export function ChatPage({ )}
)} { + setSelectedChromeUrls([ + ...selectedChromeUrls, + chromeUrl, + ]); + setchromSentUrls((chromSentUrls: string[]) => + chromSentUrls.filter( + (url) => url !== chromeUrl + ) + ); + }} + selectedChromeUrls={selectedChromeUrls} + removeSelectedChromeUrl={(chromeUrl: string) => { + setSelectedChromeUrls( + selectedChromeUrls.filter( + (url) => url !== chromeUrl + ) + ); + }} + chromSentUrls={chromSentUrls} + removeChromeSentUrls={(chromSentUrl: string) => { + setchromSentUrls( + chromSentUrls.filter( + (url) => url !== chromSentUrl + ) + ); + }} removeDocs={() => { clearSelectedDocuments(); }} diff --git a/web/src/app/chat/WrappedChat.tsx b/web/src/app/chat/WrappedChat.tsx index 6b48e442175..55d0f91c639 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 ( ( - + )} /> ); diff --git a/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx b/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx index 8ee3c6ead79..13c1e1b0d52 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 (
void; +}) => ( +
+ Website favicon +

+ {new URL(url).hostname} +

+ onRemove(url)} + size={16} + className="text-text-400 hover:text-text-600 ml-auto cursor-pointer" + /> +
+); + +const SentUrlChip = ({ + url, + onRemove, + onClick, + title, +}: { + url: string; + onRemove: (url: string) => void; + onClick: () => void; + title: string; +}) => { + return ( + + ); +}; + interface ChatInputBarProps { removeDocs: () => void; openModelSettings: () => void; @@ -46,6 +107,7 @@ interface ChatInputBarProps { stopGenerating: () => void; onSubmit: () => void; filterManager: FilterManager; + llmOverrideManager: LlmOverrideManager; chatState: ChatState; alternativeAssistant: Persona | null; // assistants @@ -57,9 +119,17 @@ interface ChatInputBarProps { handleFileUpload: (files: File[]) => void; textAreaRef: React.RefObject; toggleFilters?: () => void; + chromSentUrls?: string[]; + removeChromeSentUrls: (chromSentUrl: string) => void; + selectedChromeUrls?: string[]; + removeSelectedChromeUrl: (selectedChromeUrl: string) => void; + selectChromeUrl: (chromeUrl: string) => void; } export function ChatInputBar({ + chromSentUrls, + selectedChromeUrls, + removeSelectedChromeUrl, removeDocs, openModelSettings, showDocs, @@ -69,6 +139,7 @@ export function ChatInputBar({ setMessage, stopGenerating, onSubmit, + removeChromeSentUrls, filterManager, chatState, @@ -82,6 +153,7 @@ export function ChatInputBar({ textAreaRef, alternativeAssistant, toggleFilters, + selectChromeUrl, }: ChatInputBarProps) { useEffect(() => { const textarea = textAreaRef.current; @@ -217,6 +289,26 @@ export function ChatInputBar({ } }; + // We'll store dynamic titles in state, keyed by URL + const [fetchedTitles, setFetchedTitles] = useState>( + {} + ); + + useEffect(() => { + if (!chromSentUrls) return; + + chromSentUrls.forEach((url) => { + // Already have it? Skip + if (fetchedTitles[url]) return; + + fetchTitleFromUrl(url).then((title: string | null) => { + if (title) { + setFetchedTitles((prev) => ({ ...prev, [url]: title })); + } + }); + }); + }, [chromSentUrls, fetchedTitles]); + return (
@@ -228,6 +320,35 @@ export function ChatInputBar({ mx-auto " > + {(chromSentUrls || selectedChromeUrls) && ( +
+ {selectedChromeUrls && + selectedChromeUrls.map((url, index) => ( + + ))} + {chromSentUrls && + chromSentUrls.map((url, index) => { + const parsedUrl = new URL(url); + const displayTitle = fetchedTitles[url] || parsedUrl.hostname; + return ( + { + selectChromeUrl(url); + }} + url={url} + onRemove={removeChromeSentUrls} + /> + ); + })} +
+ )} + {showSuggestions && assistantTagOptions.length > 0 && (
{ if ( @@ -453,16 +574,6 @@ export function ChatInputBar({ onClick={toggleFilters} /> )} - {(filterManager.selectedSources.length > 0 || - filterManager.selectedDocumentSets.length > 0 || - filterManager.selectedTags.length > 0 || - filterManager.timeRange) && - toggleFilters && ( - - )}
diff --git a/web/src/app/chat/input/SimplifiedChatInputBar.tsx b/web/src/app/chat/input/SimplifiedChatInputBar.tsx new file mode 100644 index 00000000000..42536c7a974 --- /dev/null +++ b/web/src/app/chat/input/SimplifiedChatInputBar.tsx @@ -0,0 +1,249 @@ +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; + + // NEW (optional) - if we want to accept the FilterManager in this component: + filterManager?: FilterManager; + existingSources: string[]; + availableDocumentSets: { name: string }[]; + availableTags: Tag[]; +} + +export function SimplifiedChatInputBar({ + message, + setMessage, + onSubmit, + + files, + setFiles, + handleFileUpload, + textAreaRef, + // NEW (optional) - if we want to accept the FilterManager in this component: + 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) => { + const text = event.target.value; + setMessage(text); + }; + + return ( +
+
+ {files.length > 0 && ( +
+
+ {files.map((file) => ( +
+ {file.type === ChatFileType.IMAGE ? ( + { + setFiles( + files.filter( + (fileInFilter) => fileInFilter.id !== file.id + ) + ); + }} + isUploading={file.isUploading || false} + /> + ) : ( + { + setFiles( + files.filter( + (fileInFilter) => fileInFilter.id !== file.id + ) + ); + }} + isUploading={file.isUploading || false} + /> + )} +
+ ))} +
+
+ )} + +