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
63 changes: 63 additions & 0 deletions src/app/(outerbase)/local-setting/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"use client";
import LabelInput from "@/components/label-input";
import { Button } from "@/components/orbit/button";
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() {
const [token, setToken] = useState<string>("");

useEffect(() => {
if (typeof window === "undefined") return;

const agentData = getAgentFromLocalStorage();
if (!agentData) return;

setToken(agentData.token);
}, []);

const onSaveClicked = useCallback(() => {
if (!token) return;

updateAgentFromLocalStorage({
provider: "openai",
model: "gpt-4o-mini",
token,
});

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

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

<p className="text-base">
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>
);
}
11 changes: 1 addition & 10 deletions src/app/(outerbase)/nav-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,10 @@ export default function NavigationLayout({ children }: PropsWithChildren) {
<NavigationProfile />
</div>

{/* <div className="px-2">
<Input
size="base"
className="bg-secondary"
placeholder="Search for anything"
preText={<MagnifyingGlass className="mr-2" />}
/>
</div> */}

<div className="flex flex-1 flex-col overflow-scroll border-b pb-2">
<SidebarMenuHeader text="Workspace" />
<SidebarMenuItem
selected={pathname === "/local"}
selected={pathname === "/local" || pathname === "/local-setting"}
text="Local Workspace"
icon={Database}
href="/local"
Expand Down
10 changes: 10 additions & 0 deletions src/app/(outerbase)/nav-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ export default function NavigationProfile() {
</div>

<div className="p-2">
<DropdownMenuItem
className="justify-between"
onClick={() => {
router.push(`/local-setting`);
}}
>
Local Setting
<Gear size={20} />
</DropdownMenuItem>

{session && (
<DropdownMenuItem
className="justify-between"
Expand Down
38 changes: 25 additions & 13 deletions src/app/(outerbase)/w/[workspaceId]/[baseId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import {
} from "@/core/standard-extension";
import DataCatalogExtension from "@/extensions/data-catalog";
import OuterbaseExtension from "@/extensions/outerbase";
import { getOuterbaseBase } from "@/outerbase-cloud/api";
import { OuterbaseAPISource } from "@/outerbase-cloud/api-type";
import {
getOuterbaseBase,
sendOuterbaseBaseAnalytics,
} from "@/outerbase-cloud/api";
import { OuterbaseAPIBaseCredential } from "@/outerbase-cloud/api-type";
import { getOuterbaseBaseCredential } from "@/outerbase-cloud/api-workspace";
import DataCatalogOuterbaseDriver from "@/outerbase-cloud/data-catalog-driver";
import { OuterbaseMySQLDriver } from "@/outerbase-cloud/database/mysql";
import { OuterbasePostgresDriver } from "@/outerbase-cloud/database/postgresql";
Expand All @@ -26,32 +30,40 @@ export default function OuterbaseSourcePage() {
baseId: string;
}>();
const [name, setName] = useState<string>("");
const [source, setSource] = useState<OuterbaseAPISource>();
const [credential, setCredential] = useState<OuterbaseAPIBaseCredential>();

useEffect(() => {
if (!workspaceId) return;
if (!baseId) return;

getOuterbaseBase(workspaceId, baseId).then((base) => {
if (!base) return;
setSource(base.sources[0]);

setName(base.name);
getOuterbaseBaseCredential(workspaceId, base.sources[0]?.id ?? "").then(
setCredential
);
});
}, [workspaceId, baseId]);

const savedDocDriver = useMemo(() => {
if (!workspaceId || !source?.id || !baseId) return null;
return new OuterbaseQueryDriver(workspaceId, baseId, source.id);
}, [workspaceId, baseId, source?.id]);
if (!workspaceId || !credential?.id || !baseId) return null;
return new OuterbaseQueryDriver(workspaceId, baseId, credential.id);
}, [workspaceId, baseId, credential?.id]);

// We need to send analytics to update the last used time
useEffect(() => {
sendOuterbaseBaseAnalytics(workspaceId, baseId).then().catch();
}, [workspaceId, baseId]);

const [outerbaseDriver, extensions] = useMemo(() => {
if (!workspaceId || !source) return [null, null];
if (!workspaceId || !credential) return [null, null];

const dialect = source.type;
const dialect = credential.type;
const outerbaseConfig = {
workspaceId,
sourceId: source.id,
baseId: source.base_id,
sourceId: credential.id,
baseId,
token: localStorage.getItem("ob-token") ?? "",
};

Expand All @@ -70,7 +82,7 @@ export default function OuterbaseSourcePage() {
];
} else if (dialect === "mysql") {
return [
new OuterbaseMySQLDriver(outerbaseConfig),
new OuterbaseMySQLDriver(outerbaseConfig, credential.database),
new StudioExtensionManager([
...createMySQLExtensions(),
...outerbaseSpecifiedDrivers,
Expand All @@ -85,7 +97,7 @@ export default function OuterbaseSourcePage() {
...outerbaseSpecifiedDrivers,
]),
];
}, [workspaceId, source]);
}, [workspaceId, credential, baseId]);

if (!outerbaseDriver || !savedDocDriver) {
return <PageLoading>Loading Base ...</PageLoading>;
Expand Down
46 changes: 40 additions & 6 deletions src/app/(theme)/client/s/[[...driver]]/page-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,31 @@ import {
updateLocalConnectionUsed,
useLocalConnection,
} from "@/app/(outerbase)/local/hooks";
import MyStudio from "@/components/my-studio";
import { Studio } from "@/components/gui/studio";
import { StudioExtensionManager } from "@/core/extension-manager";
import {
createMySQLExtensions,
createPostgreSQLExtensions,
createSQLiteExtensions,
createStandardExtensions,
} from "@/core/standard-extension";
import { createLocalDriver } from "@/drivers/helpers";
import IndexdbSavedDoc from "@/drivers/saved-doc/indexdb-saved-doc";
import { useSearchParams } from "next/navigation";
import { useEffect, useMemo } from "react";
import { useAgentFromLocalStorage } from "@/lib/ai-agent-storage";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo } from "react";

export default function ClientPageBody() {
const params = useSearchParams();
const baseId = params.get("p") ?? "";
const { data: conn } = useLocalConnection(baseId);

const router = useRouter();

const goBack = useCallback(() => {
router.push("/");
}, [router]);

useEffect(() => {
if (!baseId) return;
updateLocalConnectionUsed(baseId).then().catch();
Expand All @@ -28,22 +42,42 @@ export default function ClientPageBody() {
return createLocalDriver(config);
}, [conn]);

const extensions = useMemo(() => {
if (!driver) return null;
const dialet = driver.getFlags().dialect;

if (dialet === "mysql") {
return new StudioExtensionManager(createMySQLExtensions());
} else if (dialet === "sqlite") {
return new StudioExtensionManager(createSQLiteExtensions());
} else if (dialet === "postgres") {
return new StudioExtensionManager(createPostgreSQLExtensions());
}

return new StudioExtensionManager(createStandardExtensions());
}, [driver]);

const agentDriver = useAgentFromLocalStorage(driver);

const docDriver = useMemo(() => {
if (conn) {
return new IndexdbSavedDoc(conn.id);
}
}, [conn]);

if (!driver || !conn) {
if (!driver || !conn || !extensions) {
return <div>Something wrong</div>;
}

return (
<MyStudio
<Studio
extensions={extensions}
driver={driver}
docDriver={docDriver}
name={conn?.content.name}
color={conn?.content.label ?? "blue"}
onBack={goBack}
docDriver={docDriver}
agentDriver={agentDriver}
/>
);
}
4 changes: 4 additions & 0 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 { useAgentFromLocalStorage } from "@/lib/ai-agent-storage";
import { useSearchParams } from "next/navigation";
import { useEffect, useMemo } from "react";

Expand All @@ -37,6 +38,8 @@ export default function EmbedPageClient({
return new StudioExtensionManager(createEmbedExtensions(driverName));
}, [driverName]);

const agentDriver = useAgentFromLocalStorage(driver);

useEffect(() => {
return driver.listen();
}, [driver]);
Expand All @@ -48,6 +51,7 @@ export default function EmbedPageClient({
docDriver={savedDocDriver}
name={searchParams.get("name") || "Unnamed Connection"}
color={searchParams.get("color") || "gray"}
agentDriver={agentDriver}
/>
);
}
Expand Down
6 changes: 5 additions & 1 deletion src/app/(theme)/playground/client/page-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { StudioExtensionManager } from "@/core/extension-manager";
import { createSQLiteExtensions } from "@/core/standard-extension";
import SqljsDriver from "@/drivers/sqljs-driver";
import { localDb } from "@/indexdb";
import { useAgentFromLocalStorage } from "@/lib/ai-agent-storage";
import downloadFileFromUrl from "@/lib/download-file";
import { saveAs } from "file-saver";
import {
Expand Down Expand Up @@ -43,6 +44,8 @@ export default function PlaygroundEditorBody({
const [handler, setHandler] = useState<FileSystemFileHandle>();
const [fileName, setFilename] = useState("");

const agentDriver = useAgentFromLocalStorage(driver);

/**
* Initialize the SQL.js library.
*/
Expand Down Expand Up @@ -274,12 +277,13 @@ export default function PlaygroundEditorBody({
name="Playground"
driver={driver}
containerClassName="w-full h-full"
agentDriver={agentDriver}
/>
);
}

return <div></div>;
}, [databaseLoading, preloadDatabase, driver, extensions]);
}, [databaseLoading, preloadDatabase, driver, extensions, agentDriver]);

return (
<>
Expand Down
10 changes: 8 additions & 2 deletions src/components/editor/prompt-plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ export interface PromptSelectedFragment {
fullText: string;
startLineNumber: number;
endLineNumber: number;
sessionId: string;
}

export type PromptCallback = (
promptQuery: string,
selected?: PromptSelectedFragment
selected: PromptSelectedFragment
) => Promise<string>;

class PlaceholderWidget extends WidgetType {
Expand Down Expand Up @@ -69,6 +70,9 @@ class PromptWidget extends WidgetType {
) {
super();

// Generate unique session id for this prompt
const sessionId = crypto.randomUUID();

plugin.lock();
this.container = document.createElement("div");

Expand All @@ -84,6 +88,7 @@ class PromptWidget extends WidgetType {
const getSelectionLines = () => {
const startLineNumber = view.state.doc.lineAt(from).number;
const endLineNumber = view.state.doc.lineAt(to).number;

return Array.from(
{ length: endLineNumber - startLineNumber + 1 },
(_, i) => startLineNumber + i
Expand Down Expand Up @@ -150,6 +155,7 @@ class PromptWidget extends WidgetType {
text: suggestedText ?? selectedOriginalText,
fullText: view.state.doc.toString(),
startLineNumber,
sessionId,
endLineNumber: view.state.doc.lineAt(
startPosition + suggestedText.length
).number,
Expand Down Expand Up @@ -555,7 +561,7 @@ export class CodeMirrorPromptPlugin {
];
}

async getSuggestion(promptQuery: string, selected?: PromptSelectedFragment) {
async getSuggestion(promptQuery: string, selected: PromptSelectedFragment) {
if (this.promptCallback) {
return this.promptCallback(promptQuery, selected);
}
Expand Down
Loading