diff --git a/package-lock.json b/package-lock.json index f715054..c5ae46f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1866,6 +1866,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/src/components/database/table-data.tsx b/src/components/database/table-data.tsx index 55e2538..ed5be6e 100644 --- a/src/components/database/table-data.tsx +++ b/src/components/database/table-data.tsx @@ -69,25 +69,75 @@ const TableHeadCell: React.FC<{ )); +const hexToDataUrl = (hex: string): string => { + const bytes = new Uint8Array( + hex.match(/.{1,2}/g)!.map((byte) => Number.parseInt(byte, 16)) + ); + const blob = new Blob([bytes], { type: "image/jpeg" }); + return URL.createObjectURL(blob); +}; + const TableBodyCell: React.FC<{ value: any; dataType?: string }> = memo( ({ value, dataType }) => { const { dateFormatValue } = useSQLiteStore(); - const CellContent = () => { + const isBlob = dataType?.toUpperCase() === "BLOB"; + + const content = useMemo(() => { if (!value) { return NULL; } + if (dataType && isDate(dataType)) { if (dateFormats[dateFormatValue]) { return dateFormats[dateFormatValue].func(value); } + return value; } - return value; - }; + + const stringValue = typeof value === "string" ? value : String(value); + return stringValue.length > 40 + ? `${stringValue.slice(0, 40)}...` + : stringValue; + }, [value, dataType, dateFormatValue]); return ( - - {CellContent()} + + + + + {isBlob ? ( + BLOB + ) : ( + content + )} + + + +
+ {isBlob && typeof value === "string" ? ( + <> + BLOB content { + e.currentTarget.style.display = "none"; + }} + /> + + Blob length: {value.length} + + + ) : ( + {content} + )} + + {dataType || "Unknown"} + +
+
+
); } diff --git a/src/components/database/upper-section.tsx b/src/components/database/upper-section.tsx index 4a586fb..6c6a7be 100644 --- a/src/components/database/upper-section.tsx +++ b/src/components/database/upper-section.tsx @@ -15,6 +15,7 @@ import { Button } from "@/components/ui/button"; import StatusMessage from "@/components/stats-message"; import ExportButtons from "@/components/settings/export-buttons"; import Settings from "@/components/settings/settings-drawer"; +import DatabaseSchema from "@/components/settings/database-schema-drawer"; import ThemeModeToggle from "@/components/settings/theme-mode-toggle"; import PageSelect from "./page-select"; import TableSelect from "./table-select"; @@ -205,9 +206,20 @@ export function DBTable() { return ( <>
-

- {databaseData.name}, ({databaseData.sizeAsString}) -

+
+
+
+ {databaseData.name} +
+
+ ({databaseData.sizeAsString}) +
+
+
+ + +
+
@@ -220,8 +232,7 @@ export function DBTable() { > {MemoizedExpandIcon} - - +
{MemoizedQueryInput} diff --git a/src/components/landing/features.tsx b/src/components/landing/features.tsx index d316e83..b37a449 100644 --- a/src/components/landing/features.tsx +++ b/src/components/landing/features.tsx @@ -1,3 +1,5 @@ +import { memo } from "react"; + import { ShieldIcon, ZapIcon, @@ -7,7 +9,7 @@ import { AppWindowIcon } from "lucide-react"; -export default function Features() { +export function Features() { return ( <>
@@ -72,3 +74,5 @@ function Feature({ icon: Icon, title, description }: FeatureProps) { ); } + +export default memo(Features); diff --git a/src/components/settings/database-schema-drawer.tsx b/src/components/settings/database-schema-drawer.tsx new file mode 100644 index 0000000..e2ad813 --- /dev/null +++ b/src/components/settings/database-schema-drawer.tsx @@ -0,0 +1,95 @@ +import useSQLiteStore from "@/store/useSQLiteStore"; +import { useMemo, memo } from "react"; + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow +} from "@/components/ui/table"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger +} from "@/components/ui/drawer"; +import { ColumnIcon } from "@/components/database/table-decorations"; +import { Button } from "../ui/button"; +import { TableIcon } from "lucide-react"; + +function DatabaseSchema() { + const { tableSchemas } = useSQLiteStore(); + + const renderedTables = useMemo(() => { + return Object.keys(tableSchemas).map((tableName) => ( +
+

+ {tableName} +

+
+ + + + Column + Type + + + + {Object.entries(tableSchemas[tableName]).map( + ([columnName, columnData]) => ( + + +
+ + {columnName} + + +
+
+ {columnData.type || "Unknown"} +
+ ) + )} +
+
+
+
+ )); + }, [tableSchemas]); + + return ( + + + + + + + Settings + Change settings. + +
+
+ {renderedTables} +
+
+ + + + + +
+
+
+
+ ); +} + +export default memo(DatabaseSchema); diff --git a/src/components/settings/settings-drawer.tsx b/src/components/settings/settings-drawer.tsx index 5796863..d68732a 100644 --- a/src/components/settings/settings-drawer.tsx +++ b/src/components/settings/settings-drawer.tsx @@ -108,7 +108,7 @@ function Settings() { Settings Change settings. -
+
{ - const bytes = new Uint8Array( - hex.match(/.{1,2}/g)!.map((byte) => Number.parseInt(byte, 16)) - ); - const blob = new Blob([bytes], { type: "image/jpeg" }); - return URL.createObjectURL(blob); -}; +// const hexToDataUrl = (hex: string): string => { +// const bytes = new Uint8Array( +// hex.match(/.{1,2}/g)!.map((byte) => Number.parseInt(byte, 16)) +// ); +// const blob = new Blob([bytes], { type: "image/jpeg" }); +// return URL.createObjectURL(blob); +// }; -interface TableCellProps extends React.TdHTMLAttributes { - dataType?: string; -} +type TableCellProps = React.TdHTMLAttributes; const TableCell = React.memo( React.forwardRef( - ({ className, children, dataType, ...props }, ref) => { - const isBlob = dataType?.toUpperCase() === "BLOB"; - const content = React.useMemo(() => { - if (typeof children === "string" && children.length > 40) { - return `${children.slice(0, 40)}...`; - } - return children; - }, [children]); - + ({ className, children, ...props }, ref) => { return ( - - - - {isBlob ? ( - BLOB - ) : ( - content - )} - - - -
- {isBlob && typeof children === "string" ? ( - <> - BLOB content { - e.currentTarget.style.display = "none"; - }} - /> - - Blob length: {children.length} - - - ) : ( - {children} - )} - { - - {dataType || "Unknown"} - - } -
-
-
+ {children} ); } diff --git a/src/types.ts b/src/types.ts index 95f72e3..e404f43 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,6 +6,7 @@ export interface TableInfo { type: string; isPrimaryKey: boolean; isForeignKey: boolean; + nullable: boolean; }; }; }