@deaquinodev/querky 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +338 -77
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/index.tsx
4
4
  import { useState as useState5, useEffect as useEffect5 } from "react";
5
5
  import { parseArgs } from "util";
6
- import { render, Box as Box8, Text as Text8 } from "ink";
6
+ import { render, Box as Box9, Text as Text9 } from "ink";
7
7
 
8
8
  // src/db/client.ts
9
9
  import { Client } from "pg";
@@ -39,10 +39,11 @@ var PgDbClient = class {
39
39
  }
40
40
  pg;
41
41
  async query(sql) {
42
- const result = await this.pg.query(sql);
42
+ const raw = await this.pg.query(sql);
43
+ const result = Array.isArray(raw) ? raw[raw.length - 1] : raw;
43
44
  return {
44
- fields: result.fields.map((f) => f.name),
45
- rows: result.rows,
45
+ fields: (result.fields ?? []).map((f) => f.name),
46
+ rows: result.rows ?? [],
46
47
  rowCount: result.rowCount ?? 0
47
48
  };
48
49
  }
@@ -196,7 +197,7 @@ import { useState as useState3, useEffect as useEffect4, useRef as useRef2 } fro
196
197
  import { writeFileSync as writeFileSync5 } from "fs";
197
198
  import { homedir as homedir4 } from "os";
198
199
  import { join as join6 } from "path";
199
- import { Box as Box6, Text as Text6, Static, useApp, useInput as useInput2, useStdin as useStdin2 } from "ink";
200
+ import { Box as Box7, Text as Text7, Static, useApp, useInput as useInput2, useStdin as useStdin2 } from "ink";
200
201
 
201
202
  // src/db/query.ts
202
203
  async function runQuery(client, sql) {
@@ -625,8 +626,12 @@ var COMMANDS = {
625
626
  description: "Save last query as a named alias",
626
627
  category: "Aliases",
627
628
  usage: "/save <name>",
628
- detail: "Saves the last executed SQL query as a named alias scoped to the current database. Run it later with /<name>. Supports positional ($1, $2) and named (:param) substitution.",
629
- example: "/save active-orders",
629
+ detail: "Saves the last executed SQL query as a named alias scoped to the current database. Run it later with /<name>. If the query contains $1, $2 placeholders or :param placeholders, values are substituted at run time.",
630
+ examples: [
631
+ "/save active-orders (no params \u2014 run with /active-orders)",
632
+ "/save user-by-id (query had WHERE id = $1 \u2014 run with /user-by-id 42)",
633
+ "/save by-status (query had WHERE status = :status \u2014 run with /by-status :status=active)"
634
+ ],
630
635
  run: (ctx) => {
631
636
  const name = ctx.args.trim();
632
637
  if (!name) return { ok: false, message: "Usage: /save <name>" };
@@ -640,8 +645,14 @@ var COMMANDS = {
640
645
  description: "Define an alias inline",
641
646
  category: "Aliases",
642
647
  usage: "/alias <name> <SQL>",
643
- detail: "Defines a named alias without running a query first. Equivalent to /save but lets you specify the SQL directly.",
644
- example: "/alias recent SELECT * FROM events ORDER BY created_at DESC LIMIT 20",
648
+ detail: "Defines a named alias without running a query first. Use $1, $2 for positional args or :param for named args \u2014 values are substituted when the alias is invoked.",
649
+ examples: [
650
+ "/alias recent SELECT * FROM events ORDER BY created_at DESC LIMIT 20",
651
+ "/alias user SELECT * FROM users WHERE id = $1",
652
+ "/alias by-email SELECT * FROM users WHERE email = :email",
653
+ "/user 42 (positional)",
654
+ "/by-email :email=alice@example.com (named)"
655
+ ],
645
656
  run: (ctx) => {
646
657
  const [name, ...rest] = ctx.args.trim().split(/\s+/);
647
658
  if (!name || rest.length === 0) return { ok: false, message: "Usage: /alias <name> <SQL>" };
@@ -678,6 +689,16 @@ var COMMANDS = {
678
689
  return removed ? { ok: true, message: `Removed /${name}` } : { ok: false, message: `No alias named /${name}` };
679
690
  }
680
691
  },
692
+ "erd": {
693
+ description: "Visualize tables and relationships as a schema diagram",
694
+ category: "Schema",
695
+ usage: "/erd",
696
+ detail: "Fetches all tables, columns, and foreign key relationships from the connected database and renders a color-coded schema diagram. Each table gets a unique color; FK references use the color of the table they point to.",
697
+ run: (ctx) => {
698
+ ctx.onErd();
699
+ return { ok: true, message: "" };
700
+ }
701
+ },
681
702
  "help": {
682
703
  description: "Show help for all commands or a specific command",
683
704
  category: "Session",
@@ -696,7 +717,8 @@ var COMMANDS = {
696
717
  description: cmd.description,
697
718
  psqlAlias: PSQL_REVERSE[name],
698
719
  detail: cmd.detail,
699
- example: cmd.example
720
+ example: cmd.example,
721
+ examples: cmd.examples
700
722
  };
701
723
  return { ok: true, message: "", helpData: { mode: "detail", entry } };
702
724
  }
@@ -745,6 +767,83 @@ function runCommand(input, ctx) {
745
767
  return { ok: false, message: `Unknown command: ${prefix}${rawName}` };
746
768
  }
747
769
 
770
+ // src/db/erd.ts
771
+ function erdSql(driver) {
772
+ if (driver === "postgresql") {
773
+ return `
774
+ SELECT c.table_name, c.column_name, c.data_type,
775
+ (pk.column_name IS NOT NULL) AS is_pk,
776
+ fk.foreign_table AS fk_table
777
+ FROM information_schema.columns c
778
+ LEFT JOIN (
779
+ SELECT kcu.table_name, kcu.column_name
780
+ FROM information_schema.table_constraints tc
781
+ JOIN information_schema.key_column_usage kcu
782
+ ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
783
+ WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_schema = 'public'
784
+ ) pk ON pk.table_name = c.table_name AND pk.column_name = c.column_name
785
+ LEFT JOIN (
786
+ SELECT kcu.table_name, kcu.column_name, ccu.table_name AS foreign_table
787
+ FROM information_schema.table_constraints tc
788
+ JOIN information_schema.key_column_usage kcu
789
+ ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
790
+ JOIN information_schema.constraint_column_usage ccu
791
+ ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema
792
+ WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = 'public'
793
+ ) fk ON fk.table_name = c.table_name AND fk.column_name = c.column_name
794
+ WHERE c.table_schema = 'public'
795
+ ORDER BY c.table_name, c.ordinal_position
796
+ `;
797
+ }
798
+ if (driver === "mysql") {
799
+ return `
800
+ SELECT c.TABLE_NAME AS table_name, c.COLUMN_NAME AS column_name,
801
+ c.DATA_TYPE AS data_type,
802
+ (c.COLUMN_KEY = 'PRI') AS is_pk,
803
+ kcu.REFERENCED_TABLE_NAME AS fk_table
804
+ FROM information_schema.COLUMNS c
805
+ LEFT JOIN information_schema.KEY_COLUMN_USAGE kcu
806
+ ON kcu.TABLE_SCHEMA = c.TABLE_SCHEMA
807
+ AND kcu.TABLE_NAME = c.TABLE_NAME
808
+ AND kcu.COLUMN_NAME = c.COLUMN_NAME
809
+ AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
810
+ WHERE c.TABLE_SCHEMA = DATABASE()
811
+ ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION
812
+ `;
813
+ }
814
+ return `
815
+ SELECT m.name AS table_name, p.name AS column_name,
816
+ p.type AS data_type,
817
+ (p.pk > 0) AS is_pk,
818
+ f."table" AS fk_table
819
+ FROM sqlite_master m
820
+ JOIN pragma_table_info(m.name) p
821
+ LEFT JOIN pragma_foreign_key_list(m.name) f ON f."from" = p.name
822
+ WHERE m.type = 'table' AND m.name NOT LIKE 'sqlite_%'
823
+ ORDER BY m.name, p.cid
824
+ `;
825
+ }
826
+ async function fetchErd(client, driver) {
827
+ const result = await client.query(erdSql(driver));
828
+ const tableMap = /* @__PURE__ */ new Map();
829
+ for (const row of result.rows) {
830
+ const tableName = String(row["table_name"] ?? "");
831
+ if (!tableMap.has(tableName)) tableMap.set(tableName, []);
832
+ const cols = tableMap.get(tableName);
833
+ const colName = String(row["column_name"] ?? "");
834
+ if (cols.some((c) => c.name === colName)) continue;
835
+ cols.push({
836
+ name: colName,
837
+ type: String(row["data_type"] ?? ""),
838
+ isPk: Boolean(row["is_pk"]),
839
+ fkTable: row["fk_table"] ? String(row["fk_table"]) : void 0
840
+ });
841
+ }
842
+ return {
843
+ tables: Array.from(tableMap.entries()).map(([name, columns]) => ({ name, columns }))
844
+ };
845
+ }
846
+
748
847
  // src/commands/shell.ts
749
848
  import { exec } from "child_process";
750
849
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync } from "fs";
@@ -1626,7 +1725,7 @@ import { Box as Box2, Text as Text2 } from "ink";
1626
1725
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1627
1726
  var COL_PAD = 1;
1628
1727
  var INDIGO = "#818cf8";
1629
- var BORDER = "white";
1728
+ var BORDER2 = "white";
1630
1729
  var NULL_COLOR = "#6366f1";
1631
1730
  var NULL_MARKER = "\u2205";
1632
1731
  function isNull(val) {
@@ -1657,10 +1756,10 @@ function hline(widths, left, mid, right) {
1657
1756
  function ExpandedTable({ columns, rows }) {
1658
1757
  const keyWidth = columns.reduce((max, col) => Math.max(max, col.length), 0);
1659
1758
  return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: rows.map((row, i) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: i < rows.length - 1 ? 1 : 0, children: [
1660
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: `\u2500[ Record ${i + 1} ]${"\u2500".repeat(Math.max(0, keyWidth + 14 - String(i + 1).length))}` }),
1759
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: `\u2500[ Record ${i + 1} ]${"\u2500".repeat(Math.max(0, keyWidth + 14 - String(i + 1).length))}` }),
1661
1760
  columns.map((col) => /* @__PURE__ */ jsxs2(Box2, { children: [
1662
1761
  /* @__PURE__ */ jsx2(Text2, { color: INDIGO, bold: true, children: col.padEnd(keyWidth) }),
1663
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: " \u2502 " }),
1762
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: " \u2502 " }),
1664
1763
  isNull(row[col]) ? /* @__PURE__ */ jsx2(Text2, { color: NULL_COLOR, dimColor: true, children: NULL_MARKER }) : /* @__PURE__ */ jsx2(Text2, { children: cellValue(row[col]) })
1665
1764
  ] }, col))
1666
1765
  ] }, i)) });
@@ -1673,31 +1772,31 @@ function Table({ columns, rows, expanded = false }) {
1673
1772
  const botLine = hline(widths, "\u2570", "\u2534", "\u256F");
1674
1773
  function renderHeaderRow(cols) {
1675
1774
  return /* @__PURE__ */ jsxs2(Box2, { children: [
1676
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" }),
1775
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" }),
1677
1776
  cols.map((v, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
1678
1777
  /* @__PURE__ */ jsx2(Text2, { color: INDIGO, bold: true, children: " ".repeat(COL_PAD) + pad(v, widths[i]) + " ".repeat(COL_PAD) }),
1679
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" })
1778
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" })
1680
1779
  ] }, i))
1681
1780
  ] });
1682
1781
  }
1683
1782
  function renderDataRow(row) {
1684
1783
  return /* @__PURE__ */ jsxs2(Box2, { children: [
1685
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" }),
1784
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" }),
1686
1785
  columns.map((col, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
1687
1786
  isNull(row[col]) ? /* @__PURE__ */ jsx2(Text2, { color: NULL_COLOR, dimColor: true, children: " ".repeat(COL_PAD) + pad(NULL_MARKER, widths[i]) + " ".repeat(COL_PAD) }) : /* @__PURE__ */ jsx2(Text2, { children: " ".repeat(COL_PAD) + pad(cellValue(row[col]), widths[i]) + " ".repeat(COL_PAD) }),
1688
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" })
1787
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" })
1689
1788
  ] }, i))
1690
1789
  ] });
1691
1790
  }
1692
1791
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
1693
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: topLine }),
1792
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: topLine }),
1694
1793
  renderHeaderRow(columns),
1695
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: midLine }),
1794
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: midLine }),
1696
1795
  rows.map((row, i) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
1697
1796
  renderDataRow(row),
1698
- i < rows.length - 1 && /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: midLine })
1797
+ i < rows.length - 1 && /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: midLine })
1699
1798
  ] }, i)),
1700
- /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: botLine })
1799
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: botLine })
1701
1800
  ] });
1702
1801
  }
1703
1802
 
@@ -1857,7 +1956,7 @@ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1857
1956
  var USAGE_WIDTH = 28;
1858
1957
  function HelpView({ data }) {
1859
1958
  if (data.mode === "detail" && data.entry) {
1860
- const { usage, description, psqlAlias, detail, example } = data.entry;
1959
+ const { usage, description, psqlAlias, detail, example, examples } = data.entry;
1861
1960
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
1862
1961
  /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "white", children: [
1863
1962
  usage,
@@ -1865,10 +1964,13 @@ function HelpView({ data }) {
1865
1964
  ] }),
1866
1965
  /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { children: description }) }),
1867
1966
  detail && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: detail }) }),
1868
- example && /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
1967
+ examples && examples.length > 0 ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
1968
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Examples:" }),
1969
+ examples.map((ex, i) => /* @__PURE__ */ jsx5(Box5, { marginLeft: 2, children: /* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: ex }) }, i))
1970
+ ] }) : example ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
1869
1971
  /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Example: " }),
1870
1972
  /* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: example })
1871
- ] })
1973
+ ] }) : null
1872
1974
  ] });
1873
1975
  }
1874
1976
  if (data.mode === "list" && data.groups) {
@@ -1894,8 +1996,137 @@ function HelpView({ data }) {
1894
1996
  return null;
1895
1997
  }
1896
1998
 
1897
- // src/ui/components/App.tsx
1999
+ // src/ui/components/ErdView.tsx
2000
+ import { Box as Box6, Text as Text6 } from "ink";
1898
2001
  import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2002
+ var TABLE_COLORS = [
2003
+ "#f472b6",
2004
+ "#34d399",
2005
+ "#fb923c",
2006
+ "#60a5fa",
2007
+ "#a78bfa",
2008
+ "#f87171",
2009
+ "#fbbf24",
2010
+ "#2dd4bf"
2011
+ ];
2012
+ var PAD = 1;
2013
+ var GAP = 2;
2014
+ var FK_PREFIX = "FK \u2192 ";
2015
+ function computeMetrics(table) {
2016
+ const nameW = Math.max(2, ...table.columns.map((c) => c.name.length));
2017
+ const typeW = Math.max(4, ...table.columns.map((c) => c.type.length));
2018
+ const keyW = Math.max(
2019
+ 2,
2020
+ ...table.columns.map((c) => {
2021
+ if (c.isPk) return 2;
2022
+ if (c.fkTable) return FK_PREFIX.length + c.fkTable.length;
2023
+ return 0;
2024
+ })
2025
+ );
2026
+ const totalW = 4 + 3 * PAD * 2 + nameW + typeW + keyW;
2027
+ return { nameW, typeW, keyW, totalW };
2028
+ }
2029
+ function groupIntoRows(metrics, termW) {
2030
+ const rows = [];
2031
+ let row = [];
2032
+ let usedW = 0;
2033
+ for (let i = 0; i < metrics.length; i++) {
2034
+ const w = metrics[i].totalW;
2035
+ if (row.length === 0) {
2036
+ row.push(i);
2037
+ usedW = w;
2038
+ } else if (usedW + GAP + w <= termW) {
2039
+ row.push(i);
2040
+ usedW += GAP + w;
2041
+ } else {
2042
+ rows.push(row);
2043
+ row = [i];
2044
+ usedW = w;
2045
+ }
2046
+ }
2047
+ if (row.length > 0) rows.push(row);
2048
+ return rows;
2049
+ }
2050
+ function TableBox({ table, m, color, colorMap }) {
2051
+ const { nameW, typeW, keyW, totalW } = m;
2052
+ const sp = " ".repeat(PAD);
2053
+ const p = (s, w) => s.slice(0, w).padEnd(w);
2054
+ const top = "\u256D" + "\u2500".repeat(totalW - 2) + "\u256E";
2055
+ const sep = "\u251C" + "\u2500".repeat(nameW + PAD * 2) + "\u252C" + "\u2500".repeat(typeW + PAD * 2) + "\u252C" + "\u2500".repeat(keyW + PAD * 2) + "\u2524";
2056
+ const bot = "\u2570" + "\u2500".repeat(nameW + PAD * 2) + "\u2534" + "\u2500".repeat(typeW + PAD * 2) + "\u2534" + "\u2500".repeat(keyW + PAD * 2) + "\u256F";
2057
+ const headerW = totalW - 4;
2058
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2059
+ /* @__PURE__ */ jsx6(Text6, { color, children: top }),
2060
+ /* @__PURE__ */ jsxs6(Box6, { children: [
2061
+ /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2062
+ /* @__PURE__ */ jsxs6(Text6, { color, bold: true, children: [
2063
+ sp,
2064
+ p(table.name, headerW),
2065
+ sp
2066
+ ] }),
2067
+ /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" })
2068
+ ] }),
2069
+ /* @__PURE__ */ jsx6(Text6, { color, children: sep }),
2070
+ table.columns.map((col, i) => /* @__PURE__ */ jsxs6(Box6, { children: [
2071
+ /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2072
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2073
+ sp,
2074
+ p(col.name, nameW),
2075
+ sp
2076
+ ] }),
2077
+ /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2078
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
2079
+ sp,
2080
+ p(col.type, typeW),
2081
+ sp
2082
+ ] }),
2083
+ /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2084
+ col.isPk ? /* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
2085
+ sp,
2086
+ p("PK", keyW),
2087
+ sp
2088
+ ] }) : col.fkTable ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
2089
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
2090
+ sp,
2091
+ FK_PREFIX
2092
+ ] }),
2093
+ /* @__PURE__ */ jsxs6(Text6, { color: colorMap.get(col.fkTable) ?? color, children: [
2094
+ p(col.fkTable, keyW - FK_PREFIX.length),
2095
+ sp
2096
+ ] })
2097
+ ] }) : /* @__PURE__ */ jsxs6(Text6, { children: [
2098
+ sp,
2099
+ " ".repeat(keyW),
2100
+ sp
2101
+ ] }),
2102
+ /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" })
2103
+ ] }, i)),
2104
+ /* @__PURE__ */ jsx6(Text6, { color, children: bot })
2105
+ ] });
2106
+ }
2107
+ function ErdView({ data }) {
2108
+ if (data.tables.length === 0) {
2109
+ return /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "No tables found in the current schema." });
2110
+ }
2111
+ const termW = process.stdout.columns ?? 80;
2112
+ const metrics = data.tables.map(computeMetrics);
2113
+ const colorMap = new Map(
2114
+ data.tables.map((t, i) => [t.name, TABLE_COLORS[i % TABLE_COLORS.length]])
2115
+ );
2116
+ const rows = groupIntoRows(metrics, termW);
2117
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: rows.map((row, ri) => /* @__PURE__ */ jsx6(Box6, { flexDirection: "row", marginBottom: ri < rows.length - 1 ? 1 : 0, children: row.map((ti, j) => /* @__PURE__ */ jsx6(Box6, { marginRight: j < row.length - 1 ? GAP : 0, children: /* @__PURE__ */ jsx6(
2118
+ TableBox,
2119
+ {
2120
+ table: data.tables[ti],
2121
+ m: metrics[ti],
2122
+ color: colorMap.get(data.tables[ti].name) ?? BORDER,
2123
+ colorMap
2124
+ }
2125
+ ) }, ti)) }, ri)) });
2126
+ }
2127
+
2128
+ // src/ui/components/App.tsx
2129
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1899
2130
  var PAGE_SIZE = 50;
1900
2131
  var PLACEHOLDER2 = "#a5b4fc";
1901
2132
  function activePageSize() {
@@ -1909,26 +2140,27 @@ function limitLines(s, n) {
1909
2140
  }
1910
2141
  function EntryView({ entry }) {
1911
2142
  const showAi = entry.aiResponse !== "" || entry.aiError !== null;
2143
+ const showErd = entry.erdData !== null;
1912
2144
  const isShell = entry.query.startsWith("!");
1913
2145
  const isCommand = !isShell && (entry.query.startsWith("/") || entry.query.startsWith("\\"));
1914
2146
  const label = isShell ? "Shell:" : isCommand ? "Command:" : "Query:";
1915
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, paddingX: 1, children: [
1916
- /* @__PURE__ */ jsxs6(Box6, { borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1917
- /* @__PURE__ */ jsxs6(Text6, { color: theme.accent, bold: true, children: [
2147
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, paddingX: 1, children: [
2148
+ /* @__PURE__ */ jsxs7(Box7, { borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
2149
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, bold: true, children: [
1918
2150
  label,
1919
2151
  " "
1920
2152
  ] }),
1921
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: entry.query })
2153
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: entry.query })
1922
2154
  ] }),
1923
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: isShell ? entry.shellOutput !== null && /* @__PURE__ */ jsx6(Text6, { children: entry.shellOutput || "(no output)" }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
1924
- entry.commandMessage && (entry.commandMessage.helpData ? /* @__PURE__ */ jsx6(HelpView, { data: entry.commandMessage.helpData }) : entry.commandMessage.ok ? /* @__PURE__ */ jsxs6(Text6, { color: theme.accent, children: [
2155
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: isShell ? entry.shellOutput !== null && /* @__PURE__ */ jsx7(Text7, { children: entry.shellOutput || "(no output)" }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
2156
+ entry.commandMessage && (entry.commandMessage.helpData ? /* @__PURE__ */ jsx7(HelpView, { data: entry.commandMessage.helpData }) : entry.commandMessage.ok ? /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, children: [
1925
2157
  "\u2713 ",
1926
2158
  entry.commandMessage.text
1927
- ] }) : /* @__PURE__ */ jsx6(ErrorBox, { message: entry.commandMessage.text })),
1928
- showAi ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1929
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, bold: true, children: "Explanation:" }),
1930
- /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: entry.aiError ? /* @__PURE__ */ jsx6(ErrorBox, { message: entry.aiError }) : /* @__PURE__ */ jsx6(Text6, { color: PLACEHOLDER2, children: entry.aiResponse }) })
1931
- ] }) : !entry.commandMessage && /* @__PURE__ */ jsx6(QueryResult, { state: entry.queryState, elapsed: entry.elapsed, page: entry.page, pageSize: PAGE_SIZE })
2159
+ ] }) : /* @__PURE__ */ jsx7(ErrorBox, { message: entry.commandMessage.text })),
2160
+ showErd ? /* @__PURE__ */ jsx7(ErdView, { data: entry.erdData }) : showAi ? /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
2161
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Explanation:" }),
2162
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", marginTop: 1, children: entry.aiError ? /* @__PURE__ */ jsx7(ErrorBox, { message: entry.aiError }) : /* @__PURE__ */ jsx7(Text7, { color: PLACEHOLDER2, children: entry.aiResponse }) })
2163
+ ] }) : !entry.commandMessage && /* @__PURE__ */ jsx7(QueryResult, { state: entry.queryState, elapsed: entry.elapsed, page: entry.page, pageSize: PAGE_SIZE })
1932
2164
  ] }) })
1933
2165
  ] });
1934
2166
  }
@@ -1952,6 +2184,8 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
1952
2184
  const [aiError, setAiError] = useState3(null);
1953
2185
  const [shellOutput, setShellOutput] = useState3(null);
1954
2186
  const [isShellRunning, setIsShellRunning] = useState3(false);
2187
+ const [erdData, setErdData] = useState3(null);
2188
+ const [isErdLoading, setIsErdLoading] = useState3(false);
1955
2189
  const [completedEntries, setCompletedEntries] = useState3([]);
1956
2190
  const entryIdRef = useRef2(0);
1957
2191
  const aliasScope = connectionState.status === "connected" ? makeScope(connectionState.driver, connectionState.user, connectionState.host, connectionState.database) : "";
@@ -2017,8 +2251,29 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2017
2251
  setIsStreaming(false);
2018
2252
  }
2019
2253
  }
2254
+ async function handleErd() {
2255
+ if (connectionState.status !== "connected") {
2256
+ setCommandMessage({ ok: false, text: "Not connected to a database." });
2257
+ return;
2258
+ }
2259
+ setErdData(null);
2260
+ setIsErdLoading(true);
2261
+ setCommandMessage(null);
2262
+ setQueryState({ status: "idle" });
2263
+ setAiResponse("");
2264
+ setAiError(null);
2265
+ try {
2266
+ const data = await fetchErd(connectionState.client, connectionState.driver);
2267
+ setErdData(data);
2268
+ } catch (err) {
2269
+ setCommandMessage({ ok: false, text: err instanceof Error ? err.message : String(err) });
2270
+ } finally {
2271
+ setIsErdLoading(false);
2272
+ }
2273
+ }
2020
2274
  async function handleQuery(sql) {
2021
2275
  if (connectionState.status !== "connected") return;
2276
+ setErdData(null);
2022
2277
  setAiResponse("");
2023
2278
  setAiError(null);
2024
2279
  setCommandMessage(null);
@@ -2076,11 +2331,13 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2076
2331
  page,
2077
2332
  aiResponse,
2078
2333
  aiError,
2079
- shellOutput
2334
+ shellOutput,
2335
+ erdData
2080
2336
  }
2081
2337
  ]);
2082
2338
  }
2083
2339
  async function handleShell(cmd) {
2340
+ setErdData(null);
2084
2341
  setShellOutput(null);
2085
2342
  setIsShellRunning(true);
2086
2343
  setCommandMessage(null);
@@ -2126,6 +2383,9 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2126
2383
  onChangeDatabase?.(db);
2127
2384
  },
2128
2385
  onExport: handleExport,
2386
+ onErd: () => {
2387
+ void handleErd();
2388
+ },
2129
2389
  onClear: () => {
2130
2390
  process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
2131
2391
  setCompletedEntries([]);
@@ -2144,6 +2404,7 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2144
2404
  });
2145
2405
  if (result.cleared) return;
2146
2406
  setLastQuery(sql);
2407
+ setErdData(null);
2147
2408
  setAiResponse("");
2148
2409
  setAiError(null);
2149
2410
  setElapsed(null);
@@ -2156,39 +2417,39 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2156
2417
  }
2157
2418
  void handleQuery(sql);
2158
2419
  }
2159
- const isLoading = queryState.status === "running" || isStreaming || isShellRunning;
2420
+ const isLoading = queryState.status === "running" || isStreaming || isShellRunning || isErdLoading;
2160
2421
  const isConnected = connectionState.status === "connected";
2161
2422
  const showAi = aiResponse !== "" || isStreaming || aiError !== null;
2162
2423
  const isShellEntry = lastQuery.startsWith("!");
2163
2424
  const isCommand = !isShellEntry && (lastQuery.startsWith("/") || lastQuery.startsWith("\\"));
2164
2425
  const activeLabel = isShellEntry ? "Shell:" : isCommand ? "Command:" : "Query:";
2165
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2166
- /* @__PURE__ */ jsx6(Static, { items: completedEntries, children: (entry) => /* @__PURE__ */ jsx6(EntryView, { entry }, entry.id) }),
2167
- /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
2168
- lastQuery === "" && /* @__PURE__ */ jsx6(Banner, { connectionState }),
2169
- lastQuery !== "" && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 2, children: [
2170
- /* @__PURE__ */ jsxs6(Box6, { borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
2171
- /* @__PURE__ */ jsxs6(Text6, { color: theme.accent, bold: true, children: [
2426
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
2427
+ /* @__PURE__ */ jsx7(Static, { items: completedEntries, children: (entry) => /* @__PURE__ */ jsx7(EntryView, { entry }, entry.id) }),
2428
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, children: [
2429
+ lastQuery === "" && /* @__PURE__ */ jsx7(Banner, { connectionState }),
2430
+ lastQuery !== "" && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 2, children: [
2431
+ /* @__PURE__ */ jsxs7(Box7, { borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
2432
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, bold: true, children: [
2172
2433
  activeLabel,
2173
2434
  " "
2174
2435
  ] }),
2175
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: lastQuery })
2436
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: lastQuery })
2176
2437
  ] }),
2177
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: isShellEntry ? isShellRunning ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "running\u2026" }) : /* @__PURE__ */ jsx6(Text6, { children: limitLines(shellOutput ?? "", activePageSize()) }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
2178
- commandMessage && (commandMessage.helpData ? /* @__PURE__ */ jsx6(HelpView, { data: commandMessage.helpData }) : commandMessage.ok ? /* @__PURE__ */ jsxs6(Text6, { color: theme.accent, children: [
2438
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: isShellEntry ? isShellRunning ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "running\u2026" }) : /* @__PURE__ */ jsx7(Text7, { children: limitLines(shellOutput ?? "", activePageSize()) }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
2439
+ commandMessage && (commandMessage.helpData ? /* @__PURE__ */ jsx7(HelpView, { data: commandMessage.helpData }) : commandMessage.ok ? /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, children: [
2179
2440
  "\u2713 ",
2180
2441
  commandMessage.text
2181
- ] }) : /* @__PURE__ */ jsx6(ErrorBox, { message: commandMessage.text })),
2182
- showAi ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2183
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, bold: true, children: "Explanation:" }),
2184
- /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: aiError ? /* @__PURE__ */ jsx6(ErrorBox, { message: aiError }) : /* @__PURE__ */ jsxs6(Text6, { color: PLACEHOLDER2, children: [
2442
+ ] }) : /* @__PURE__ */ jsx7(ErrorBox, { message: commandMessage.text })),
2443
+ isErdLoading ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Fetching schema\u2026" }) : erdData ? /* @__PURE__ */ jsx7(ErdView, { data: erdData }) : showAi ? /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
2444
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Explanation:" }),
2445
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", marginTop: 1, children: aiError ? /* @__PURE__ */ jsx7(ErrorBox, { message: aiError }) : /* @__PURE__ */ jsxs7(Text7, { color: PLACEHOLDER2, children: [
2185
2446
  aiResponse,
2186
- isStreaming && /* @__PURE__ */ jsx6(Text6, { color: PLACEHOLDER2, children: "\u258B" })
2447
+ isStreaming && /* @__PURE__ */ jsx7(Text7, { color: PLACEHOLDER2, children: "\u258B" })
2187
2448
  ] }) })
2188
- ] }) : !commandMessage && /* @__PURE__ */ jsx6(QueryResult, { state: queryState, elapsed, page, pageSize: activePageSize() })
2449
+ ] }) : !commandMessage && /* @__PURE__ */ jsx7(QueryResult, { state: queryState, elapsed, page, pageSize: activePageSize() })
2189
2450
  ] }) })
2190
2451
  ] }),
2191
- isConnected ? /* @__PURE__ */ jsx6(
2452
+ isConnected ? /* @__PURE__ */ jsx7(
2192
2453
  QueryInput,
2193
2454
  {
2194
2455
  onSubmit: handleSubmit,
@@ -2200,16 +2461,16 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2200
2461
  aliases,
2201
2462
  schema: schema ?? void 0
2202
2463
  }
2203
- ) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Not connected. Press Ctrl+C to exit." }),
2204
- (vimEnabled || inputIsShell) && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { bold: true, color: inputIsShell ? theme.shellMode : vimMode === "NORMAL" ? theme.normalMode : theme.insertMode, children: isRawModeSupported ? inputIsShell ? "[SHELL]" : `[${vimMode}]` : "" }) })
2464
+ ) : /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Not connected. Press Ctrl+C to exit." }),
2465
+ (vimEnabled || inputIsShell) && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { bold: true, color: inputIsShell ? theme.shellMode : vimMode === "NORMAL" ? theme.normalMode : theme.insertMode, children: isRawModeSupported ? inputIsShell ? "[SHELL]" : `[${vimMode}]` : "" }) })
2205
2466
  ] })
2206
2467
  ] });
2207
2468
  }
2208
2469
 
2209
2470
  // src/ui/components/ConnectionWizard.tsx
2210
2471
  import { useState as useState4 } from "react";
2211
- import { Box as Box7, Text as Text7, useInput as useInput3, useStdin as useStdin3 } from "ink";
2212
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2472
+ import { Box as Box8, Text as Text8, useInput as useInput3, useStdin as useStdin3 } from "ink";
2473
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2213
2474
  var DRIVERS = ["postgresql", "mysql", "sqlite"];
2214
2475
  var LABEL_WIDTH = 10;
2215
2476
  function fieldLabels(driver) {
@@ -2364,36 +2625,36 @@ function ConnectionWizard({ onConnect, initialError }) {
2364
2625
  { isActive: isRawModeSupported }
2365
2626
  );
2366
2627
  const isPassword = (idx) => fields.driver !== "sqlite" && idx === 5;
2367
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 2, paddingTop: 2, paddingBottom: 1, children: [
2368
- /* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accent, children: "Connect to a database" }) }),
2628
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 2, paddingTop: 2, paddingBottom: 1, children: [
2629
+ /* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: "Connect to a database" }) }),
2369
2630
  labels.map((label, idx) => {
2370
2631
  const isFocused = focus === idx;
2371
2632
  const isDriver = idx === 0;
2372
- return /* @__PURE__ */ jsxs7(Box7, { children: [
2373
- /* @__PURE__ */ jsx7(Text7, { color: isFocused ? theme.accent : void 0, bold: isFocused, children: label.padEnd(LABEL_WIDTH) }),
2374
- isDriver ? /* @__PURE__ */ jsx7(Box7, { children: DRIVERS.map((d, i) => /* @__PURE__ */ jsxs7(Box7, { children: [
2375
- i > 0 && /* @__PURE__ */ jsx7(Text7, { children: " " }),
2376
- /* @__PURE__ */ jsxs7(Text7, { color: fields.driver === d ? theme.accent : void 0, bold: fields.driver === d, children: [
2633
+ return /* @__PURE__ */ jsxs8(Box8, { children: [
2634
+ /* @__PURE__ */ jsx8(Text8, { color: isFocused ? theme.accent : void 0, bold: isFocused, children: label.padEnd(LABEL_WIDTH) }),
2635
+ isDriver ? /* @__PURE__ */ jsx8(Box8, { children: DRIVERS.map((d, i) => /* @__PURE__ */ jsxs8(Box8, { children: [
2636
+ i > 0 && /* @__PURE__ */ jsx8(Text8, { children: " " }),
2637
+ /* @__PURE__ */ jsxs8(Text8, { color: fields.driver === d ? theme.accent : void 0, bold: fields.driver === d, children: [
2377
2638
  fields.driver === d ? "\u25CF " : "\u25CB ",
2378
2639
  d.charAt(0).toUpperCase() + d.slice(1)
2379
2640
  ] })
2380
- ] }, d)) }) : /* @__PURE__ */ jsxs7(Box7, { children: [
2381
- /* @__PURE__ */ jsx7(Text7, { children: isPassword(idx) ? "\u2022".repeat(getTextValue(idx).length) : getTextValue(idx) }),
2382
- isFocused && /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "\u258C" }),
2383
- isFocused && isPassword(idx) && keychainHint && /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " (from keychain)" })
2641
+ ] }, d)) }) : /* @__PURE__ */ jsxs8(Box8, { children: [
2642
+ /* @__PURE__ */ jsx8(Text8, { children: isPassword(idx) ? "\u2022".repeat(getTextValue(idx).length) : getTextValue(idx) }),
2643
+ isFocused && /* @__PURE__ */ jsx8(Text8, { color: theme.accent, bold: true, children: "\u258C" }),
2644
+ isFocused && isPassword(idx) && keychainHint && /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " (from keychain)" })
2384
2645
  ] })
2385
2646
  ] }, label);
2386
2647
  }),
2387
- /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: connecting ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Connecting\u2026" }) : error ? /* @__PURE__ */ jsxs7(Text7, { color: theme.error, children: [
2648
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: connecting ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Connecting\u2026" }) : error ? /* @__PURE__ */ jsxs8(Text8, { color: theme.error, children: [
2388
2649
  "\u2717 ",
2389
2650
  error
2390
2651
  ] }) : null }),
2391
- /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Tab \xB7 \u2191\u2193 navigate Enter connect \u2190\u2192 cycle driver" }) })
2652
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Tab \xB7 \u2191\u2193 navigate Enter connect \u2190\u2192 cycle driver" }) })
2392
2653
  ] });
2393
2654
  }
2394
2655
 
2395
2656
  // src/index.tsx
2396
- import { jsx as jsx8 } from "react/jsx-runtime";
2657
+ import { jsx as jsx9 } from "react/jsx-runtime";
2397
2658
  var { values } = parseArgs({
2398
2659
  args: process.argv.slice(2).filter((a) => a !== "--"),
2399
2660
  options: {
@@ -2423,10 +2684,10 @@ function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey
2423
2684
  }
2424
2685
  }, []);
2425
2686
  if (initialDsn2 && !connectionState && !dsnError) {
2426
- return /* @__PURE__ */ jsx8(Box8, { paddingX: 2, paddingTop: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Connecting\u2026" }) });
2687
+ return /* @__PURE__ */ jsx9(Box9, { paddingX: 2, paddingTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Connecting\u2026" }) });
2427
2688
  }
2428
2689
  if (!connectionState) {
2429
- return /* @__PURE__ */ jsx8(ConnectionWizard, { onConnect: setConnectionState, initialError: dsnError ?? void 0 });
2690
+ return /* @__PURE__ */ jsx9(ConnectionWizard, { onConnect: setConnectionState, initialError: dsnError ?? void 0 });
2430
2691
  }
2431
2692
  async function handleChangeDatabase(database) {
2432
2693
  if (connectionState.status !== "connected") return;
@@ -2436,7 +2697,7 @@ function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey
2436
2697
  const next = await connectParams({ ...current.params, database });
2437
2698
  setConnectionState(next);
2438
2699
  }
2439
- return /* @__PURE__ */ jsx8(
2700
+ return /* @__PURE__ */ jsx9(
2440
2701
  App,
2441
2702
  {
2442
2703
  connectionState,
@@ -2450,6 +2711,6 @@ function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey
2450
2711
  );
2451
2712
  }
2452
2713
  render(
2453
- /* @__PURE__ */ jsx8(Root, { initialDsn, aiUrl, aiModel, aiKey }),
2714
+ /* @__PURE__ */ jsx9(Root, { initialDsn, aiUrl, aiModel, aiKey }),
2454
2715
  { exitOnCtrlC: true }
2455
2716
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deaquinodev/querky",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "A quirky terminal SQL client with vim mode, AI features, and schema-aware autocomplete",
5
5
  "main": "dist/index.js",
6
6
  "files": [