Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/drizzle-studio/biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.7/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.8/schema.json",
"extends": "//",
"root": false
}
2 changes: 1 addition & 1 deletion apps/stripe/biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.7/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.8/schema.json",
"extends": "//",
"root": false
}
2 changes: 1 addition & 1 deletion apps/web/biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.7/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.8/schema.json",
"css": {
"parser": {
"tailwindDirectives": true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client";

import * as React from "react";
import { useAppPreferences } from "~/components/preferences/app-preferences-provider";
import { SidebarProvider } from "~/components/ui/sidebar";
import { useIsMobile } from "~/hooks/use-mobile";

type AppSidebarProviderProps = Omit<
React.ComponentProps<typeof SidebarProvider>,
"open" | "onOpenChange" | "initialWidthRem" | "onResizeEnd"
>;

export function AppSidebarProvider({
children,
...props
}: AppSidebarProviderProps) {
const { preferences, updatePreferences } = useAppPreferences();
const isMobile = useIsMobile();
const [open, setOpen] = React.useState(() => preferences.appSidebarOpen);

const handleOpenChange = React.useCallback(
(nextOpen: boolean) => {
if (open === nextOpen) {
return;
}

if (!isMobile) {
updatePreferences({ appSidebarOpen: nextOpen });
}

setOpen(nextOpen);
},
[open, isMobile, updatePreferences],
);

const handleResizeEnd = React.useCallback(
(widthRem: number) => {
if (isMobile) {
return;
}

updatePreferences({ appSidebarWidthRem: widthRem });
},
[isMobile, updatePreferences],
);

return (
<SidebarProvider
{...props}
open={open}
onOpenChange={handleOpenChange}
initialWidthRem={preferences.appSidebarWidthRem}
onResizeEnd={handleResizeEnd}
>
{children}
</SidebarProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client";

import * as React from "react";
import { useAppPreferences } from "~/components/preferences/app-preferences-provider";
import { SidebarProvider } from "~/components/ui/sidebar";
import { useIsMobile } from "~/hooks/use-mobile";

type ChatSidebarProviderProps = Omit<
React.ComponentProps<typeof SidebarProvider>,
"open" | "onOpenChange" | "initialWidthRem" | "onResizeEnd"
>;

export function ChatSidebarProvider({
children,
...props
}: ChatSidebarProviderProps) {
const { preferences, updatePreferences } = useAppPreferences();
const isMobile = useIsMobile();
const [open, setOpen] = React.useState(() => preferences.chatSidebarOpen);

const handleOpenChange = React.useCallback(
(nextOpen: boolean) => {
if (open === nextOpen) {
return;
}

if (!isMobile) {
updatePreferences({ chatSidebarOpen: nextOpen });
}

setOpen(nextOpen);
},
[isMobile, updatePreferences, open],
);

const handleResizeEnd = React.useCallback(
(widthRem: number) => {
if (isMobile) {
return;
}

updatePreferences({ chatSidebarWidthRem: widthRem });
},
[isMobile, updatePreferences],
);

return (
<SidebarProvider
{...props}
open={open}
onOpenChange={handleOpenChange}
initialWidthRem={preferences.chatSidebarWidthRem}
onResizeEnd={handleResizeEnd}
>
{children}
</SidebarProvider>
);
}
11 changes: 8 additions & 3 deletions apps/web/src/app/(app)/@chatSidebar/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ import { ThreadMessages } from "~/components/assistant-ui/thread-messages";
import { ComposerSources } from "~/components/assistant-ui/thread-sources";
import { ThreadWelcome } from "~/components/assistant-ui/thread-welcome";
import { Sidebar, SidebarContent } from "~/components/ui/sidebar";
import {
CHAT_SIDEBAR_WIDTH_REM_DEFAULT,
CHAT_SIDEBAR_WIDTH_REM_MAX,
CHAT_SIDEBAR_WIDTH_REM_MIN,
} from "~/preferences/app-preferences";

const CHAT_SIDEBAR_DEFAULT_WIDTH = "20rem";
const CHAT_SIDEBAR_MIN_WIDTH = "20rem";
const CHAT_SIDEBAR_WIDTH_MAX = "50rem";
const CHAT_SIDEBAR_DEFAULT_WIDTH = `${CHAT_SIDEBAR_WIDTH_REM_DEFAULT}rem`;
const CHAT_SIDEBAR_MIN_WIDTH = `${CHAT_SIDEBAR_WIDTH_REM_MIN}rem`;
const CHAT_SIDEBAR_WIDTH_MAX = `${CHAT_SIDEBAR_WIDTH_REM_MAX}rem`;
const CHAT_THREAD_MIN_WIDTH = "18rem"; // Leave 2rem for the padding.
const CHAT_THREAD_MAX_WIDTH = "50rem";
const CHAT_SIDEBAR_REASONING_CONTENT_ID =
Expand Down
16 changes: 9 additions & 7 deletions apps/web/src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { withAuth } from "~/app/_guards/page-guards";
import { SidebarInset, SidebarProvider } from "~/components/ui/sidebar";
import { SidebarInset } from "~/components/ui/sidebar";
import { Toaster } from "~/components/ui/toast";
import { getAppPreferences } from "~/preferences/get-preferences";
import { AppLayoutProvider } from "../_components/app-layout-provider";
import ChatSidebarTrigger from "./@chatSidebar/_components/chat-sidebar-trigger";
import "./styles.css";
import { AppLayoutProvider } from "../_components/app-layout-provider";
import { AppProviders } from "../_components/app-providers";
import { AppSidebarProvider } from "./@appSidebar/_components/app-sidebar-provider";
import { ChatSidebarProvider } from "./@chatSidebar/_components/chat-sidebar-provider";
import ChatSidebarTrigger from "./@chatSidebar/_components/chat-sidebar-trigger";
import { AppContainer } from "./_components/app-container";
import { AppProgressBar } from "./_components/app-progress-bar";

Expand All @@ -32,9 +34,9 @@ async function AppLayout({
return (
<AppProviders initialPreferences={preferences}>
<AppProgressBar className="fixed top-0 left-0 z-7000 h-1 w-full" />
<SidebarProvider className="flex min-h-screen-safe flex-col">
<ChatSidebarProvider className="flex min-h-screen-safe flex-col">
<div className="flex flex-1">
<SidebarProvider>
<AppSidebarProvider>
{appSidebar}
<SidebarInset className="flex max-h-dvh min-w-sm flex-col gap-y-2">
<AppLayoutProvider>
Expand All @@ -45,11 +47,11 @@ async function AppLayout({
<div className="mt-auto">{chatDrawer}</div>
</AppLayoutProvider>
</SidebarInset>
</SidebarProvider>
</AppSidebarProvider>
{chatSidebar}
<ChatSidebarTrigger className="fixed right-2 bottom-2 z-4500 hidden md:flex" />
</div>
</SidebarProvider>
</ChatSidebarProvider>
{subscriptionModal}
<Toaster />
</AppProviders>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/(app)/pages/_components/page-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type PageShellProps = React.ComponentProps<"div"> & {
page: Pick<Page, "id">;
};

export async function PageShell({
export function PageShell({
className,
children,
page,
Expand Down
26 changes: 24 additions & 2 deletions apps/web/src/components/preferences/app-preferences-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ import {
} from "~/preferences/app-preferences";
import { setAppPreferencesAction } from "./app-preferences.actions";

function isAbortOrNetworkError(error: unknown) {
if (error instanceof DOMException && error.name === "AbortError") {
return true;
}

if (error instanceof TypeError) {
return /(networkerror|failed to fetch|load failed|fetch resource)/i.test(
error.message,
);
}

return false;
}

type AppPreferencesContextValue = {
preferences: AppPreferences;
setPreferences: (
Expand All @@ -35,8 +49,16 @@ export function AppPreferencesProvider({
[initialPreferences],
);
const [preferences, savePreferences] = useOptimisticActionState(
async (_current: AppPreferences, next: AppPreferences) =>
setAppPreferencesAction(next),
async (current: AppPreferences, next: AppPreferences) => {
try {
return await setAppPreferencesAction(next);
} catch (error) {
if (!isAbortOrNetworkError(error)) {
console.error("Failed to persist app preferences.", error);
}
return current;
Comment thread
rmolinamir marked this conversation as resolved.
}
},
initialState,
(
current: AppPreferences,
Expand Down
Loading
Loading