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
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
"use client";
import { createDialog } from "@/components/create-dialog";
import LabelInput from "@/components/label-input";
import { Button } from "@/components/orbit/button";
import {
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
getAgentFromLocalStorage,
updateAgentFromLocalStorage,
} from "@/lib/ai-agent-storage";
import { useCallback, useEffect, useState } from "react";
import { toast } from "sonner";
import NavigationLayout from "../nav-layout";

export default function LocalSettingPage() {
export const localSettingDialog = createDialog(({ close }) => {
const [token, setToken] = useState<string>("");

useEffect(() => {
Expand All @@ -30,34 +34,34 @@ export default function LocalSettingPage() {
token,
});

toast("Setting saved!");
}, [token]);
close(undefined);
}, [token, close]);

return (
<NavigationLayout>
<div className="flex max-w-[600px] flex-col gap-4 p-8">
<h1 className="text-xl font-bold">Local Setting</h1>
<>
<DialogHeader>
<DialogTitle>Local Setting</DialogTitle>

<p className="text-base">
<DialogDescription>
Bring your OpenAI token to enable the AI assistant. Your token is
stored in localStorage. We do not store your token on our server.
</p>

<LabelInput
type="password"
label="Token"
placeholder="Token"
size="lg"
value={token}
onValueChange={setToken}
/>

<div>
<Button size="lg" variant="primary" onClick={onSaveClicked}>
Save
</Button>
</div>
</div>
</NavigationLayout>
</DialogDescription>
</DialogHeader>

<LabelInput
type="password"
label="Token"
placeholder="Token"
size="lg"
value={token}
onValueChange={setToken}
/>

<DialogFooter>
<Button size="lg" variant="primary" onClick={onSaveClicked}>
Save
</Button>
</DialogFooter>
</>
);
}
});
3 changes: 2 additions & 1 deletion src/app/(outerbase)/nav-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { useTheme } from "next-themes";
import { useRouter } from "next/navigation";
import { useCallback } from "react";
import { localSettingDialog } from "./local-setting-dialog";
import { useSession } from "./session-provider";

export default function NavigationProfile() {
Expand Down Expand Up @@ -88,7 +89,7 @@ export default function NavigationProfile() {
<DropdownMenuItem
className="justify-between"
onClick={() => {
router.push(`/local-setting`);
localSettingDialog.show({}).then().catch();
}}
>
Local Setting
Expand Down
5 changes: 3 additions & 2 deletions src/app/(theme)/embed/[driver]/page-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "@/drivers/iframe-driver";
import ElectronSavedDocs from "@/drivers/saved-doc/electron-saved-doc";
import DoltExtension from "@/extensions/dolt";
import LocalSettingSidebar from "@/extensions/local-setting-sidebar";
import { useAgentFromLocalStorage } from "@/lib/ai-agent-storage";
import { useSearchParams } from "next/navigation";
import { useEffect, useMemo } from "react";
Expand Down Expand Up @@ -79,9 +80,9 @@ function createDatabaseDriver(driverName: string) {

function createEmbedExtensions(driverName: string) {
if (driverName === "turso") {
return createSQLiteExtensions();
return [...createSQLiteExtensions(), new LocalSettingSidebar()];
} else if (driverName === "sqlite" || driverName === "starbase") {
return createSQLiteExtensions();
return [...createSQLiteExtensions(), new LocalSettingSidebar()];
} else if (driverName === "mysql") {
return createMySQLExtensions();
} else if (driverName === "dolt") {
Expand Down
22 changes: 11 additions & 11 deletions src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { Cross2Icon } from "@radix-ui/react-icons";
import * as React from "react";

import { cn } from "@/lib/utils";

Expand All @@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
className
)}
{...props}
Expand All @@ -38,13 +38,13 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
Expand Down Expand Up @@ -88,7 +88,7 @@ const DialogTitle = React.forwardRef<
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
"text-lg leading-none font-semibold tracking-tight",
className
)}
{...props}
Expand All @@ -102,21 +102,21 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
className={cn("text-muted-foreground text-base", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;

export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogDescription,
DialogTrigger,
};
19 changes: 19 additions & 0 deletions src/extensions/local-setting-sidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { localSettingDialog } from "@/app/(outerbase)/local-setting-dialog";
import { StudioExtension } from "@/core/extension-base";
import { StudioExtensionContext } from "@/core/extension-manager";
import { MagicWand } from "@phosphor-icons/react";

export default class LocalSettingSidebar extends StudioExtension {
extensionName = "local-setting-sidebar";

init(studio: StudioExtensionContext): void {
studio.registerSidebar({
key: "local-setting-sidebar",
name: "Local Settings",
icon: <MagicWand size={24} />,
onClick: () => {
localSettingDialog.show({}).then().catch();
},
});
}
}
11 changes: 8 additions & 3 deletions src/lib/ai-agent-storage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChatGPTDriver } from "@/drivers/agent/chatgpt";
import { BaseDriver } from "@/drivers/base-driver";
import { useMemo } from "react";
import useSWR, { mutate } from "swr";

export interface LocalAgentType {
provider: "openai";
Expand Down Expand Up @@ -28,15 +29,19 @@ export function getAgentFromLocalStorage(): LocalAgentType | undefined {

export function updateAgentFromLocalStorage(data: LocalAgentType) {
localStorage.setItem("agent", JSON.stringify(data));
mutate("/local-agent-setting", data);
}

export function useAgentFromLocalStorage(databaseDriver?: BaseDriver | null) {
const { data: agentConfig } = useSWR(
"/local-agent-setting",
getAgentFromLocalStorage
);

return useMemo(() => {
if (!databaseDriver) return undefined;

const agentConfig = getAgentFromLocalStorage();
if (!agentConfig) return undefined;

return new ChatGPTDriver(databaseDriver, agentConfig.token);
}, [databaseDriver]);
}, [databaseDriver, agentConfig]);
}