@deaquinodev/querky 0.4.6 → 0.4.8

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 +131 -114
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.tsx
4
- import { useState as useState5, useEffect as useEffect5 } from "react";
4
+ import { useState as useState6, useEffect as useEffect5 } from "react";
5
5
  import { parseArgs } from "util";
6
6
  import { render, Box as Box9, Text as Text9 } from "ink";
7
7
 
@@ -193,11 +193,11 @@ async function connectDsn(dsn) {
193
193
  }
194
194
 
195
195
  // src/ui/components/App.tsx
196
- import { useState as useState3, useEffect as useEffect4, useRef as useRef2 } from "react";
196
+ import { useState as useState4, useEffect as useEffect4, useRef as useRef2 } from "react";
197
197
  import { writeFileSync as writeFileSync5 } from "fs";
198
198
  import { homedir as homedir4 } from "os";
199
- import { join as join6 } from "path";
200
- import { Box as Box7, Text as Text7, Static, useApp, useInput as useInput2, useStdin as useStdin2 } from "ink";
199
+ import { join as join5 } from "path";
200
+ import { Box as Box7, Text as Text7, useApp, useInput as useInput2, useStdin as useStdin2, Static } from "ink";
201
201
 
202
202
  // src/db/query.ts
203
203
  async function runQuery(client, sql) {
@@ -1050,7 +1050,7 @@ async function fetchSchema(client, driver) {
1050
1050
  }
1051
1051
 
1052
1052
  // src/ui/components/QueryInput.tsx
1053
- import { useEffect as useEffect2 } from "react";
1053
+ import { useEffect as useEffect2, useState as useState2 } from "react";
1054
1054
  import { Box, Text } from "ink";
1055
1055
 
1056
1056
  // src/ui/hooks/useVimInput.ts
@@ -1620,8 +1620,13 @@ function QueryInput({ onSubmit, isLoading, onModeChange, onShellModeChange, vimE
1620
1620
  const isShellMode = value.startsWith("!");
1621
1621
  const isCommand = !isShellMode && value.startsWith("/");
1622
1622
  const partial = isCommand ? value.slice(1) : "";
1623
- const suggestions = isCommand ? getCompletions(partial, aliases) : !isShellMode && schema ? getSqlCompletions(value, cursorPos, schema) : [];
1623
+ const liveSuggestions = isCommand ? getCompletions(partial, aliases) : !isShellMode && schema ? getSqlCompletions(value, cursorPos, schema) : [];
1624
1624
  const sqlToken = isCommand ? "" : getCurrentToken(value, cursorPos);
1625
+ const [suggestions, setSuggestions] = useState2([]);
1626
+ useEffect2(() => {
1627
+ const t = setTimeout(() => setSuggestions(liveSuggestions), 150);
1628
+ return () => clearTimeout(t);
1629
+ }, [value, cursorPos]);
1625
1630
  const descMap = {
1626
1631
  ...BUILTIN_DESC_MAP,
1627
1632
  ...Object.fromEntries(
@@ -1644,7 +1649,7 @@ function QueryInput({ onSubmit, isLoading, onModeChange, onShellModeChange, vimE
1644
1649
  const isMultiLine = !isEmpty && !isShellMode && !isCommand && value.includes("\n");
1645
1650
  const dOff = isShellMode ? 2 : 0;
1646
1651
  const placeholder = "Type a SQL query\u2026";
1647
- const previewResult = !isMultiLine && suggestionIndex >= 0 && suggestions.length > 0 ? handleSuggestionAccept(value, cursorPos, suggestions[suggestionIndex]) : null;
1652
+ const previewResult = !isMultiLine && suggestionIndex >= 0 && liveSuggestions.length > 0 ? handleSuggestionAccept(value, cursorPos, liveSuggestions[suggestionIndex]) : null;
1648
1653
  const renderValue = previewResult?.value ?? value;
1649
1654
  const renderCursor = previewResult?.cursor ?? cursorPos;
1650
1655
  const lineWidth = innerWidth - 4;
@@ -1667,6 +1672,17 @@ function QueryInput({ onSubmit, isLoading, onModeChange, onShellModeChange, vimE
1667
1672
  0
1668
1673
  );
1669
1674
  const hintsInline = totalHintsWidth <= termWidth;
1675
+ const STRIP_VISIBLE = 4;
1676
+ const stripPrefixW = isCommand ? 1 : 0;
1677
+ const stripAvailW = termWidth - 2 - 3 - (STRIP_VISIBLE - 1) * 3 - STRIP_VISIBLE * stripPrefixW;
1678
+ const stripMaxPerItem = Math.max(6, Math.floor(stripAvailW / STRIP_VISIBLE));
1679
+ const hasSuggestions = !isEmpty && !isShellMode && !isMultiLine && suggestions.length > 0;
1680
+ const stripWinStart = hasSuggestions ? Math.min(Math.max(0, suggestionIndex - 1), Math.max(0, suggestions.length - STRIP_VISIBLE)) : 0;
1681
+ const stripWindow = hasSuggestions ? suggestions.slice(stripWinStart, stripWinStart + STRIP_VISIBLE) : [];
1682
+ const stripNavHint = hasSuggestions ? `Tab/\u2191\u2193 navigate Enter select ${suggestionIndex + 1}/${suggestions.length}` : " ";
1683
+ function truncateSugg(s) {
1684
+ return s.length > stripMaxPerItem ? s.slice(0, stripMaxPerItem - 1) + "\u2026" : s;
1685
+ }
1670
1686
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1671
1687
  isMultiLine ? /* @__PURE__ */ jsxs(Fragment, { children: [
1672
1688
  /* @__PURE__ */ jsx(Text, { backgroundColor: BG, children: emptyLine }),
@@ -1707,35 +1723,30 @@ function QueryInput({ onSubmit, isLoading, onModeChange, onShellModeChange, vimE
1707
1723
  ] }),
1708
1724
  /* @__PURE__ */ jsx(Text, { backgroundColor: BG, children: emptyLine })
1709
1725
  ] }),
1710
- suggestions.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [
1711
- suggestions.map((name, i) => {
1712
- const selected = i === suggestionIndex;
1713
- if (isCommand) {
1714
- return /* @__PURE__ */ jsxs(Box, { children: [
1715
- /* @__PURE__ */ jsxs(Text, { children: [
1716
- /* @__PURE__ */ jsx(Text, { dimColor: !selected, color: selected ? ACCENT : void 0, children: "/" }),
1717
- /* @__PURE__ */ jsx(FuzzyHighlight, { text: name, token: partial, selected })
1718
- ] }),
1719
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1720
- " \u2014 ",
1721
- descMap[name]
1722
- ] })
1726
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1727
+ /* @__PURE__ */ jsx(Box, { marginLeft: 2, children: hasSuggestions ? /* @__PURE__ */ jsxs(Fragment, { children: [
1728
+ stripWindow.map((name, i) => {
1729
+ const realIdx = stripWinStart + i;
1730
+ const selected = realIdx === suggestionIndex;
1731
+ return /* @__PURE__ */ jsxs(Text, { children: [
1732
+ i > 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
1733
+ isCommand && /* @__PURE__ */ jsx(Text, { dimColor: !selected, color: selected ? ACCENT : void 0, children: "/" }),
1734
+ /* @__PURE__ */ jsx(FuzzyHighlight, { text: truncateSugg(name), token: isCommand ? partial : sqlToken, selected })
1723
1735
  ] }, name);
1724
- }
1725
- return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(FuzzyHighlight, { text: name, token: sqlToken, selected }) }, name);
1726
- }),
1727
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Tab/\u2191\u2193 navigate Enter select" })
1728
- ] }),
1729
- /* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: hintsInline ? "row" : "column", children: allHints.map(([key, desc], i) => /* @__PURE__ */ jsxs(Text, { children: [
1730
- hintsInline && i > 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " | " }),
1731
- /* @__PURE__ */ jsx(Text, { color: ACCENT, bold: true, children: desc }),
1732
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: `: ${key}` })
1733
- ] }, desc)) })
1736
+ }),
1737
+ suggestions.length > STRIP_VISIBLE && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2026" })
1738
+ ] }) : /* @__PURE__ */ jsx(Box, { flexDirection: hintsInline ? "row" : "column", children: allHints.map(([key, desc], i) => /* @__PURE__ */ jsxs(Text, { children: [
1739
+ hintsInline && i > 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " | " }),
1740
+ /* @__PURE__ */ jsx(Text, { color: ACCENT, bold: true, children: desc }),
1741
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: `: ${key}` })
1742
+ ] }, desc)) }) }),
1743
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: stripNavHint })
1744
+ ] })
1734
1745
  ] });
1735
1746
  }
1736
1747
 
1737
1748
  // src/ui/components/QueryResult.tsx
1738
- import { useState as useState2, useEffect as useEffect3 } from "react";
1749
+ import { useState as useState3, useEffect as useEffect3 } from "react";
1739
1750
  import { Box as Box3, Text as Text3 } from "ink";
1740
1751
 
1741
1752
  // src/ui/components/Table.tsx
@@ -1743,7 +1754,7 @@ import { Box as Box2, Text as Text2 } from "ink";
1743
1754
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1744
1755
  var COL_PAD = 1;
1745
1756
  var INDIGO = "#818cf8";
1746
- var BORDER2 = "white";
1757
+ var BORDER = "white";
1747
1758
  var NULL_COLOR = "#6366f1";
1748
1759
  var NULL_MARKER = "\u2205";
1749
1760
  function isNull(val) {
@@ -1774,10 +1785,10 @@ function hline(widths, left, mid, right) {
1774
1785
  function ExpandedTable({ columns, rows }) {
1775
1786
  const keyWidth = columns.reduce((max, col) => Math.max(max, col.length), 0);
1776
1787
  return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: rows.map((row, i) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: i < rows.length - 1 ? 1 : 0, children: [
1777
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: `\u2500[ Record ${i + 1} ]${"\u2500".repeat(Math.max(0, keyWidth + 14 - String(i + 1).length))}` }),
1788
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: `\u2500[ Record ${i + 1} ]${"\u2500".repeat(Math.max(0, keyWidth + 14 - String(i + 1).length))}` }),
1778
1789
  columns.map((col) => /* @__PURE__ */ jsxs2(Box2, { children: [
1779
1790
  /* @__PURE__ */ jsx2(Text2, { color: INDIGO, bold: true, children: col.padEnd(keyWidth) }),
1780
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: " \u2502 " }),
1791
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: " \u2502 " }),
1781
1792
  isNull(row[col]) ? /* @__PURE__ */ jsx2(Text2, { color: NULL_COLOR, dimColor: true, children: NULL_MARKER }) : /* @__PURE__ */ jsx2(Text2, { children: cellValue(row[col]) })
1782
1793
  ] }, col))
1783
1794
  ] }, i)) });
@@ -1790,31 +1801,31 @@ function Table({ columns, rows, expanded = false }) {
1790
1801
  const botLine = hline(widths, "\u2570", "\u2534", "\u256F");
1791
1802
  function renderHeaderRow(cols) {
1792
1803
  return /* @__PURE__ */ jsxs2(Box2, { children: [
1793
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" }),
1804
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" }),
1794
1805
  cols.map((v, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
1795
1806
  /* @__PURE__ */ jsx2(Text2, { color: INDIGO, bold: true, children: " ".repeat(COL_PAD) + pad(v, widths[i]) + " ".repeat(COL_PAD) }),
1796
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" })
1807
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" })
1797
1808
  ] }, i))
1798
1809
  ] });
1799
1810
  }
1800
1811
  function renderDataRow(row) {
1801
1812
  return /* @__PURE__ */ jsxs2(Box2, { children: [
1802
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" }),
1813
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" }),
1803
1814
  columns.map((col, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
1804
1815
  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) }),
1805
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: "\u2502" })
1816
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: "\u2502" })
1806
1817
  ] }, i))
1807
1818
  ] });
1808
1819
  }
1809
1820
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
1810
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: topLine }),
1821
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: topLine }),
1811
1822
  renderHeaderRow(columns),
1812
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: midLine }),
1823
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: midLine }),
1813
1824
  rows.map((row, i) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
1814
1825
  renderDataRow(row),
1815
- i < rows.length - 1 && /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: midLine })
1826
+ i < rows.length - 1 && /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: midLine })
1816
1827
  ] }, i)),
1817
- /* @__PURE__ */ jsx2(Text2, { color: BORDER2, children: botLine })
1828
+ /* @__PURE__ */ jsx2(Text2, { color: BORDER, children: botLine })
1818
1829
  ] });
1819
1830
  }
1820
1831
 
@@ -1833,7 +1844,7 @@ function formatDuration(ms) {
1833
1844
  return `${(ms / 1e3).toFixed(2)}s`;
1834
1845
  }
1835
1846
  function useTerminalWidth() {
1836
- const [width, setWidth] = useState2(process.stdout.columns ?? 80);
1847
+ const [width, setWidth] = useState3(process.stdout.columns ?? 80);
1837
1848
  useEffect3(() => {
1838
1849
  const handler = () => setWidth(process.stdout.columns ?? 80);
1839
1850
  process.stdout.on("resize", handler);
@@ -1902,16 +1913,8 @@ function QueryResult({ state, elapsed, page, pageSize }) {
1902
1913
 
1903
1914
  // src/ui/components/Banner.tsx
1904
1915
  import { Box as Box4, Text as Text4 } from "ink";
1905
- import { readFileSync as readFileSync5 } from "fs";
1906
- import { fileURLToPath } from "url";
1907
- import { dirname, join as join5 } from "path";
1908
1916
  import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1909
- var __dirname = dirname(fileURLToPath(import.meta.url));
1910
- var pkg = { version: "unknown" };
1911
- try {
1912
- pkg = JSON.parse(readFileSync5(join5(__dirname, "../../../package.json"), "utf8"));
1913
- } catch {
1914
- }
1917
+ var version = true ? "0.4.8" : process.env.npm_package_version ?? "unknown";
1915
1918
  var LOGO = [
1916
1919
  " \u2597\u2584\u2584\u2584\u2584\u2584\u2596",
1917
1920
  "\u2590\u2591 \u25CF \u25CF \u2591\u258C",
@@ -1935,7 +1938,7 @@ function Banner({ connectionState }) {
1935
1938
  "Querky ",
1936
1939
  /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
1937
1940
  "v",
1938
- pkg.version
1941
+ version
1939
1942
  ] })
1940
1943
  ] }),
1941
1944
  /* @__PURE__ */ jsx4(Text4, { children: " " }),
@@ -2019,6 +2022,7 @@ var TABLE_COLORS = [
2019
2022
  "#fbbf24",
2020
2023
  "#2dd4bf"
2021
2024
  ];
2025
+ var BORDER2 = "#4b5563";
2022
2026
  var PAD = 1;
2023
2027
  var GAP = 2;
2024
2028
  var FK_PREFIX = "FK \u2192 ";
@@ -2066,31 +2070,31 @@ function TableBox({ table, m, color, colorMap }) {
2066
2070
  const bot = "\u2570" + "\u2500".repeat(nameW + PAD * 2) + "\u2534" + "\u2500".repeat(typeW + PAD * 2) + "\u2534" + "\u2500".repeat(keyW + PAD * 2) + "\u256F";
2067
2071
  const headerW = totalW - 4;
2068
2072
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2069
- /* @__PURE__ */ jsx6(Text6, { color, children: top }),
2073
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: top }),
2070
2074
  /* @__PURE__ */ jsxs6(Box6, { children: [
2071
- /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2075
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: "\u2502" }),
2072
2076
  /* @__PURE__ */ jsxs6(Text6, { color, bold: true, children: [
2073
2077
  sp,
2074
2078
  p(table.name, headerW),
2075
2079
  sp
2076
2080
  ] }),
2077
- /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" })
2081
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: "\u2502" })
2078
2082
  ] }),
2079
- /* @__PURE__ */ jsx6(Text6, { color, children: sep }),
2083
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: sep }),
2080
2084
  table.columns.map((col, i) => /* @__PURE__ */ jsxs6(Box6, { children: [
2081
- /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2085
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: "\u2502" }),
2082
2086
  /* @__PURE__ */ jsxs6(Text6, { children: [
2083
2087
  sp,
2084
2088
  p(col.name, nameW),
2085
2089
  sp
2086
2090
  ] }),
2087
- /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2091
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: "\u2502" }),
2088
2092
  /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
2089
2093
  sp,
2090
2094
  p(col.type, typeW),
2091
2095
  sp
2092
2096
  ] }),
2093
- /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" }),
2097
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: "\u2502" }),
2094
2098
  col.isPk ? /* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
2095
2099
  sp,
2096
2100
  p("PK", keyW),
@@ -2109,9 +2113,9 @@ function TableBox({ table, m, color, colorMap }) {
2109
2113
  " ".repeat(keyW),
2110
2114
  sp
2111
2115
  ] }),
2112
- /* @__PURE__ */ jsx6(Text6, { color, children: "\u2502" })
2116
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: "\u2502" })
2113
2117
  ] }, i)),
2114
- /* @__PURE__ */ jsx6(Text6, { color, children: bot })
2118
+ /* @__PURE__ */ jsx6(Text6, { color: BORDER2, children: bot })
2115
2119
  ] });
2116
2120
  }
2117
2121
  function ErdView({ data }) {
@@ -2129,7 +2133,7 @@ function ErdView({ data }) {
2129
2133
  {
2130
2134
  table: data.tables[ti],
2131
2135
  m: metrics[ti],
2132
- color: colorMap.get(data.tables[ti].name) ?? BORDER,
2136
+ color: colorMap.get(data.tables[ti].name) ?? BORDER2,
2133
2137
  colorMap
2134
2138
  }
2135
2139
  ) }, ti)) }, ri)) });
@@ -2148,26 +2152,42 @@ function limitLines(s, n) {
2148
2152
  return lines.slice(0, n).join("\n") + `
2149
2153
  \u2026 +${lines.length - n} more lines (scroll up after next submit)`;
2150
2154
  }
2151
- function displayQuery(query) {
2152
- const cols = process.stdout.columns ?? 80;
2153
- const max = Math.max(20, cols - 16);
2154
- const flat = query.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
2155
- return flat.length > max ? flat.slice(0, max) + "\u2026" : flat;
2155
+ var QUERY_HEADER_COLOR = "#9ca3af";
2156
+ function QueryHeader({ label, query }) {
2157
+ const isMultiLine = query.includes("\n");
2158
+ if (isMultiLine) {
2159
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
2160
+ /* @__PURE__ */ jsxs7(Text7, { children: [
2161
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "\u25CF " }),
2162
+ /* @__PURE__ */ jsxs7(Text7, { color: QUERY_HEADER_COLOR, bold: true, children: [
2163
+ label,
2164
+ ":"
2165
+ ] })
2166
+ ] }),
2167
+ query.split("\n").map((line, i) => /* @__PURE__ */ jsxs7(Text7, { color: QUERY_HEADER_COLOR, bold: true, children: [
2168
+ " ",
2169
+ line
2170
+ ] }, i))
2171
+ ] });
2172
+ }
2173
+ return /* @__PURE__ */ jsxs7(Text7, { children: [
2174
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "\u25CF " }),
2175
+ /* @__PURE__ */ jsxs7(Text7, { color: QUERY_HEADER_COLOR, bold: true, children: [
2176
+ label,
2177
+ "(",
2178
+ query,
2179
+ ")"
2180
+ ] })
2181
+ ] });
2156
2182
  }
2157
2183
  function EntryView({ entry }) {
2158
2184
  const showAi = entry.aiResponse !== "" || entry.aiError !== null;
2159
2185
  const showErd = entry.erdData !== null;
2160
2186
  const isShell = entry.query.startsWith("!");
2161
2187
  const isCommand = !isShell && (entry.query.startsWith("/") || entry.query.startsWith("\\"));
2162
- const label = isShell ? "Shell:" : isCommand ? "Command:" : "Query:";
2188
+ const label = isShell ? "Shell" : isCommand ? "Command" : "Query";
2163
2189
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, paddingX: 1, children: [
2164
- /* @__PURE__ */ jsxs7(Box7, { borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
2165
- /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, bold: true, children: [
2166
- label,
2167
- " "
2168
- ] }),
2169
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: displayQuery(entry.query) })
2170
- ] }),
2190
+ /* @__PURE__ */ jsx7(QueryHeader, { label, query: entry.query }),
2171
2191
  /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: isShell ? entry.shellOutput !== null && /* @__PURE__ */ jsx7(Text7, { children: entry.shellOutput || "(no output)" }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
2172
2192
  entry.commandMessage && (entry.commandMessage.helpData ? /* @__PURE__ */ jsx7(HelpView, { data: entry.commandMessage.helpData }) : entry.commandMessage.ok ? /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, children: [
2173
2193
  "\u2713 ",
@@ -2183,29 +2203,29 @@ function EntryView({ entry }) {
2183
2203
  function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2, onChangeDatabase }) {
2184
2204
  const { exit } = useApp();
2185
2205
  const { isRawModeSupported } = useStdin2();
2186
- const [queryState, setQueryState] = useState3({ status: "idle" });
2187
- const [lastQuery, setLastQuery] = useState3("");
2188
- const [lastSqlQuery, setLastSqlQuery] = useState3("");
2189
- const [lastResult, setLastResult] = useState3(null);
2190
- const [page, setPage] = useState3(0);
2191
- const [vimMode, setVimMode] = useState3("INSERT");
2192
- const [inputIsShell, setInputIsShell] = useState3(false);
2193
- const [elapsed, setElapsed] = useState3(null);
2194
- const [vimEnabled, setVimEnabled] = useState3(true);
2195
- const [commandMessage, setCommandMessage] = useState3(null);
2196
- const [history, setHistory] = useState3(() => loadHistory());
2197
- const [schema, setSchema] = useState3(null);
2198
- const [aiResponse, setAiResponse] = useState3("");
2199
- const [isStreaming, setIsStreaming] = useState3(false);
2200
- const [aiError, setAiError] = useState3(null);
2201
- const [shellOutput, setShellOutput] = useState3(null);
2202
- const [isShellRunning, setIsShellRunning] = useState3(false);
2203
- const [erdData, setErdData] = useState3(null);
2204
- const [isErdLoading, setIsErdLoading] = useState3(false);
2205
- const [completedEntries, setCompletedEntries] = useState3([]);
2206
+ const [queryState, setQueryState] = useState4({ status: "idle" });
2207
+ const [lastQuery, setLastQuery] = useState4("");
2208
+ const [lastSqlQuery, setLastSqlQuery] = useState4("");
2209
+ const [lastResult, setLastResult] = useState4(null);
2210
+ const [page, setPage] = useState4(0);
2211
+ const [vimMode, setVimMode] = useState4("INSERT");
2212
+ const [inputIsShell, setInputIsShell] = useState4(false);
2213
+ const [elapsed, setElapsed] = useState4(null);
2214
+ const [vimEnabled, setVimEnabled] = useState4(true);
2215
+ const [commandMessage, setCommandMessage] = useState4(null);
2216
+ const [history, setHistory] = useState4(() => loadHistory());
2217
+ const [schema, setSchema] = useState4(null);
2218
+ const [aiResponse, setAiResponse] = useState4("");
2219
+ const [isStreaming, setIsStreaming] = useState4(false);
2220
+ const [aiError, setAiError] = useState4(null);
2221
+ const [shellOutput, setShellOutput] = useState4(null);
2222
+ const [isShellRunning, setIsShellRunning] = useState4(false);
2223
+ const [erdData, setErdData] = useState4(null);
2224
+ const [isErdLoading, setIsErdLoading] = useState4(false);
2225
+ const [completedEntries, setCompletedEntries] = useState4([]);
2206
2226
  const entryIdRef = useRef2(0);
2207
2227
  const aliasScope = connectionState.status === "connected" ? makeScope(connectionState.driver, connectionState.user, connectionState.host, connectionState.database) : "";
2208
- const [aliases, setAliases] = useState3(
2228
+ const [aliases, setAliases] = useState4(
2209
2229
  () => aliasScope ? getAllAliases(aliasScope) : {}
2210
2230
  );
2211
2231
  function handleSaveAlias(name, query) {
@@ -2313,7 +2333,7 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2313
2333
  return;
2314
2334
  }
2315
2335
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
2316
- const filename = join6(homedir4(), `q-export-${ts}.${format}`);
2336
+ const filename = join5(homedir4(), `q-export-${ts}.${format}`);
2317
2337
  try {
2318
2338
  if (format === "json") {
2319
2339
  writeFileSync5(filename, JSON.stringify(lastResult.rows, null, 2));
@@ -2403,7 +2423,6 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2403
2423
  void handleErd();
2404
2424
  },
2405
2425
  onClear: () => {
2406
- process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
2407
2426
  setCompletedEntries([]);
2408
2427
  setLastQuery("");
2409
2428
  setCommandMessage(null);
@@ -2413,6 +2432,7 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2413
2432
  setAiError(null);
2414
2433
  setElapsed(null);
2415
2434
  setPage(0);
2435
+ process.stdout.write("\x1B[2J\x1B[H");
2416
2436
  },
2417
2437
  aliases,
2418
2438
  onSaveAlias: handleSaveAlias,
@@ -2444,13 +2464,7 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2444
2464
  /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, children: [
2445
2465
  lastQuery === "" && /* @__PURE__ */ jsx7(Banner, { connectionState }),
2446
2466
  lastQuery !== "" && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 2, children: [
2447
- /* @__PURE__ */ jsxs7(Box7, { borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
2448
- /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, bold: true, children: [
2449
- activeLabel,
2450
- " "
2451
- ] }),
2452
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: displayQuery(lastQuery) })
2453
- ] }),
2467
+ /* @__PURE__ */ jsx7(QueryHeader, { label: activeLabel.replace(":", ""), query: lastQuery }),
2454
2468
  /* @__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: [
2455
2469
  commandMessage && (commandMessage.helpData ? /* @__PURE__ */ jsx7(HelpView, { data: commandMessage.helpData }) : commandMessage.ok ? /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, children: [
2456
2470
  "\u2713 ",
@@ -2484,7 +2498,7 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
2484
2498
  }
2485
2499
 
2486
2500
  // src/ui/components/ConnectionWizard.tsx
2487
- import { useState as useState4 } from "react";
2501
+ import { useState as useState5 } from "react";
2488
2502
  import { Box as Box8, Text as Text8, useInput as useInput3, useStdin as useStdin3 } from "ink";
2489
2503
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2490
2504
  var DRIVERS = ["postgresql", "mysql", "sqlite"];
@@ -2495,8 +2509,8 @@ function fieldLabels(driver) {
2495
2509
  }
2496
2510
  function ConnectionWizard({ onConnect, initialError }) {
2497
2511
  const { isRawModeSupported } = useStdin3();
2498
- const [focus, setFocus] = useState4(0);
2499
- const [fields, setFields] = useState4({
2512
+ const [focus, setFocus] = useState5(0);
2513
+ const [fields, setFields] = useState5({
2500
2514
  driver: "postgresql",
2501
2515
  host: "localhost",
2502
2516
  port: "5432",
@@ -2504,9 +2518,9 @@ function ConnectionWizard({ onConnect, initialError }) {
2504
2518
  user: "",
2505
2519
  password: ""
2506
2520
  });
2507
- const [connecting, setConnecting] = useState4(false);
2508
- const [error, setError] = useState4(initialError ?? null);
2509
- const [keychainHint, setKeychainHint] = useState4(false);
2521
+ const [connecting, setConnecting] = useState5(false);
2522
+ const [error, setError] = useState5(initialError ?? null);
2523
+ const [keychainHint, setKeychainHint] = useState5(false);
2510
2524
  const labels = fieldLabels(fields.driver);
2511
2525
  const fieldCount = labels.length;
2512
2526
  function getTextValue(idx) {
@@ -2686,8 +2700,8 @@ var aiUrl = values["ai-url"];
2686
2700
  var aiModel = values["ai-model"];
2687
2701
  var aiKey = values["api-key"] || process.env.Q_CLI_API_KEY || "";
2688
2702
  function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2 }) {
2689
- const [connectionState, setConnectionState] = useState5(null);
2690
- const [dsnError, setDsnError] = useState5(null);
2703
+ const [connectionState, setConnectionState] = useState6(null);
2704
+ const [dsnError, setDsnError] = useState6(null);
2691
2705
  useEffect5(() => {
2692
2706
  if (initialDsn2) {
2693
2707
  connectDsn(initialDsn2).then((state) => {
@@ -2726,6 +2740,9 @@ function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey
2726
2740
  }
2727
2741
  );
2728
2742
  }
2743
+ process.stdout.write("\x1B[?1049h\x1B[H");
2744
+ process.on("exit", () => process.stdout.write("\x1B[?1049l"));
2745
+ process.on("SIGTERM", () => process.exit(0));
2729
2746
  render(
2730
2747
  /* @__PURE__ */ jsx9(Root, { initialDsn, aiUrl, aiModel, aiKey }),
2731
2748
  { exitOnCtrlC: true }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deaquinodev/querky",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
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": [