@tiens.nguyen/gu-cli 1.0.877 → 1.0.878
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/gu-repl.mjs +16 -7
- package/package.json +1 -1
package/gu-repl.mjs
CHANGED
|
@@ -568,6 +568,10 @@ const fmtTokens = (n) => {
|
|
|
568
568
|
* naming it here would imply the picker is something you have to break out of.
|
|
569
569
|
*/
|
|
570
570
|
const PICK_HINT = "↑/↓ then Enter · Esc to cancel";
|
|
571
|
+
// #249: a list whose last rows are orange actions says Esc UNDER them (pickFromList's footer);
|
|
572
|
+
// its header keeps only the arrows, so the instruction is not given twice.
|
|
573
|
+
const PICK_ARROWS = "↑/↓ then Enter";
|
|
574
|
+
const ESC_FOOTER = "Esc to cancel";
|
|
571
575
|
const PICK_MANY_HINT = "↑/↓ · space toggles · ctrl+a all · Enter saves · Esc cancels";
|
|
572
576
|
// How many EXTRA models one server offers alongside its default. Task #245 made it a rule, not a
|
|
573
577
|
// readability cap: a server URL offers AT MOST 3 models, default included (MAX_MODELS_PER_SERVER
|
|
@@ -7157,8 +7161,8 @@ async function chooseModel() {
|
|
|
7157
7161
|
// enough in name to have already been mixed up. The fix is to say which one THIS is — not to
|
|
7158
7162
|
// print the other two here: the agent model and the embedder are independent settings with
|
|
7159
7163
|
// their own commands, and listing them together implies a relationship that does not exist.
|
|
7160
|
-
console.log(dim(` Choose the CODING model — the one that writes code (${
|
|
7161
|
-
const idx = await pickFromList(labels, activeIdx >= 0 ? activeIdx : 0);
|
|
7164
|
+
console.log(dim(` Choose the CODING model — the one that writes code (${PICK_ARROWS}):`));
|
|
7165
|
+
const idx = await pickFromList(labels, activeIdx >= 0 ? activeIdx : 0, { footer: ESC_FOOTER });
|
|
7162
7166
|
if (idx < 0) {
|
|
7163
7167
|
console.log(dim(" (cancelled)\n"));
|
|
7164
7168
|
return;
|
|
@@ -8883,12 +8887,12 @@ async function chooseEmbedModel() {
|
|
|
8883
8887
|
const ADD = labels.length;
|
|
8884
8888
|
labels.push(actionRow("add a provider — OpenAI, Ollama…"));
|
|
8885
8889
|
const head = ` Embedding model (now ${current} on ${host || currentUrl})`;
|
|
8886
|
-
const hint = `(${
|
|
8890
|
+
const hint = `(${PICK_ARROWS}):`;
|
|
8887
8891
|
const cols = Math.max(20, process.stdout.columns || 80);
|
|
8888
8892
|
console.log(dim(head.length + 2 + hint.length < cols ? `${head} ${hint}` : `${head}\n ${hint}`));
|
|
8889
8893
|
// The cursor starts on the one in use: the set one, else the default.
|
|
8890
8894
|
const inUse = rows.findIndex((r) => ec.sameEmbedModel(r.model, current));
|
|
8891
|
-
const mi = await pickFromList(labels, Math.max(0, inUse));
|
|
8895
|
+
const mi = await pickFromList(labels, Math.max(0, inUse), { footer: ESC_FOOTER });
|
|
8892
8896
|
if (mi < 0) {
|
|
8893
8897
|
console.log(dim(" (cancelled)\n"));
|
|
8894
8898
|
return;
|
|
@@ -9078,10 +9082,10 @@ async function chooseAgentModel() {
|
|
|
9078
9082
|
labels.push(actionRow("add a provider — Claude API, OpenAI, Kimi, Ollama…"));
|
|
9079
9083
|
// The hint drops to its own row when the header would wrap (80 columns over SSH).
|
|
9080
9084
|
const head = ` Agent model — the one that TALKS (now ${current || "unset"}${curHost ? ` on ${curHost}` : ""})`;
|
|
9081
|
-
const hint = `(${
|
|
9085
|
+
const hint = `(${PICK_ARROWS}):`;
|
|
9082
9086
|
const cols = Math.max(20, process.stdout.columns || 80);
|
|
9083
9087
|
console.log(dim(head.length + 2 + hint.length < cols ? `${head} ${hint}` : `${head}\n ${hint}`));
|
|
9084
|
-
const idx = await pickFromList(labels, Math.max(0, saved.findIndex((c) => c.current)));
|
|
9088
|
+
const idx = await pickFromList(labels, Math.max(0, saved.findIndex((c) => c.current)), { footer: ESC_FOOTER });
|
|
9085
9089
|
if (idx < 0) {
|
|
9086
9090
|
console.log(dim(" (cancelled)\n"));
|
|
9087
9091
|
return;
|
|
@@ -11245,6 +11249,8 @@ let listChecked = new Set();
|
|
|
11245
11249
|
// arrive after the choosing is over.
|
|
11246
11250
|
let listMax = 0;
|
|
11247
11251
|
let listNote = "";
|
|
11252
|
+
/** #249: a dim line under the rows (pickFromList's `footer`), drawn and erased with the block. */
|
|
11253
|
+
let listFooter = "";
|
|
11248
11254
|
// When true, ctrl+d in a single-select picker finishes with a DELETE request for the
|
|
11249
11255
|
// highlighted row instead of choosing it (task #222). Off unless a caller asks for it, so no
|
|
11250
11256
|
// existing picker gains a destructive key by accident.
|
|
@@ -11274,7 +11280,7 @@ function drawList(redraw) {
|
|
|
11274
11280
|
// provider's /v1/models was taller than the terminal: the screen scrolled, the highlighted row
|
|
11275
11281
|
// went off the top, and the ESC[nA walk below then counted rows that had moved — "cannot scroll
|
|
11276
11282
|
// up or down, stuck here". Two rows of margin keep the block clear of the line above it.
|
|
11277
|
-
const maxBlock = Math.max(5, viewportHeight() - 2) - (listNote ? 1 : 0) - (listHeader ? listHeaderRows : 0);
|
|
11283
|
+
const maxBlock = Math.max(5, viewportHeight() - 2) - (listNote ? 1 : 0) - (listFooter ? 1 : 0) - (listHeader ? listHeaderRows : 0);
|
|
11278
11284
|
const win = listWindow(listItems.length, listSel, listTop, maxBlock);
|
|
11279
11285
|
listTop = win.top;
|
|
11280
11286
|
const row = (i) => {
|
|
@@ -11302,6 +11308,7 @@ function drawList(redraw) {
|
|
|
11302
11308
|
// The cap's message rides UNDER the list, in the same block, so it is erased and redrawn with
|
|
11303
11309
|
// it — a note printed into the scrolling output would stay on screen after the list is gone.
|
|
11304
11310
|
if (listNote) rows.push(clipVisible(GUTTER + yellow(listNote), width - 1));
|
|
11311
|
+
if (listFooter) rows.push(clipVisible(GUTTER + dim(listFooter), width - 1));
|
|
11305
11312
|
// On a redraw the caret sits one row BELOW the block (each draw ends with "\n").
|
|
11306
11313
|
const up = redraw ? physicalRows(listRowsDrawn, width) : 0;
|
|
11307
11314
|
// #321: what the screen held under the picker when it opened, and how far it reaches — so the
|
|
@@ -11346,6 +11353,7 @@ function finishList(index) {
|
|
|
11346
11353
|
listChecked = new Set();
|
|
11347
11354
|
listMax = 0;
|
|
11348
11355
|
listNote = "";
|
|
11356
|
+
listFooter = "";
|
|
11349
11357
|
const allowDelete = listAllowDelete;
|
|
11350
11358
|
const deleting = listDeleteRequested;
|
|
11351
11359
|
listAllowDelete = false;
|
|
@@ -11486,6 +11494,7 @@ function pickFromList(items, initialSel = 0, opts = {}) {
|
|
|
11486
11494
|
return;
|
|
11487
11495
|
}
|
|
11488
11496
|
listMulti = false; // explicit: a cancelled multi-select must not leak its mode into this one
|
|
11497
|
+
listFooter = typeof opts.footer === "string" ? opts.footer : "";
|
|
11489
11498
|
listAllowDelete = opts.allowDelete === true;
|
|
11490
11499
|
listDeleteRequested = false;
|
|
11491
11500
|
listKeys = opts.keys && typeof opts.keys === "object" ? opts.keys : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gu-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.878",
|
|
4
4
|
"description": "Gu CLI — the gu terminal plus the local worker that runs agent / OCR / PDF / embedding jobs on your Mac (Ollama / MLX / OpenAI-compatible).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|