@kaynooo/cli-docker 0.7.0 → 0.8.0

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/dc-cli.js CHANGED
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
627
627
  function jsonOption(holds) {
628
628
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
629
629
  }
630
-
631
630
  class Exit extends Error {
632
631
  code;
633
632
  hint;
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
1032
1031
  `).at(-1) ?? "";
1033
1032
  return last === "" ? `exit ${code}` : last;
1034
1033
  }
1034
+ // ../kit/src/folders.ts
1035
+ var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
1035
1036
  // ../kit/src/secrets.ts
1036
1037
  var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
1037
1038
  var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
@@ -1404,13 +1405,12 @@ class Redactor {
1404
1405
  var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
1405
1406
  var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
1406
1407
  var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
1407
- var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
1408
1408
  var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
1409
1409
  function from(dir, command) {
1410
1410
  return dir === "." ? command : ["env", "-C", dir, ...command];
1411
1411
  }
1412
1412
  function children(dir = ".") {
1413
- return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED2.has(child.name)).map((child) => child.name).sort();
1413
+ return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
1414
1414
  }
1415
1415
  function up(dir) {
1416
1416
  const dirs = [];
package/dist/dlog-cli.js CHANGED
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
627
627
  function jsonOption(holds) {
628
628
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
629
629
  }
630
-
631
630
  class Exit extends Error {
632
631
  code;
633
632
  hint;
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
1032
1031
  `).at(-1) ?? "";
1033
1032
  return last === "" ? `exit ${code}` : last;
1034
1033
  }
1034
+ // ../kit/src/folders.ts
1035
+ var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
1035
1036
  // ../kit/src/secrets.ts
1036
1037
  var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
1037
1038
  var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
@@ -1404,13 +1405,12 @@ class Redactor {
1404
1405
  var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
1405
1406
  var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
1406
1407
  var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
1407
- var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
1408
1408
  var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
1409
1409
  function from(dir, command) {
1410
1410
  return dir === "." ? command : ["env", "-C", dir, ...command];
1411
1411
  }
1412
1412
  function children(dir = ".") {
1413
- return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED2.has(child.name)).map((child) => child.name).sort();
1413
+ return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
1414
1414
  }
1415
1415
  function up(dir) {
1416
1416
  const dirs = [];
package/dist/dx-cli.js CHANGED
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
627
627
  function jsonOption(holds) {
628
628
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
629
629
  }
630
-
631
630
  class Exit extends Error {
632
631
  code;
633
632
  hint;
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
1032
1031
  `).at(-1) ?? "";
1033
1032
  return last === "" ? `exit ${code}` : last;
1034
1033
  }
1034
+ // ../kit/src/folders.ts
1035
+ var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
1035
1036
  // ../kit/src/secrets.ts
1036
1037
  var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
1037
1038
  var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
@@ -1503,6 +1504,14 @@ var KEYS = {
1503
1504
  "\x1B[B": "down",
1504
1505
  "\x1BOB": "down",
1505
1506
  "\x0E": "down",
1507
+ "\x1B[5~": "pageup",
1508
+ "\x1B[6~": "pagedown",
1509
+ "\x1B[H": "home",
1510
+ "\x1B[1~": "home",
1511
+ "\x1BOH": "home",
1512
+ "\x1B[F": "end",
1513
+ "\x1B[4~": "end",
1514
+ "\x1BOF": "end",
1506
1515
  "\r": "enter",
1507
1516
  "\n": "enter",
1508
1517
  "\x03": "cancel",
@@ -1511,7 +1520,10 @@ var KEYS = {
1511
1520
  "\b": "erase",
1512
1521
  "\x15": "clear",
1513
1522
  "\x17": "word",
1514
- "\t": "tab"
1523
+ "\t": "tab",
1524
+ "\x1F": "help",
1525
+ "\x1BOP": "help",
1526
+ "\x1B[11~": "help"
1515
1527
  };
1516
1528
  var CTRL_KEY = /^ctrl-[abd-gkln-z]$/;
1517
1529
  function repeat(value, count) {
@@ -1581,7 +1593,7 @@ function parseKeys(input, paste = { active: false }, ctrls = []) {
1581
1593
  });
1582
1594
  }
1583
1595
  function createState(rows, { multi = false, prompt = "> ", query = "", header = "", expect = [], searched = Number.POSITIVE_INFINITY } = {}) {
1584
- const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, marked: new Set, multi, prompt, done: undefined };
1596
+ const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, help: false, page: 10, marked: new Set, multi, prompt, done: undefined };
1585
1597
  return withRows(state, rows);
1586
1598
  }
1587
1599
  function withRows(state, rows, header = state.header) {
@@ -1603,6 +1615,8 @@ function highlighted(state) {
1603
1615
  function press(state, key) {
1604
1616
  if (state.done !== undefined)
1605
1617
  return state;
1618
+ if (state.help)
1619
+ return { ...state, help: false };
1606
1620
  if (typeof key === "object") {
1607
1621
  if ("text" in key)
1608
1622
  return withQuery(state, state.query + key.text);
@@ -1613,8 +1627,18 @@ function press(state, key) {
1613
1627
  return move(state, -1);
1614
1628
  case "down":
1615
1629
  return move(state, 1);
1630
+ case "pageup":
1631
+ return move(state, -state.page);
1632
+ case "pagedown":
1633
+ return move(state, state.page);
1634
+ case "home":
1635
+ return move(state, -state.matches.length);
1636
+ case "end":
1637
+ return move(state, state.matches.length);
1616
1638
  case "cancel":
1617
1639
  return { ...state, done: "cancel" };
1640
+ case "help":
1641
+ return { ...state, help: true };
1618
1642
  case "erase":
1619
1643
  return withQuery(state, [...state.query].slice(0, -1).join(""));
1620
1644
  case "clear":
@@ -1794,7 +1818,18 @@ function fitStyled(line, width, plain = identity) {
1794
1818
  function caretColumn(state, layout) {
1795
1819
  return Math.min(layout.listWidth, Bun.stringWidth(state.prompt) + Bun.stringWidth(state.query) + 1);
1796
1820
  }
1821
+ function helpLines(state, layout, theme) {
1822
+ const lines = [
1823
+ "type to filter, up, down, ctrl-p, ctrl-n, pgup, pgdn, home, end move",
1824
+ `enter picks${state.multi ? ", tab marks" : ""}, ctrl-u clears the query, ctrl-w a word, esc or ctrl-c cancels`,
1825
+ ...state.expect.length > 0 ? [`${state.expect.join(", ")} end the pick as this command asks`] : []
1826
+ ];
1827
+ const title = "keys, any key closes this help";
1828
+ return [{ text: theme.accent(title), width: title.length }, ...lines.map((line) => fit(` ${line}`, layout.listWidth, theme.label))];
1829
+ }
1797
1830
  function listLines(state, layout, theme) {
1831
+ if (state.help)
1832
+ return helpLines(state, layout, theme);
1798
1833
  const header = state.header === "" ? [] : state.header.split(`
1799
1834
  `).map((line) => fitStyled(line, layout.listWidth, theme.label));
1800
1835
  const rows = Math.max(1, layout.listHeight - 2 - header.length);
@@ -1803,7 +1838,8 @@ function listLines(state, layout, theme) {
1803
1838
  const marks = (state.marked.size > 0 ? ` (${state.marked.size} marked)` : "") + (state.status === "" ? "" : ` ${state.status}`);
1804
1839
  const promptWidth = Bun.stringWidth(state.prompt);
1805
1840
  const query = fit(state.query, Math.max(1, layout.listWidth - promptWidth), identity);
1806
- const count = fit(` ${state.matches.length}/${state.items.length}${marks}`, layout.listWidth, theme.label);
1841
+ const place = state.matches.length > rows ? ` (${state.cursor + 1} of ${state.matches.length})` : "";
1842
+ const count = fit(` ${state.matches.length}/${state.items.length}${place}${marks}`, layout.listWidth, theme.label);
1807
1843
  const lines = [{ text: theme.accent(state.prompt) + query.text, width: promptWidth + query.width }, count, ...header];
1808
1844
  if (state.matches.length === 0)
1809
1845
  lines.push(fit(" no match", layout.listWidth, theme.label));
@@ -1830,6 +1866,7 @@ function render(state, layout, theme, preview = []) {
1830
1866
 
1831
1867
  // ../kit/src/picker/terminal.ts
1832
1868
  var PREVIEW_DELAY_MS = 120;
1869
+ var ESCAPE_WAIT_MS = 30;
1833
1870
  var OPEN = { ctrl: "open" };
1834
1871
  var HIDE_CURSOR = "\x1B[?25l";
1835
1872
  var SHOW_CURSOR = "\x1B[?25h";
@@ -1885,20 +1922,29 @@ async function select(rows, settings, terminal = openTerminal()) {
1885
1922
  let shownLine;
1886
1923
  let finish;
1887
1924
  let restored = false;
1925
+ let escape;
1926
+ let caret;
1888
1927
  function draw(clear = false) {
1889
1928
  if (restored)
1890
1929
  return;
1891
1930
  const layout = layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition);
1892
1931
  let frame = HIDE_CURSOR;
1893
1932
  if (reserved !== layout.height || clear) {
1933
+ frame += clear && caret !== undefined ? up(Math.floor((caret - 1) / terminal.columns)) : "";
1894
1934
  frame += CLEAR_BELOW + `
1895
1935
  `.repeat(layout.height - 1) + up(layout.height - 1);
1896
1936
  reserved = layout.height;
1897
1937
  }
1938
+ caret = caretColumn(state, layout);
1898
1939
  frame += `\r${render(state, layout, theme, preview).map((line) => `\x1B[2K${line}`).join(`
1899
- `)}${up(layout.height - 1)}\r\x1B[${caretColumn(state, layout)}G${SHOW_CURSOR}`;
1940
+ `)}${up(layout.height - 1)}\r\x1B[${caret}G${SHOW_CURSOR}`;
1900
1941
  terminal.write(frame);
1901
1942
  }
1943
+ function pageSize() {
1944
+ const headerLines = state.header === "" ? 0 : state.header.split(`
1945
+ `).length;
1946
+ return Math.max(1, layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition).listHeight - 2 - headerLines);
1947
+ }
1902
1948
  function refreshPreview() {
1903
1949
  const at = highlighted(state);
1904
1950
  if (settings.preview === undefined || at === shownLine)
@@ -1928,6 +1974,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1928
1974
  return;
1929
1975
  restored = true;
1930
1976
  clearTimeout(previewTimer);
1977
+ clearTimeout(escape);
1931
1978
  previewing.abort();
1932
1979
  terminal.write(`${CLEAR_BELOW}${PASTE_OFF}${SHOW_CURSOR}`);
1933
1980
  terminal.close();
@@ -1966,7 +2013,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1966
2013
  if (key === OPEN || typeof key === "object" && "ctrl" in key && key.ctrl === reload?.key)
1967
2014
  await reloadRows();
1968
2015
  else
1969
- state = press(state, key);
2016
+ state = press({ ...state, page: pageSize() }, key);
1970
2017
  }
1971
2018
  } finally {
1972
2019
  draining = false;
@@ -1979,6 +2026,20 @@ async function select(rows, settings, terminal = openTerminal()) {
1979
2026
  draw();
1980
2027
  }
1981
2028
  function onChunk(chunk) {
2029
+ if (escape !== undefined) {
2030
+ clearTimeout(escape);
2031
+ escape = undefined;
2032
+ chunk = `\x1B${chunk}`;
2033
+ } else if (chunk === "\x1B") {
2034
+ escape = setTimeout(() => {
2035
+ escape = undefined;
2036
+ takeKeys("\x1B");
2037
+ }, ESCAPE_WAIT_MS);
2038
+ return;
2039
+ }
2040
+ takeKeys(chunk);
2041
+ }
2042
+ function takeKeys(chunk) {
1982
2043
  const keys = parseKeys(chunk, paste, [...settings.expect ?? [], ...reload?.key === undefined ? [] : [reload.key]]);
1983
2044
  if (busy && keys.includes("cancel")) {
1984
2045
  stop();
@@ -2092,13 +2153,12 @@ import { dirname as dirname6, join as join8, relative, resolve as resolve3 } fro
2092
2153
  var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
2093
2154
  var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
2094
2155
  var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
2095
- var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
2096
2156
  var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
2097
2157
  function from(dir, command) {
2098
2158
  return dir === "." ? command : ["env", "-C", dir, ...command];
2099
2159
  }
2100
2160
  function children(dir = ".") {
2101
- return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED2.has(child.name)).map((child) => child.name).sort();
2161
+ return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
2102
2162
  }
2103
2163
  function up2(dir) {
2104
2164
  const dirs = [];
package/dist/sf-cli.js CHANGED
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
627
627
  function jsonOption(holds) {
628
628
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
629
629
  }
630
-
631
630
  class Exit extends Error {
632
631
  code;
633
632
  hint;
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
1032
1031
  `).at(-1) ?? "";
1033
1032
  return last === "" ? `exit ${code}` : last;
1034
1033
  }
1034
+ // ../kit/src/folders.ts
1035
+ var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
1035
1036
  // ../kit/src/secrets.ts
1036
1037
  var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
1037
1038
  var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
@@ -1156,6 +1157,14 @@ var KEYS = {
1156
1157
  "\x1B[B": "down",
1157
1158
  "\x1BOB": "down",
1158
1159
  "\x0E": "down",
1160
+ "\x1B[5~": "pageup",
1161
+ "\x1B[6~": "pagedown",
1162
+ "\x1B[H": "home",
1163
+ "\x1B[1~": "home",
1164
+ "\x1BOH": "home",
1165
+ "\x1B[F": "end",
1166
+ "\x1B[4~": "end",
1167
+ "\x1BOF": "end",
1159
1168
  "\r": "enter",
1160
1169
  "\n": "enter",
1161
1170
  "\x03": "cancel",
@@ -1164,7 +1173,10 @@ var KEYS = {
1164
1173
  "\b": "erase",
1165
1174
  "\x15": "clear",
1166
1175
  "\x17": "word",
1167
- "\t": "tab"
1176
+ "\t": "tab",
1177
+ "\x1F": "help",
1178
+ "\x1BOP": "help",
1179
+ "\x1B[11~": "help"
1168
1180
  };
1169
1181
  var CTRL_KEY = /^ctrl-[abd-gkln-z]$/;
1170
1182
  function repeat(value, count) {
@@ -1234,7 +1246,7 @@ function parseKeys(input, paste = { active: false }, ctrls = []) {
1234
1246
  });
1235
1247
  }
1236
1248
  function createState(rows, { multi = false, prompt = "> ", query = "", header = "", expect = [], searched = Number.POSITIVE_INFINITY } = {}) {
1237
- const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, marked: new Set, multi, prompt, done: undefined };
1249
+ const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, help: false, page: 10, marked: new Set, multi, prompt, done: undefined };
1238
1250
  return withRows(state, rows);
1239
1251
  }
1240
1252
  function withRows(state, rows, header = state.header) {
@@ -1256,6 +1268,8 @@ function highlighted(state) {
1256
1268
  function press(state, key) {
1257
1269
  if (state.done !== undefined)
1258
1270
  return state;
1271
+ if (state.help)
1272
+ return { ...state, help: false };
1259
1273
  if (typeof key === "object") {
1260
1274
  if ("text" in key)
1261
1275
  return withQuery(state, state.query + key.text);
@@ -1266,8 +1280,18 @@ function press(state, key) {
1266
1280
  return move(state, -1);
1267
1281
  case "down":
1268
1282
  return move(state, 1);
1283
+ case "pageup":
1284
+ return move(state, -state.page);
1285
+ case "pagedown":
1286
+ return move(state, state.page);
1287
+ case "home":
1288
+ return move(state, -state.matches.length);
1289
+ case "end":
1290
+ return move(state, state.matches.length);
1269
1291
  case "cancel":
1270
1292
  return { ...state, done: "cancel" };
1293
+ case "help":
1294
+ return { ...state, help: true };
1271
1295
  case "erase":
1272
1296
  return withQuery(state, [...state.query].slice(0, -1).join(""));
1273
1297
  case "clear":
@@ -1447,7 +1471,18 @@ function fitStyled(line, width, plain = identity) {
1447
1471
  function caretColumn(state, layout) {
1448
1472
  return Math.min(layout.listWidth, Bun.stringWidth(state.prompt) + Bun.stringWidth(state.query) + 1);
1449
1473
  }
1474
+ function helpLines(state, layout, theme) {
1475
+ const lines = [
1476
+ "type to filter, up, down, ctrl-p, ctrl-n, pgup, pgdn, home, end move",
1477
+ `enter picks${state.multi ? ", tab marks" : ""}, ctrl-u clears the query, ctrl-w a word, esc or ctrl-c cancels`,
1478
+ ...state.expect.length > 0 ? [`${state.expect.join(", ")} end the pick as this command asks`] : []
1479
+ ];
1480
+ const title = "keys, any key closes this help";
1481
+ return [{ text: theme.accent(title), width: title.length }, ...lines.map((line) => fit(` ${line}`, layout.listWidth, theme.label))];
1482
+ }
1450
1483
  function listLines(state, layout, theme) {
1484
+ if (state.help)
1485
+ return helpLines(state, layout, theme);
1451
1486
  const header = state.header === "" ? [] : state.header.split(`
1452
1487
  `).map((line) => fitStyled(line, layout.listWidth, theme.label));
1453
1488
  const rows = Math.max(1, layout.listHeight - 2 - header.length);
@@ -1456,7 +1491,8 @@ function listLines(state, layout, theme) {
1456
1491
  const marks = (state.marked.size > 0 ? ` (${state.marked.size} marked)` : "") + (state.status === "" ? "" : ` ${state.status}`);
1457
1492
  const promptWidth = Bun.stringWidth(state.prompt);
1458
1493
  const query = fit(state.query, Math.max(1, layout.listWidth - promptWidth), identity);
1459
- const count = fit(` ${state.matches.length}/${state.items.length}${marks}`, layout.listWidth, theme.label);
1494
+ const place = state.matches.length > rows ? ` (${state.cursor + 1} of ${state.matches.length})` : "";
1495
+ const count = fit(` ${state.matches.length}/${state.items.length}${place}${marks}`, layout.listWidth, theme.label);
1460
1496
  const lines = [{ text: theme.accent(state.prompt) + query.text, width: promptWidth + query.width }, count, ...header];
1461
1497
  if (state.matches.length === 0)
1462
1498
  lines.push(fit(" no match", layout.listWidth, theme.label));
@@ -1483,6 +1519,7 @@ function render(state, layout, theme, preview = []) {
1483
1519
 
1484
1520
  // ../kit/src/picker/terminal.ts
1485
1521
  var PREVIEW_DELAY_MS = 120;
1522
+ var ESCAPE_WAIT_MS = 30;
1486
1523
  var OPEN = { ctrl: "open" };
1487
1524
  var HIDE_CURSOR = "\x1B[?25l";
1488
1525
  var SHOW_CURSOR = "\x1B[?25h";
@@ -1538,20 +1575,29 @@ async function select(rows, settings, terminal = openTerminal()) {
1538
1575
  let shownLine;
1539
1576
  let finish;
1540
1577
  let restored = false;
1578
+ let escape;
1579
+ let caret;
1541
1580
  function draw(clear = false) {
1542
1581
  if (restored)
1543
1582
  return;
1544
1583
  const layout = layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition);
1545
1584
  let frame = HIDE_CURSOR;
1546
1585
  if (reserved !== layout.height || clear) {
1586
+ frame += clear && caret !== undefined ? up(Math.floor((caret - 1) / terminal.columns)) : "";
1547
1587
  frame += CLEAR_BELOW + `
1548
1588
  `.repeat(layout.height - 1) + up(layout.height - 1);
1549
1589
  reserved = layout.height;
1550
1590
  }
1591
+ caret = caretColumn(state, layout);
1551
1592
  frame += `\r${render(state, layout, theme, preview).map((line) => `\x1B[2K${line}`).join(`
1552
- `)}${up(layout.height - 1)}\r\x1B[${caretColumn(state, layout)}G${SHOW_CURSOR}`;
1593
+ `)}${up(layout.height - 1)}\r\x1B[${caret}G${SHOW_CURSOR}`;
1553
1594
  terminal.write(frame);
1554
1595
  }
1596
+ function pageSize() {
1597
+ const headerLines = state.header === "" ? 0 : state.header.split(`
1598
+ `).length;
1599
+ return Math.max(1, layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition).listHeight - 2 - headerLines);
1600
+ }
1555
1601
  function refreshPreview() {
1556
1602
  const at = highlighted(state);
1557
1603
  if (settings.preview === undefined || at === shownLine)
@@ -1581,6 +1627,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1581
1627
  return;
1582
1628
  restored = true;
1583
1629
  clearTimeout(previewTimer);
1630
+ clearTimeout(escape);
1584
1631
  previewing.abort();
1585
1632
  terminal.write(`${CLEAR_BELOW}${PASTE_OFF}${SHOW_CURSOR}`);
1586
1633
  terminal.close();
@@ -1619,7 +1666,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1619
1666
  if (key === OPEN || typeof key === "object" && "ctrl" in key && key.ctrl === reload?.key)
1620
1667
  await reloadRows();
1621
1668
  else
1622
- state = press(state, key);
1669
+ state = press({ ...state, page: pageSize() }, key);
1623
1670
  }
1624
1671
  } finally {
1625
1672
  draining = false;
@@ -1632,6 +1679,20 @@ async function select(rows, settings, terminal = openTerminal()) {
1632
1679
  draw();
1633
1680
  }
1634
1681
  function onChunk(chunk) {
1682
+ if (escape !== undefined) {
1683
+ clearTimeout(escape);
1684
+ escape = undefined;
1685
+ chunk = `\x1B${chunk}`;
1686
+ } else if (chunk === "\x1B") {
1687
+ escape = setTimeout(() => {
1688
+ escape = undefined;
1689
+ takeKeys("\x1B");
1690
+ }, ESCAPE_WAIT_MS);
1691
+ return;
1692
+ }
1693
+ takeKeys(chunk);
1694
+ }
1695
+ function takeKeys(chunk) {
1635
1696
  const keys = parseKeys(chunk, paste, [...settings.expect ?? [], ...reload?.key === undefined ? [] : [reload.key]]);
1636
1697
  if (busy && keys.includes("cancel")) {
1637
1698
  stop();
@@ -1738,11 +1799,9 @@ function list(rows) {
1738
1799
 
1739
1800
  // ../kit/src/prompt.ts
1740
1801
  import process16 from "process";
1741
- var KEY_SEQUENCES2 = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
1742
- var CONTROL3 = /^[\x00-\x1F\x7F]/;
1743
- var KEYS2 = { "\x1B[A": "up", "\x1BOA": "up", "\x1B[B": "down", "\x1BOB": "down", "\r": "enter", "\n": "enter", "\x03": "cancel", "\x1B": "cancel", "\x7F": "erase", "\b": "erase" };
1744
- function readKeys(input) {
1745
- return (input.match(KEY_SEQUENCES2) ?? []).flatMap((sequence) => KEYS2[sequence] ?? (CONTROL3.test(sequence) ? [] : [{ text: sequence }]));
1802
+ var PROMPT_KEYS = new Set(["up", "down", "pageup", "pagedown", "home", "end", "enter", "cancel", "erase"]);
1803
+ function readKeys(input, paste = { active: false }) {
1804
+ return parseKeys(input, paste).filter((key) => typeof key === "object" ? "text" in key : PROMPT_KEYS.has(key));
1746
1805
  }
1747
1806
  async function input2(question, { default: fallback, mask = false, validate, ...settings } = {}) {
1748
1807
  let value = "";
@@ -1781,22 +1840,29 @@ async function ask(question, { hint, terminal = process16 }, render, press) {
1781
1840
  if (!isInteractive(terminal))
1782
1841
  fail(`cannot ask "${question}" off a terminal`, 2, hint);
1783
1842
  const colors = paint(stderr);
1784
- let drawn = 0;
1843
+ let drawn = [];
1844
+ let current = "asking";
1785
1845
  let outcome;
1846
+ const columns = () => stderr.columns || 80;
1786
1847
  const draw = (state) => {
1787
- const frame = render(colors, state, { columns: stderr.columns || 80, rows: stderr.rows || 24 });
1788
- stderr.write(`${drawn > 1 ? `\x1B[${drawn - 1}A` : ""}\r\x1B[0J${frame.join(`
1848
+ const frame = render(colors, state, { columns: columns(), rows: stderr.rows || 24 });
1849
+ const rows = drawn.reduce((sum, line) => sum + Math.max(1, Math.ceil(Bun.stringWidth(line) / columns())), 0);
1850
+ stderr.write(`${rows > 1 ? `\x1B[${rows - 1}A` : ""}\r\x1B[0J${frame.join(`
1789
1851
  `)}`);
1790
- drawn = frame.length;
1852
+ drawn = frame;
1853
+ current = state;
1791
1854
  };
1855
+ const redraw = () => draw(current);
1856
+ const paste = { active: false };
1792
1857
  try {
1858
+ stderr.on?.("resize", redraw);
1793
1859
  stdin.setRawMode(true);
1794
1860
  stdin.resume();
1795
- stderr.write("\x1B[?25l");
1861
+ stderr.write("\x1B[?25l\x1B[?2004h");
1796
1862
  draw("asking");
1797
1863
  return await new Promise((resolve) => {
1798
1864
  function onData(data) {
1799
- for (const key of readKeys(data.toString())) {
1865
+ for (const key of readKeys(data.toString(), paste)) {
1800
1866
  const pressed = press(key);
1801
1867
  if (pressed !== undefined) {
1802
1868
  settle(pressed);
@@ -1818,9 +1884,10 @@ async function ask(question, { hint, terminal = process16 }, render, press) {
1818
1884
  stdin.on("end", onEnd);
1819
1885
  });
1820
1886
  } finally {
1887
+ stderr.off?.("resize", redraw);
1821
1888
  draw(outcome ?? "cancel");
1822
1889
  stderr.write(`
1823
- \x1B[?25h`);
1890
+ \x1B[?25h\x1B[?2004l`);
1824
1891
  stdin.setRawMode(false);
1825
1892
  stdin.pause();
1826
1893
  }
@@ -2184,13 +2251,12 @@ class Redactor {
2184
2251
  var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
2185
2252
  var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
2186
2253
  var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
2187
- var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
2188
2254
  var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
2189
2255
  function from(dir, command) {
2190
2256
  return dir === "." ? command : ["env", "-C", dir, ...command];
2191
2257
  }
2192
2258
  function children(dir = ".") {
2193
- return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED2.has(child.name)).map((child) => child.name).sort();
2259
+ return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
2194
2260
  }
2195
2261
  function up2(dir) {
2196
2262
  const dirs = [];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kaynooo/cli-docker",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.8.0",
5
5
  "description": "dc, dx, dlog and sf: docker compose on the stack found here, including one only its containers tell of, a compose service's shell, logs, restart, stop or rebuild, every service's health and recent errors in one call, and a Symfony console command picked and run.",
6
6
  "author": "Kaynooo <contact@kaynooo.fr>",
7
7
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "sf": "$npm_execpath src/sf-cli.ts"
38
38
  },
39
39
  "devDependencies": {
40
- "@kaynooo/cli-env": "0.11.0",
41
- "@kaynooo/cli-kit": "0.22.0"
40
+ "@kaynooo/cli-env": "0.12.0",
41
+ "@kaynooo/cli-kit": "0.26.0"
42
42
  }
43
43
  }
package/src/stack.ts CHANGED
@@ -3,7 +3,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs'
3
3
  import { dirname, join, relative, resolve } from 'node:path'
4
4
  import { loadKnown } from '@kaynooo/cli-env/files'
5
5
  import { Redactor } from '@kaynooo/cli-env/redactor'
6
- import { captureOutput, composeError } from '@kaynooo/cli-kit'
6
+ import { captureOutput, composeError, SKIPPED_FOLDERS } from '@kaynooo/cli-kit'
7
7
 
8
8
  /** How to reach one compose project: the command a user runs, and the one this package lists it with. */
9
9
  export interface Stack {
@@ -54,7 +54,6 @@ export interface Container {
54
54
  const LABEL_FORMAT = ['project', 'project.config_files', 'project.environment_file', 'project.working_dir'].map(label => `{{.Label "com.docker.compose.${label}"}}`).join('\t')
55
55
  const COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m
56
56
  const COMPOSE_FILES = ['compose.yaml', 'compose.yml', 'docker-compose.yaml', 'docker-compose.yml']
57
- const SKIPPED = new Set(['node_modules', 'vendor', 'dist', 'build', 'target', 'venv', '__pycache__'])
58
57
 
59
58
  export const ACTIONS: Action[] = ['shell', 'logs', 'restart', 'up', 'stop', 'build']
60
59
 
@@ -66,7 +65,7 @@ export function from(dir: string, command: string[]): string[] {
66
65
  /** The direct child folders, hidden and built ones aside. */
67
66
  export function children(dir = '.'): string[] {
68
67
  return readdirSync(dir, { withFileTypes: true })
69
- .filter(child => child.isDirectory() && !child.name.startsWith('.') && !SKIPPED.has(child.name))
68
+ .filter(child => child.isDirectory() && !child.name.startsWith('.') && !SKIPPED_FOLDERS.has(child.name))
70
69
  .map(child => child.name)
71
70
  .sort()
72
71
  }