From d325b07182bc3c5925d98712a2790d39989e87a3 Mon Sep 17 00:00:00 2001 From: Sean Hatfield Date: Thu, 19 Feb 2026 12:40:36 -0800 Subject: [PATCH] Implement new home page redesign (#4931) * remove legacy home page components, update home page to new layout * update PromptInput component styles to match new designs, make quick action buttons functional * home page chat creates new thread in last used workspace * fix slash commands and agent popup on home page * disable llm workspace selector action in home page * add drag and drop file support to home page * fix behavior of drag and drop on home page * handle pasting attachments in home page * update empty state of workspace chat to use new ui * update empty workspace ui to match home page design, fix flickering loading states * convert quick action buttons to component, add to empty state ws chat * fix hover state light mode in quick actions * add suggested messages subcomponent to empty ws/thread * adjust width, rounded edges of prompt input * only show quick actions for admin/manager role * fix hover states for quick actions and suggested messages component * make upload document quick action trigger parsed document upload * fix mic behavior in homepage, ws chat, ws thread chat * fix margin between prompt input and quick actions * Simplify message presets by removing heading input (#4915) * Remove heading input from message presets, merge legacy headings on edit * filter out empty messages from state after saving * mark form as dirty on input change * styling --------- Co-authored-by: Timothy Carambat * convert SuggestedMessages to component, render SuggestedMessages in home page to target ws * fix broken handleMessageChange reference * add translations for QuickActions * lint * fix home page chat submission broken by PromptInput onChange removal * fix prompt input remount race condition, home page suggested message flicker * remove unused handleSendSuggestedMessage from ChatHistory * add greeting text to main-page translations, remove defaults * fix file deletion in parsed files menu on home page * add virtual thread sidebar state and workspace indicator on home page * show workspace llm selector on home page when workspace exists * show home page for all user roles with rbac quick actions * fix positioning of agent and slash command popups * remove workspace indicator from home page, match empty state spacing * Normalize translations for home page redesign (#4986) * normalize translations * update translations with DMR * accidentally changed es translation * normalize translations for main-page.greeting * update translations with DMR --------- Co-authored-by: Timothy Carambat * update translations * create new workspace in native language Cleanup workspace page from empty state handling * update quick action show logic * fix send button --------- Co-authored-by: Timothy Carambat --- .../ThreadContainer/ThreadItem/index.jsx | 13 +- .../ThreadContainer/index.jsx | 32 +- .../Sidebar/ActiveWorkspaces/index.jsx | 24 +- .../ChatContainer/ChatHistory/index.jsx | 69 +--- .../PromptInput/AgentMenu/index.jsx | 15 +- .../AttachItem/ParsedFilesMenu/index.jsx | 12 +- .../PromptInput/AttachItem/index.jsx | 13 +- .../PromptInput/LLMSelector/action.jsx | 12 +- .../PromptInput/LLMSelector/index.jsx | 5 +- .../PromptInput/LLMSelector/utils.js | 1 + .../PromptInput/SlashCommands/index.jsx | 22 +- .../PromptInput/SpeechToText/index.jsx | 4 +- .../StopGenerationButton/index.jsx | 34 +- .../PromptInput/TextSizeMenu/index.jsx | 2 +- .../ChatContainer/PromptInput/index.jsx | 114 +++--- .../WorkspaceChat/ChatContainer/index.jsx | 116 ++++++- .../src/components/WorkspaceChat/index.jsx | 11 +- .../src/components/lib/QuickActions/index.jsx | 57 +++ .../lib/SuggestedMessages/index.jsx | 32 ++ frontend/src/locales/ar/common.js | 6 + frontend/src/locales/cs/common.js | 6 + frontend/src/locales/da/common.js | 6 + frontend/src/locales/de/common.js | 6 + frontend/src/locales/en/common.js | 6 + frontend/src/locales/es/common.js | 6 + frontend/src/locales/et/common.js | 6 + frontend/src/locales/fa/common.js | 6 + frontend/src/locales/fr/common.js | 6 + frontend/src/locales/he/common.js | 6 + frontend/src/locales/it/common.js | 6 + frontend/src/locales/ja/common.js | 6 + frontend/src/locales/ko/common.js | 6 + frontend/src/locales/lv/common.js | 6 + frontend/src/locales/nl/common.js | 6 + frontend/src/locales/pl/common.js | 6 + frontend/src/locales/pt_BR/common.js | 6 + frontend/src/locales/ro/common.js | 6 + frontend/src/locales/ru/common.js | 6 + frontend/src/locales/tr/common.js | 6 + frontend/src/locales/vn/common.js | 6 + frontend/src/locales/zh/common.js | 6 + frontend/src/locales/zh_TW/common.js | 6 + .../ChecklistItem/icons/SlashCommand.jsx | 28 -- .../Home/Checklist/ChecklistItem/index.jsx | 81 ----- .../pages/Main/Home/Checklist/constants.js | 168 --------- .../src/pages/Main/Home/Checklist/index.jsx | 216 ------------ .../pages/Main/Home/ExploreFeatures/index.jsx | 151 -------- .../src/pages/Main/Home/QuickLinks/index.jsx | 96 ------ .../src/pages/Main/Home/Resources/index.jsx | 48 --- .../src/pages/Main/Home/Updates/index.jsx | 209 ----------- frontend/src/pages/Main/Home/index.jsx | 324 +++++++++++++++++- frontend/src/pages/Main/index.jsx | 5 +- .../SuggestedChatMessages/index.jsx | 43 ++- frontend/src/utils/constants.js | 1 + 54 files changed, 861 insertions(+), 1235 deletions(-) create mode 100644 frontend/src/components/lib/QuickActions/index.jsx create mode 100644 frontend/src/components/lib/SuggestedMessages/index.jsx delete mode 100644 frontend/src/pages/Main/Home/Checklist/ChecklistItem/icons/SlashCommand.jsx delete mode 100644 frontend/src/pages/Main/Home/Checklist/ChecklistItem/index.jsx delete mode 100644 frontend/src/pages/Main/Home/Checklist/constants.js delete mode 100644 frontend/src/pages/Main/Home/Checklist/index.jsx delete mode 100644 frontend/src/pages/Main/Home/ExploreFeatures/index.jsx delete mode 100644 frontend/src/pages/Main/Home/QuickLinks/index.jsx delete mode 100644 frontend/src/pages/Main/Home/Resources/index.jsx delete mode 100644 frontend/src/pages/Main/Home/Updates/index.jsx diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx index 3170ebb7..c6699dbb 100644 --- a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx @@ -24,12 +24,15 @@ export default function ThreadItem({ hasNext, ctrlPressed = false, }) { - const { slug, threadSlug = null } = useParams(); + const { slug: urlSlug, threadSlug = null } = useParams(); + const workspaceSlug = workspace?.slug ?? urlSlug; const optionsContainer = useRef(null); const [showOptions, setShowOptions] = useState(false); - const linkTo = !thread.slug - ? paths.workspace.chat(slug) - : paths.workspace.thread(slug, thread.slug); + const linkTo = thread.virtual + ? "/" + : !thread.slug + ? paths.workspace.chat(workspaceSlug) + : paths.workspace.thread(workspaceSlug, thread.slug); const { ref } = useScrollActiveItemIntoView({ isActive, @@ -114,7 +117,7 @@ export default function ThreadItem({

)} - {!!thread.slug && !thread.deleted && ( + {!!thread.slug && !thread.deleted && !thread.virtual && (
{" "} {/* Added flex and items-center */} diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx index 113717fe..022e2412 100644 --- a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx @@ -7,7 +7,10 @@ import ThreadItem from "./ThreadItem"; import { useParams } from "react-router-dom"; export const THREAD_RENAME_EVENT = "renameThread"; -export default function ThreadContainer({ workspace }) { +export default function ThreadContainer({ + workspace, + isVirtualThread = false, +}) { const { threadSlug = null } = useParams(); const [threads, setThreads] = useState([]); const [loading, setLoading] = useState(true); @@ -109,6 +112,12 @@ export default function ThreadContainer({ workspace }) { }, 500); } + function getActiveThreadIdx() { + if (isVirtualThread) return threads.length + 1; + const idx = threads.findIndex((t) => t?.slug === threadSlug); + return idx >= 0 ? idx + 1 : 0; + } + if (loading) { return (
@@ -117,11 +126,7 @@ export default function ThreadContainer({ workspace }) { ); } - const activeThreadIdx = !!threads.find( - (thread) => thread?.slug === threadSlug - ) - ? threads.findIndex((thread) => thread?.slug === threadSlug) + 1 - : 0; + const activeThreadIdx = getActiveThreadIdx(); return (
@@ -129,8 +134,9 @@ export default function ThreadContainer({ workspace }) { idx={0} activeIdx={activeThreadIdx} isActive={activeThreadIdx === 0} + workspace={workspace} thread={{ slug: null, name: "default" }} - hasNext={threads.length > 0} + hasNext={threads.length > 0 || isVirtualThread} /> {threads.map((thread, i) => ( ))} + {isVirtualThread && ( + + )} { async function getWorkspaces() { @@ -71,6 +73,20 @@ export default function ActiveWorkspaces() { reorderWorkspaces(result.source.index, result.destination.index); }; + // When on the home page, resolve which workspace should be virtually active + const virtualActiveSlug = (() => { + if (!isHomePage || workspaces.length === 0) return null; + const lastVisited = safeJsonParse( + localStorage.getItem(LAST_VISITED_WORKSPACE) + ); + if ( + lastVisited?.slug && + workspaces.some((ws) => ws.slug === lastVisited.slug) + ) + return lastVisited.slug; + return workspaces[0]?.slug ?? null; + })(); + return ( @@ -83,7 +99,8 @@ export default function ActiveWorkspaces() { {...provided.droppableProps} > {workspaces.map((workspace, index) => { - const isActive = workspace.slug === slug; + const isVirtuallyActive = workspace.slug === virtualActiveSlug; + const isActive = workspace.slug === slug || isVirtuallyActive; return ( )}
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx index e439502a..de2b8971 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx @@ -13,7 +13,6 @@ import { useManageWorkspaceModal } from "../../../Modals/ManageWorkspace"; import ManageWorkspace from "../../../Modals/ManageWorkspace"; import { ArrowDown } from "@phosphor-icons/react"; import debounce from "lodash.debounce"; -import useUser from "@/hooks/useUser"; import Chartable from "./Chartable"; import Workspace from "@/models/workspace"; import { useParams } from "react-router-dom"; @@ -21,7 +20,6 @@ import paths from "@/utils/paths"; import Appearance from "@/models/appearance"; import useTextSize from "@/hooks/useTextSize"; import useChatHistoryScrollHandle from "@/hooks/useChatHistoryScrollHandle"; -import { useTranslation } from "react-i18next"; import { useChatMessageAlignment } from "@/hooks/useChatMessageAlignment"; import { ThoughtExpansionProvider } from "./ThoughtContainer"; @@ -32,16 +30,13 @@ export default forwardRef(function ( sendCommand, updateHistory, regenerateAssistantMessage, - hasAttachments = false, }, ref ) { - const { t } = useTranslation(); const lastScrollTopRef = useRef(0); const chatHistoryRef = useRef(null); - const { user } = useUser(); const { threadSlug = null } = useParams(); - const { showing, showModal, hideModal } = useManageWorkspaceModal(); + const { showing, hideModal } = useManageWorkspaceModal(); const [isAtBottom, setIsAtBottom] = useState(true); const [isUserScrolling, setIsUserScrolling] = useState(false); const isStreaming = history[history.length - 1]?.animate; @@ -98,10 +93,6 @@ export default forwardRef(function ( scrollToBottom, }); - const handleSendSuggestedMessage = (heading, message) => { - sendCommand({ text: `${heading} ${message}`, autoSubmit: true }); - }; - const saveEditedMessage = async ({ editedMessage, chatId, @@ -197,46 +188,6 @@ export default forwardRef(function ( [compiledHistory.length, lastMessageInfo] ); - if (history.length === 0 && !hasAttachments) { - return ( -
-
-

- {t("chat_window.welcome")} -

- {!user || user.role !== "default" ? ( -

- {t("chat_window.get_started")} - - {t("chat_window.upload")} - - {t("chat_window.or")}{" "} - {t("chat_window.send_chat")} -

- ) : ( -

- {t("chat_window.get_started_default")}{" "} - {t("chat_window.send_chat")} -

- )} - -
- {showing && ( - - )} -
- ); - } - return (
{ }; }; -function WorkspaceChatSuggestions({ suggestions = [], sendSuggestion }) { - if (suggestions.length === 0) return null; - return ( -
- {suggestions.map((suggestion, index) => ( - - ))} -
- ); -} - /** * Builds the history of messages for the chat. * This is mostly useful for rendering the history in a way that is easy to understand. diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AgentMenu/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AgentMenu/index.jsx index 59662bf0..bd855a37 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AgentMenu/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AgentMenu/index.jsx @@ -16,13 +16,13 @@ export default function AvailableAgentsButton({ showing, setShowAgents }) { data-tooltip-content={t("chat_window.agents")} aria-label={t("chat_window.agents")} onClick={() => setShowAgents(!showing)} - className={`flex justify-center items-center cursor-pointer ${ + className={`flex justify-center items-center cursor-pointer opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 ${ showing ? "!opacity-100" : "" }`} >