From c5741eb308c24b9663e54872020dec4556706711 Mon Sep 17 00:00:00 2001 From: "Visal .In" Date: Tue, 16 Apr 2024 20:31:19 +0700 Subject: [PATCH] doc: add gui README --- gui/README.md | 108 +++++++++++++++++++++++++++ gui/src/contexts/config-provider.tsx | 2 +- gui/src/studio.tsx | 2 +- 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 gui/README.md diff --git a/gui/README.md b/gui/README.md new file mode 100644 index 00000000..d49c273a --- /dev/null +++ b/gui/README.md @@ -0,0 +1,108 @@ +## LibSQL Studio GUI + +LibSQL Studio GUI is a standalone database GUI React component for SQLite. + +```typescript +import { Studio } from "@libsqlstudio/gui"; +import TursoDriver from "./driver"; + +export default App() { + const driver = useMemo(() => { + return new TursoDriver("url_here", "token"); + }, []) + + return +} +``` + +Implementing SQLite Driver + +```typescript +// driver.ts +import { + createClient, + Client, + InStatement, + ResultSet, +} from "@libsql/client/web"; +import { + SqliteLikeBaseDriver, + DatabaseHeader, + DatabaseResultSet, + DatabaseRow, + convertSqliteType, +} from "@libsqlstudio/gui/driver"; + +export default class TursoDriver extends SqliteLikeBaseDriver { + protected client: Client; + protected endpoint: string = ""; + protected authToken = ""; + protected bigInt = false; + + constructor(url: string, authToken: string) { + super(); + this.endpoint = url; + this.authToken = authToken; + this.bigInt = bigInt; + + this.client = createClient({ + url: this.endpoint, + authToken: this.authToken, + }); + } + + async query(stmt: InStatement) { + const r = await this.client.execute(stmt); + return transformRawResult(r); + } + + async transaction(stmt: InStatement[]) { + return (await this.client.batch(stmt, "write")).map(transformRawResult); + } +} + +function transformRawResult(raw: ResultSet): DatabaseResultSet { + const headerSet = new Set(); + + const headers: DatabaseHeader[] = raw.columns.map((colName, colIdx) => { + const colType = raw.columnTypes[colIdx]; + let renameColName = colName; + + for (let i = 0; i < 20; i++) { + if (!headerSet.has(renameColName)) break; + renameColName = `__${colName}_${i}`; + } + + headerSet.add(renameColName); + + return { + name: renameColName, + displayName: colName, + originalType: colType, + type: convertSqliteType(colType), + }; + }); + + const rows = raw.rows.map((r) => + headers.reduce((a, b, idx) => { + a[b.name] = r[idx]; + return a; + }, {} as DatabaseRow) + ); + + return { + rows, + rowsAffected: raw.rowsAffected, + headers, + lastInsertRowid: + raw.lastInsertRowid === undefined + ? undefined + : Number(raw.lastInsertRowid), + }; +} +``` diff --git a/gui/src/contexts/config-provider.tsx b/gui/src/contexts/config-provider.tsx index 95304674..b84be802 100644 --- a/gui/src/contexts/config-provider.tsx +++ b/gui/src/contexts/config-provider.tsx @@ -5,7 +5,7 @@ import { createContext, useContext, useMemo } from "react"; interface ConfigContextProps { color: string; name: string; - onBack: () => void; + onBack?: () => void; sideBarFooterComponent?: ReactElement; extensions?: StudioExtension[]; } diff --git a/gui/src/studio.tsx b/gui/src/studio.tsx index cbe4a3ad..aa18ab15 100644 --- a/gui/src/studio.tsx +++ b/gui/src/studio.tsx @@ -15,7 +15,7 @@ interface StudioProps { name: string; color: string; - onBack: () => void; + onBack?: () => void; sideBarFooterComponent?: ReactElement; theme?: "dark" | "light";