Merge remote-tracking branch 'upstream/dev' into feat/ui-revamp

This commit is contained in:
Anish Sarkar 2026-05-02 12:54:49 +05:30
commit 9b1b5a504e
148 changed files with 19460 additions and 2708 deletions

View file

@ -26,9 +26,9 @@ import {
type Tab,
} from "@/atoms/tabs/tabs.atom";
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { ActionLogSheet } from "@/components/agent-action-log/action-log-sheet";
import { SearchSpaceSettingsDialog } from "@/components/settings/search-space-settings-dialog";
import { TeamDialog } from "@/components/settings/team-dialog";
import { ActionLogSheet } from "@/components/agent-action-log/action-log-sheet";
import { UserSettingsDialog } from "@/components/settings/user-settings-dialog";
import {
AlertDialog,
@ -681,14 +681,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
}
}, [chatToRename, newChatTitle, queryClient, searchSpaceId, tSidebar]);
// Page usage
const pageUsage = user
? {
pagesUsed: user.pages_used,
pagesLimit: user.pages_limit,
}
: undefined;
// Detect if we're on the chat page (needs overflow-hidden for chat's own scroll)
const isChatPage = pathname?.includes("/new-chat") ?? false;
@ -723,7 +715,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
onManageMembers={handleManageMembers}
onUserSettings={handleUserSettings}
onLogout={handleLogout}
pageUsage={pageUsage}
theme={theme}
setTheme={setTheme}
isChatPage={isChatPage}

View file

@ -120,45 +120,22 @@ interface LayoutShellProps {
function MainContentPanel({
isChatPage,
isSidebarCollapsed,
onTabSwitch,
onNewChat,
leftActions,
showResizeHandle = false,
onResizeMouseDown,
children,
}: {
isChatPage: boolean;
isSidebarCollapsed: boolean;
onTabSwitch?: (tab: Tab) => void;
onNewChat?: () => void;
leftActions?: React.ReactNode;
showResizeHandle?: boolean;
onResizeMouseDown?: (e: React.MouseEvent) => void;
children: React.ReactNode;
}) {
const activeTab = useAtomValue(activeTabAtom);
const isDocumentTab = activeTab?.type === "document";
return (
<div
className={cn(
"relative flex flex-1 flex-col min-w-0 -ml-2",
isSidebarCollapsed ? "" : "border-l border-border/60"
)}
>
{showResizeHandle && onResizeMouseDown && (
<div
role="slider"
aria-label="Resize sidebar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={50}
tabIndex={0}
onMouseDown={onResizeMouseDown}
className="absolute left-0 top-0 hidden md:block h-full w-2 -translate-x-1/2 cursor-col-resize z-30 focus:outline-none"
/>
)}
<div className="relative isolate flex flex-1 flex-col min-w-0">
<TabBar
onTabSwitch={onTabSwitch}
onNewChat={onNewChat}
@ -538,14 +515,26 @@ export function LayoutShell({
</SidebarSlideOutPanel>
</div>
{/* Resize handle — negative margins eat the flex gap so spacing stays unchanged */}
{!isCollapsed && (
<div
role="slider"
aria-label="Resize sidebar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={50}
tabIndex={0}
onMouseDown={onResizeMouseDown}
className="hidden md:block h-full cursor-col-resize z-30 focus:outline-none"
style={{ width: 8, marginLeft: -8, marginRight: -8 }}
/>
)}
{/* Main content panel */}
<MainContentPanel
isChatPage={isChatPage}
isSidebarCollapsed={isCollapsed}
onTabSwitch={onTabSwitch}
onNewChat={onNewChat}
showResizeHandle={!isCollapsed}
onResizeMouseDown={onResizeMouseDown}
leftActions={
isCollapsed ? (
<SidebarCollapseButton isCollapsed={isCollapsed} onToggle={toggleCollapsed} />

View file

@ -0,0 +1,15 @@
"use client";
import { useQuery } from "@rocicorp/zero/react";
import { useIsAnonymous } from "@/contexts/anonymous-mode";
import { queries } from "@/zero/queries";
import { PageUsageDisplay } from "./PageUsageDisplay";
export function AuthenticatedPageUsageDisplay() {
const isAnonymous = useIsAnonymous();
const [me] = useQuery(queries.user.me({}));
if (isAnonymous || !me) return null;
return <PageUsageDisplay pagesUsed={me.pagesUsed} pagesLimit={me.pagesLimit} />;
}

View file

@ -23,9 +23,7 @@ import { useTranslations } from "next-intl";
import type React from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import {
mentionedDocumentsAtom,
} from "@/atoms/chat/mentioned-documents.atom";
import { mentionedDocumentsAtom } from "@/atoms/chat/mentioned-documents.atom";
import { connectorDialogOpenAtom } from "@/atoms/connector-dialog/connector-dialog.atoms";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { deleteDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
@ -74,12 +72,12 @@ import type { DocumentTypeEnum } from "@/contracts/types/document.types";
import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { useMediaQuery } from "@/hooks/use-media-query";
import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
import { anonymousChatApiService } from "@/lib/apis/anonymous-chat-api.service";
import { documentsApiService } from "@/lib/apis/documents-api.service";
import { foldersApiService } from "@/lib/apis/folders-api.service";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
import { authenticatedFetch } from "@/lib/auth-utils";
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
import { uploadFolderScan } from "@/lib/folder-sync-upload";
import { getSupportedExtensionsSet } from "@/lib/supported-extensions";
import { queries } from "@/zero/queries/index";

View file

@ -1,23 +1,18 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { useQuery } from "@rocicorp/zero/react";
import { Progress } from "@/components/ui/progress";
import { useIsAnonymous } from "@/contexts/anonymous-mode";
import { stripeApiService } from "@/lib/apis/stripe-api.service";
import { queries } from "@/zero/queries";
export function PremiumTokenUsageDisplay() {
const isAnonymous = useIsAnonymous();
const { data: tokenStatus } = useQuery({
queryKey: ["token-status"],
queryFn: () => stripeApiService.getTokenStatus(),
staleTime: 60_000,
enabled: !isAnonymous,
});
const [me] = useQuery(queries.user.me({}));
if (!tokenStatus) return null;
if (isAnonymous || !me) return null;
const usagePercentage = Math.min(
(tokenStatus.premium_tokens_used / Math.max(tokenStatus.premium_tokens_limit, 1)) * 100,
(me.premiumTokensUsed / Math.max(me.premiumTokensLimit, 1)) * 100,
100
);
@ -31,8 +26,7 @@ export function PremiumTokenUsageDisplay() {
<div className="space-y-1.5">
<div className="flex justify-between items-center text-xs">
<span className="text-muted-foreground">
{formatTokens(tokenStatus.premium_tokens_used)} /{" "}
{formatTokens(tokenStatus.premium_tokens_limit)} tokens
{formatTokens(me.premiumTokensUsed)} / {formatTokens(me.premiumTokensLimit)} tokens
</span>
<span className="font-medium">{usagePercentage.toFixed(0)}%</span>
</div>

View file

@ -12,9 +12,9 @@ import { useIsAnonymous } from "@/contexts/anonymous-mode";
import { cn } from "@/lib/utils";
import { SIDEBAR_MIN_WIDTH } from "../../hooks/useSidebarResize";
import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types";
import { AuthenticatedPageUsageDisplay } from "./AuthenticatedPageUsageDisplay";
import { ChatListItem } from "./ChatListItem";
import { NavSection } from "./NavSection";
import { PageUsageDisplay } from "./PageUsageDisplay";
import { PremiumTokenUsageDisplay } from "./PremiumTokenUsageDisplay";
import { SidebarButton } from "./SidebarButton";
import { SidebarCollapseButton } from "./SidebarCollapseButton";
@ -342,9 +342,7 @@ function SidebarUsageFooter({
return (
<div className="px-3 py-3 border-t border-border/60 space-y-3">
<PremiumTokenUsageDisplay />
{pageUsage && (
<PageUsageDisplay pagesUsed={pageUsage.pagesUsed} pagesLimit={pageUsage.pagesLimit} />
)}
<AuthenticatedPageUsageDisplay />
<div className="space-y-0.5">
<Link
href={`/dashboard/${searchSpaceId}/more-pages`}

View file

@ -316,10 +316,10 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
</Button>
</AlertDescription>
</Alert>
<MarkdownViewer content={doc.source_markdown} />
<MarkdownViewer content={doc.source_markdown} enableCitations />
</>
) : (
<MarkdownViewer content={doc.source_markdown} />
<MarkdownViewer content={doc.source_markdown} enableCitations />
)}
</div>
</div>