Compare commits

...

47 Commits

Author SHA1 Message Date
Raunak Bhagat
07f3b83f3d Merge branch 'search' into search-2 2026-01-27 21:56:45 -08:00
Raunak Bhagat
ad88d10388 Fix path 2026-01-27 21:56:28 -08:00
Raunak Bhagat
b1499126a2 Merge search 2026-01-27 21:50:37 -08:00
Raunak Bhagat
a1876fe653 Merge main 2026-01-27 21:48:20 -08:00
Raunak Bhagat
0baa494ea4 Add empty message 2026-01-27 21:45:09 -08:00
Raunak Bhagat
4249b1383f Small tweaks 2026-01-27 21:42:02 -08:00
Raunak Bhagat
cb05d5799a Make function the default export 2026-01-27 21:36:42 -08:00
Raunak Bhagat
886d333cf2 Saving appFocus changes 2026-01-27 21:36:13 -08:00
Raunak Bhagat
6d5cee1589 Make selector readonly on non-new-session pages 2026-01-27 21:21:22 -08:00
Raunak Bhagat
f248b47719 Remove classNames from InputSelect 2026-01-27 21:17:36 -08:00
Raunak Bhagat
964fb12d7d Saving changes 2026-01-27 21:12:59 -08:00
Raunak Bhagat
1b34649da8 Saving changes 2026-01-27 20:43:24 -08:00
Raunak Bhagat
7c83d15c8d Make submitting a new query NOT reset back to pending state 2026-01-27 18:56:07 -08:00
Raunak Bhagat
75a388194a Add layout updates 2026-01-27 18:42:29 -08:00
Raunak Bhagat
f4668e907d Saving changes 2026-01-27 18:14:21 -08:00
Raunak Bhagat
c9e423b58e Saving changes 2026-01-27 18:09:41 -08:00
Raunak Bhagat
0c8d0c39e6 Impl filtering 2026-01-27 15:59:21 -08:00
Raunak Bhagat
ae8e981bdf Add chips 2026-01-27 15:06:57 -08:00
Raunak Bhagat
f832e18376 More changes being saved 2026-01-27 14:39:06 -08:00
Raunak Bhagat
e2572f2f5b Fix layout on main app page 2026-01-27 12:48:52 -08:00
Raunak Bhagat
4b142ae13c Fix scroll padding at the bottom 2026-01-27 12:48:32 -08:00
Raunak Bhagat
f434e50417 Merge search 2026-01-27 12:37:51 -08:00
Raunak Bhagat
21481d5646 Merge main 2026-01-27 11:58:49 -08:00
Raunak Bhagat
55953b7b4e Merge branch 'main' into search 2026-01-27 11:57:34 -08:00
Raunak Bhagat
9b54254411 Saving changes 2026-01-27 11:09:42 -08:00
Raunak Bhagat
8789ef02b9 Saving changes 2026-01-27 02:33:39 -08:00
Raunak Bhagat
42d4460f7b Move some more things around 2026-01-27 01:22:52 -08:00
Raunak Bhagat
12d6370682 Add CSS width to components 2026-01-27 00:59:17 -08:00
Raunak Bhagat
11dc1e4d45 Saving changes 2026-01-27 00:47:42 -08:00
Raunak Bhagat
96f1bdb0ed Saving changes 2026-01-26 17:33:20 -08:00
Raunak Bhagat
3c52435833 Saving changes 2026-01-26 16:39:39 -08:00
Raunak Bhagat
9e731ec41f Merge search 2026-01-26 15:38:03 -08:00
Raunak Bhagat
d69e6123c7 Merge main 2026-01-26 15:28:13 -08:00
Raunak Bhagat
31a6ec2a1e More changes 2026-01-26 14:52:37 -08:00
Raunak Bhagat
12c2c69a9f Fix more layouts for search 2026-01-26 12:36:11 -08:00
Raunak Bhagat
9e17618579 Saving changes 2026-01-26 09:37:05 -08:00
Raunak Bhagat
06d805e547 Edit inputs 2026-01-26 09:30:16 -08:00
Raunak Bhagat
e1b172483a Make line item's description wrap around 2026-01-25 18:00:14 -08:00
Raunak Bhagat
5cbf2de282 feat: add AppModeProvider context and move providers to /providers
- Create AppModeProvider for unified search/chat mode state (auto/search/chat)
- Move AppProvider and AppModeProvider to new /web/src/providers directory
- Update AppHeader to use mode dropdown (InputSelect) instead of tabs
- Mode toggle now appears in header (top-left) when no active chat session
- AppHeader is now flex-positioned instead of sticky

The AppMode context allows any component to access/modify the current mode:
- auto: LLM classifies query as search or chat
- search: Forces quick document search
- chat: Forces conversational interaction

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 17:59:34 -08:00
Raunak Bhagat
93ea53e3aa Merge branch 'search' into search-2 2026-01-25 17:20:02 -08:00
Raunak Bhagat
42941b62e8 fix(tests): update e2e tests for /chat -> /app route rename
Update all e2e test files to use the new /app route instead of /chat.
This follows the route rename in commit b796c9100.

Changes:
- Updated page.goto("/chat") to page.goto("/app")
- Updated waitForURL patterns for /app routes
- Updated URL assertions and includes() checks
- Preserved /api/chat/* API endpoint references (unchanged)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:43:18 -08:00
Raunak Bhagat
d728d32165 Saving changes 2026-01-24 21:54:13 -08:00
Raunak Bhagat
d95c92bd8f merge main 2026-01-24 20:51:50 -08:00
Raunak Bhagat
620debc3ab Remove file 2026-01-24 20:45:52 -08:00
Raunak Bhagat
07cef247c6 Merge branch 'main' into search 2026-01-24 20:45:07 -08:00
Raunak Bhagat
af9fd36fcf Update names 2026-01-23 13:38:47 -08:00
Raunak Bhagat
b796c91004 refactor: rename /chat route to /app
- Rename src/app/chat/ directory to src/app/app/
- Update all route references from /chat to /app
- Update all import paths from @/app/chat/ to @/app/app/
- Add backwards compatibility redirects in next.config.js

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 09:23:08 -08:00
303 changed files with 2911 additions and 3270 deletions

View File

@@ -142,6 +142,7 @@ export { default as SvgStep3End } from "@opal/icons/step3-end";
export { default as SvgStop } from "@opal/icons/stop";
export { default as SvgStopCircle } from "@opal/icons/stop-circle";
export { default as SvgSun } from "@opal/icons/sun";
export { default as SvgTag } from "@opal/icons/tag";
export { default as SvgTerminal } from "@opal/icons/terminal";
export { default as SvgTerminalSmall } from "@opal/icons/terminal-small";
export { default as SvgTextLinesSmall } from "@opal/icons/text-lines-small";

View File

@@ -0,0 +1,20 @@
import type { IconProps } from "@opal/types";
const SvgTag = ({ size, ...props }: IconProps) => (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
{...props}
>
<path
d="M4.66666 4.66668H4.67333M13.7267 8.94001L8.94666 13.72C8.82283 13.844 8.67578 13.9423 8.51392 14.0094C8.35205 14.0765 8.17855 14.1111 8.00333 14.1111C7.82811 14.1111 7.65461 14.0765 7.49274 14.0094C7.33088 13.9423 7.18383 13.844 7.05999 13.72L1.33333 8.00001V1.33334H7.99999L13.7267 7.06001C13.975 7.30983 14.1144 7.64776 14.1144 8.00001C14.1144 8.35226 13.975 8.69019 13.7267 8.94001Z"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
export default SvgTag;

View File

@@ -98,6 +98,20 @@ const nextConfig = {
},
];
},
async redirects() {
return [
{
source: "/chat",
destination: "/app",
permanent: true,
},
{
source: "/chat/:path*",
destination: "/app/:path*",
permanent: true,
},
];
},
};
// Sentry configuration for error monitoring:

View File

@@ -15,7 +15,7 @@ import {
togglePersonaVisibility,
} from "./lib";
import { FiEdit2 } from "react-icons/fi";
import { useUser } from "@/components/user/UserProvider";
import { useUser } from "@/providers/UserProvider";
import IconButton from "@/refresh-components/buttons/IconButton";
import ConfirmationModalLayout from "@/refresh-components/layouts/ConfirmationModalLayout";
import Button from "@/refresh-components/buttons/Button";
@@ -244,7 +244,7 @@ export function PersonasTable({
className="mr-1 my-auto cursor-pointer"
onClick={() =>
router.push(
`/chat/agents/edit/${
`/app/agents/edit/${
persona.id
}?u=${Date.now()}&admin=true` as Route
)

View File

@@ -64,7 +64,7 @@ function MainContent({
<Separator />
<Title>Create an Assistant</Title>
<CreateButton href="/chat/agents/create?admin=true">
<CreateButton href="/app/agents/create?admin=true">
New Assistant
</CreateButton>
@@ -109,7 +109,7 @@ function MainContent({
<Text className="text-subtle text-sm mb-4">
...and so much more!
</Text>
<CreateButton href="/chat/agents/create?admin=true">
<CreateButton href="/app/agents/create?admin=true">
Create Your First Assistant
</CreateButton>
<div className="mt-6 pt-6 border-t border-border">

View File

@@ -102,7 +102,7 @@ export default function SlackChannelConfigsTable({
) ? (
<Link
href={
`/chat/agents/edit/${slackChannelConfig.persona.id}` as Route
`/app/agents/edit/${slackChannelConfig.persona.id}` as Route
}
className="text-primary hover:underline"
>

View File

@@ -18,7 +18,7 @@ import CardSection from "@/components/admin/CardSection";
import { useRouter } from "next/navigation";
import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces";
import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE";
import { SEARCH_TOOL_ID } from "@/app/chat/components/tools/constants";
import { SEARCH_TOOL_ID } from "@/app/app/components/tools/constants";
import { SlackChannelConfigFormFields } from "./SlackChannelConfigFormFields";
export const SlackChannelConfigCreationForm = ({

View File

@@ -385,9 +385,7 @@ export function SlackChannelConfigFormFields({
<button
type="button"
onClick={() =>
router.push(
`/chat/agents/edit/${persona.id}` as Route
)
router.push(`/app/agents/edit/${persona.id}` as Route)
}
key={persona.id}
className="p-2 bg-background-100 cursor-pointer rounded-md flex items-center gap-2"

View File

@@ -13,7 +13,7 @@ import {
GoogleDriveCredentialJson,
GoogleDriveServiceAccountCredentialJson,
} from "@/lib/connectors/credentials";
import { useUser } from "@/components/user/UserProvider";
import { useUser } from "@/providers/UserProvider";
import {
useGoogleAppCredential,
useGoogleServiceAccountKey,

View File

@@ -13,7 +13,7 @@ import {
import { GmailAuthSection, GmailJsonUploadSection } from "./Credential";
import { usePublicCredentials, useBasicConnectorStatus } from "@/lib/hooks";
import Title from "@/components/ui/title";
import { useUser } from "@/components/user/UserProvider";
import { useUser } from "@/providers/UserProvider";
import {
useGoogleAppCredential,
useGoogleServiceAccountKey,

View File

@@ -144,7 +144,6 @@ export function DiscordChannelsTable({
)
}
disabled={disabled}
className="w-[160px]"
>
<InputSelect.Trigger placeholder="-" />
<InputSelect.Content>

View File

@@ -364,7 +364,6 @@ export default function Page({ params }: Props) {
)
}
disabled={isUpdating || !guild?.enabled || personasLoading}
className="w-[200px]"
>
<InputSelect.Trigger placeholder="Select agent" />
<InputSelect.Content>

View File

@@ -20,7 +20,7 @@ import Button from "@/refresh-components/buttons/Button";
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
import { IsPublicGroupSelector } from "@/components/IsPublicGroupSelector";
import React, { useEffect, useState } from "react";
import { useUser } from "@/components/user/UserProvider";
import { useUser } from "@/providers/UserProvider";
import { ConnectorMultiSelect } from "@/components/ConnectorMultiSelect";
import { NonSelectableConnectors } from "@/components/NonSelectableConnectors";
import { FederatedConnectorSelector } from "@/components/FederatedConnectorSelector";

View File

@@ -1,4 +1,4 @@
import { useUser } from "@/components/user/UserProvider";
import { useUser } from "@/providers/UserProvider";
import { errorHandlingFetcher } from "@/lib/fetcher";
import useSWR from "swr";
import { KGConfig, KGConfigRaw } from "./interfaces";

View File

@@ -27,7 +27,7 @@ export default function AnonymousPage({
throw new Error("Failed to login as anonymous user");
}
// Redirect to the chat page and force a refresh
window.location.href = "/chat";
window.location.href = "/app";
} catch (error) {
console.error("Error logging in as anonymous user:", error);
redirect("/auth/signup?error=Anonymous");

View File

@@ -57,7 +57,7 @@ export async function GET(req: NextRequest) {
redirectTo = `/admin/actions/edit-mcp?server_id=${serverId}`;
} else {
// For user flow, redirect to chat
redirectTo = "/chat";
redirectTo = "/app";
}
}

View File

@@ -23,14 +23,14 @@ export default function Page(props: PageProps) {
// Handle invalid ID (NaN)
useEffect(() => {
if (isNaN(agentId)) {
router.push("/chat");
router.push("/app");
}
}, [agentId, router]);
// Redirect to home if agent not found after loading completes
useEffect(() => {
if (!isLoading && !agent) {
router.push("/chat");
router.push("/app");
}
}, [isLoading, agent, router]);

View File

@@ -5,10 +5,10 @@ import { HealthCheckBanner } from "@/components/health/healthcheck";
import {
personaIncludesRetrieval,
getAvailableContextTokens,
} from "@/app/chat/services/lib";
} from "@/app/app/services/lib";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { usePopup } from "@/components/admin/connectors/Popup";
import { SEARCH_PARAM_NAMES } from "@/app/chat/services/searchParams";
import { SEARCH_PARAM_NAMES } from "@/app/app/services/searchParams";
import { useFederatedConnectors, useFilters, useLlmManager } from "@/lib/hooks";
import { useForcedTools } from "@/lib/hooks/useForcedTools";
import OnyxInitializingLoader from "@/components/OnyxInitializingLoader";
@@ -17,15 +17,15 @@ import { useSettingsContext } from "@/components/settings/SettingsProvider";
import Dropzone from "react-dropzone";
import ChatInputBar, {
ChatInputBarHandle,
} from "@/app/chat/components/input/ChatInputBar";
} from "@/app/app/components/input/ChatInputBar";
import useChatSessions from "@/hooks/useChatSessions";
import useCCPairs from "@/hooks/useCCPairs";
import { useTags } from "@/lib/hooks/useTags";
import { useDocumentSets } from "@/lib/hooks/useDocumentSets";
import { useAgents } from "@/hooks/useAgents";
import { ChatPopup } from "@/app/chat/components/ChatPopup";
import { AppPopup } from "@/app/app/components/AppPopup";
import ExceptionTraceModal from "@/components/modals/ExceptionTraceModal";
import { useUser } from "@/components/user/UserProvider";
import { useUser } from "@/providers/UserProvider";
import NoAssistantModal from "@/components/modals/NoAssistantModal";
import TextView from "@/components/chat/TextView";
import Modal from "@/refresh-components/Modal";
@@ -35,33 +35,38 @@ import { getSourceMetadata } from "@/lib/sources";
import { SourceMetadata } from "@/lib/search/interfaces";
import { FederatedConnectorDetail, UserRole, ValidSources } from "@/lib/types";
import DocumentsSidebar from "@/sections/document-sidebar/DocumentsSidebar";
import { useChatController } from "@/app/chat/hooks/useChatController";
import { useAssistantController } from "@/app/chat/hooks/useAssistantController";
import { useChatSessionController } from "@/app/chat/hooks/useChatSessionController";
import { useDeepResearchToggle } from "@/app/chat/hooks/useDeepResearchToggle";
import { useIsDefaultAgent } from "@/app/chat/hooks/useIsDefaultAgent";
import { useChatController } from "@/hooks/useChatController";
import { useAssistantController } from "@/app/app/hooks/useAssistantController";
import { useChatSessionController } from "@/app/app/hooks/useChatSessionController";
import { useDeepResearchToggle } from "@/app/app/hooks/useDeepResearchToggle";
import { useIsDefaultAgent } from "@/app/app/hooks/useIsDefaultAgent";
import useQueryController from "@/hooks/useQueryController";
import useAppFocus from "@/hooks/useAppFocus";
import { useAppBackground } from "@/providers/AppBackgroundProvider";
import {
useChatSessionStore,
useCurrentMessageHistory,
} from "@/app/chat/stores/useChatSessionStore";
} from "@/app/app/stores/useChatSessionStore";
import {
useCurrentChatState,
useIsReady,
useDocumentSidebarVisible,
} from "@/app/chat/stores/useChatSessionStore";
} from "@/app/app/stores/useChatSessionStore";
import FederatedOAuthModal from "@/components/chat/FederatedOAuthModal";
import ChatScrollContainer, {
ChatScrollContainerHandle,
} from "@/components/chat/ChatScrollContainer";
import MessageList from "@/components/chat/MessageList";
import WelcomeMessage from "@/app/chat/components/WelcomeMessage";
import ProjectContextPanel from "@/app/chat/components/projects/ProjectContextPanel";
import { useProjectsContext } from "@/app/chat/projects/ProjectsContext";
import ChatUI from "@/sections/ChatUI";
import SearchResults from "@/sections/SearchUI";
import SourceFilter from "@/sections/SourceFilter";
import WelcomeMessage from "@/app/app/components/WelcomeMessage";
import ProjectContextPanel from "@/app/app/components/projects/ProjectContextPanel";
import { useProjectsContext } from "@/app/app/projects/ProjectsContext";
import {
getProjectTokenCount,
getMaxSelectedDocumentTokens,
} from "@/app/chat/projects/projectsService";
import ProjectChatSessionList from "@/app/chat/components/projects/ProjectChatSessionList";
} from "@/app/app/projects/projectsService";
import ProjectChatSessionList from "@/app/app/components/projects/ProjectChatSessionList";
import { cn } from "@/lib/utils";
import Suggestions from "@/sections/Suggestions";
import OnboardingFlow from "@/refresh-components/onboarding/OnboardingFlow";
@@ -69,20 +74,15 @@ import { OnboardingStep } from "@/refresh-components/onboarding/types";
import { useShowOnboarding } from "@/hooks/useShowOnboarding";
import * as AppLayouts from "@/layouts/app-layouts";
import { SvgChevronDown, SvgFileText } from "@opal/icons";
import ChatHeader from "@/app/chat/components/ChatHeader";
import IconButton from "@/refresh-components/buttons/IconButton";
import Spacer from "@/refresh-components/Spacer";
import { DEFAULT_CONTEXT_TOKENS } from "@/lib/constants";
import {
CHAT_BACKGROUND_NONE,
getBackgroundById,
} from "@/lib/constants/chatBackgrounds";
import Spacer from "@/refresh-components/Spacer";
export interface ChatPageProps {
firstMessage?: string;
}
export default function ChatPage({ firstMessage }: ChatPageProps) {
export default function AppPage({ firstMessage }: ChatPageProps) {
// Performance tracking
// Keeping this here in case we need to track down slow renders in the future
// const renderCount = useRef(0);
@@ -190,6 +190,8 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
assistantId: selectedAssistant?.id,
});
const { appBackgroundUrl, hasBackground } = useAppBackground();
const [presentingDocument, setPresentingDocument] =
useState<MinimalOnyxDocument | null>(null);
@@ -263,9 +265,11 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
}
}, [lastFailedFiles, setPopup, clearLastFailedFiles]);
const [projectPanelVisible, setProjectPanelVisible] = useState(true);
const chatInputBarRef = useRef<ChatInputBarHandle>(null);
// Determine current app focus for conditional rendering
const appFocus = useAppFocus();
const filterManager = useFilters();
const isDefaultAgent = useIsDefaultAgent({
@@ -378,6 +382,36 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
});
useSendMessageToParent();
const onChat = useCallback(
(message: string) => {
onSubmit({
message,
currentMessageFiles,
deepResearch: deepResearchEnabled,
});
if (showOnboarding) {
finishOnboarding();
}
},
[
onSubmit,
currentMessageFiles,
deepResearchEnabled,
showOnboarding,
finishOnboarding,
]
);
// Unified query controller - handles classification and search routing
const queryController = useQueryController(onChat);
// Source filter state for search results
const [selectedSources, setSelectedSources] = useState<string[]>([]);
// Reset source filter when search results change
useEffect(() => {
setSelectedSources([]);
}, [queryController.searchResults]);
const retrievalEnabled = useMemo(() => {
if (liveAssistant) {
@@ -443,17 +477,28 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
}
const handleChatInputSubmit = useCallback(
(message: string) => {
onSubmit({
message,
currentMessageFiles: currentMessageFiles,
deepResearch: deepResearchEnabled,
});
if (showOnboarding) {
finishOnboarding();
async (message: string) => {
// If we're in an existing chat session, always use chat mode
// (appMode only applies to new sessions)
if (currentChatSessionId) {
queryController.reset();
onSubmit({
message,
currentMessageFiles,
deepResearch: deepResearchEnabled,
});
if (showOnboarding) {
finishOnboarding();
}
return;
}
// For new sessions, let the query controller handle routing
await queryController.submit(message);
},
[
currentChatSessionId,
queryController,
onSubmit,
currentMessageFiles,
deepResearchEnabled,
@@ -462,6 +507,14 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
]
);
// Handle document click from search results
const handleSearchDocumentClick = useCallback(
(doc: MinimalOnyxDocument) => {
setPresentingDocument(doc);
},
[setPresentingDocument]
);
// Memoized callbacks for DocumentsSidebar
const handleMobileDocumentSidebarClose = useCallback(() => {
updateCurrentDocumentSidebarVisible(false);
@@ -490,15 +543,6 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
</div>
) : null;
useEffect(() => {
if (!!currentProjectId && !currentChatSessionId) {
setProjectPanelVisible(true);
}
if (!!currentChatSessionId) {
setProjectPanelVisible(false);
}
}, [currentProjectId, currentChatSessionId]);
// When no chat session exists but a project is selected, fetch the
// total tokens for the project's files so upload UX can compare
// against available context similar to session-based flows.
@@ -576,14 +620,52 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
);
}
// Chat background logic
const chatBackgroundId = user?.preferences?.chat_background;
const chatBackground = getBackgroundById(chatBackgroundId ?? null);
const hasBackground =
chatBackground && chatBackground.url !== CHAT_BACKGROUND_NONE;
if (!isReady) return <OnyxInitializingLoader />;
const hasSuggestions = (liveAssistant?.starter_messages?.length ?? 0) > 0;
const showWelcomeMessage = !queryController.classification;
const showOnboardingUi =
showOnboarding ||
(user?.role !== UserRole.ADMIN && !user?.personalization?.name);
const chatInputBar = (
<ChatInputBar
ref={chatInputBarRef}
deepResearchEnabled={deepResearchEnabled}
toggleDeepResearch={toggleDeepResearch}
toggleDocumentSidebar={toggleDocumentSidebar}
filterManager={filterManager}
llmManager={llmManager}
removeDocs={() => setSelectedDocuments([])}
retrievalEnabled={retrievalEnabled}
selectedDocuments={selectedDocuments}
initialMessage={
queryController.query ||
searchParams?.get(SEARCH_PARAM_NAMES.USER_PROMPT) ||
""
}
stopGenerating={stopGenerating}
onSubmit={handleChatInputSubmit}
chatState={currentChatState}
currentSessionFileTokenCount={
currentChatSessionId
? currentSessionFileTokenCount
: projectContextTokenCount
}
availableContextTokens={availableContextTokens}
selectedAssistant={selectedAssistant || liveAssistant}
handleFileUpload={handleMessageSpecificFileUpload}
setPresentingDocument={setPresentingDocument}
disabled={
(!llmManager.isLoadingProviders &&
llmManager.hasAnyProvider === false) ||
(!isLoadingOnboarding &&
onboardingState.currentStep !== OnboardingStep.Complete)
}
isClassifying={queryController.isClassifying}
/>
);
return (
<>
<HealthCheckBanner />
@@ -592,7 +674,7 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
Only used in the EE version of the app. */}
{popup}
<ChatPopup />
<AppPopup />
{retrievalEnabled && documentSidebarVisible && settings.isMobile && (
<div className="md:hidden">
@@ -638,7 +720,7 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
<FederatedOAuthModal />
<AppLayouts.Root disableFooter>
<AppLayouts.Root>
<Dropzone
onDrop={(acceptedFiles) =>
handleMessageSpecificFileUpload(acceptedFiles)
@@ -648,163 +730,156 @@ export default function ChatPage({ firstMessage }: ChatPageProps) {
{({ getRootProps }) => (
<div
className={cn(
"h-full w-full flex flex-col items-center outline-none relative",
hasBackground && "bg-cover bg-center bg-fixed"
"flex flex-col h-full items-center outline-none relative",
!!appBackgroundUrl && "bg-cover bg-center bg-fixed"
)}
style={
hasBackground
? { backgroundImage: `url(${chatBackground.url})` }
!!appBackgroundUrl
? { backgroundImage: `url(${appBackgroundUrl})` }
: undefined
}
{...getRootProps({ tabIndex: -1 })}
>
{/* Semi-transparent overlay for readability when background is set */}
{hasBackground && (
{!!appBackgroundUrl && (
<div className="absolute inset-0 bg-background/80 pointer-events-none" />
)}
{/* ProjectUI */}
{!!currentProjectId && projectPanelVisible && (
<ProjectContextPanel
projectTokenCount={projectContextTokenCount}
availableContextTokens={availableContextTokens}
setPresentingDocument={setPresentingDocument}
/>
)}
{queryController.classification === "search" ? (
// ========== SEARCH MODE: Grid Layout ==========
<div className="grid grid-cols-[3fr_1fr] grid-rows-[auto_1fr] h-full w-full gap-x-8">
{/* Row 1, Col 1: ChatInputBar */}
<div className="flex flex-col w-full justify-center items-end py-1 pointer-events-auto">
<div className="w-[var(--main-app-width)]">
{chatInputBar}
</div>
</div>
{/* ChatUI */}
{!!currentChatSessionId && liveAssistant && (
<ChatScrollContainer
ref={scrollContainerRef}
sessionId={currentChatSessionId}
anchorSelector={anchorSelector}
autoScroll={autoScrollEnabled}
isStreaming={isStreaming}
onScrollButtonVisibilityChange={setShowScrollButton}
disableFadeOverlay={hasBackground}
>
<AppLayouts.StickyHeader>
<ChatHeader />
</AppLayouts.StickyHeader>
<MessageList
liveAssistant={liveAssistant}
llmManager={llmManager}
deepResearchEnabled={deepResearchEnabled}
currentMessageFiles={currentMessageFiles}
setPresentingDocument={setPresentingDocument}
onSubmit={onSubmit}
onMessageSelection={onMessageSelection}
stopGenerating={stopGenerating}
onResubmit={handleResubmitLastMessage}
anchorNodeId={anchorNodeId}
disableBlur={!hasBackground}
/>
</ChatScrollContainer>
)}
{/* Row 1, Col 2: Empty */}
<div />
{!currentChatSessionId && !currentProjectId && (
<div className="w-full flex-1 flex flex-col items-center justify-end">
<WelcomeMessage
agent={liveAssistant}
isDefaultAgent={isDefaultAgent}
/>
<Spacer rem={1.5} />
{/* Row 2, Col 1: Search Results */}
<div className="min-h-0 overflow-hidden flex flex-col items-end">
<SearchResults
results={queryController.searchResults}
llmSelectedDocIds={queryController.llmSelectedDocIds}
onDocumentClick={handleSearchDocumentClick}
selectedSources={selectedSources}
/>
</div>
{/* Row 2, Col 2: Source Filter */}
<div className="min-h-0 overflow-hidden">
<SourceFilter
results={queryController.searchResults}
selectedSources={selectedSources}
onSourceChange={setSelectedSources}
/>
</div>
</div>
)}
{/* ChatInputBar container - absolutely positioned when in chat, centered when no session */}
<div
className={cn(
"flex justify-center",
currentChatSessionId
? "absolute bottom-6 left-0 right-0 pointer-events-none"
: "w-full"
)}
>
<div
className={cn(
"w-[min(50rem,100%)] z-sticky flex flex-col px-4",
currentChatSessionId && "pointer-events-auto"
)}
>
{/* Scroll to bottom button - positioned above ChatInputBar */}
{showScrollButton && (
<div className="mb-2 self-center">
<IconButton
icon={SvgChevronDown}
onClick={handleScrollToBottom}
aria-label="Scroll to bottom"
) : (
// ========== NORMAL MODE: Flex Layout ==========
<>
{/* Upper Block */}
<div className="flex-1 min-h-0 w-full flex flex-col justify-end items-center overflow-y-auto overflow-x-hidden">
{/* ProjectUI */}
{appFocus.isProject() && (
<ProjectContextPanel
projectTokenCount={projectContextTokenCount}
availableContextTokens={availableContextTokens}
setPresentingDocument={setPresentingDocument}
/>
)}
{/* ChatUI */}
{(appFocus.isChat() ||
queryController.classification === "chat") && (
<ChatScrollContainer
ref={scrollContainerRef}
sessionId={appFocus.getId()!}
anchorSelector={anchorSelector}
autoScroll={autoScrollEnabled}
isStreaming={isStreaming}
onScrollButtonVisibilityChange={setShowScrollButton}
disableFadeOverlay={hasBackground}
>
{showScrollButton && (
<div className="absolute bottom-4 z-sticky">
<IconButton
icon={SvgChevronDown}
onClick={handleScrollToBottom}
secondary
aria-label="Scroll to bottom"
/>
</div>
)}
<ChatUI
liveAssistant={liveAssistant!}
llmManager={llmManager}
deepResearchEnabled={deepResearchEnabled}
currentMessageFiles={currentMessageFiles}
setPresentingDocument={setPresentingDocument}
onSubmit={onSubmit}
onMessageSelection={onMessageSelection}
stopGenerating={stopGenerating}
onResubmit={handleResubmitLastMessage}
anchorNodeId={anchorNodeId}
disableBlur={!hasBackground}
/>
</ChatScrollContainer>
)}
{/* Onboarding */}
{(appFocus.isNewSession() || appFocus.isAgent()) &&
showOnboardingUi && (
<>
<OnboardingFlow
handleHideOnboarding={hideOnboarding}
handleFinishOnboarding={finishOnboarding}
state={onboardingState}
actions={onboardingActions}
llmDescriptors={llmDescriptors}
/>
<Spacer rem={1} />
</>
)}
{/* Welcome Message */}
{(appFocus.isNewSession() || appFocus.isAgent()) &&
showWelcomeMessage && (
<>
<WelcomeMessage
agent={liveAssistant}
isDefaultAgent={isDefaultAgent}
/>
<Spacer rem={1} />
</>
)}
</div>
{/* ChatInputBar - centrally laid out */}
<div className="w-[var(--main-app-width)] pointer-events-auto py-1">
{chatInputBar}
</div>
{/* Lower Block */}
{((appFocus.isNewSession() &&
queryController.classification !== "chat") ||
appFocus.isAgent() ||
appFocus.isProject()) && (
<div className="flex-1 min-h-0 w-full">
{/* Agent-Suggestions */}
{appFocus.isAgent() && hasSuggestions && (
<Suggestions onSubmit={onSubmit} />
)}
{/* Project Sessions */}
{appFocus.isProject() && <ProjectChatSessionList />}
</div>
)}
{(showOnboarding ||
(user?.role !== UserRole.ADMIN &&
!user?.personalization?.name)) &&
currentProjectId === null && (
<OnboardingFlow
handleHideOnboarding={hideOnboarding}
handleFinishOnboarding={finishOnboarding}
state={onboardingState}
actions={onboardingActions}
llmDescriptors={llmDescriptors}
/>
)}
<ChatInputBar
ref={chatInputBarRef}
deepResearchEnabled={deepResearchEnabled}
toggleDeepResearch={toggleDeepResearch}
toggleDocumentSidebar={toggleDocumentSidebar}
filterManager={filterManager}
llmManager={llmManager}
removeDocs={() => setSelectedDocuments([])}
retrievalEnabled={retrievalEnabled}
selectedDocuments={selectedDocuments}
initialMessage={
searchParams?.get(SEARCH_PARAM_NAMES.USER_PROMPT) || ""
}
stopGenerating={stopGenerating}
onSubmit={handleChatInputSubmit}
chatState={currentChatState}
currentSessionFileTokenCount={
currentChatSessionId
? currentSessionFileTokenCount
: projectContextTokenCount
}
availableContextTokens={availableContextTokens}
selectedAssistant={selectedAssistant || liveAssistant}
handleFileUpload={handleMessageSpecificFileUpload}
setPresentingDocument={setPresentingDocument}
disabled={
(!llmManager.isLoadingProviders &&
llmManager.hasAnyProvider === false) ||
(!isLoadingOnboarding &&
onboardingState.currentStep !== OnboardingStep.Complete)
}
/>
<Spacer rem={0.5} />
{!!currentProjectId && <ProjectChatSessionList />}
</div>
</div>
{/* SearchUI */}
{!currentChatSessionId && !currentProjectId && (
<div className="flex flex-1 flex-col items-center w-full">
{liveAssistant?.starter_messages &&
liveAssistant.starter_messages.length > 0 &&
messageHistory.length === 0 &&
!currentProjectId &&
!currentChatSessionId && (
<div className="max-w-[50rem] w-full">
<Suggestions onSubmit={onSubmit} />
</div>
)}
</div>
</>
)}
<AppLayouts.Footer />
</div>
)}
</Dropzone>

View File

@@ -25,7 +25,7 @@ const CustomLogoHeaderIcon = ({ className, size = 24 }: IconProps) => (
/>
);
export function ChatPopup() {
export function AppPopup() {
const [completedFlow, setCompletedFlow] = useState(true);
const [showConsentError, setShowConsentError] = useState(false);
const [consentChecked, setConsentChecked] = useState(false);

View File

@@ -10,6 +10,7 @@ import Text from "@/refresh-components/texts/Text";
import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces";
import { useState, useEffect } from "react";
import { useSettingsContext } from "@/components/settings/SettingsProvider";
import { Section } from "@/layouts/general-layouts";
export interface WelcomeMessageProps {
agent?: MinimalPersonaSnapshot;
@@ -38,31 +39,32 @@ export default function WelcomeMessage({
if (isDefaultAgent) {
content = (
<div data-testid="onyx-logo" className="flex flex-row items-center gap-4">
<Logo folded size={32} />
<Text as="p" headingH2>
{greeting}
</Text>
<div data-testid="onyx-logo">
<Section flexDirection="row" gap={1}>
<Logo folded size={32} />
<Text as="p" headingH2>
{greeting}
</Text>
</Section>
</div>
);
} else if (agent) {
content = (
<>
<div
data-testid="assistant-name-display"
className="flex flex-row items-center gap-3"
>
<AgentAvatar agent={agent} size={36} />
<Text as="p" headingH2>
{agent.name}
</Text>
<Section gap={0.25}>
<div data-testid="assistant-name-display">
<Section flexDirection="row" gap={1}>
<AgentAvatar agent={agent} size={36} />
<Text as="p" headingH2>
{agent.name}
</Text>
</Section>
</div>
{agent.description && (
<Text as="p" secondaryBody text03>
<Text secondaryBody text03>
{agent.description}
</Text>
)}
</>
</Section>
);
}
@@ -71,10 +73,7 @@ export default function WelcomeMessage({
if (!content) return null;
return (
<div
data-testid="chat-intro"
className="flex flex-col items-center justify-center gap-3 max-w-[50rem]"
>
<div data-testid="chat-intro" className="w-[min(50rem,100%)]">
{content}
</div>
);

View File

@@ -1,7 +1,7 @@
"use client";
import { useEffect } from "react";
import { buildImgUrl } from "@/app/chat/components/files/images/utils";
import { buildImgUrl } from "@/app/app/components/files/images/utils";
import { cn } from "@/lib/utils";
import * as Dialog from "@radix-ui/react-dialog";

View File

@@ -1,8 +1,8 @@
import { useState } from "react";
import { FiDownload } from "react-icons/fi";
import { ImageShape } from "@/app/chat/services/streamingModels";
import { FullImageModal } from "@/app/chat/components/files/images/FullImageModal";
import { buildImgUrl } from "@/app/chat/components/files/images/utils";
import { ImageShape } from "@/app/app/services/streamingModels";
import { FullImageModal } from "@/app/app/components/files/images/FullImageModal";
import { buildImgUrl } from "@/app/app/components/files/images/utils";
import IconButton from "@/refresh-components/buttons/IconButton";
import { cn } from "@/lib/utils";

View File

@@ -11,24 +11,24 @@ import React, {
import LineItem from "@/refresh-components/buttons/LineItem";
import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces";
import LLMPopover from "@/refresh-components/popovers/LLMPopover";
import { InputPrompt } from "@/app/chat/interfaces";
import { InputPrompt } from "@/app/app/interfaces";
import { FilterManager, LlmManager, useFederatedConnectors } from "@/lib/hooks";
import usePromptShortcuts from "@/hooks/usePromptShortcuts";
import useFilter from "@/hooks/useFilter";
import useCCPairs from "@/hooks/useCCPairs";
import { OnyxDocument, MinimalOnyxDocument } from "@/lib/search/interfaces";
import { ChatState } from "@/app/chat/interfaces";
import { ChatState } from "@/app/app/interfaces";
import { useForcedTools } from "@/lib/hooks/useForcedTools";
import { getFormattedDateRangeString } from "@/lib/dateUtils";
import { truncateString, cn } from "@/lib/utils";
import { useUser } from "@/components/user/UserProvider";
import { useUser } from "@/providers/UserProvider";
import { SettingsContext } from "@/components/settings/SettingsProvider";
import { useProjectsContext } from "@/app/chat/projects/ProjectsContext";
import { FileCard } from "@/app/chat/components/input/FileCard";
import { useProjectsContext } from "@/app/app/projects/ProjectsContext";
import { FileCard } from "@/app/app/components/input/FileCard";
import {
ProjectFile,
UserFileStatus,
} from "@/app/chat/projects/projectsService";
} from "@/app/app/projects/projectsService";
import IconButton from "@/refresh-components/buttons/IconButton";
import FilePickerPopover from "@/refresh-components/popovers/FilePickerPopover";
import ActionsPopover from "@/refresh-components/popovers/ActionsPopover";
@@ -36,7 +36,7 @@ import SelectButton from "@/refresh-components/buttons/SelectButton";
import {
getIconForAction,
hasSearchToolsAvailable,
} from "@/app/chat/services/actionUtils";
} from "@/app/app/services/actionUtils";
import {
SvgArrowUp,
SvgCalendar,
@@ -49,6 +49,7 @@ import {
SvgX,
} from "@opal/icons";
import Popover from "@/refresh-components/Popover";
import SimpleLoader from "@/refresh-components/loaders/SimpleLoader";
const LINE_HEIGHT = 24;
const MIN_INPUT_HEIGHT = 44;
@@ -120,6 +121,8 @@ export interface ChatInputBarProps {
setPresentingDocument?: (document: MinimalOnyxDocument) => void;
toggleDeepResearch: () => void;
disabled: boolean;
/** Whether query classification is in progress */
isClassifying?: boolean;
ref?: React.Ref<ChatInputBarHandle>;
}
@@ -145,6 +148,7 @@ const ChatInputBar = React.memo(
toggleDeepResearch,
setPresentingDocument,
disabled,
isClassifying = false,
ref,
}: ChatInputBarProps) => {
// Internal message state - kept local to avoid parent re-renders on every keystroke
@@ -389,7 +393,7 @@ const ChatInputBar = React.memo(
e.preventDefault();
if (tabbingIconIndex === sortedFilteredPrompts.length) {
// "Create a new prompt" is selected
window.open("/chat/settings/chat-preferences", "_self");
window.open("/app/settings/chat-preferences", "_self");
} else {
const selectedPrompt = sortedFilteredPrompts[tabbingIconIndex];
if (selectedPrompt) {
@@ -516,7 +520,7 @@ const ChatInputBar = React.memo(
sortedFilteredPrompts.length > 0 ? null : undefined,
<LineItem
key="create-new"
href="/chat/settings/chat-preferences"
href="/app/settings/chat-preferences"
icon={SvgPlus}
selected={tabbingIconIndex === sortedFilteredPrompts.length}
emphasized={tabbingIconIndex === sortedFilteredPrompts.length}
@@ -690,9 +694,17 @@ const ChatInputBar = React.memo(
{/* Submit button - always visible */}
<IconButton
id="onyx-chat-input-send-button"
icon={chatState === "input" ? SvgArrowUp : SvgStop}
icon={
isClassifying
? SimpleLoader
: chatState === "input"
? SvgArrowUp
: SvgStop
}
disabled={
(chatState === "input" && !message) || hasUploadingFiles
isClassifying ||
(chatState === "input" && !message) ||
hasUploadingFiles
}
onClick={() => {
if (chatState == "streaming") {

View File

@@ -1,13 +1,14 @@
"use client";
import React, { useMemo } from "react";
import type { ProjectFile } from "@/app/chat/projects/projectsService";
import { UserFileStatus } from "@/app/chat/projects/projectsService";
import type { ProjectFile } from "@/app/app/projects/projectsService";
import { UserFileStatus } from "@/app/app/projects/projectsService";
import Text from "@/refresh-components/texts/Text";
import Truncated from "@/refresh-components/texts/Truncated";
import { cn, isImageFile } from "@/lib/utils";
import SimpleLoader from "@/refresh-components/loaders/SimpleLoader";
import { SvgFileText, SvgX } from "@opal/icons";
function ImageFileCard({
file,
imageUrl,

View File

@@ -4,8 +4,8 @@ import { useState } from "react";
import Button from "@/refresh-components/buttons/Button";
import { Callout } from "@/components/ui/callout";
import Text from "@/components/ui/text";
import { ChatSession, ChatSessionSharedStatus } from "@/app/chat/interfaces";
import { SEARCH_PARAM_NAMES } from "@/app/chat/services/searchParams";
import { ChatSession, ChatSessionSharedStatus } from "@/app/app/interfaces";
import { SEARCH_PARAM_NAMES } from "@/app/app/services/searchParams";
import { usePopup } from "@/components/admin/connectors/Popup";
import { structureValue } from "@/lib/llm/utils";
import { LlmDescriptor, useLlmManager } from "@/lib/hooks";
@@ -14,15 +14,15 @@ import { AdvancedOptionsToggle } from "@/components/AdvancedOptionsToggle";
import { cn } from "@/lib/utils";
import { useCurrentAgent } from "@/hooks/useAgents";
import { useSearchParams } from "next/navigation";
import { useChatSessionStore } from "@/app/chat/stores/useChatSessionStore";
import { useChatSessionStore } from "@/app/app/stores/useChatSessionStore";
import ConfirmationModalLayout from "@/refresh-components/layouts/ConfirmationModalLayout";
import CopyIconButton from "@/refresh-components/buttons/CopyIconButton";
import { copyAll } from "@/app/chat/message/copyingUtils";
import { copyAll } from "@/app/app/message/copyingUtils";
import { SvgCopy, SvgShare } from "@opal/icons";
function buildShareLink(chatSessionId: string) {
const baseUrl = `${window.location.protocol}//${window.location.host}`;
return `${baseUrl}/chat/shared/${chatSessionId}`;
return `${baseUrl}/app/shared/${chatSessionId}`;
}
async function generateShareLink(chatSessionId: string) {
@@ -53,7 +53,7 @@ async function generateSeedLink(
modelOverride.modelName
)
: null;
return `${baseUrl}/chat${
return `${baseUrl}/app${
message
? `?${SEARCH_PARAM_NAMES.USER_PROMPT}=${encodeURIComponent(message)}`
: ""

View File

@@ -4,12 +4,11 @@ import React, { useMemo } from "react";
import Link from "next/link";
import { ChatSessionMorePopup } from "@/components/sidebar/ChatSessionMorePopup";
import { useProjectsContext } from "../../projects/ProjectsContext";
import { ChatSession } from "@/app/chat/interfaces";
import { ChatSession } from "@/app/app/interfaces";
import AgentAvatar from "@/refresh-components/avatars/AgentAvatar";
import { useAgents } from "@/hooks/useAgents";
import { formatRelativeTime } from "./project_utils";
import Text from "@/refresh-components/texts/Text";
import { cn } from "@/lib/utils";
import { cn, formatRelativeTime } from "@/lib/utils";
import { UNNAMED_CHAT } from "@/lib/constants";
import ChatSessionSkeleton from "@/refresh-components/skeletons/ChatSessionSkeleton";
import { SvgBubbleText } from "@opal/icons";
@@ -60,7 +59,7 @@ export default function ProjectChatSessionList() {
{projectChats.map((chat) => (
<Link
key={chat.id}
href={{ pathname: "/chat", query: { chatId: chat.id } }}
href={{ pathname: "/app", query: { chatId: chat.id } }}
className="relative flex w-full"
onMouseEnter={() => setHoveredChatId(chat.id)}
onMouseLeave={() => setHoveredChatId(null)}

View File

@@ -18,7 +18,7 @@ import CreateButton from "@/refresh-components/buttons/CreateButton";
import { FileCard } from "../input/FileCard";
import { hasNonImageFiles } from "@/lib/utils";
import IconButton from "@/refresh-components/buttons/IconButton";
import { FileCardSkeleton } from "@/app/chat/components/input/FileCard";
import { FileCardSkeleton } from "@/app/app/components/input/FileCard";
import ButtonRenaming from "@/refresh-components/buttons/ButtonRenaming";
import { UserFileStatus } from "../../projects/projectsService";
import { SvgAddLines, SvgEdit, SvgFiles, SvgFolderOpen } from "@opal/icons";

View File

@@ -1,9 +1,9 @@
"use client";
import { useState } from "react";
import { ChatFileType, FileDescriptor } from "@/app/chat/interfaces";
import { ChatFileType, FileDescriptor } from "@/app/app/interfaces";
import Attachment from "@/refresh-components/Attachment";
import { InMessageImage } from "@/app/chat/components/files/images/InMessageImage";
import { InMessageImage } from "@/app/app/components/files/images/InMessageImage";
import CsvContent from "@/components/tools/CSVContent";
import TextView from "@/components/chat/TextView";
import { MinimalOnyxDocument } from "@/lib/search/interfaces";

View File

@@ -1,9 +1,9 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
import { FileDescriptor } from "@/app/chat/interfaces";
import { FileDescriptor } from "@/app/app/interfaces";
import "katex/dist/katex.min.css";
import MessageSwitcher from "@/app/chat/message/MessageSwitcher";
import MessageSwitcher from "@/app/app/message/MessageSwitcher";
import Text from "@/refresh-components/texts/Text";
import { cn } from "@/lib/utils";
import IconButton from "@/refresh-components/buttons/IconButton";

View File

@@ -17,25 +17,25 @@ import {
TopLevelBranching,
StopReason,
Stop,
} from "@/app/chat/services/streamingModels";
import { CitationMap } from "@/app/chat/interfaces";
import { FullChatState } from "@/app/chat/message/messageComponents/interfaces";
import { FeedbackType } from "@/app/chat/interfaces";
} from "@/app/app/services/streamingModels";
import { CitationMap } from "@/app/app/interfaces";
import { FullChatState } from "@/app/app/message/messageComponents/interfaces";
import { FeedbackType } from "@/app/app/interfaces";
import { OnyxDocument } from "@/lib/search/interfaces";
import CitedSourcesToggle from "@/app/chat/message/messageComponents/CitedSourcesToggle";
import CitedSourcesToggle from "@/app/app/message/messageComponents/CitedSourcesToggle";
import { TooltipGroup } from "@/components/tooltip/CustomTooltip";
import {
useChatSessionStore,
useDocumentSidebarVisible,
useSelectedNodeForDocDisplay,
useCurrentChatState,
} from "@/app/chat/stores/useChatSessionStore";
} from "@/app/app/stores/useChatSessionStore";
import {
handleCopy,
convertMarkdownTablesToTsv,
} from "@/app/chat/message/copyingUtils";
import MessageSwitcher from "@/app/chat/message/MessageSwitcher";
import { BlinkingDot } from "@/app/chat/message/BlinkingDot";
} from "@/app/app/message/copyingUtils";
import MessageSwitcher from "@/app/app/message/MessageSwitcher";
import { BlinkingDot } from "@/app/app/message/BlinkingDot";
import {
getTextContent,
isActualToolCallPacket,
@@ -43,19 +43,19 @@ import {
isFinalAnswerComing,
isStreamingComplete,
isToolPacket,
} from "@/app/chat/services/packetUtils";
import { removeThinkingTokens } from "@/app/chat/services/thinkingTokens";
import { useMessageSwitching } from "@/app/chat/message/messageComponents/hooks/useMessageSwitching";
import MultiToolRenderer from "@/app/chat/message/messageComponents/MultiToolRenderer";
import { RendererComponent } from "@/app/chat/message/messageComponents/renderMessageComponent";
import { parseToolKey } from "@/app/chat/message/messageComponents/toolDisplayHelpers";
} from "@/app/app/services/packetUtils";
import { removeThinkingTokens } from "@/app/app/services/thinkingTokens";
import { useMessageSwitching } from "@/app/app/message/messageComponents/hooks/useMessageSwitching";
import MultiToolRenderer from "@/app/app/message/messageComponents/MultiToolRenderer";
import { RendererComponent } from "@/app/app/message/messageComponents/renderMessageComponent";
import { parseToolKey } from "@/app/app/message/messageComponents/toolDisplayHelpers";
import AgentAvatar from "@/refresh-components/avatars/AgentAvatar";
import IconButton from "@/refresh-components/buttons/IconButton";
import CopyIconButton from "@/refresh-components/buttons/CopyIconButton";
import LLMPopover from "@/refresh-components/popovers/LLMPopover";
import { parseLlmDescriptor } from "@/lib/llm/utils";
import { LlmDescriptor, LlmManager } from "@/lib/hooks";
import { Message } from "@/app/chat/interfaces";
import { Message } from "@/app/app/interfaces";
import { useCreateModal } from "@/refresh-components/contexts/ModalContext";
import FeedbackModal, {
FeedbackModalProps,

View File

@@ -1,3 +1,5 @@
"use client";
import { useState, useMemo, useEffect, JSX } from "react";
import {
FiCheckCircle,
@@ -12,7 +14,7 @@ import {
PacketType,
SearchToolPacket,
StopReason,
} from "@/app/chat/services/streamingModels";
} from "@/app/app/services/streamingModels";
import { FullChatState, RendererResult } from "./interfaces";
import { RendererComponent } from "./renderMessageComponent";
import { isToolPacket } from "../../services/packetUtils";
@@ -32,8 +34,8 @@ import {
constructCurrentSearchState,
} from "./renderers/SearchToolRenderer";
import { SvgChevronDown, SvgChevronDownSmall, SvgXCircle } from "@opal/icons";
import { LoadingSpinner } from "../../chat_search/LoadingSpinner";
import SimpleTooltip from "@/refresh-components/SimpleTooltip";
import SimpleLoader from "@/refresh-components/loaders/SimpleLoader";
enum DisplayType {
REGULAR = "regular",
@@ -269,7 +271,7 @@ function ParallelToolTabs({
>
{tab.name}
</span>
{isLoading && <LoadingSpinner size="small" />}
{isLoading && <SimpleLoader />}
{tab.isComplete && !isLoading && tab.hasError && (
<FiXCircle
className={cn(

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { Packet } from "@/app/chat/services/streamingModels";
import { Packet } from "@/app/app/services/streamingModels";
// Control the rate of packet streaming (packets per second)
const PACKET_DELAY_MS = 10;

View File

@@ -1,4 +1,4 @@
import { Packet } from "@/app/chat/services/streamingModels";
import { Packet } from "@/app/app/services/streamingModels";
import { useMemo, useState, useCallback, useEffect } from "react";
import { useRef } from "react";
import { getToolKey, parseToolKey } from "../toolDisplayHelpers";

View File

@@ -5,14 +5,14 @@ import remarkMath from "remark-math";
import rehypeHighlight from "rehype-highlight";
import rehypeKatex from "rehype-katex";
import "katex/dist/katex.min.css";
import "@/app/chat/message/custom-code-styles.css";
import { FullChatState } from "@/app/chat/message/messageComponents/interfaces";
import "@/app/app/message/custom-code-styles.css";
import { FullChatState } from "@/app/app/message/messageComponents/interfaces";
import {
MemoizedAnchor,
MemoizedParagraph,
} from "@/app/chat/message/MemoizedTextComponents";
import { extractCodeText, preprocessLaTeX } from "@/app/chat/message/codeUtils";
import { CodeBlock } from "@/app/chat/message/CodeBlock";
} from "@/app/app/message/MemoizedTextComponents";
import { extractCodeText, preprocessLaTeX } from "@/app/app/message/codeUtils";
import { CodeBlock } from "@/app/app/message/CodeBlock";
import { transformLinkUri, cn } from "@/lib/utils";
/**

View File

@@ -10,7 +10,7 @@ import {
import { MessageRenderer } from "../interfaces";
import { truncateString } from "@/lib/utils";
import { OnyxDocument } from "@/lib/search/interfaces";
import { SourceChip2 } from "@/app/chat/components/SourceChip2";
import { SourceChip2 } from "@/app/app/components/SourceChip2";
import { BlinkingDot } from "../../BlinkingDot";
import { clearTimeoutRefs } from "../timing";

View File

@@ -5,12 +5,12 @@ import {
PythonToolStart,
PythonToolDelta,
SectionEnd,
} from "@/app/chat/services/streamingModels";
} from "@/app/app/services/streamingModels";
import {
MessageRenderer,
RenderType,
} from "@/app/chat/message/messageComponents/interfaces";
import { CodeBlock } from "@/app/chat/message/CodeBlock";
} from "@/app/app/message/messageComponents/interfaces";
import { CodeBlock } from "@/app/app/message/CodeBlock";
import hljs from "highlight.js/lib/core";
import python from "highlight.js/lib/languages/python";
import { SvgCode } from "@opal/icons";

View File

@@ -10,12 +10,13 @@ import {
} from "../../../services/streamingModels";
import { MessageRenderer, RendererResult } from "../interfaces";
import { truncateString } from "@/lib/utils";
import { SourceChip2 } from "@/app/chat/components/SourceChip2";
import { SourceChip2 } from "@/app/app/components/SourceChip2";
import { BlinkingDot } from "../../BlinkingDot";
import { OnyxDocument } from "@/lib/search/interfaces";
import { ResultIcon } from "@/components/chat/sources/SourceCard";
import { IconType } from "react-icons";
import { OnyxIconType } from "@/components/icons/icons";
import { WebResultIcon } from "@/components/WebResultIcon";
import { SourceIcon } from "@/components/SourceIcon";
const MAX_TITLE_LENGTH = 25;
@@ -37,6 +38,19 @@ export interface SearchState {
isInternetSearch: boolean;
}
const ResultIcon = ({ doc, size }: { doc: OnyxDocument; size: number }) => {
return (
<div className="flex-none">
{" "}
{doc.is_internet || doc.source_type === "web" ? (
<WebResultIcon size={size} url={doc.link} />
) : (
<SourceIcon iconSize={size} sourceType={doc.source_type} />
)}
</div>
);
};
export const constructCurrentSearchState = (
packets: SearchToolPacket[]
): SearchState => {

View File

@@ -1,7 +1,7 @@
"use client";
import React, { FunctionComponent, useMemo, useCallback } from "react";
import { StopReason } from "@/app/chat/services/streamingModels";
import { StopReason } from "@/app/app/services/streamingModels";
import { FullChatState } from "../interfaces";
import { TurnGroup, TransformedStep } from "./transformers";
import { cn } from "@/lib/utils";
@@ -19,18 +19,18 @@ import {
useTimelineExpansion,
useTimelineMetrics,
useTimelineHeader,
} from "@/app/chat/message/messageComponents/timeline/hooks";
} from "@/app/app/message/messageComponents/timeline/hooks";
import {
isResearchAgentPackets,
stepSupportsCompact,
} from "@/app/chat/message/messageComponents/timeline/packetHelpers";
} from "@/app/app/message/messageComponents/timeline/packetHelpers";
import {
StreamingHeader,
CollapsedHeader,
ExpandedHeader,
StoppedHeader,
ParallelStreamingHeader,
} from "@/app/chat/message/messageComponents/timeline/headers";
} from "@/app/app/message/messageComponents/timeline/headers";
// =============================================================================
// TimelineStep Component - Memoized to prevent re-renders

View File

@@ -6,7 +6,7 @@ import React, {
useCallback,
FunctionComponent,
} from "react";
import { StopReason } from "@/app/chat/services/streamingModels";
import { StopReason } from "@/app/app/services/streamingModels";
import { FullChatState } from "../interfaces";
import { TurnGroup } from "./transformers";
import { getToolName, getToolIcon } from "../toolDisplayHelpers";
@@ -17,7 +17,7 @@ import {
import Tabs from "@/refresh-components/Tabs";
import { SvgBranch } from "@opal/icons";
import { StepContainer } from "./StepContainer";
import { isResearchAgentPackets } from "@/app/chat/message/messageComponents/timeline/packetHelpers";
import { isResearchAgentPackets } from "@/app/app/message/messageComponents/timeline/packetHelpers";
import { IconProps } from "@/components/icons/icons";
export interface ParallelTimelineTabsProps {

View File

@@ -1,7 +1,7 @@
"use client";
import React, { useState, JSX } from "react";
import { Packet, StopReason } from "@/app/chat/services/streamingModels";
import { Packet, StopReason } from "@/app/app/services/streamingModels";
import { FullChatState, RenderType, RendererResult } from "../interfaces";
import { findRenderer } from "../renderMessageComponent";

View File

@@ -2,7 +2,7 @@ import React from "react";
import { SvgExpand } from "@opal/icons";
import Button from "@/refresh-components/buttons/Button";
import Text from "@/refresh-components/texts/Text";
import type { UniqueTool } from "@/app/chat/message/messageComponents/timeline/hooks";
import type { UniqueTool } from "@/app/app/message/messageComponents/timeline/hooks";
export interface CollapsedHeaderProps {
uniqueTools: UniqueTool[];

View File

@@ -10,15 +10,15 @@ import {
Stop,
SearchToolStart,
CustomToolStart,
} from "@/app/chat/services/streamingModels";
import { CitationMap } from "@/app/chat/interfaces";
} from "@/app/app/services/streamingModels";
import { CitationMap } from "@/app/app/interfaces";
import { OnyxDocument } from "@/lib/search/interfaces";
import {
isActualToolCallPacket,
isToolPacket,
isDisplayPacket,
} from "@/app/chat/services/packetUtils";
import { parseToolKey } from "@/app/chat/message/messageComponents/toolDisplayHelpers";
} from "@/app/app/services/packetUtils";
import { parseToolKey } from "@/app/app/message/messageComponents/toolDisplayHelpers";
// Re-export parseToolKey for consumers that import from this module
export { parseToolKey };

View File

@@ -3,20 +3,20 @@ import {
Packet,
StreamingCitation,
StopReason,
} from "@/app/chat/services/streamingModels";
import { CitationMap } from "@/app/chat/interfaces";
} from "@/app/app/services/streamingModels";
import { CitationMap } from "@/app/app/interfaces";
import { OnyxDocument } from "@/lib/search/interfaces";
import {
ProcessorState,
GroupedPacket,
createInitialState,
processPackets,
} from "@/app/chat/message/messageComponents/timeline/hooks/packetProcessor";
} from "@/app/app/message/messageComponents/timeline/hooks/packetProcessor";
import {
transformPacketGroups,
groupStepsByTurn,
TurnGroup,
} from "@/app/chat/message/messageComponents/timeline/transformers";
} from "@/app/app/message/messageComponents/timeline/transformers";
export interface UsePacketProcessorResult {
// Data

View File

@@ -5,8 +5,8 @@ import {
SearchToolPacket,
StopReason,
CustomToolStart,
} from "@/app/chat/services/streamingModels";
import { constructCurrentSearchState } from "@/app/chat/message/messageComponents/timeline/renderers/search/searchStateUtils";
} from "@/app/app/services/streamingModels";
import { constructCurrentSearchState } from "@/app/app/message/messageComponents/timeline/renderers/search/searchStateUtils";
export interface TimelineHeaderResult {
headerText: string;

View File

@@ -2,12 +2,12 @@ import { useMemo } from "react";
import {
TurnGroup,
TransformedStep,
} from "@/app/chat/message/messageComponents/timeline/transformers";
import { getToolIconByName } from "@/app/chat/message/messageComponents/toolDisplayHelpers";
} from "@/app/app/message/messageComponents/timeline/transformers";
import { getToolIconByName } from "@/app/app/message/messageComponents/toolDisplayHelpers";
import {
isResearchAgentPackets,
stepSupportsCompact,
} from "@/app/chat/message/messageComponents/timeline/packetHelpers";
} from "@/app/app/message/messageComponents/timeline/packetHelpers";
export interface UniqueTool {
key: string;

View File

@@ -1,4 +1,4 @@
import { Packet, PacketType } from "@/app/chat/services/streamingModels";
import { Packet, PacketType } from "@/app/app/services/streamingModels";
// Packet types with renderers supporting compact mode
export const COMPACT_SUPPORTED_PACKET_TYPES = new Set<PacketType>([

View File

@@ -5,12 +5,12 @@ import {
PythonToolStart,
PythonToolDelta,
SectionEnd,
} from "@/app/chat/services/streamingModels";
} from "@/app/app/services/streamingModels";
import {
MessageRenderer,
RenderType,
} from "@/app/chat/message/messageComponents/interfaces";
import { CodeBlock } from "@/app/chat/message/CodeBlock";
} from "@/app/app/message/messageComponents/interfaces";
import { CodeBlock } from "@/app/app/message/CodeBlock";
import hljs from "highlight.js/lib/core";
import python from "highlight.js/lib/languages/python";
import { SvgTerminal } from "@opal/icons";

View File

@@ -4,15 +4,15 @@ import { FiList } from "react-icons/fi";
import {
DeepResearchPlanPacket,
PacketType,
} from "@/app/chat/services/streamingModels";
} from "@/app/app/services/streamingModels";
import {
MessageRenderer,
FullChatState,
} from "@/app/chat/message/messageComponents/interfaces";
import { usePacketAnimationAndCollapse } from "@/app/chat/message/messageComponents/hooks/usePacketAnimationAndCollapse";
} from "@/app/app/message/messageComponents/interfaces";
import { usePacketAnimationAndCollapse } from "@/app/app/message/messageComponents/hooks/usePacketAnimationAndCollapse";
import MinimalMarkdown from "@/components/chat/MinimalMarkdown";
import ExpandableTextDisplay from "@/refresh-components/texts/ExpandableTextDisplay";
import { mutedTextMarkdownComponents } from "@/app/chat/message/messageComponents/timeline/renderers/sharedMarkdownComponents";
import { mutedTextMarkdownComponents } from "@/app/app/message/messageComponents/timeline/renderers/sharedMarkdownComponents";
/**
* Renderer for deep research plan packets.

View File

@@ -15,19 +15,19 @@ import {
ResearchAgentPacket,
ResearchAgentStart,
IntermediateReportDelta,
} from "@/app/chat/services/streamingModels";
} from "@/app/app/services/streamingModels";
import {
MessageRenderer,
FullChatState,
} from "@/app/chat/message/messageComponents/interfaces";
import { getToolName } from "@/app/chat/message/messageComponents/toolDisplayHelpers";
import { StepContainer } from "@/app/chat/message/messageComponents/timeline/StepContainer";
} from "@/app/app/message/messageComponents/interfaces";
import { getToolName } from "@/app/app/message/messageComponents/toolDisplayHelpers";
import { StepContainer } from "@/app/app/message/messageComponents/timeline/StepContainer";
import {
TimelineRendererComponent,
TimelineRendererResult,
} from "@/app/chat/message/messageComponents/timeline/TimelineRendererComponent";
} from "@/app/app/message/messageComponents/timeline/TimelineRendererComponent";
import ExpandableTextDisplay from "@/refresh-components/texts/ExpandableTextDisplay";
import { useMarkdownRenderer } from "@/app/chat/message/messageComponents/markdownUtils";
import { useMarkdownRenderer } from "@/app/app/message/messageComponents/markdownUtils";
interface NestedToolGroup {
sub_turn_index: number;

View File

@@ -1,11 +1,11 @@
import React from "react";
import { FiLink } from "react-icons/fi";
import { FetchToolPacket } from "@/app/chat/services/streamingModels";
import { FetchToolPacket } from "@/app/app/services/streamingModels";
import {
MessageRenderer,
RenderType,
} from "@/app/chat/message/messageComponents/interfaces";
import { BlinkingDot } from "@/app/chat/message/BlinkingDot";
} from "@/app/app/message/messageComponents/interfaces";
import { BlinkingDot } from "@/app/app/message/BlinkingDot";
import { OnyxDocument } from "@/lib/search/interfaces";
import { ValidSources } from "@/lib/types";
import { SearchChipList, SourceInfo } from "../search/SearchChipList";

View File

@@ -3,7 +3,7 @@ import {
FetchToolPacket,
FetchToolUrls,
FetchToolDocuments,
} from "@/app/chat/services/streamingModels";
} from "@/app/app/services/streamingModels";
import { OnyxDocument } from "@/lib/search/interfaces";
export const INITIAL_URLS_TO_SHOW = 3;

View File

@@ -10,14 +10,14 @@ import {
PacketType,
ReasoningDelta,
ReasoningPacket,
} from "@/app/chat/services/streamingModels";
} from "@/app/app/services/streamingModels";
import {
MessageRenderer,
FullChatState,
} from "@/app/chat/message/messageComponents/interfaces";
} from "@/app/app/message/messageComponents/interfaces";
import MinimalMarkdown from "@/components/chat/MinimalMarkdown";
import ExpandableTextDisplay from "@/refresh-components/texts/ExpandableTextDisplay";
import { mutedTextMarkdownComponents } from "@/app/chat/message/messageComponents/timeline/renderers/sharedMarkdownComponents";
import { mutedTextMarkdownComponents } from "@/app/app/message/messageComponents/timeline/renderers/sharedMarkdownComponents";
import { SvgCircle } from "@opal/icons";
const THINKING_MIN_DURATION_MS = 500; // 0.5 second minimum for "Thinking" state

View File

@@ -1,11 +1,11 @@
import React from "react";
import { SvgSearch, SvgGlobe, SvgSearchMenu } from "@opal/icons";
import { SearchToolPacket } from "@/app/chat/services/streamingModels";
import { SearchToolPacket } from "@/app/app/services/streamingModels";
import {
MessageRenderer,
RenderType,
} from "@/app/chat/message/messageComponents/interfaces";
import { BlinkingDot } from "@/app/chat/message/BlinkingDot";
} from "@/app/app/message/messageComponents/interfaces";
import { BlinkingDot } from "@/app/app/message/BlinkingDot";
import { OnyxDocument } from "@/lib/search/interfaces";
import { ValidSources } from "@/lib/types";
import { SearchChipList, SourceInfo } from "./SearchChipList";

Some files were not shown because too many files have changed in this diff Show More