diff --git a/src/components/gui/connection-dialog.tsx b/src/components/gui/connection-dialog.tsx
index c502e853..f65b6432 100644
--- a/src/components/gui/connection-dialog.tsx
+++ b/src/components/gui/connection-dialog.tsx
@@ -1,5 +1,5 @@
import { Button } from "@/components/ui/button";
-import { useConfig } from "@/context/config-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useMemo } from "react";
import ServerLoadingAnimation from "../icons/server-loading";
@@ -11,7 +11,7 @@ export default function ConnectingDialog({
message?: string;
onRetry?: () => void;
}>) {
- const { name, onBack } = useConfig();
+ const { name, onBack } = useStudioContext();
const isElectron = useMemo(() => {
return typeof window !== "undefined" && window.outerbaseIpc;
@@ -56,7 +56,7 @@ export default function ConnectingDialog({
}
return (
-
+
{body}
diff --git a/src/components/gui/database-gui.tsx b/src/components/gui/database-gui.tsx
index 404ccf7c..6bd94c5d 100644
--- a/src/components/gui/database-gui.tsx
+++ b/src/components/gui/database-gui.tsx
@@ -1,29 +1,28 @@
"use client";
+import QueryWindow from "@/components/gui/tabs/query-tab";
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
-import WindowTabs, { WindowTabItemProps } from "./windows-tab";
-import QueryWindow from "@/components/gui/tabs/query-tab";
-import SidebarTab, { SidebarTabItem } from "./sidebar-tab";
import SchemaView from "./schema-sidebar";
+import SidebarTab, { SidebarTabItem } from "./sidebar-tab";
import ToolSidebar from "./sidebar/tools-sidebar";
+import WindowTabs, { WindowTabItemProps } from "./windows-tab";
-import { useDatabaseDriver } from "@/context/driver-provider";
-import SavedDocTab from "./sidebar/saved-doc-tab";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
-import { Binoculars, GearSix, Table } from "@phosphor-icons/react";
-import { normalizedPathname, sendAnalyticEvents } from "@/lib/tracking";
-import { useConfig } from "@/context/config-provider";
-import { cn } from "@/lib/utils";
import { scc } from "@/core/command";
import {
tabCloseChannel,
tabOpenChannel,
tabReplaceChannel,
} from "@/core/extension-tab";
+import { normalizedPathname, sendAnalyticEvents } from "@/lib/tracking";
+import { cn } from "@/lib/utils";
+import { Binoculars, GearSix, Table } from "@phosphor-icons/react";
+import SavedDocTab from "./sidebar/saved-doc-tab";
export default function DatabaseGui() {
const DEFAULT_WIDTH = 300;
@@ -34,8 +33,8 @@ export default function DatabaseGui() {
setDefaultWidthPercentage((DEFAULT_WIDTH / window.innerWidth) * 100);
}, []);
- const { databaseDriver, docDriver } = useDatabaseDriver();
- const { extensions, containerClassName } = useConfig();
+ const { databaseDriver, docDriver, extensions, containerClassName } =
+ useStudioContext();
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const { currentSchemaName } = useSchema();
diff --git a/src/components/gui/main-connection.tsx b/src/components/gui/main-connection.tsx
index 9477d5f7..d13316b7 100644
--- a/src/components/gui/main-connection.tsx
+++ b/src/components/gui/main-connection.tsx
@@ -2,8 +2,7 @@
import { TooltipProvider } from "@/components/ui/tooltip";
import { WEBSITE_NAME } from "@/const";
import { AutoCompleteProvider } from "@/context/auto-complete-provider";
-import { useConfig } from "@/context/config-provider";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { SchemaProvider } from "@/context/schema-provider";
import { useEffect } from "react";
import { DialogProvider } from "../create-dialog";
@@ -11,7 +10,7 @@ import ContextMenuHandler from "./context-menu-handler";
import DatabaseGui from "./database-gui";
function MainConnection() {
- const { databaseDriver: driver } = useDatabaseDriver();
+ const { databaseDriver: driver } = useStudioContext();
useEffect(() => {
return () => {
@@ -28,7 +27,7 @@ function MainConnection() {
}
function MainConnectionContainer() {
- const { name } = useConfig();
+ const { name } = useStudioContext();
useEffect(() => {
document.title = name + " - " + WEBSITE_NAME;
diff --git a/src/components/gui/query-result-table.tsx b/src/components/gui/query-result-table.tsx
index 1bce90c4..7ecba951 100644
--- a/src/components/gui/query-result-table.tsx
+++ b/src/components/gui/query-result-table.tsx
@@ -2,7 +2,7 @@ import OptimizeTable, {
OptimizeTableHeaderWithIndexProps,
} from "@/components/gui/table-optimized";
import OptimizeTableState from "@/components/gui/table-optimized/OptimizeTableState";
-import { useConfig } from "@/context/config-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { ColumnSortOption } from "@/drivers/base-driver";
import { exportDataAsDelimitedText } from "@/lib/export-helper";
import { KEY_BINDING } from "@/lib/key-matcher";
@@ -52,7 +52,8 @@ function Header({
if (internalState.getSelectedColIndex().includes(colIndex)) {
if (internalState.isFullSelectionCol(colIndex)) {
- textClass = "grow line-clamp-1 font-mono font-bold text-black dark:text-white font-bold";
+ textClass =
+ "grow line-clamp-1 font-mono font-bold text-black dark:text-white font-bold";
thClass =
"flex grow items-center px-2 overflow-hidden bg-neutral-100 dark:bg-neutral-900";
} else {
@@ -126,7 +127,7 @@ export default function ResultTable({
}: ResultTableProps) {
const [stickyHeaderIndex, setStickHeaderIndex] = useState
();
- const { extensions } = useConfig();
+ const { extensions } = useStudioContext();
const headerIndex = useMemo(() => {
if (visibleColumnIndexList) return visibleColumnIndexList;
@@ -135,7 +136,7 @@ export default function ResultTable({
const renderHeader = useCallback(
(header: OptimizeTableHeaderWithIndexProps) => {
- const extensionMenu = extensions.getQueryHeaderContextMenu(header);
+ const extensionMenu = extensions.getQueryHeaderContextMenu(header, data);
const extensionMenuItems = extensionMenu.map((item) => {
if (item.component) {
return (
diff --git a/src/components/gui/save-doc-button.tsx b/src/components/gui/save-doc-button.tsx
index 601eee5a..7c8e356b 100644
--- a/src/components/gui/save-doc-button.tsx
+++ b/src/components/gui/save-doc-button.tsx
@@ -1,12 +1,12 @@
+import { useStudioContext } from "@/context/driver-provider";
import {
SavedDocData,
SavedDocInput,
SavedDocNamespace,
} from "@/drivers/saved-doc/saved-doc-driver";
-import { Button } from "../ui/button";
-import { useDatabaseDriver } from "@/context/driver-provider";
-import { useCallback, useState } from "react";
import { LucideFolderGit, LucideLoader } from "lucide-react";
+import { useCallback, useState } from "react";
+import { Button } from "../ui/button";
import {
DropdownMenu,
DropdownMenuContent,
@@ -29,7 +29,7 @@ export default function SaveDocButton({
}: Props) {
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
- const { docDriver } = useDatabaseDriver();
+ const { docDriver } = useStudioContext();
const [namespaceList, setNamespaceList] = useState([]);
const onSaveQuery = useCallback(
@@ -86,7 +86,7 @@ export default function SaveDocButton({
variant={"secondary"}
>
{loading ? (
-
+
) : (
<>>
)}
@@ -104,7 +104,7 @@ export default function SaveDocButton({
onSaveQuery(n.id);
}}
>
- {n.name}
+ {n.name}
);
})}
diff --git a/src/components/gui/schema-editor/column-default-value-input.tsx b/src/components/gui/schema-editor/column-default-value-input.tsx
index c7aa7e3b..deefd728 100644
--- a/src/components/gui/schema-editor/column-default-value-input.tsx
+++ b/src/components/gui/schema-editor/column-default-value-input.tsx
@@ -5,7 +5,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { DatabaseTableColumnConstraint } from "@/drivers/base-driver";
import { ChevronsUpDown } from "lucide-react";
import { ChangeEvent, useCallback, useMemo } from "react";
@@ -20,7 +20,7 @@ export default function ColumnDefaultValueInput({
disabled?: boolean;
onChange: (constraint: DatabaseTableColumnConstraint) => void;
}>) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const display = useMemo(() => {
if (
constraint?.defaultValue !== undefined &&
diff --git a/src/components/gui/schema-editor/index.tsx b/src/components/gui/schema-editor/index.tsx
index 836c7430..3624ce5e 100644
--- a/src/components/gui/schema-editor/index.tsx
+++ b/src/components/gui/schema-editor/index.tsx
@@ -1,4 +1,4 @@
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { DatabaseTableSchemaChange } from "@/drivers/base-driver";
import { checkSchemaChange } from "@/lib/sql/sql-generate.schema";
import { LucideCode, LucideCopy, LucidePlus, LucideSave } from "lucide-react";
@@ -27,7 +27,7 @@ export default function SchemaEditor({
onSave,
onDiscard,
}: Readonly) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const isCreateScript = value.name.old === "";
const onAddColumn = useCallback(() => {
diff --git a/src/components/gui/schema-editor/schema-create/schema-create-form.tsx b/src/components/gui/schema-editor/schema-create/schema-create-form.tsx
index ade83957..79a61f50 100644
--- a/src/components/gui/schema-editor/schema-create/schema-create-form.tsx
+++ b/src/components/gui/schema-editor/schema-create/schema-create-form.tsx
@@ -1,23 +1,29 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
+import { DatabaseSchemaChange } from "@/drivers/base-driver";
import { LucideAlertCircle, LucideLoader, LucideSave } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
-import { useDatabaseDriver } from "@/context/driver-provider";
-import { DatabaseSchemaChange } from "@/drivers/base-driver";
-export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?: string; onClose: () => void; }) {
- const { databaseDriver } = useDatabaseDriver();
+export function SchemaDatabaseCreateForm({
+ schemaName,
+ onClose,
+}: {
+ schemaName?: string;
+ onClose: () => void;
+}) {
+ const { databaseDriver } = useStudioContext();
const { schema, refresh: refreshSchema } = useSchema();
const [isExecuting, setIsExecuting] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const [value, setValue] = useState({
name: {
- new: '',
- old: ''
+ new: "",
+ old: "",
},
- createScript: '',
- collate: ''
+ createScript: "",
+ collate: "",
});
const previewScript = useMemo(() => {
@@ -27,52 +33,60 @@ export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?:
const onSave = useCallback(() => {
{
setIsExecuting(true);
- databaseDriver.transaction([previewScript]).then(() => {
- refreshSchema();
- onClose();
- }).catch((err) => setErrorMessage((err as Error).message)).finally(() => {
- setIsExecuting(false);
- })
+ databaseDriver
+ .transaction([previewScript])
+ .then(() => {
+ refreshSchema();
+ onClose();
+ })
+ .catch((err) => setErrorMessage((err as Error).message))
+ .finally(() => {
+ setIsExecuting(false);
+ });
}
- }, [databaseDriver, onClose, previewScript, refreshSchema])
+ }, [databaseDriver, onClose, previewScript, refreshSchema]);
- const schemaNames = Object.keys(schema).filter(s => s !== schemaName).map(s => s);
- const schemaNameExists = schemaNames.includes(value.name.new || '');
+ const schemaNames = Object.keys(schema)
+ .filter((s) => s !== schemaName)
+ .map((s) => s);
+ const schemaNameExists = schemaNames.includes(value.name.new || "");
return (
-
+
{errorMessage && (
-
-
+
)}
-
+
-
Schema Name
+
Schema Name
{
setValue({
...value,
name: {
...value.name,
- new: e.currentTarget.value
- }
- })
+ new: e.currentTarget.value,
+ },
+ });
}}
disabled={!!schemaName}
- className={`w-full ${schemaNameExists ? 'border-red-600' : ''}`}
+ className={`w-full ${schemaNameExists ? "border-red-600" : ""}`}
/>
- {
- schemaNameExists &&
The schema name `{value.name.new}` already exists.
- }
+ {schemaNameExists && (
+
+ The schema name `{value.name.new}` already exists.
+
+ )}
-
-
-
+
+
+
@@ -90,5 +104,5 @@ export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?:
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/src/components/gui/schema-editor/schema-editor-column-list.tsx b/src/components/gui/schema-editor/schema-editor-column-list.tsx
index a8c0f629..efa925fe 100644
--- a/src/components/gui/schema-editor/schema-editor-column-list.tsx
+++ b/src/components/gui/schema-editor/schema-editor-column-list.tsx
@@ -1,6 +1,6 @@
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import {
DatabaseTableColumn,
DatabaseTableColumnChange,
@@ -104,7 +104,7 @@ function ColumnItemType({
onChange: (type: string) => void;
disabled?: boolean;
}) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
if (
databaseDriver.columnTypeSelector.type === "dropdown" &&
diff --git a/src/components/gui/schema-editor/schema-save-dialog.tsx b/src/components/gui/schema-editor/schema-save-dialog.tsx
index d083d390..5df75b75 100644
--- a/src/components/gui/schema-editor/schema-save-dialog.tsx
+++ b/src/components/gui/schema-editor/schema-save-dialog.tsx
@@ -6,7 +6,7 @@ import {
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
import { DatabaseTableSchemaChange } from "@/drivers/base-driver";
import {
@@ -31,7 +31,7 @@ export default function SchemaSaveDialog({
onClose: () => void;
fetchTable: (schemeName: string, tableName: string) => Promise
;
}) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const { refresh: refreshSchema } = useSchema();
const [isExecuting, setIsExecuting] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
diff --git a/src/components/gui/schema-sidebar-list.tsx b/src/components/gui/schema-sidebar-list.tsx
index a965dd8f..bcf9b7b4 100644
--- a/src/components/gui/schema-sidebar-list.tsx
+++ b/src/components/gui/schema-sidebar-list.tsx
@@ -1,5 +1,4 @@
-import { useConfig } from "@/context/config-provider";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
import { OpenContextMenuList } from "@/core/channel-builtin";
import { scc } from "@/core/command";
@@ -124,8 +123,7 @@ function flattenSchemaGroup(
}
export default function SchemaList({ search }: Readonly) {
- const { databaseDriver } = useDatabaseDriver();
- const { extensions } = useConfig();
+ const { databaseDriver, extensions } = useStudioContext();
const [selected, setSelected] = useState("");
const { refresh, schema, currentSchemaName } = useSchema();
const [editSchema, setEditSchema] = useState(null);
diff --git a/src/components/gui/schema-sidebar.tsx b/src/components/gui/schema-sidebar.tsx
index d8ccf9fe..1d31e156 100644
--- a/src/components/gui/schema-sidebar.tsx
+++ b/src/components/gui/schema-sidebar.tsx
@@ -1,28 +1,26 @@
-import { LucideSearch } from "lucide-react";
-import { useMemo, useState } from "react";
-import SchemaList from "./schema-sidebar-list";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
+import { scc } from "@/core/command";
+import { StudioExtensionMenuItem } from "@/core/extension-manager";
import { cn } from "@/lib/utils";
-import { buttonVariants } from "../ui/button";
import { Plus } from "@phosphor-icons/react";
-import { useDatabaseDriver } from "@/context/driver-provider";
-import SchemaCreateDialog from "./schema-editor/schema-create";
+import { LucideSearch } from "lucide-react";
+import { useMemo, useState } from "react";
+import { buttonVariants } from "../ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
-import { scc } from "@/core/command";
-import { StudioExtensionMenuItem } from "@/core/extension-manager";
-import { useConfig } from "@/context/config-provider";
+import SchemaCreateDialog from "./schema-editor/schema-create";
+import SchemaList from "./schema-sidebar-list";
export default function SchemaView() {
const [search, setSearch] = useState("");
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver, extensions } = useStudioContext();
const { currentSchemaName } = useSchema();
const [isCreateSchema, setIsCreateSchema] = useState(false);
- const { extensions } = useConfig();
const contentMenu = useMemo(() => {
const items: StudioExtensionMenuItem[] = [];
@@ -62,7 +60,7 @@ export default function SchemaView() {
buttonVariants({
size: "icon",
}),
- "rounded-full h-8 w-8 bg-neutral-800 dark:bg-neutral-200"
+ "h-8 w-8 rounded-full bg-neutral-800 dark:bg-neutral-200"
)}
onClick={contentMenu[0].onClick}
>
@@ -79,7 +77,7 @@ export default function SchemaView() {
buttonVariants({
size: "icon",
}),
- "rounded-full h-8 w-8 bg-neutral-800 dark:bg-neutral-200"
+ "h-8 w-8 rounded-full bg-neutral-800 dark:bg-neutral-200"
)}
>
@@ -98,7 +96,7 @@ export default function SchemaView() {
);
}, [contentMenu]);
return (
-
+
{isCreateSchema && (
{
@@ -107,22 +105,22 @@ export default function SchemaView() {
/>
)}
-
-
-
Tables
+
+
+
Tables
{activatorButton}
-
-
+
+
{
diff --git a/src/components/gui/sidebar-tab.tsx b/src/components/gui/sidebar-tab.tsx
index 72feb877..56378e92 100644
--- a/src/components/gui/sidebar-tab.tsx
+++ b/src/components/gui/sidebar-tab.tsx
@@ -1,4 +1,4 @@
-import { useConfig } from "@/context/config-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { cn } from "@/lib/utils";
import { ArrowLeft } from "@phosphor-icons/react";
import { useTheme } from "next-themes";
@@ -40,14 +40,10 @@ export default function SidebarTab({ tabs }: Readonly
) {
const disableToggle =
searchParams.get("disableThemeToggle") === "1" || forcedTheme;
- const config = useConfig();
+ const config = useStudioContext();
return (
-
+
diff --git a/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx b/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx
index 005af665..71febeaa 100644
--- a/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx
+++ b/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx
@@ -7,7 +7,7 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { SavedDocNamespace } from "@/drivers/saved-doc/saved-doc-driver";
import { useCallback, useState } from "react";
@@ -20,7 +20,7 @@ export default function CreateNamespaceDialog({
onCreated,
onClose,
}: CreateNamespaceButtonProps) {
- const { docDriver } = useDatabaseDriver();
+ const { docDriver } = useStudioContext();
const [namespace, setNamespace] = useState("");
const [error, setError] = useState("");
@@ -64,7 +64,7 @@ export default function CreateNamespaceDialog({
onChange={(e) => setNamespace(e.currentTarget.value)}
/>
- {error && {error}
}
+ {error && {error}
}
diff --git a/src/components/gui/sidebar/saved-doc-tab/index.tsx b/src/components/gui/sidebar/saved-doc-tab/index.tsx
index a87128b9..d8ecd9aa 100644
--- a/src/components/gui/sidebar/saved-doc-tab/index.tsx
+++ b/src/components/gui/sidebar/saved-doc-tab/index.tsx
@@ -1,28 +1,28 @@
-import { useDatabaseDriver } from "@/context/driver-provider";
-import {
- SavedDocData,
- SavedDocGroupByNamespace,
- SavedDocNamespace,
-} from "@/drivers/saved-doc/saved-doc-driver";
import { ListView, ListViewItem } from "@/components/listview";
-import { LucideTrash } from "lucide-react";
-import { useCallback, useEffect, useState } from "react";
-import RenameNamespaceDialog from "./rename-namespace-dialog";
-import RemoveDocDialog from "./remove-doc-dialog";
-import { TAB_PREFIX_SAVED_QUERY } from "@/const";
-import RemoveNamespaceDialog from "./remove-namespace-dialog";
-import { cn } from "@/lib/utils";
import { buttonVariants } from "@/components/ui/button";
-import { Binoculars, Folder, Plus } from "@phosphor-icons/react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
-import CreateNamespaceDialog from "./create-namespace-button";
-import { scc } from "@/core/command";
+import { TAB_PREFIX_SAVED_QUERY } from "@/const";
+import { useStudioContext } from "@/context/driver-provider";
import { OpenContextMenuList } from "@/core/channel-builtin";
+import { scc } from "@/core/command";
+import {
+ SavedDocData,
+ SavedDocGroupByNamespace,
+ SavedDocNamespace,
+} from "@/drivers/saved-doc/saved-doc-driver";
+import { cn } from "@/lib/utils";
+import { Binoculars, Folder, Plus } from "@phosphor-icons/react";
+import { LucideTrash } from "lucide-react";
+import { useCallback, useEffect, useState } from "react";
+import CreateNamespaceDialog from "./create-namespace-button";
+import RemoveDocDialog from "./remove-doc-dialog";
+import RemoveNamespaceDialog from "./remove-namespace-dialog";
+import RenameNamespaceDialog from "./rename-namespace-dialog";
type SavedDocListData =
| {
@@ -56,7 +56,7 @@ function mapDoc(
}
export default function SavedDocTab() {
- const { docDriver } = useDatabaseDriver();
+ const { docDriver } = useStudioContext();
const [selected, setSelected] = useState();
const [collapsed, setCollapsed] = useState>(new Set());
@@ -157,7 +157,7 @@ export default function SavedDocTab() {
-
Queries
+
Queries
diff --git a/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx b/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx
index c788954b..4f816562 100644
--- a/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx
+++ b/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx
@@ -6,7 +6,7 @@ import {
DialogFooter,
DialogTitle,
} from "@/components/ui/dialog";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { SavedDocData } from "@/drivers/saved-doc/saved-doc-driver";
import { useCallback, useState } from "react";
@@ -17,7 +17,7 @@ interface Props {
}
export default function RemoveDocDialog({ doc, onClose, onComplete }: Props) {
- const { docDriver } = useDatabaseDriver();
+ const { docDriver } = useStudioContext();
const [loading, setLoading] = useState(false);
const onDeleteClicked = useCallback(() => {
diff --git a/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx b/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx
index 9b5b89c3..5fe4df8c 100644
--- a/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx
+++ b/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx
@@ -6,7 +6,7 @@ import {
DialogFooter,
DialogTitle,
} from "@/components/ui/dialog";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import {
SavedDocData,
SavedDocNamespace,
@@ -24,7 +24,7 @@ export default function RemoveNamespaceDialog({
onComplete,
value,
}: Props) {
- const { docDriver } = useDatabaseDriver();
+ const { docDriver } = useStudioContext();
const [loading, setLoading] = useState(false);
const onDeleteClicked = useCallback(() => {
diff --git a/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx b/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx
index e2b75a5f..97f6d7f8 100644
--- a/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx
+++ b/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx
@@ -7,7 +7,7 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { SavedDocNamespace } from "@/drivers/saved-doc/saved-doc-driver";
import { useCallback, useState } from "react";
@@ -22,7 +22,7 @@ export default function RenameNamespaceDialog({
onComplete,
value,
}: Props) {
- const { docDriver } = useDatabaseDriver();
+ const { docDriver } = useStudioContext();
const [namespace, setNamespace] = useState(value.name);
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
@@ -66,7 +66,7 @@ export default function RenameNamespaceDialog({
value={namespace}
onChange={(e) => setNamespace(e.currentTarget.value)}
/>
- {error && {error}
}
+ {error && {error}
}
@@ -108,7 +108,7 @@ function ForeignKeyColumnSnippet(props: SneakpeakProps) {
{props.fkTableName}
-
@@ -143,7 +143,7 @@ function BlobCellValue({
return (
-
+
vec({floatArray.length})
@@ -159,7 +159,7 @@ function BlobCellValue({
{prettifyBytes(bytes.subarray(0, 64))}
-
+
{bytes.length.toLocaleString(undefined, {
maximumFractionDigits: 0,
})}
@@ -203,6 +203,11 @@ export default function GenericCell({
}, [header, value]);
const content = useMemo(() => {
+ if (header.decorator) {
+ const decoratorContent = header.decorator(value);
+ if (decoratorContent !== null) return decoratorContent;
+ }
+
if (value === null) {
return NULL;
}
diff --git a/src/components/gui/table-combobox/TableColumnCombobox.tsx b/src/components/gui/table-combobox/TableColumnCombobox.tsx
index 71733e47..ba1531fe 100644
--- a/src/components/gui/table-combobox/TableColumnCombobox.tsx
+++ b/src/components/gui/table-combobox/TableColumnCombobox.tsx
@@ -1,4 +1,4 @@
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import type { DatabaseTableSchema } from "@/drivers/base-driver";
import { cn } from "@/lib/utils";
import { Popover, PopoverTrigger } from "@radix-ui/react-popover";
@@ -29,7 +29,7 @@ export default function TableColumnCombobox({
disabled?: boolean;
borderless?: boolean;
}>) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [open, setOpen] = useState(false);
const [schema, setSchema] = useState();
diff --git a/src/components/gui/table-optimized/OptimizeTableState.tsx b/src/components/gui/table-optimized/OptimizeTableState.tsx
index 22c6b1f1..5f6529ce 100644
--- a/src/components/gui/table-optimized/OptimizeTableState.tsx
+++ b/src/components/gui/table-optimized/OptimizeTableState.tsx
@@ -5,7 +5,7 @@ import {
import { formatNumber } from "@/lib/convertNumber";
import { selectArrayFromIndexList } from "@/lib/export-helper";
import deepEqual from "deep-equal";
-import { OptimizeTableHeaderProps } from ".";
+import { OptimizeTableHeaderProps, TableCellDecorator } from ".";
export interface OptimizeTableRowValue {
raw: Record;
@@ -42,6 +42,7 @@ export default class OptimizeTableState {
public gutterColumnWidth = 40;
protected headers: OptimizeTableHeaderProps[] = [];
+ public headerRevision = 1;
protected headerWidth: number[] = [];
protected editMode = false;
@@ -203,6 +204,28 @@ export default class OptimizeTableState {
return this.headers;
}
+ updateHeaderDecorator(
+ header: OptimizeTableHeaderProps,
+ decorator: TableCellDecorator | undefined
+ ) {
+ const idx = this.headers.findIndex((h) => h.name === header.name);
+
+ if (idx >= 0) {
+ this.headers = this.headers.map((h) => {
+ if (h.name === header.name) {
+ return {
+ ...h,
+ decorator,
+ };
+ }
+ return h;
+ });
+
+ this.headerRevision++;
+ this.broadcastChange();
+ }
+ }
+
getValue(y: number, x: number): unknown {
const rowChange = this.data[y]?.change;
if (rowChange) {
diff --git a/src/components/gui/table-optimized/index.tsx b/src/components/gui/table-optimized/index.tsx
index cd93330a..fb012a57 100644
--- a/src/components/gui/table-optimized/index.tsx
+++ b/src/components/gui/table-optimized/index.tsx
@@ -19,6 +19,8 @@ import TableFakeRowPadding from "./table-fake-row-padding";
import TableHeaderList from "./table-header-list";
import useTableVisibilityRecalculation from "./useTableVisibilityRecalculation";
+export type TableCellDecorator = (value: unknown) => ReactElement | null;
+
export interface TableHeaderMetadata {
from?: {
schema: string;
@@ -41,6 +43,7 @@ export interface TableHeaderMetadata {
columnSchema?: DatabaseTableColumn;
}
+
export interface OptimizeTableHeaderProps {
name: string;
@@ -52,6 +55,8 @@ export interface OptimizeTableHeaderProps {
tooltip?: string;
};
+ decorator?: TableCellDecorator;
+
setting: {
resizable: boolean;
readonly: boolean;
@@ -60,6 +65,7 @@ export interface OptimizeTableHeaderProps {
onContextMenu?: (e: React.MouseEvent, headerIndex: number) => void;
metadata: TableHeaderMetadata;
+ store: Map;
}
export interface OptimizeTableHeaderWithIndexProps
@@ -279,12 +285,14 @@ export default function OptimizeTable({
return () => internalState.removeChangeListener(changeCallback);
}, [internalState, rerender]);
+ const headerRevision = internalState.headerRevision;
const headerWithIndex = useMemo(() => {
// Attach the actual index
const headers = internalState.getHeaders().map((header, idx) => ({
...header,
index: idx,
sticky: idx === stickyHeaderIndex,
+ headerRevision, // this is basically useless, but we do it to ignore deps warning
}));
// We will rearrange the index based on specified index
@@ -297,7 +305,7 @@ export default function OptimizeTable({
...(stickyHeaderIndex !== undefined ? [headers[stickyHeaderIndex]] : []),
...headerAfterArranged.filter((x) => x.index !== stickyHeaderIndex),
];
- }, [internalState, arrangeHeaderIndex, stickyHeaderIndex]);
+ }, [internalState, arrangeHeaderIndex, stickyHeaderIndex, headerRevision]);
const { visibileRange, onHeaderResize } = useTableVisibilityRecalculation({
containerRef,
diff --git a/src/components/gui/table-result/context-menu.tsx b/src/components/gui/table-result/context-menu.tsx
index 9d795b25..bd20f39f 100644
--- a/src/components/gui/table-result/context-menu.tsx
+++ b/src/components/gui/table-result/context-menu.tsx
@@ -1,16 +1,16 @@
-import { useCallback } from "react";
-import { KEY_BINDING } from "@/lib/key-matcher";
-import OptimizeTableState from "../table-optimized/OptimizeTableState";
-import { useFullEditor } from "../providers/full-editor-provider";
+import { useStudioContext } from "@/context/driver-provider";
+import { openContextMenuFromEvent } from "@/core/channel-builtin";
import {
exportRowsToExcel,
exportRowsToJson,
exportRowsToSqlInsert,
} from "@/lib/export-helper";
+import { KEY_BINDING } from "@/lib/key-matcher";
import { LucidePlus, LucideTrash2 } from "lucide-react";
+import { useCallback } from "react";
+import { useFullEditor } from "../providers/full-editor-provider";
+import OptimizeTableState from "../table-optimized/OptimizeTableState";
import TableStateActions from "../table-optimized/table-state-actions";
-import { openContextMenuFromEvent } from "@/core/channel-builtin";
-import { useConfig } from "@/context/config-provider";
export default function useTableResultContextMenu({
tableName,
@@ -24,7 +24,7 @@ export default function useTableResultContextMenu({
pasteCallback: (state: OptimizeTableState) => void;
}) {
const { openEditor } = useFullEditor();
- const { extensions } = useConfig();
+ const { extensions } = useStudioContext();
return useCallback(
({
diff --git a/src/components/gui/tabs-result/explain-result-tab.tsx b/src/components/gui/tabs-result/explain-result-tab.tsx
index b8528f4c..b3443091 100644
--- a/src/components/gui/tabs-result/explain-result-tab.tsx
+++ b/src/components/gui/tabs-result/explain-result-tab.tsx
@@ -1,15 +1,15 @@
-import { useDatabaseDriver } from "@/context/driver-provider";
-import { QueryExplanation } from "../query-explanation";
+import { useStudioContext } from "@/context/driver-provider";
import { DatabaseResultSet } from "@/drivers/base-driver";
+import { QueryExplanation } from "../query-explanation";
export default function ExplainResultTab({
data,
}: {
data: DatabaseResultSet;
}) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
return (
-
+
You are about to drop the following tables.
-
+
{selectedItems.map((t) => (
-
-
+
ln(x) + ex-1 - cos(x) = 0
Solve this equaltion or type confirm
@@ -81,7 +81,7 @@ function ConfirmDialog({
}
export default function MassDropTableTab() {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const { schema: initialSchema, currentSchemaName, refresh } = useSchema();
const [schema] = useState(initialSchema);
const [selectedSchema] = useState(currentSchemaName);
@@ -168,27 +168,27 @@ export default function MassDropTableTab() {
if (status === "Failed")
statusIcon = (
-
+
);
else if (status.includes("..."))
statusIcon = (
-
+
);
else if (status === "Dropped" || status === "Emptied")
statusIcon = (
-
+
);
return (
<>
-
+ |
{t.name}
|
-
+ |
{t.type}
|
-
+ |
{statusIcon}
{status}
|
@@ -217,7 +217,7 @@ export default function MassDropTableTab() {
)}
-
+
Drop & Empty Multiple Tables
@@ -240,7 +240,7 @@ export default function MassDropTableTab() {
-
+
(null);
@@ -91,8 +90,6 @@ export default function QueryWindow({
);
const [savedKey, setSavedKey] = useState(initialSavedKey);
const [placeholders, setPlaceholders] = useState>({});
-
- const { agentDriver } = useConfig();
const { schema } = useSchema();
useEffect(() => {
diff --git a/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx b/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx
index 4025b28a..be9552d4 100644
--- a/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx
+++ b/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx
@@ -1,20 +1,20 @@
-import React, { PropsWithChildren } from "react";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
+import { PropsWithChildren } from "react";
+import { scc } from "@/core/command";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "../../../ui/context-menu";
-import { scc } from "@/core/command";
export default function ContextMenuERD({
schemaName,
tableName,
children,
}: PropsWithChildren<{ schemaName: string; tableName: string }>) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const handleEditTable = () => {
scc.tabs.openBuiltinSchema({
diff --git a/src/components/gui/tabs/schema-editor-tab.tsx b/src/components/gui/tabs/schema-editor-tab.tsx
index bd5a0cd5..5d2960e6 100644
--- a/src/components/gui/tabs/schema-editor-tab.tsx
+++ b/src/components/gui/tabs/schema-editor-tab.tsx
@@ -1,5 +1,5 @@
import OpacityLoading from "@/components/gui/loading-opacity";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { DatabaseTableSchemaChange } from "@/drivers/base-driver";
import { createTableSchemaDraft } from "@/lib/sql/sql-generate.schema";
import { cloneDeep } from "lodash";
@@ -26,7 +26,7 @@ export default function SchemaEditorTab({
schemaName,
tableName,
}: Readonly) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [schema, setSchema] = useState({
...EMPTY_SCHEMA,
schemaName,
diff --git a/src/components/gui/tabs/table-data-tab.tsx b/src/components/gui/tabs/table-data-tab.tsx
index e47bba6b..43741d0d 100644
--- a/src/components/gui/tabs/table-data-tab.tsx
+++ b/src/components/gui/tabs/table-data-tab.tsx
@@ -6,7 +6,7 @@ import {
AlertDialogDescription,
AlertDialogFooter,
} from "@/components/ui/alert-dialog";
-// import { Button } from "@/components/ui/button";
+
import { Button } from "@/components/orbit/button";
import {
Tooltip,
@@ -14,7 +14,7 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useAutoComplete } from "@/context/auto-complete-provider";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
import {
ColumnSortOption,
@@ -50,7 +50,7 @@ export default function TableDataWindow({
}: TableDataContentProps) {
const { updateTableSchema } = useAutoComplete();
const { schema } = useSchema();
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [error, setError] = useState();
const [executeError, setExecuteError] = useState(null);
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
index e5704bcf..0e4a237f 100644
--- a/src/components/ui/dropdown-menu.tsx
+++ b/src/components/ui/dropdown-menu.tsx
@@ -31,7 +31,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
void;
- extensions: StudioExtensionManager;
- containerClassName?: string;
- agentDriver?: AgentDriverList;
-}
-
-const ConfigContext = createContext({
- name: "",
- color: "",
- extensions: new StudioExtensionManager([]),
- onBack: noop,
- containerClassName: "",
-});
-
-export function useConfig() {
- return useContext(ConfigContext);
-}
-
-export function ConfigProvider({
- children,
- config,
-}: PropsWithChildren<{ config: ConfigContextProps }>) {
- return (
- {children}
- );
-}
diff --git a/src/context/driver-provider.tsx b/src/context/driver-provider.tsx
index 9e380019..f294a345 100644
--- a/src/context/driver-provider.tsx
+++ b/src/context/driver-provider.tsx
@@ -1,29 +1,41 @@
+import { StudioExtensionManager } from "@/core/extension-manager";
+import AgentDriverList from "@/drivers/agent/list";
import type { BaseDriver } from "@/drivers/base-driver";
import { SavedDocDriver } from "@/drivers/saved-doc/saved-doc-driver";
+import { noop } from "lodash";
import { type PropsWithChildren, createContext, useContext } from "react";
-const DriverContext = createContext<{
+export interface StudioContextProps {
databaseDriver: BaseDriver;
docDriver?: SavedDocDriver;
-}>({
+
+ // Moving from useConfig previously
+ color: string;
+ name: string;
+ onBack?: () => void;
+ extensions: StudioExtensionManager;
+ containerClassName?: string;
+ agentDriver?: AgentDriverList;
+}
+
+const StudioContext = createContext({
databaseDriver: {} as unknown as BaseDriver,
+ name: "",
+ color: "",
+ extensions: new StudioExtensionManager([]),
+ onBack: noop,
+ containerClassName: "",
});
-export function useDatabaseDriver() {
- return useContext(DriverContext);
+export function useStudioContext() {
+ return useContext(StudioContext);
}
-export function DriverProvider({
+export function StudioContextProvider({
children,
- driver,
- docDriver,
-}: PropsWithChildren<{
- driver: BaseDriver;
- docDriver?: SavedDocDriver;
-}>) {
+ value,
+}: PropsWithChildren<{ value: StudioContextProps }>) {
return (
-
- {children}
-
+ {children}
);
}
diff --git a/src/context/schema-provider.tsx b/src/context/schema-provider.tsx
index b62695da..7cb89dcb 100644
--- a/src/context/schema-provider.tsx
+++ b/src/context/schema-provider.tsx
@@ -10,8 +10,7 @@ import {
useState,
} from "react";
import { useAutoComplete } from "./auto-complete-provider";
-import { useConfig } from "./config-provider";
-import { useDatabaseDriver } from "./driver-provider";
+import { useStudioContext } from "./driver-provider";
type AutoCompletionSchema = Record | string[]>;
@@ -68,8 +67,7 @@ export function SchemaProvider({ children }: Readonly) {
const { updateTableList, updateTableSchema } = useAutoComplete();
const [error, setError] = useState();
const [loading, setLoading] = useState(true);
- const { databaseDriver } = useDatabaseDriver();
- const { extensions } = useConfig();
+ const { databaseDriver, extensions } = useStudioContext();
const [schema, setSchema] = useState({});
const [currentSchema, setCurrentSchema] = useState([]);
diff --git a/src/core/extension-manager.tsx b/src/core/extension-manager.tsx
index 55c53898..ad787164 100644
--- a/src/core/extension-manager.tsx
+++ b/src/core/extension-manager.tsx
@@ -1,4 +1,5 @@
import { OptimizeTableHeaderProps } from "@/components/gui/table-optimized";
+import OptimizeTableState from "@/components/gui/table-optimized/OptimizeTableState";
import { DatabaseSchemaItem, DatabaseSchemas } from "@/drivers/base-driver";
import { ReactElement } from "react";
import { IStudioExtension } from "./extension-base";
@@ -28,7 +29,8 @@ type CreateResourceMenuHandler = (
) => StudioExtensionMenuItem | undefined;
type QueryHeaderResultMenuHandler = (
- header: OptimizeTableHeaderProps
+ header: OptimizeTableHeaderProps,
+ state: OptimizeTableState
) => StudioExtensionMenuItem | undefined;
type AfterFetchSchemaHandler = (schema: DatabaseSchemas) => void;
@@ -120,9 +122,12 @@ export class StudioExtensionManager extends StudioExtensionContext {
.filter(Boolean) as StudioExtensionMenuItem[];
}
- getQueryHeaderContextMenu(header: OptimizeTableHeaderProps) {
+ getQueryHeaderContextMenu(
+ header: OptimizeTableHeaderProps,
+ state: OptimizeTableState
+ ) {
return this.queryResultHeaderContextMenu
- .map((handler) => handler(header))
+ .map((handler) => handler(header, state))
.filter(Boolean) as StudioExtensionMenuItem[];
}
diff --git a/src/core/standard-extension.tsx b/src/core/standard-extension.tsx
index 3923b6f2..3f121c8a 100644
--- a/src/core/standard-extension.tsx
+++ b/src/core/standard-extension.tsx
@@ -3,6 +3,7 @@
*/
import ColumnDescriptorExtension from "@/extensions/column-descriptor";
+import DataDecoratorExtension from "@/extensions/data-decorator";
import QueryHistoryConsoleLogExtension from "@/extensions/query-console-log";
import TriggerEditorExtension from "@/extensions/trigger-editor";
import ViewEditorExtension from "@/extensions/view-editor";
@@ -12,6 +13,7 @@ export function createStandardExtensions() {
new QueryHistoryConsoleLogExtension(),
new ViewEditorExtension(),
new ColumnDescriptorExtension(),
+ new DataDecoratorExtension(),
];
}
diff --git a/src/extensions/data-catalog/data-catalog-tab.tsx b/src/extensions/data-catalog/data-catalog-tab.tsx
index eb4df4ab..0f621ac6 100644
--- a/src/extensions/data-catalog/data-catalog-tab.tsx
+++ b/src/extensions/data-catalog/data-catalog-tab.tsx
@@ -1,7 +1,7 @@
import { Toolbar, ToolbarFiller } from "@/components/gui/toolbar";
import { Button } from "@/components/orbit/button";
import { Dialog, DialogContent } from "@/components/ui/dialog";
-import { useConfig } from "@/context/config-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useEffect, useState } from "react";
import DataCatalogExtension from ".";
import { DataCatalogEntryModal } from "./data-catalog-entry-modal";
@@ -10,7 +10,7 @@ import EmptyTermDefinition from "./empty-definition";
import TermDefinitionList from "./term-definition-list";
export default function DataCatalogTab() {
- const { extensions } = useConfig();
+ const { extensions } = useStudioContext();
const dataCatalogExtension =
extensions.getExtension("data-catalog");
const driver = dataCatalogExtension?.driver;
diff --git a/src/extensions/data-catalog/data-catalog-table-column-modal.tsx b/src/extensions/data-catalog/data-catalog-table-column-modal.tsx
index fcb2e4ca..a44af2e0 100644
--- a/src/extensions/data-catalog/data-catalog-table-column-modal.tsx
+++ b/src/extensions/data-catalog/data-catalog-table-column-modal.tsx
@@ -7,7 +7,7 @@ import {
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { MagicWand } from "@phosphor-icons/react";
import { produce } from "immer";
import { LucideLoader } from "lucide-react";
@@ -30,7 +30,7 @@ export default function DataCatalogTableColumnModal({
const { driver } = useDataCatalogContext();
const modelColumn = driver.getColumn(schemaName, tableName, columnName);
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [column, setColumn] = useState(() => ({
definition: modelColumn?.definition || "",
samples: modelColumn?.samples || [],
diff --git a/src/extensions/data-catalog/data-model-tab.tsx b/src/extensions/data-catalog/data-model-tab.tsx
index 125fe8fc..311339c2 100644
--- a/src/extensions/data-catalog/data-model-tab.tsx
+++ b/src/extensions/data-catalog/data-model-tab.tsx
@@ -2,7 +2,7 @@ import SchemaNameSelect from "@/components/gui/schema-editor/schema-name-select"
import { Toolbar, ToolbarFiller } from "@/components/gui/toolbar";
import { Input } from "@/components/orbit/input";
import { Toggle } from "@/components/orbit/toggle";
-import { useConfig } from "@/context/config-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
import { DatabaseTableSchema } from "@/drivers/base-driver";
import { MagnifyingGlass } from "@phosphor-icons/react";
@@ -31,7 +31,7 @@ export default function DataCatalogModelTab() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, setRevision] = useState(1);
- const { extensions } = useConfig();
+ const { extensions } = useStudioContext();
const driver = useMemo(() => {
const dataCatalogExtension =
diff --git a/src/extensions/data-decorator/editor.tsx b/src/extensions/data-decorator/editor.tsx
new file mode 100644
index 00000000..fad9697b
--- /dev/null
+++ b/src/extensions/data-decorator/editor.tsx
@@ -0,0 +1,126 @@
+import { OptimizeTableHeaderProps } from "@/components/gui/table-optimized";
+import OptimizeTableState from "@/components/gui/table-optimized/OptimizeTableState";
+import {
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+} from "@/components/ui/dropdown-menu";
+import { Check } from "@phosphor-icons/react";
+import { useState } from "react";
+import z from "zod";
+
+function currencyDecorator(value: unknown) {
+ if (typeof value === "number" || typeof value === "bigint") {
+ return (
+
+ {new Intl.NumberFormat("en-US", {
+ style: "currency",
+ currency: "USD",
+ }).format(Number(value))}
+
+ );
+ }
+
+ return null;
+}
+
+function unixToDateTimeDecorator(value: unknown) {
+ if (typeof value === "number" || typeof value === "bigint") {
+ return (
+
+ {new Date(Number(value) * 1000).toISOString()}
+
+ );
+ }
+
+ return null;
+}
+
+function unixMsToDateTimeDecorator(value: unknown) {
+ if (typeof value === "number" || typeof value === "bigint") {
+ return (
+
+ {new Date(Number(value)).toISOString()}
+
+ );
+ }
+
+ return null;
+}
+
+export function DecoratorEditor({
+ header,
+ state,
+}: {
+ header: OptimizeTableHeaderProps;
+ state: OptimizeTableState;
+}) {
+ const [type, setType] = useState(() => {
+ const setting = header.store.get("decorator");
+
+ const schema = z.object({ type: z.enum(["currency", "unix", "unix-ms"]) });
+ const validated = schema.safeParse(setting);
+
+ if (validated.error) {
+ return null;
+ }
+
+ return validated.data.type;
+ });
+
+ return (
+
+ Format
+
+ {
+ state.updateHeaderDecorator(header, undefined);
+ setType(null);
+ header.store.set("decorator", undefined);
+ }}
+ >
+ {type === null ? : null}
+ None
+
+
+ {
+ state.updateHeaderDecorator(header, currencyDecorator);
+ setType("currency");
+ header.store.set("decorator", { type: "currency" });
+ }}
+ >
+ {type === "currency" ? : null}
+ Currency
+
+
+ {
+ state.updateHeaderDecorator(header, unixMsToDateTimeDecorator);
+ setType("unix-ms");
+ header.store.set("decorator", { type: "unix-ms" });
+ }}
+ >
+ {type === "unix-ms" ? : null}
+ Unix Timestamp (ms) to datetime
+
+ {
+ state.updateHeaderDecorator(header, unixToDateTimeDecorator);
+ setType("unix");
+ header.store.set("decorator", { type: "unix" });
+ }}
+ >
+ {type === "unix" ? : null}
+ Unix Timestamp (s) to datetime
+
+
+
+ );
+}
diff --git a/src/extensions/data-decorator/index.tsx b/src/extensions/data-decorator/index.tsx
new file mode 100644
index 00000000..9d36833a
--- /dev/null
+++ b/src/extensions/data-decorator/index.tsx
@@ -0,0 +1,17 @@
+import { StudioExtension } from "@/core/extension-base";
+import { StudioExtensionContext } from "@/core/extension-manager";
+import { DecoratorEditor } from "./editor";
+
+export default class DataDecoratorExtension extends StudioExtension {
+ extensionName = "data-decorator";
+
+ init(studio: StudioExtensionContext): void {
+ studio.registerQueryHeaderContextMenu((header, state) => {
+ return {
+ key: "data-decorator",
+ title: "Decorate",
+ component: ,
+ };
+ });
+ }
+}
diff --git a/src/extensions/dolt/dolt-create-branch.tsx b/src/extensions/dolt/dolt-create-branch.tsx
index 0055c80a..7eeccfcb 100644
--- a/src/extensions/dolt/dolt-create-branch.tsx
+++ b/src/extensions/dolt/dolt-create-branch.tsx
@@ -8,12 +8,12 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { GitBranch, Loader } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
export default function useDoltCreateBranchModal(refreshStatus: () => void) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState();
@@ -56,7 +56,7 @@ export default function useDoltCreateBranchModal(refreshStatus: () => void) {
{commitHash ? (
<>
Creating new branch from{" "}
-
+
{commitHash}
>
@@ -80,9 +80,9 @@ export default function useDoltCreateBranchModal(refreshStatus: () => void) {
diff --git a/src/extensions/dolt/dolt-sidebar.tsx b/src/extensions/dolt/dolt-sidebar.tsx
index 95d3605b..04575437 100644
--- a/src/extensions/dolt/dolt-sidebar.tsx
+++ b/src/extensions/dolt/dolt-sidebar.tsx
@@ -19,7 +19,7 @@ import {
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
import { cn } from "@/lib/utils";
import { GitBranch, Minus, Plus, Table } from "@phosphor-icons/react";
@@ -51,7 +51,7 @@ function DoltCommitLog({
const { modal: createBranchModal, openModal: openCreateBranch } =
useDoltCreateBranchModal(refreshStatus);
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const { showDialog: showCommonDialog } = useCommonDialog();
const onResetClicked = useCallback(
@@ -167,7 +167,7 @@ function DoltChangeItem({
status: DoltStatusResultItem;
refreshStatus: () => void;
}) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [loading, setLoading] = useState(false);
const onStageClicked = useCallback(
@@ -251,7 +251,7 @@ function DoltChanges({
statusList: DoltStatusResultItem[];
refreshStatus: () => void;
}) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [loading, setLoading] = useState(false);
const [commitMessage, setCommitMessage] = useState("");
@@ -344,7 +344,7 @@ function DoltChanges({
export default function DoltSidebar() {
const { currentSchemaName } = useSchema();
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [branchList, setBranchList] = useState([]);
const [selectedBranch, setSelectedBranch] = useState("main");
const [commitList, setCommitList] = useState([]);
diff --git a/src/extensions/trigger-editor/trigger-editor.tsx b/src/extensions/trigger-editor/trigger-editor.tsx
index e7a0bc2b..4bbe634e 100644
--- a/src/extensions/trigger-editor/trigger-editor.tsx
+++ b/src/extensions/trigger-editor/trigger-editor.tsx
@@ -9,7 +9,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
import {
DatabaseTriggerSchema,
@@ -25,7 +25,7 @@ export interface TriggerEditorProps {
}
export default function TriggerEditor({ value, onChange }: TriggerEditorProps) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const { autoCompleteSchema, schema } = useSchema();
const extendedAutoCompleteSchema = useMemo(() => {
diff --git a/src/extensions/trigger-editor/trigger-save-dialog.tsx b/src/extensions/trigger-editor/trigger-save-dialog.tsx
index bdb20e96..cad56d07 100644
--- a/src/extensions/trigger-editor/trigger-save-dialog.tsx
+++ b/src/extensions/trigger-editor/trigger-save-dialog.tsx
@@ -1,3 +1,4 @@
+import CodePreview from "@/components/gui/code-preview";
import {
AlertDialog,
AlertDialogCancel,
@@ -5,13 +6,12 @@ import {
AlertDialogFooter,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
-import { LucideAlertCircle, LucideLoader, LucideSave } from "lucide-react";
-import { useCallback, useState } from "react";
import { Button } from "@/components/ui/button";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { useSchema } from "@/context/schema-provider";
import { DatabaseTriggerSchema } from "@/drivers/base-driver";
-import CodePreview from "@/components/gui/code-preview";
+import { LucideAlertCircle, LucideLoader, LucideSave } from "lucide-react";
+import { useCallback, useState } from "react";
import { triggerEditorExtensionTab } from ".";
interface Props {
@@ -22,30 +22,28 @@ interface Props {
export function TriggerSaveDialog(props: Props) {
const { refresh: refreshSchema } = useSchema();
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [isExecuting, setIsExecuting] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const onSave = useCallback(() => {
setIsExecuting(true);
databaseDriver
- .transaction(
- props.previewScript
- )
+ .transaction(props.previewScript)
.then(() => {
refreshSchema();
triggerEditorExtensionTab.replace({
schemaName: props.trigger.schemaName,
- name: props.trigger.name ?? '',
- tableName: props.trigger.tableName
- })
+ name: props.trigger.name ?? "",
+ tableName: props.trigger.tableName,
+ });
props.onClose();
})
.catch((err) => setErrorMessage((err as Error).message))
.finally(() => {
setIsExecuting(false);
});
- }, [databaseDriver, props, refreshSchema])
+ }, [databaseDriver, props, refreshSchema]);
return (
@@ -53,8 +51,8 @@ export function TriggerSaveDialog(props: Props) {
Preview
{errorMessage && (
-
-
+
)}
@@ -65,9 +63,9 @@ export function TriggerSaveDialog(props: Props) {
Cancel
diff --git a/src/extensions/trigger-editor/trigger-tab.tsx b/src/extensions/trigger-editor/trigger-tab.tsx
index 84d3463b..d2712c54 100644
--- a/src/extensions/trigger-editor/trigger-tab.tsx
+++ b/src/extensions/trigger-editor/trigger-tab.tsx
@@ -1,12 +1,12 @@
import OpacityLoading from "@/components/gui/loading-opacity";
-import { useDatabaseDriver } from "@/context/driver-provider";
+import { useStudioContext } from "@/context/driver-provider";
import { DatabaseTriggerSchema } from "@/drivers/base-driver";
import { produce } from "immer";
import { isEqual } from "lodash";
import { useCallback, useEffect, useMemo, useState } from "react";
import { TriggerController } from "./trigger-controller";
-import { TriggerSaveDialog } from "./trigger-save-dialog";
import TriggerEditor from "./trigger-editor";
+import { TriggerSaveDialog } from "./trigger-save-dialog";
export interface TriggerTabProps {
name: string;
@@ -29,7 +29,7 @@ export default function TriggerTab({
schemaName,
tableName,
}: TriggerTabProps) {
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const [isSaving, setIsSaving] = useState(false);
// If name is specified, it means the trigger is already exist
@@ -76,7 +76,7 @@ export default function TriggerTab({
}
return (
-
+
{isSaving && (
);
-}
\ No newline at end of file
+}
diff --git a/src/extensions/view-editor/view-editor.tsx b/src/extensions/view-editor/view-editor.tsx
index c3cffe6d..90c567ce 100644
--- a/src/extensions/view-editor/view-editor.tsx
+++ b/src/extensions/view-editor/view-editor.tsx
@@ -1,11 +1,11 @@
import { Input } from "@/components/ui/input";
+import { useStudioContext } from "@/context/driver-provider";
+import { useSchema } from "@/context/schema-provider";
import { DatabaseViewSchema } from "@/drivers/base-driver";
import { produce } from "immer";
-import SqlEditor from "../../components/gui/sql-editor";
-import { useDatabaseDriver } from "@/context/driver-provider";
import { useMemo } from "react";
-import { useSchema } from "@/context/schema-provider";
import SchemaNameSelect from "../../components/gui/schema-editor/schema-name-select";
+import SqlEditor from "../../components/gui/sql-editor";
interface Props {
value: DatabaseViewSchema;
@@ -14,7 +14,7 @@ interface Props {
export default function ViewEditor(props: Props) {
const { value, onChange } = props;
- const { databaseDriver } = useDatabaseDriver();
+ const { databaseDriver } = useStudioContext();
const { autoCompleteSchema, schema } = useSchema();
const extendedAutoCompleteSchema = useMemo(() => {
@@ -26,7 +26,7 @@ export default function ViewEditor(props: Props) {
return (
<>
-