@deaquinodev/querky 0.4.1 → 0.4.3
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.
- package/dist/index.js +204 -66
- 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
|
|
6
|
+
import { render, Box as Box8, Text as Text8 } from "ink";
|
|
7
7
|
|
|
8
8
|
// src/db/client.ts
|
|
9
9
|
import { Client } from "pg";
|
|
@@ -196,7 +196,7 @@ import { useState as useState3, useEffect as useEffect4, useRef as useRef2 } fro
|
|
|
196
196
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
197
197
|
import { homedir as homedir4 } from "os";
|
|
198
198
|
import { join as join6 } from "path";
|
|
199
|
-
import { Box as
|
|
199
|
+
import { Box as Box6, Text as Text6, Static, useApp, useInput as useInput2, useStdin as useStdin2 } from "ink";
|
|
200
200
|
|
|
201
201
|
// src/db/query.ts
|
|
202
202
|
async function runQuery(client, sql) {
|
|
@@ -466,6 +466,9 @@ var PSQL_ALIASES = {
|
|
|
466
466
|
du: "users",
|
|
467
467
|
c: "changeDatabase"
|
|
468
468
|
};
|
|
469
|
+
var PSQL_REVERSE = Object.fromEntries(
|
|
470
|
+
Object.entries(PSQL_ALIASES).filter(([alias, name]) => alias !== name).map(([alias, name]) => [name, `\\${alias}`])
|
|
471
|
+
);
|
|
469
472
|
function describeBasicSql(driver, table) {
|
|
470
473
|
const t = table.replace(/'/g, "''");
|
|
471
474
|
if (driver === "postgresql") {
|
|
@@ -488,7 +491,10 @@ function describeFullSql(driver, table) {
|
|
|
488
491
|
}
|
|
489
492
|
var COMMANDS = {
|
|
490
493
|
"clear": {
|
|
491
|
-
description: "Clear the scrollback
|
|
494
|
+
description: "Clear the terminal scrollback",
|
|
495
|
+
category: "Session",
|
|
496
|
+
usage: "/clear",
|
|
497
|
+
detail: "Erases the visible screen and scrollback buffer. The next query starts fresh from the top.",
|
|
492
498
|
run: (ctx) => {
|
|
493
499
|
ctx.onClear();
|
|
494
500
|
return { ok: true, message: "", cleared: true };
|
|
@@ -496,6 +502,9 @@ var COMMANDS = {
|
|
|
496
502
|
},
|
|
497
503
|
"toggle-vim-mode": {
|
|
498
504
|
description: "Toggle vim keybindings on/off",
|
|
505
|
+
category: "Session",
|
|
506
|
+
usage: "/toggle-vim-mode",
|
|
507
|
+
detail: "Switches the query input between standard editing and vim keybindings. In NORMAL mode use motions like w, b, 0, $, x, dd. Enter INSERT mode with i, a, A, I, o, O.",
|
|
499
508
|
run: (ctx) => {
|
|
500
509
|
const next = !ctx.vimEnabled;
|
|
501
510
|
ctx.setVimEnabled(next);
|
|
@@ -503,7 +512,11 @@ var COMMANDS = {
|
|
|
503
512
|
}
|
|
504
513
|
},
|
|
505
514
|
"d": {
|
|
506
|
-
description: "List tables, or describe a table
|
|
515
|
+
description: "List tables, or describe a table",
|
|
516
|
+
category: "Schema",
|
|
517
|
+
usage: "/d [table]",
|
|
518
|
+
detail: "Without an argument, lists all tables in the current database. With a table name, shows columns, types, nullability, and defaults.",
|
|
519
|
+
example: "/d users",
|
|
507
520
|
run: (ctx) => {
|
|
508
521
|
if (!ctx.args.trim()) {
|
|
509
522
|
ctx.onQuery(DB_QUERIES[ctx.driver].tables);
|
|
@@ -514,7 +527,11 @@ var COMMANDS = {
|
|
|
514
527
|
}
|
|
515
528
|
},
|
|
516
529
|
"describe": {
|
|
517
|
-
description: "Describe a table with
|
|
530
|
+
description: "Describe a table with PK/FK/UQ constraints",
|
|
531
|
+
category: "Schema",
|
|
532
|
+
usage: "/describe <table>",
|
|
533
|
+
detail: "Like /d but includes constraint information \u2014 PK (primary key), FK (foreign key), UQ (unique) \u2014 in an extra key column. SQLite shows PK only.",
|
|
534
|
+
example: "/describe orders",
|
|
518
535
|
run: (ctx) => {
|
|
519
536
|
const table = ctx.args.trim();
|
|
520
537
|
if (!table) return { ok: false, message: "Usage: /describe <table>" };
|
|
@@ -524,6 +541,9 @@ var COMMANDS = {
|
|
|
524
541
|
},
|
|
525
542
|
"databases": {
|
|
526
543
|
description: "List available databases",
|
|
544
|
+
category: "Schema",
|
|
545
|
+
usage: "/databases",
|
|
546
|
+
detail: "Runs a driver-appropriate query to list all databases on the server.",
|
|
527
547
|
run: (ctx) => {
|
|
528
548
|
ctx.onQuery(DB_QUERIES[ctx.driver].databases);
|
|
529
549
|
return { ok: true, message: "" };
|
|
@@ -531,6 +551,9 @@ var COMMANDS = {
|
|
|
531
551
|
},
|
|
532
552
|
"tables": {
|
|
533
553
|
description: "List tables in the current database",
|
|
554
|
+
category: "Schema",
|
|
555
|
+
usage: "/tables",
|
|
556
|
+
detail: "Lists tables in the current database's public schema.",
|
|
534
557
|
run: (ctx) => {
|
|
535
558
|
ctx.onQuery(DB_QUERIES[ctx.driver].tables);
|
|
536
559
|
return { ok: true, message: "" };
|
|
@@ -538,13 +561,20 @@ var COMMANDS = {
|
|
|
538
561
|
},
|
|
539
562
|
"users": {
|
|
540
563
|
description: "List database users",
|
|
564
|
+
category: "Schema",
|
|
565
|
+
usage: "/users",
|
|
566
|
+
detail: "Lists users and their roles. On SQLite, shows a placeholder message since SQLite has no user system.",
|
|
541
567
|
run: (ctx) => {
|
|
542
568
|
ctx.onQuery(DB_QUERIES[ctx.driver].users);
|
|
543
569
|
return { ok: true, message: "" };
|
|
544
570
|
}
|
|
545
571
|
},
|
|
546
572
|
"changeDatabase": {
|
|
547
|
-
description: "Switch to a different database
|
|
573
|
+
description: "Switch to a different database",
|
|
574
|
+
category: "Session",
|
|
575
|
+
usage: "/changeDatabase <dbname>",
|
|
576
|
+
detail: "Switches the active connection to the specified database. Without an argument, shows the current database.",
|
|
577
|
+
example: "/changeDatabase myapp_prod",
|
|
548
578
|
run: (ctx) => {
|
|
549
579
|
if (!ctx.args) return { ok: true, message: `Connected to database: ${ctx.currentDatabase}` };
|
|
550
580
|
ctx.onChangeDatabase(ctx.args.trim());
|
|
@@ -552,7 +582,11 @@ var COMMANDS = {
|
|
|
552
582
|
}
|
|
553
583
|
},
|
|
554
584
|
"export": {
|
|
555
|
-
description: "Export last result to a file
|
|
585
|
+
description: "Export last result to a file",
|
|
586
|
+
category: "Data",
|
|
587
|
+
usage: "/export csv|json",
|
|
588
|
+
detail: "Writes the last query result to a timestamped file in your home directory. CSV files include a header row; JSON files are an array of row objects.",
|
|
589
|
+
example: "/export csv",
|
|
556
590
|
run: (ctx) => {
|
|
557
591
|
const fmt = ctx.args.trim().toLowerCase();
|
|
558
592
|
if (fmt !== "csv" && fmt !== "json") {
|
|
@@ -563,7 +597,11 @@ var COMMANDS = {
|
|
|
563
597
|
}
|
|
564
598
|
},
|
|
565
599
|
"explain": {
|
|
566
|
-
description: "Explain a SQL query using AI
|
|
600
|
+
description: "Explain a SQL query using AI",
|
|
601
|
+
category: "AI",
|
|
602
|
+
usage: "/explain <SQL>",
|
|
603
|
+
detail: "Sends the query to your configured AI endpoint (Ollama by default, or any OpenAI-compatible API) and streams a plain-language explanation.",
|
|
604
|
+
example: "/explain SELECT * FROM orders WHERE status = 'pending'",
|
|
567
605
|
run: (ctx) => {
|
|
568
606
|
if (!ctx.args) return { ok: false, message: "Usage: /explain <SQL query>" };
|
|
569
607
|
ctx.onExplain(ctx.args);
|
|
@@ -572,6 +610,9 @@ var COMMANDS = {
|
|
|
572
610
|
},
|
|
573
611
|
"explain-previous": {
|
|
574
612
|
description: "Explain the last executed query using AI",
|
|
613
|
+
category: "AI",
|
|
614
|
+
usage: "/explain-previous",
|
|
615
|
+
detail: "Like /explain but uses the last SQL query you ran. Useful for quickly understanding a query after seeing its results.",
|
|
575
616
|
run: (ctx) => {
|
|
576
617
|
if (!ctx.lastSqlQuery) {
|
|
577
618
|
return { ok: false, message: "No query to explain \u2014 run a SQL query first." };
|
|
@@ -581,7 +622,11 @@ var COMMANDS = {
|
|
|
581
622
|
}
|
|
582
623
|
},
|
|
583
624
|
"save": {
|
|
584
|
-
description: "Save last query as
|
|
625
|
+
description: "Save last query as a named alias",
|
|
626
|
+
category: "Aliases",
|
|
627
|
+
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",
|
|
585
630
|
run: (ctx) => {
|
|
586
631
|
const name = ctx.args.trim();
|
|
587
632
|
if (!name) return { ok: false, message: "Usage: /save <name>" };
|
|
@@ -592,7 +637,11 @@ var COMMANDS = {
|
|
|
592
637
|
}
|
|
593
638
|
},
|
|
594
639
|
"alias": {
|
|
595
|
-
description: "Define an alias inline
|
|
640
|
+
description: "Define an alias inline",
|
|
641
|
+
category: "Aliases",
|
|
642
|
+
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",
|
|
596
645
|
run: (ctx) => {
|
|
597
646
|
const [name, ...rest] = ctx.args.trim().split(/\s+/);
|
|
598
647
|
if (!name || rest.length === 0) return { ok: false, message: "Usage: /alias <name> <SQL>" };
|
|
@@ -603,6 +652,9 @@ var COMMANDS = {
|
|
|
603
652
|
},
|
|
604
653
|
"aliases": {
|
|
605
654
|
description: "List all saved aliases for this database",
|
|
655
|
+
category: "Aliases",
|
|
656
|
+
usage: "/aliases",
|
|
657
|
+
detail: "Prints all saved aliases for the current database connection, with their SQL template.",
|
|
606
658
|
run: (ctx) => {
|
|
607
659
|
const entries = Object.entries(ctx.aliases);
|
|
608
660
|
if (entries.length === 0) return { ok: true, message: "No aliases saved. Use /save <name> or /alias <name> <SQL>." };
|
|
@@ -614,13 +666,52 @@ var COMMANDS = {
|
|
|
614
666
|
}
|
|
615
667
|
},
|
|
616
668
|
"unalias": {
|
|
617
|
-
description: "Remove a saved alias
|
|
669
|
+
description: "Remove a saved alias",
|
|
670
|
+
category: "Aliases",
|
|
671
|
+
usage: "/unalias <name>",
|
|
672
|
+
detail: "Removes a previously saved alias. Only affects aliases for the current database connection.",
|
|
673
|
+
example: "/unalias active-orders",
|
|
618
674
|
run: (ctx) => {
|
|
619
675
|
const name = ctx.args.trim();
|
|
620
676
|
if (!name) return { ok: false, message: "Usage: /unalias <name>" };
|
|
621
677
|
const removed = ctx.onDeleteAlias(name);
|
|
622
678
|
return removed ? { ok: true, message: `Removed /${name}` } : { ok: false, message: `No alias named /${name}` };
|
|
623
679
|
}
|
|
680
|
+
},
|
|
681
|
+
"help": {
|
|
682
|
+
description: "Show help for all commands or a specific command",
|
|
683
|
+
category: "Session",
|
|
684
|
+
usage: "/help [command]",
|
|
685
|
+
detail: "Without arguments, lists all commands grouped by category. With a command name, shows detailed usage and an example.",
|
|
686
|
+
example: "/help explain",
|
|
687
|
+
run: (ctx) => {
|
|
688
|
+
const arg = ctx.args.trim();
|
|
689
|
+
if (arg) {
|
|
690
|
+
const name = PSQL_ALIASES[arg] ?? arg;
|
|
691
|
+
const cmd = COMMANDS[name];
|
|
692
|
+
if (!cmd) return { ok: false, message: `Unknown command: /${arg}` };
|
|
693
|
+
const entry = {
|
|
694
|
+
name,
|
|
695
|
+
usage: cmd.usage,
|
|
696
|
+
description: cmd.description,
|
|
697
|
+
psqlAlias: PSQL_REVERSE[name],
|
|
698
|
+
detail: cmd.detail,
|
|
699
|
+
example: cmd.example
|
|
700
|
+
};
|
|
701
|
+
return { ok: true, message: "", helpData: { mode: "detail", entry } };
|
|
702
|
+
}
|
|
703
|
+
const categoryOrder = ["AI", "Schema", "Data", "Aliases", "Session"];
|
|
704
|
+
const groups = categoryOrder.map((cat) => ({
|
|
705
|
+
category: cat,
|
|
706
|
+
entries: Object.entries(COMMANDS).filter(([, cmd]) => cmd.category === cat).map(([name, cmd]) => ({
|
|
707
|
+
name,
|
|
708
|
+
usage: cmd.usage,
|
|
709
|
+
description: cmd.description,
|
|
710
|
+
psqlAlias: PSQL_REVERSE[name]
|
|
711
|
+
}))
|
|
712
|
+
})).filter((g) => g.entries.length > 0);
|
|
713
|
+
return { ok: true, message: "", helpData: { mode: "list", groups } };
|
|
714
|
+
}
|
|
624
715
|
}
|
|
625
716
|
};
|
|
626
717
|
var BUILTIN_COMMAND_LIST = Object.entries(COMMANDS).map(([name, cmd]) => ({
|
|
@@ -1617,11 +1708,14 @@ var ERROR_FG = "#ff4444";
|
|
|
1617
1708
|
function ErrorBox({ message }) {
|
|
1618
1709
|
const cols = Math.max(0, (process.stdout.columns ?? 80) - 2);
|
|
1619
1710
|
const blank = " ".repeat(cols);
|
|
1620
|
-
const
|
|
1621
|
-
const padded = label.length < cols ? label + " ".repeat(cols - label.length) : label;
|
|
1711
|
+
const lines = message.split("\n");
|
|
1622
1712
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
|
|
1623
1713
|
/* @__PURE__ */ jsx3(Text3, { backgroundColor: ERROR_BG, children: blank }),
|
|
1624
|
-
|
|
1714
|
+
lines.map((line, i) => {
|
|
1715
|
+
const content = (i === 0 ? " \u2717 " : " ") + line;
|
|
1716
|
+
const padded = content.length < cols ? content + " ".repeat(cols - content.length) : content;
|
|
1717
|
+
return /* @__PURE__ */ jsx3(Text3, { backgroundColor: ERROR_BG, color: ERROR_FG, bold: true, children: padded }, i);
|
|
1718
|
+
}),
|
|
1625
1719
|
/* @__PURE__ */ jsx3(Text3, { backgroundColor: ERROR_BG, children: blank })
|
|
1626
1720
|
] });
|
|
1627
1721
|
}
|
|
@@ -1757,8 +1851,51 @@ function Banner({ connectionState }) {
|
|
|
1757
1851
|
] });
|
|
1758
1852
|
}
|
|
1759
1853
|
|
|
1854
|
+
// src/ui/components/HelpView.tsx
|
|
1855
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
1856
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1857
|
+
var USAGE_WIDTH = 28;
|
|
1858
|
+
function HelpView({ data }) {
|
|
1859
|
+
if (data.mode === "detail" && data.entry) {
|
|
1860
|
+
const { usage, description, psqlAlias, detail, example } = data.entry;
|
|
1861
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1862
|
+
/* @__PURE__ */ jsxs5(Text5, { bold: true, color: "white", children: [
|
|
1863
|
+
usage,
|
|
1864
|
+
psqlAlias ? ` ${psqlAlias}` : ""
|
|
1865
|
+
] }),
|
|
1866
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { children: description }) }),
|
|
1867
|
+
detail && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: detail }) }),
|
|
1868
|
+
example && /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
1869
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Example: " }),
|
|
1870
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: example })
|
|
1871
|
+
] })
|
|
1872
|
+
] });
|
|
1873
|
+
}
|
|
1874
|
+
if (data.mode === "list" && data.groups) {
|
|
1875
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1876
|
+
data.groups.map((group, gi) => /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: gi < data.groups.length - 1 ? 1 : 0, children: [
|
|
1877
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: theme.accent, children: group.category }),
|
|
1878
|
+
group.entries.map((entry) => /* @__PURE__ */ jsxs5(Box5, { marginLeft: 2, children: [
|
|
1879
|
+
/* @__PURE__ */ jsx5(Text5, { color: "white", children: entry.usage.padEnd(USAGE_WIDTH) }),
|
|
1880
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: entry.description }),
|
|
1881
|
+
entry.psqlAlias && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1882
|
+
" ",
|
|
1883
|
+
entry.psqlAlias
|
|
1884
|
+
] })
|
|
1885
|
+
] }, entry.name))
|
|
1886
|
+
] }, group.category)),
|
|
1887
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1888
|
+
"/help ",
|
|
1889
|
+
"<command>",
|
|
1890
|
+
" for more details."
|
|
1891
|
+
] }) })
|
|
1892
|
+
] });
|
|
1893
|
+
}
|
|
1894
|
+
return null;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1760
1897
|
// src/ui/components/App.tsx
|
|
1761
|
-
import { Fragment as Fragment3, jsx as
|
|
1898
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1762
1899
|
var PAGE_SIZE = 50;
|
|
1763
1900
|
var PLACEHOLDER2 = "#a5b4fc";
|
|
1764
1901
|
function activePageSize() {
|
|
@@ -1775,23 +1912,23 @@ function EntryView({ entry }) {
|
|
|
1775
1912
|
const isShell = entry.query.startsWith("!");
|
|
1776
1913
|
const isCommand = !isShell && (entry.query.startsWith("/") || entry.query.startsWith("\\"));
|
|
1777
1914
|
const label = isShell ? "Shell:" : isCommand ? "Command:" : "Query:";
|
|
1778
|
-
return /* @__PURE__ */
|
|
1779
|
-
/* @__PURE__ */
|
|
1780
|
-
/* @__PURE__ */
|
|
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: [
|
|
1781
1918
|
label,
|
|
1782
1919
|
" "
|
|
1783
1920
|
] }),
|
|
1784
|
-
/* @__PURE__ */
|
|
1921
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: entry.query })
|
|
1785
1922
|
] }),
|
|
1786
|
-
/* @__PURE__ */
|
|
1787
|
-
entry.commandMessage && (entry.commandMessage.ok ? /* @__PURE__ */
|
|
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: [
|
|
1788
1925
|
"\u2713 ",
|
|
1789
1926
|
entry.commandMessage.text
|
|
1790
|
-
] }) : /* @__PURE__ */
|
|
1791
|
-
showAi ? /* @__PURE__ */
|
|
1792
|
-
/* @__PURE__ */
|
|
1793
|
-
/* @__PURE__ */
|
|
1794
|
-
] }) : !entry.commandMessage && /* @__PURE__ */
|
|
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 })
|
|
1795
1932
|
] }) })
|
|
1796
1933
|
] });
|
|
1797
1934
|
}
|
|
@@ -2012,7 +2149,8 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
|
|
|
2012
2149
|
setElapsed(null);
|
|
2013
2150
|
setPage(0);
|
|
2014
2151
|
setQueryState({ status: "idle" });
|
|
2015
|
-
if (result.
|
|
2152
|
+
if (result.helpData) setCommandMessage({ ok: result.ok, text: "", helpData: result.helpData });
|
|
2153
|
+
else if (result.message) setCommandMessage({ ok: result.ok, text: result.message });
|
|
2016
2154
|
else setCommandMessage(null);
|
|
2017
2155
|
return;
|
|
2018
2156
|
}
|
|
@@ -2024,33 +2162,33 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
|
|
|
2024
2162
|
const isShellEntry = lastQuery.startsWith("!");
|
|
2025
2163
|
const isCommand = !isShellEntry && (lastQuery.startsWith("/") || lastQuery.startsWith("\\"));
|
|
2026
2164
|
const activeLabel = isShellEntry ? "Shell:" : isCommand ? "Command:" : "Query:";
|
|
2027
|
-
return /* @__PURE__ */
|
|
2028
|
-
/* @__PURE__ */
|
|
2029
|
-
/* @__PURE__ */
|
|
2030
|
-
lastQuery === "" && /* @__PURE__ */
|
|
2031
|
-
lastQuery !== "" && /* @__PURE__ */
|
|
2032
|
-
/* @__PURE__ */
|
|
2033
|
-
/* @__PURE__ */
|
|
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: [
|
|
2034
2172
|
activeLabel,
|
|
2035
2173
|
" "
|
|
2036
2174
|
] }),
|
|
2037
|
-
/* @__PURE__ */
|
|
2175
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: lastQuery })
|
|
2038
2176
|
] }),
|
|
2039
|
-
/* @__PURE__ */
|
|
2040
|
-
commandMessage && (commandMessage.ok ? /* @__PURE__ */
|
|
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: [
|
|
2041
2179
|
"\u2713 ",
|
|
2042
2180
|
commandMessage.text
|
|
2043
|
-
] }) : /* @__PURE__ */
|
|
2044
|
-
showAi ? /* @__PURE__ */
|
|
2045
|
-
/* @__PURE__ */
|
|
2046
|
-
/* @__PURE__ */
|
|
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: [
|
|
2047
2185
|
aiResponse,
|
|
2048
|
-
isStreaming && /* @__PURE__ */
|
|
2186
|
+
isStreaming && /* @__PURE__ */ jsx6(Text6, { color: PLACEHOLDER2, children: "\u258B" })
|
|
2049
2187
|
] }) })
|
|
2050
|
-
] }) : !commandMessage && /* @__PURE__ */
|
|
2188
|
+
] }) : !commandMessage && /* @__PURE__ */ jsx6(QueryResult, { state: queryState, elapsed, page, pageSize: activePageSize() })
|
|
2051
2189
|
] }) })
|
|
2052
2190
|
] }),
|
|
2053
|
-
isConnected ? /* @__PURE__ */
|
|
2191
|
+
isConnected ? /* @__PURE__ */ jsx6(
|
|
2054
2192
|
QueryInput,
|
|
2055
2193
|
{
|
|
2056
2194
|
onSubmit: handleSubmit,
|
|
@@ -2062,16 +2200,16 @@ function App({ connectionState, aiUrl: aiUrl2, aiModel: aiModel2, aiKey: aiKey2,
|
|
|
2062
2200
|
aliases,
|
|
2063
2201
|
schema: schema ?? void 0
|
|
2064
2202
|
}
|
|
2065
|
-
) : /* @__PURE__ */
|
|
2066
|
-
(vimEnabled || inputIsShell) && /* @__PURE__ */
|
|
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}]` : "" }) })
|
|
2067
2205
|
] })
|
|
2068
2206
|
] });
|
|
2069
2207
|
}
|
|
2070
2208
|
|
|
2071
2209
|
// src/ui/components/ConnectionWizard.tsx
|
|
2072
2210
|
import { useState as useState4 } from "react";
|
|
2073
|
-
import { Box as
|
|
2074
|
-
import { jsx as
|
|
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";
|
|
2075
2213
|
var DRIVERS = ["postgresql", "mysql", "sqlite"];
|
|
2076
2214
|
var LABEL_WIDTH = 10;
|
|
2077
2215
|
function fieldLabels(driver) {
|
|
@@ -2226,36 +2364,36 @@ function ConnectionWizard({ onConnect, initialError }) {
|
|
|
2226
2364
|
{ isActive: isRawModeSupported }
|
|
2227
2365
|
);
|
|
2228
2366
|
const isPassword = (idx) => fields.driver !== "sqlite" && idx === 5;
|
|
2229
|
-
return /* @__PURE__ */
|
|
2230
|
-
/* @__PURE__ */
|
|
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" }) }),
|
|
2231
2369
|
labels.map((label, idx) => {
|
|
2232
2370
|
const isFocused = focus === idx;
|
|
2233
2371
|
const isDriver = idx === 0;
|
|
2234
|
-
return /* @__PURE__ */
|
|
2235
|
-
/* @__PURE__ */
|
|
2236
|
-
isDriver ? /* @__PURE__ */
|
|
2237
|
-
i > 0 && /* @__PURE__ */
|
|
2238
|
-
/* @__PURE__ */
|
|
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: [
|
|
2239
2377
|
fields.driver === d ? "\u25CF " : "\u25CB ",
|
|
2240
2378
|
d.charAt(0).toUpperCase() + d.slice(1)
|
|
2241
2379
|
] })
|
|
2242
|
-
] }, d)) }) : /* @__PURE__ */
|
|
2243
|
-
/* @__PURE__ */
|
|
2244
|
-
isFocused && /* @__PURE__ */
|
|
2245
|
-
isFocused && isPassword(idx) && keychainHint && /* @__PURE__ */
|
|
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)" })
|
|
2246
2384
|
] })
|
|
2247
2385
|
] }, label);
|
|
2248
2386
|
}),
|
|
2249
|
-
/* @__PURE__ */
|
|
2387
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: connecting ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Connecting\u2026" }) : error ? /* @__PURE__ */ jsxs7(Text7, { color: theme.error, children: [
|
|
2250
2388
|
"\u2717 ",
|
|
2251
2389
|
error
|
|
2252
2390
|
] }) : null }),
|
|
2253
|
-
/* @__PURE__ */
|
|
2391
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Tab \xB7 \u2191\u2193 navigate Enter connect \u2190\u2192 cycle driver" }) })
|
|
2254
2392
|
] });
|
|
2255
2393
|
}
|
|
2256
2394
|
|
|
2257
2395
|
// src/index.tsx
|
|
2258
|
-
import { jsx as
|
|
2396
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2259
2397
|
var { values } = parseArgs({
|
|
2260
2398
|
args: process.argv.slice(2).filter((a) => a !== "--"),
|
|
2261
2399
|
options: {
|
|
@@ -2285,10 +2423,10 @@ function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey
|
|
|
2285
2423
|
}
|
|
2286
2424
|
}, []);
|
|
2287
2425
|
if (initialDsn2 && !connectionState && !dsnError) {
|
|
2288
|
-
return /* @__PURE__ */
|
|
2426
|
+
return /* @__PURE__ */ jsx8(Box8, { paddingX: 2, paddingTop: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Connecting\u2026" }) });
|
|
2289
2427
|
}
|
|
2290
2428
|
if (!connectionState) {
|
|
2291
|
-
return /* @__PURE__ */
|
|
2429
|
+
return /* @__PURE__ */ jsx8(ConnectionWizard, { onConnect: setConnectionState, initialError: dsnError ?? void 0 });
|
|
2292
2430
|
}
|
|
2293
2431
|
async function handleChangeDatabase(database) {
|
|
2294
2432
|
if (connectionState.status !== "connected") return;
|
|
@@ -2298,7 +2436,7 @@ function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey
|
|
|
2298
2436
|
const next = await connectParams({ ...current.params, database });
|
|
2299
2437
|
setConnectionState(next);
|
|
2300
2438
|
}
|
|
2301
|
-
return /* @__PURE__ */
|
|
2439
|
+
return /* @__PURE__ */ jsx8(
|
|
2302
2440
|
App,
|
|
2303
2441
|
{
|
|
2304
2442
|
connectionState,
|
|
@@ -2312,6 +2450,6 @@ function Root({ initialDsn: initialDsn2, aiUrl: aiUrl2, aiModel: aiModel2, aiKey
|
|
|
2312
2450
|
);
|
|
2313
2451
|
}
|
|
2314
2452
|
render(
|
|
2315
|
-
/* @__PURE__ */
|
|
2453
|
+
/* @__PURE__ */ jsx8(Root, { initialDsn, aiUrl, aiModel, aiKey }),
|
|
2316
2454
|
{ exitOnCtrlC: true }
|
|
2317
2455
|
);
|