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
5 changes: 5 additions & 0 deletions apps/web/src/components/chat/global-chat-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { lazy, Suspense, useState } from "react"
import { useMountEffect } from "@/hooks/use-mount-effect"
import { useMapleOrganizationId } from "@/hooks/use-maple-organization"
import { isDialogOpen, isEditableTarget } from "@maple/ui/lib/keyboard"
import { aiChatEnabled } from "@/lib/region"

const OPEN_CHAT_EVENT = "maple:open-chat-sheet"

Expand Down Expand Up @@ -35,6 +36,10 @@ export function openGlobalChat() {
* history restores from the durable stream on reopen, same as AlertChatSheet.
*/
export function GlobalChatSheet() {
return aiChatEnabled ? <GlobalChatSheetInner /> : null
}

function GlobalChatSheetInner() {
const [open, setOpen] = useState(false)
const orgId = useMapleOrganizationId()

Expand Down
23 changes: 14 additions & 9 deletions apps/web/src/components/command-palette/command-palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from "@/components/icons"
import { isClerkAuthEnabled } from "@/lib/services/common/auth-mode"
import { openGlobalChat } from "@/components/chat/global-chat-sheet"
import { aiChatEnabled } from "@/lib/region"
import { paletteNavItems } from "@/components/dashboard/nav-items"
import { useDashboardPreferences } from "@/hooks/use-dashboard-preferences"
import { useDashboardsRead } from "@/hooks/use-dashboard-store"
Expand Down Expand Up @@ -197,15 +198,19 @@ function PaletteContent({

const isDark = theme === "dark"
const actions: PaletteEntry[] = [
{
id: "action:ask-maple-ai",
title: "Ask Maple AI",
group: "Actions",
keywords: "chat ai assistant maple ask question",
icon: ChatBubbleSparkleIcon,
run: openGlobalChat,
shortcut: "C",
},
...(aiChatEnabled
? [
{
id: "action:ask-maple-ai",
title: "Ask Maple AI",
group: "Actions",
keywords: "chat ai assistant maple ask question",
icon: ChatBubbleSparkleIcon,
run: openGlobalChat,
shortcut: "C",
} satisfies PaletteEntry,
]
: []),
{
id: "action:toggle-theme",
title: isDark ? "Switch to light mode" : "Switch to dark mode",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type WidgetFixContext,
} from "@/components/chat/widget-fix-context"
import { encodeAlertChartToSearchParam } from "@/lib/alerts/widget-chart-param"
import { aiChatEnabled } from "@/lib/region"
import { dataSourceRawSql, isQueryDataSource } from "@maple/widgets/dashboard"
import { Result, useAtomValue } from "@/lib/effect-atom"
import { dashboardSharesAtom } from "@/components/dashboard-builder/toolbar/dashboard-shares"
Expand Down Expand Up @@ -147,7 +148,7 @@ export function WidgetActionsProvider({
: undefined

const fix =
dashboardId && errorKind === "decode"
aiChatEnabled && dashboardId && errorKind === "decode"
? () => {
const ctx: WidgetFixContext = {
dashboardId,
Expand Down
37 changes: 20 additions & 17 deletions apps/web/src/components/layout/dashboard-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@maple/ui/components/ui
import { Kbd } from "@maple/ui/components/ui/kbd"
import { ChatBubbleSparkleIcon, LayoutLeftIcon, LayoutRightIcon } from "@/components/icons"
import { openGlobalChat } from "@/components/chat/global-chat-sheet"
import { aiChatEnabled } from "@/lib/region"
import { ConnectButton } from "@/components/header/connect-button"
import { OnboardingChecklistButton } from "@/components/header/onboarding-checklist-button"
import { QuotaBanner } from "@/components/billing/quota-banner"
Expand Down Expand Up @@ -112,23 +113,25 @@ function Breadcrumbs({ items, children }: { items: BreadcrumbEntry[]; children?:
</BreadcrumbList>
</Breadcrumb>
<div className="ml-auto flex shrink-0 items-center gap-2">
<Tooltip>
<TooltipTrigger
render={
<Button
variant="outline"
size="icon-sm"
aria-label="Open AI chat"
onClick={openGlobalChat}
/>
}
>
<ChatBubbleSparkleIcon size={16} />
</TooltipTrigger>
<TooltipContent className="flex items-center gap-1.5">
Ask Maple AI <Kbd>C</Kbd>
</TooltipContent>
</Tooltip>
{aiChatEnabled && (
<Tooltip>
<TooltipTrigger
render={
<Button
variant="outline"
size="icon-sm"
aria-label="Open AI chat"
onClick={openGlobalChat}
/>
}
>
<ChatBubbleSparkleIcon size={16} />
</TooltipTrigger>
<TooltipContent className="flex items-center gap-1.5">
Ask Maple AI <Kbd>C</Kbd>
</TooltipContent>
</Tooltip>
)}
<OnboardingChecklistButton />
<ConnectButton />
{/* Self-gating: renders only when the sidebar has collapsed to a sheet *and* a
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/lib/region.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ export function regionAppUrl(region: MapleRegion): string | undefined {

/** Whether this deployment has more than one region, and so whether regions are worth showing. */
export const hasMultipleRegions = Object.keys(regionAppUrls).length > 1

/** Maple AI chat is off on the EU instance for now; every chat entry point checks this. */
export const aiChatEnabled = currentRegion !== "eu"
6 changes: 5 additions & 1 deletion apps/web/src/routes/chat.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createFileRoute } from "@tanstack/react-router"
import { createFileRoute, redirect } from "@tanstack/react-router"
import { Schema } from "effect"
import { ChatPage } from "@/components/chat/chat-page"
import { aiChatEnabled } from "@/lib/region"
import { decodeAlertContextFromSearchParam } from "@/components/chat/alert-context"
import {
alertContextToInvestigation,
Expand Down Expand Up @@ -29,6 +30,9 @@ const ChatSearch = Schema.Struct({
export const Route = createFileRoute("/chat")({
component: ChatRoute,
validateSearch: Schema.toStandardSchemaV1(ChatSearch),
beforeLoad: () => {
if (!aiChatEnabled) throw redirect({ to: "/" })
},
})

function ChatRoute() {
Expand Down
Loading