@jannael/glinter 1.2.8 → 1.2.9

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 (3) hide show
  1. package/README.md +104 -104
  2. package/dist/index.js +122 -118
  3. package/package.json +14 -18
package/README.md CHANGED
@@ -1,104 +1,104 @@
1
- <p align="center">
2
- <br>
3
- <br>
4
- <a href="https://glinter.jannael.com" target="_blank" rel="noopener noreferrer">
5
- <picture>
6
- <img alt="Glinter" src="https://github.com/Jannael/glinter/raw/main/apps/web/public/og.png">
7
- </picture>
8
- </a>
9
- </p>
10
-
11
- Glinter is a high-performance, transparent Git wrapper built with **Bun**.
12
-
13
- [Tutorial](https://youtu.be/LQWp6flYeyI)
14
-
15
- [Commands](https://glinter.jannael.com/#commands)
16
-
17
- [Aliases](https://glinter.jannael.com/alias)
18
-
19
- [Quick start](https://glinter.jannael.com/#quick-start)
20
-
21
- ## Preview
22
-
23
- ### Badge
24
-
25
- | Preview | Copy |
26
- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
27
- | [![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com) | `[![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com)` |
28
-
29
- <video src="https://github.com/user-attachments/assets/63b401a0-e1e1-453c-9e38-c36cd14e200f" controls="false" autoplay="true" loop="true" muted="true" style="max-width: 100%;">
30
- Your browser does not support the video tag.
31
- </video>
32
-
33
- ### Screenshots
34
-
35
- | `g add` | `g commit` |
36
- | ------------------------------------ | --------------------------------------- |
37
- | ![glinter add](./screenshots/ga.png) | ![glinter commit](./screenshots/gc.png) |
38
-
39
- ## Security
40
-
41
- Security is a top priority for this project.
42
-
43
- This project uses Bun as its runtime and package manager. You don't need to worry about package vulnerabilities — the `bunfig.toml` configuration includes `minimumReleaseAge = 259200` (3 days), which prevents newly published packages from being installed automatically. This protects against supply chain attacks that have become increasingly common in the npm ecosystem.
44
-
45
- ## Features
46
-
47
- - **Abbreviation**: You can use `g` instead of `git`.
48
-
49
- - **Safe by Default**: Automatically filters and prevents accidental staging of sensitive files: `.env` and `node_modules`.
50
-
51
- - **Transparent Wrapper**: For every other command (like `commit`, `push`, `log`, or `status`), Glinter acts as a direct tunnel to Git. It preserves all original colors, formatting, and interactive features of the native Git CLI.
52
-
53
- ## Test environment
54
-
55
- - for windows i used my own laptop (windows 11) so if you have a problem please create an issue
56
-
57
- - for linux i used WSL (windows subsystem linux) with a kali-linux vm so if you have a problem please create an issue
58
-
59
- ## How it works
60
-
61
- Glinter is designed to be as "natural" as possible, meaning it shouldn't feel like a wrapper at all.
62
-
63
- ### 1. The Transparent Proxy
64
-
65
- In `src/index.ts`, Glinter uses `Bun.spawn` with `stdio: 'inherit'`. This is a low-level operation that connects the standard input, output, and error streams of the Git process directly to your terminal.
66
-
67
- - **Result**: Git "knows" it's in a real terminal, so it correctly detects colors and allows for interactive prompts (like credential entry).
68
-
69
- ### 2. Reliable Status Parsing
70
-
71
- For the interactive `add` feature, Glinter runs `git status --porcelain`.
72
-
73
- - **Why?**: Standard `git status` output is designed for humans and can change based on your Git version or system language. `--porcelain` is a machine-readable format that is consistent across all environments, making the file detection 100% reliable.
74
-
75
- ### 3. Interactive Selection
76
-
77
- Using the `@clack/prompts` library, Glinter transforms the raw status data into a selectable list.
78
-
79
- - The selection logic uses Bun's high-speed shell to execute the final `git add` command, correctly escaping filenames to handle spaces and special characters.
80
-
81
- ## Installation
82
-
83
- To use Glinter as your primary Git interface (e.g., using the command `g`):
84
-
85
- ```bash
86
- npm install -g @jannael/glinter
87
- ```
88
-
89
- ### For Development
90
-
91
- 1. **Clone the repo**
92
- 2. **Install dependencies**: `bun install`
93
- 3. **Link the binary**: `bun link`
94
-
95
- now you can simply run:
96
-
97
- ```bash
98
- g add # Opens the interactive selector
99
- g commit # Opens commit type + message prompt
100
- g add <file> # Runs standard git add <file>
101
- g commit -m "" # Runs standard git commit -m ""
102
- g status # Runs standard git status
103
- g push # Runs standard git push
104
- ```
1
+ <p align="center">
2
+ <br>
3
+ <br>
4
+ <a href="https://glinter.jannael.com" target="_blank" rel="noopener noreferrer">
5
+ <picture>
6
+ <img alt="Glinter" src="https://github.com/Jannael/glinter/raw/main/apps/web/public/og.png">
7
+ </picture>
8
+ </a>
9
+ </p>
10
+
11
+ Glinter is a high-performance, transparent Git wrapper built with **Bun**.
12
+
13
+ [Tutorial](https://youtu.be/LQWp6flYeyI)
14
+
15
+ [Commands](https://glinter.jannael.com/#commands)
16
+
17
+ [Aliases](https://glinter.jannael.com/alias)
18
+
19
+ [Quick start](https://glinter.jannael.com/#quick-start)
20
+
21
+ ## Preview
22
+
23
+ ### Badge
24
+
25
+ | Preview | Copy |
26
+ | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
27
+ | [![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com) | `[![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com)` |
28
+
29
+ <video src="https://github.com/user-attachments/assets/63b401a0-e1e1-453c-9e38-c36cd14e200f" controls="false" autoplay="true" loop="true" muted="true" style="max-width: 100%;">
30
+ Your browser does not support the video tag.
31
+ </video>
32
+
33
+ ### Screenshots
34
+
35
+ | `g add` | `g commit` |
36
+ | ------------------------------------ | --------------------------------------- |
37
+ | ![glinter add](./screenshots/ga.png) | ![glinter commit](./screenshots/gc.png) |
38
+
39
+ ## Security
40
+
41
+ Security is a top priority for this project.
42
+
43
+ This project uses Bun as its runtime and package manager. You don't need to worry about package vulnerabilities — the `bunfig.toml` configuration includes `minimumReleaseAge = 259200` (3 days), which prevents newly published packages from being installed automatically. This protects against supply chain attacks that have become increasingly common in the npm ecosystem.
44
+
45
+ ## Features
46
+
47
+ - **Abbreviation**: You can use `g` instead of `git`.
48
+
49
+ - **Safe by Default**: Automatically filters and prevents accidental staging of sensitive files: `.env` and `node_modules`.
50
+
51
+ - **Transparent Wrapper**: For every other command (like `commit`, `push`, `log`, or `status`), Glinter acts as a direct tunnel to Git. It preserves all original colors, formatting, and interactive features of the native Git CLI.
52
+
53
+ ## Test environment
54
+
55
+ - for windows i used my own laptop (windows 11) so if you have a problem please create an issue
56
+
57
+ - for linux i used WSL (windows subsystem linux) with a kali-linux vm so if you have a problem please create an issue
58
+
59
+ ## How it works
60
+
61
+ Glinter is designed to be as "natural" as possible, meaning it shouldn't feel like a wrapper at all.
62
+
63
+ ### 1. The Transparent Proxy
64
+
65
+ In `src/index.ts`, Glinter uses `Bun.spawn` with `stdio: 'inherit'`. This is a low-level operation that connects the standard input, output, and error streams of the Git process directly to your terminal.
66
+
67
+ - **Result**: Git "knows" it's in a real terminal, so it correctly detects colors and allows for interactive prompts (like credential entry).
68
+
69
+ ### 2. Reliable Status Parsing
70
+
71
+ For the interactive `add` feature, Glinter runs `git status --porcelain`.
72
+
73
+ - **Why?**: Standard `git status` output is designed for humans and can change based on your Git version or system language. `--porcelain` is a machine-readable format that is consistent across all environments, making the file detection 100% reliable.
74
+
75
+ ### 3. Interactive Selection
76
+
77
+ Using the `@clack/prompts` library, Glinter transforms the raw status data into a selectable list.
78
+
79
+ - The selection logic uses Bun's high-speed shell to execute the final `git add` command, correctly escaping filenames to handle spaces and special characters.
80
+
81
+ ## Installation
82
+
83
+ To use Glinter as your primary Git interface (e.g., using the command `g`):
84
+
85
+ ```bash
86
+ npm install -g @jannael/glinter
87
+ ```
88
+
89
+ ### For Development
90
+
91
+ 1. **Clone the repo**
92
+ 2. **Install dependencies**: `bun install`
93
+ 3. **Link the binary**: `bun link`
94
+
95
+ now you can simply run:
96
+
97
+ ```bash
98
+ g add # Opens the interactive selector
99
+ g commit # Opens commit type + message prompt
100
+ g add <file> # Runs standard git add <file>
101
+ g commit -m "" # Runs standard git commit -m ""
102
+ g status # Runs standard git status
103
+ g push # Runs standard git push
104
+ ```
package/dist/index.js CHANGED
@@ -1188,8 +1188,8 @@ var u$1 = class u extends V {
1188
1188
  var o$1 = /* @__PURE__ */ new Set(["up", "down", "left", "right"]);
1189
1189
 
1190
1190
  class h extends V {
1191
- #s = false;
1192
- #t;
1191
+ #t = false;
1192
+ #s;
1193
1193
  focused = "editor";
1194
1194
  get userInputWithCursor() {
1195
1195
  if (this.state === "submit")
@@ -1197,10 +1197,10 @@ class h extends V {
1197
1197
  const t2 = this.userInput;
1198
1198
  if (this.cursor >= t2.length)
1199
1199
  return `${t2}\u2588`;
1200
- const s = t2.slice(0, this.cursor), r2 = t2[this.cursor], e = t2.slice(this.cursor + 1);
1200
+ const s = t2.slice(0, this.cursor), r2 = t2[this.cursor], i = t2.slice(this.cursor + 1);
1201
1201
  return r2 === `
1202
1202
  ` ? `${s}\u2588
1203
- ${e}` : `${s}${styleText("inverse", r2)}${e}`;
1203
+ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
1204
1204
  }
1205
1205
  get cursor() {
1206
1206
  return this._cursor;
@@ -1230,37 +1230,41 @@ ${e}` : `${s}${styleText("inverse", r2)}${e}`;
1230
1230
  }
1231
1231
  }
1232
1232
  _shouldSubmit(t2, s) {
1233
- if (this.#t)
1233
+ if (this.#s)
1234
1234
  return this.focused === "submit" ? true : (this.#r(`
1235
1235
  `), this._cursor++, false);
1236
- const r2 = this.#s;
1237
- return this.#s = true, r2 ? (this.userInput[this.cursor - 1] === `
1236
+ const r2 = this.#t;
1237
+ return this.#t = true, r2 && this.cursor === this.userInput.length ? (this.userInput[this.cursor - 1] === `
1238
1238
  ` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#r(`
1239
1239
  `), this._cursor++, false);
1240
1240
  }
1241
1241
  constructor(t2) {
1242
- super(t2, false), this.#t = t2.showSubmit ?? false, this.on("key", (s, r2) => {
1243
- if (r2?.name && o$1.has(r2.name)) {
1244
- this.#i(r2.name);
1242
+ const s = t2.initialUserInput ?? t2.initialValue;
1243
+ super({
1244
+ ...t2,
1245
+ initialUserInput: s
1246
+ }, false), s !== undefined && (this._cursor = s.length), this.#s = t2.showSubmit ?? false, this.on("key", (r2, i) => {
1247
+ if (i?.name && o$1.has(i.name)) {
1248
+ this.#t = false, this.#i(i.name);
1245
1249
  return;
1246
1250
  }
1247
- if (s === "\t" && this.#t) {
1251
+ if (r2 === "\t" && this.#s) {
1248
1252
  this.focused = this.focused === "editor" ? "submit" : "editor";
1249
1253
  return;
1250
1254
  }
1251
- if (r2?.name !== "return") {
1252
- if (this.#s = false, r2?.name === "backspace" && this.cursor > 0) {
1255
+ if (i?.name !== "return") {
1256
+ if (this.#t = false, i?.name === "backspace" && this.cursor > 0) {
1253
1257
  this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--;
1254
1258
  return;
1255
1259
  }
1256
- if (r2?.name === "delete" && this.cursor < this.userInput.length) {
1260
+ if (i?.name === "delete" && this.cursor < this.userInput.length) {
1257
1261
  this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
1258
1262
  return;
1259
1263
  }
1260
- s && (this.#t && this.focused === "submit" && (this.focused = "editor"), this.#r(s ?? ""), this._cursor++);
1264
+ r2 && (this.#s && this.focused === "submit" && (this.focused = "editor"), this.#r(r2 ?? ""), this._cursor++);
1261
1265
  }
1262
- }), this.on("userInput", (s) => {
1263
- this._setValue(s);
1266
+ }), this.on("userInput", (r2) => {
1267
+ this._setValue(r2);
1264
1268
  }), this.on("finalize", () => {
1265
1269
  this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
1266
1270
  });
@@ -1377,7 +1381,7 @@ function isUnicodeSupported() {
1377
1381
  }
1378
1382
  var unicode = isUnicodeSupported();
1379
1383
  var isCI = () => process.env.CI === "true";
1380
- var unicodeOr = (e, o2) => unicode ? e : o2;
1384
+ var unicodeOr = (o2, e) => unicode ? o2 : e;
1381
1385
  var S_STEP_ACTIVE = unicodeOr("\u25C6", "*");
1382
1386
  var S_STEP_CANCEL = unicodeOr("\u25A0", "x");
1383
1387
  var S_STEP_ERROR = unicodeOr("\u25B2", "x");
@@ -1403,8 +1407,8 @@ var S_INFO = unicodeOr("\u25CF", "\u2022");
1403
1407
  var S_SUCCESS = unicodeOr("\u25C6", "*");
1404
1408
  var S_WARN = unicodeOr("\u25B2", "!");
1405
1409
  var S_ERROR = unicodeOr("\u25A0", "x");
1406
- var symbol = (e) => {
1407
- switch (e) {
1410
+ var symbol = (o2) => {
1411
+ switch (o2) {
1408
1412
  case "initial":
1409
1413
  case "active":
1410
1414
  return styleText2("cyan", S_STEP_ACTIVE);
@@ -1416,8 +1420,8 @@ var symbol = (e) => {
1416
1420
  return styleText2("green", S_STEP_SUBMIT);
1417
1421
  }
1418
1422
  };
1419
- var symbolBar = (e) => {
1420
- switch (e) {
1423
+ var symbolBar = (o2) => {
1424
+ switch (o2) {
1421
1425
  case "initial":
1422
1426
  case "active":
1423
1427
  return styleText2("cyan", S_BAR);
@@ -1429,6 +1433,10 @@ var symbolBar = (e) => {
1429
1433
  return styleText2("green", S_BAR);
1430
1434
  }
1431
1435
  };
1436
+ function formatInstructionFooter(o2, e) {
1437
+ const r2 = [`${e ? `${styleText2("cyan", S_BAR)} ` : ""}${o2.join(" \u2022 ")}`];
1438
+ return e && r2.push(styleText2("cyan", S_BAR_END)), r2;
1439
+ }
1432
1440
  var E$1 = (l2, o2, g2, c2, h2, O = false) => {
1433
1441
  let r2 = o2, w = 0;
1434
1442
  if (O)
@@ -1513,61 +1521,48 @@ ${g2}
1513
1521
  }
1514
1522
  }).prompt();
1515
1523
  };
1516
- var cancel = (o2 = "", t2) => {
1517
- const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_END)} ` : "";
1518
- i.write(`${e}${styleText2("red", o2)}
1519
-
1520
- `);
1521
- };
1522
- var intro = (o2 = "", t2) => {
1523
- const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_START)} ` : "";
1524
- i.write(`${e}${o2}
1525
- `);
1526
- };
1527
- var outro = (o2 = "", t2) => {
1528
- const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR)}
1529
- ${styleText2("gray", S_BAR_END)} ` : "";
1530
- i.write(`${e}${o2}
1531
-
1532
- `);
1533
- };
1534
- var d = (n2, a3) => n2.split(`
1535
- `).map((m2) => a3(m2)).join(`
1524
+ var MULTISELECT_INSTRUCTIONS = [
1525
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
1526
+ `${styleText2("dim", "Space:")} select`,
1527
+ `${styleText2("dim", "Enter:")} confirm`
1528
+ ];
1529
+ var m2 = (n2, o2) => n2.split(`
1530
+ `).map((d) => o2(d)).join(`
1536
1531
  `);
1537
1532
  var multiselect = (n2) => {
1538
- const a3 = (t2, o2) => {
1533
+ const o2 = (t2, a3) => {
1539
1534
  const r2 = t2.label ?? String(t2.value);
1540
- return o2 === "disabled" ? `${styleText2("gray", S_CHECKBOX_INACTIVE)} ${d(r2, (l2) => styleText2(["strikethrough", "gray"], l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint ?? "disabled"})`)}` : ""}` : o2 === "active" ? `${styleText2("cyan", S_CHECKBOX_ACTIVE)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : o2 === "selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${d(r2, (l2) => styleText2("dim", l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : o2 === "cancelled" ? `${d(r2, (l2) => styleText2(["strikethrough", "dim"], l2))}` : o2 === "active-selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : o2 === "submitted" ? `${d(r2, (l2) => styleText2("dim", l2))}` : `${styleText2("dim", S_CHECKBOX_INACTIVE)} ${d(r2, (l2) => styleText2("dim", l2))}`;
1541
- }, m2 = n2.required ?? true;
1535
+ return a3 === "disabled" ? `${styleText2("gray", S_CHECKBOX_INACTIVE)} ${m2(r2, (l2) => styleText2(["strikethrough", "gray"], l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint ?? "disabled"})`)}` : ""}` : a3 === "active" ? `${styleText2("cyan", S_CHECKBOX_ACTIVE)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : a3 === "selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${m2(r2, (l2) => styleText2("dim", l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : a3 === "cancelled" ? `${m2(r2, (l2) => styleText2(["strikethrough", "dim"], l2))}` : a3 === "active-selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : a3 === "submitted" ? `${m2(r2, (l2) => styleText2("dim", l2))}` : `${styleText2("dim", S_CHECKBOX_INACTIVE)} ${m2(r2, (l2) => styleText2("dim", l2))}`;
1536
+ }, d = n2.required ?? true;
1542
1537
  return new a$1({
1543
1538
  options: n2.options,
1544
1539
  signal: n2.signal,
1545
1540
  input: n2.input,
1546
1541
  output: n2.output,
1547
1542
  initialValues: n2.initialValues,
1548
- required: m2,
1543
+ required: d,
1549
1544
  cursorAt: n2.cursorAt,
1550
1545
  validate(t2) {
1551
- if (m2 && (t2 === undefined || t2.length === 0))
1546
+ if (d && (t2 === undefined || t2.length === 0))
1552
1547
  return `Please select at least one option.
1553
1548
  ${styleText2("reset", styleText2("dim", `Press ${styleText2(["gray", "bgWhite", "inverse"], " space ")} to select, ${styleText2("gray", styleText2("bgWhite", styleText2("inverse", " enter ")))} to submit`))}`;
1554
1549
  },
1555
1550
  render() {
1556
- const t2 = n2.withGuide ?? settings.withGuide, o2 = wrapTextWithPrefix(n2.output, n2.message, t2 ? `${symbolBar(this.state)} ` : "", `${symbol(this.state)} `), r2 = `${t2 ? `${styleText2("gray", S_BAR)}
1557
- ` : ""}${o2}
1558
- `, l2 = this.value ?? [], g2 = (i, u3) => {
1551
+ const t2 = n2.withGuide ?? settings.withGuide, a3 = wrapTextWithPrefix(n2.output, n2.message, t2 ? `${symbolBar(this.state)} ` : "", `${symbol(this.state)} `), r2 = `${t2 ? `${styleText2("gray", S_BAR)}
1552
+ ` : ""}${a3}
1553
+ `, l2 = this.value ?? [], p2 = (i, u3) => {
1559
1554
  if (i.disabled)
1560
- return a3(i, "disabled");
1555
+ return o2(i, "disabled");
1561
1556
  const s = l2.includes(i.value);
1562
- return u3 && s ? a3(i, "active-selected") : s ? a3(i, "selected") : a3(i, u3 ? "active" : "inactive");
1557
+ return u3 && s ? o2(i, "active-selected") : s ? o2(i, "selected") : o2(i, u3 ? "active" : "inactive");
1563
1558
  };
1564
1559
  switch (this.state) {
1565
1560
  case "submit": {
1566
- const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => a3(s, "submitted")).join(styleText2("dim", ", ")) || styleText2("dim", "none"), u3 = wrapTextWithPrefix(n2.output, i, t2 ? `${styleText2("gray", S_BAR)} ` : "");
1561
+ const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => o2(s, "submitted")).join(styleText2("dim", ", ")) || styleText2("dim", "none"), u3 = wrapTextWithPrefix(n2.output, i, t2 ? `${styleText2("gray", S_BAR)} ` : "");
1567
1562
  return `${r2}${u3}`;
1568
1563
  }
1569
1564
  case "cancel": {
1570
- const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => a3(s, "cancelled")).join(styleText2("dim", ", "));
1565
+ const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => o2(s, "cancelled")).join(styleText2("dim", ", "));
1571
1566
  if (i.trim() === "")
1572
1567
  return `${r2}${styleText2("gray", S_BAR)}`;
1573
1568
  const u3 = wrapTextWithPrefix(n2.output, i, t2 ? `${styleText2("gray", S_BAR)} ` : "");
@@ -1576,9 +1571,9 @@ ${styleText2("gray", S_BAR)}` : ""}`;
1576
1571
  }
1577
1572
  case "error": {
1578
1573
  const i = t2 ? `${styleText2("yellow", S_BAR)} ` : "", u3 = this.error.split(`
1579
- `).map((h2, x) => x === 0 ? `${t2 ? `${styleText2("yellow", S_BAR_END)} ` : ""}${styleText2("yellow", h2)}` : ` ${h2}`).join(`
1574
+ `).map(($, x) => x === 0 ? `${t2 ? `${styleText2("yellow", S_BAR_END)} ` : ""}${styleText2("yellow", $)}` : ` ${$}`).join(`
1580
1575
  `), s = r2.split(`
1581
- `).length, v = u3.split(`
1576
+ `).length, g2 = u3.split(`
1582
1577
  `).length + 1;
1583
1578
  return `${r2}${i}${limitOptions({
1584
1579
  output: n2.output,
@@ -1586,8 +1581,8 @@ ${styleText2("gray", S_BAR)}` : ""}`;
1586
1581
  cursor: this.cursor,
1587
1582
  maxItems: n2.maxItems,
1588
1583
  columnPadding: i.length,
1589
- rowPadding: s + v,
1590
- style: g2
1584
+ rowPadding: s + g2,
1585
+ style: p2
1591
1586
  }).join(`
1592
1587
  ${i}`)}
1593
1588
  ${u3}
@@ -1595,24 +1590,43 @@ ${u3}
1595
1590
  }
1596
1591
  default: {
1597
1592
  const i = t2 ? `${styleText2("cyan", S_BAR)} ` : "", u3 = r2.split(`
1598
- `).length, s = t2 ? 2 : 1;
1593
+ `).length, s = formatInstructionFooter(MULTISELECT_INSTRUCTIONS, t2), g2 = s.join(`
1594
+ `), $ = s.length + 1;
1599
1595
  return `${r2}${i}${limitOptions({
1600
1596
  output: n2.output,
1601
1597
  options: this.options,
1602
1598
  cursor: this.cursor,
1603
1599
  maxItems: n2.maxItems,
1604
1600
  columnPadding: i.length,
1605
- rowPadding: u3 + s,
1606
- style: g2
1601
+ rowPadding: u3 + $,
1602
+ style: p2
1607
1603
  }).join(`
1608
1604
  ${i}`)}
1609
- ${t2 ? styleText2("cyan", S_BAR_END) : ""}
1605
+ ${g2}
1610
1606
  `;
1611
1607
  }
1612
1608
  }
1613
1609
  }
1614
1610
  }).prompt();
1615
1611
  };
1612
+ var cancel = (o2 = "", t2) => {
1613
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_END)} ` : "";
1614
+ i.write(`${e}${styleText2("red", o2)}
1615
+
1616
+ `);
1617
+ };
1618
+ var intro = (o2 = "", t2) => {
1619
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_START)} ` : "";
1620
+ i.write(`${e}${o2}
1621
+ `);
1622
+ };
1623
+ var outro = (o2 = "", t2) => {
1624
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR)}
1625
+ ${styleText2("gray", S_BAR_END)} ` : "";
1626
+ i.write(`${e}${o2}
1627
+
1628
+ `);
1629
+ };
1616
1630
  var W = (l2) => styleText2("magenta", l2);
1617
1631
  var spinner = ({
1618
1632
  indicator: l2 = "dots",
@@ -1622,18 +1636,18 @@ var spinner = ({
1622
1636
  errorMessage: O,
1623
1637
  frames: E = unicode ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"],
1624
1638
  delay: F = unicode ? 80 : 120,
1625
- signal: m2,
1639
+ signal: m3,
1626
1640
  ...I
1627
1641
  } = {}) => {
1628
1642
  const u3 = isCI();
1629
- let M2, T3, d2 = false, S = false, s = "", p2, w = performance.now();
1643
+ let M2, T3, d = false, S = false, s = "", p2, w = performance.now();
1630
1644
  const x = getColumns(n2), k = I?.styleFrame ?? W, g2 = (e) => {
1631
1645
  const r2 = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
1632
- S = e === 1, d2 && (a3(r2, e), S && typeof h2 == "function" && h2());
1646
+ S = e === 1, d && (a3(r2, e), S && typeof h2 == "function" && h2());
1633
1647
  }, f2 = () => g2(2), i = () => g2(1), A = () => {
1634
- process.on("uncaughtExceptionMonitor", f2), process.on("unhandledRejection", f2), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g2), m2 && m2.addEventListener("abort", i);
1648
+ process.on("uncaughtExceptionMonitor", f2), process.on("unhandledRejection", f2), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g2), m3 && m3.addEventListener("abort", i);
1635
1649
  }, H = () => {
1636
- process.removeListener("uncaughtExceptionMonitor", f2), process.removeListener("unhandledRejection", f2), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g2), m2 && m2.removeEventListener("abort", i);
1650
+ process.removeListener("uncaughtExceptionMonitor", f2), process.removeListener("unhandledRejection", f2), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g2), m3 && m3.removeEventListener("abort", i);
1637
1651
  }, y = () => {
1638
1652
  if (p2 === undefined)
1639
1653
  return;
@@ -1649,7 +1663,7 @@ var spinner = ({
1649
1663
  const r2 = (performance.now() - e) / 1000, t2 = Math.floor(r2 / 60), o2 = Math.floor(r2 % 60);
1650
1664
  return t2 > 0 ? `[${t2}m ${o2}s]` : `[${o2}s]`;
1651
1665
  }, N = I.withGuide ?? settings.withGuide, P2 = (e = "") => {
1652
- d2 = true, M2 = block({ output: n2 }), s = C2(e), w = performance.now(), N && n2.write(`${styleText2("gray", S_BAR)}
1666
+ d = true, M2 = block({ output: n2 }), s = C2(e), w = performance.now(), N && n2.write(`${styleText2("gray", S_BAR)}
1653
1667
  `);
1654
1668
  let r2 = 0, t2 = 0;
1655
1669
  A(), T3 = setInterval(() => {
@@ -1673,9 +1687,9 @@ var spinner = ({
1673
1687
  n2.write(j), r2 = r2 + 1 < E.length ? r2 + 1 : 0, t2 = t2 < 4 ? t2 + 0.125 : 0;
1674
1688
  }, F);
1675
1689
  }, a3 = (e = "", r2 = 0, t2 = false) => {
1676
- if (!d2)
1690
+ if (!d)
1677
1691
  return;
1678
- d2 = false, clearInterval(T3), y();
1692
+ d = false, clearInterval(T3), y();
1679
1693
  const o2 = r2 === 0 ? styleText2("green", S_STEP_SUBMIT) : r2 === 1 ? styleText2("red", S_STEP_CANCEL) : styleText2("red", S_STEP_ERROR);
1680
1694
  s = e ?? s, t2 || (l2 === "timer" ? n2.write(`${o2} ${s} ${_2(w)}
1681
1695
  `) : n2.write(`${o2} ${s}
@@ -1700,20 +1714,24 @@ var u3 = {
1700
1714
  heavy: unicodeOr("\u2501", "="),
1701
1715
  block: unicodeOr("\u2588", "#")
1702
1716
  };
1703
- var c2 = (e, a3) => e.includes(`
1704
- `) ? e.split(`
1705
- `).map((t2) => a3(t2)).join(`
1706
- `) : a3(e);
1707
- var select = (e) => {
1708
- const a3 = (t2, d2) => {
1709
- const s = t2.label ?? String(t2.value);
1710
- switch (d2) {
1717
+ var SELECT_INSTRUCTIONS = [
1718
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
1719
+ `${styleText2("dim", "Enter:")} confirm`
1720
+ ];
1721
+ var c2 = (t2, a3) => t2.includes(`
1722
+ `) ? t2.split(`
1723
+ `).map((i) => a3(i)).join(`
1724
+ `) : a3(t2);
1725
+ var select = (t2) => {
1726
+ const a3 = (i, m3) => {
1727
+ const s = i.label ?? String(i.value);
1728
+ switch (m3) {
1711
1729
  case "disabled":
1712
- return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (n2) => styleText2("gray", n2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint ?? "disabled"})`)}` : ""}`;
1730
+ return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (n2) => styleText2("gray", n2))}${i.hint ? ` ${styleText2("dim", `(${i.hint ?? "disabled"})`)}` : ""}`;
1713
1731
  case "selected":
1714
1732
  return `${c2(s, (n2) => styleText2("dim", n2))}`;
1715
1733
  case "active":
1716
- return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}`;
1734
+ return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${i.hint ? ` ${styleText2("dim", `(${i.hint})`)}` : ""}`;
1717
1735
  case "cancelled":
1718
1736
  return `${c2(s, (n2) => styleText2(["strikethrough", "dim"], n2))}`;
1719
1737
  default:
@@ -1721,39 +1739,40 @@ var select = (e) => {
1721
1739
  }
1722
1740
  };
1723
1741
  return new a2({
1724
- options: e.options,
1725
- signal: e.signal,
1726
- input: e.input,
1727
- output: e.output,
1728
- initialValue: e.initialValue,
1742
+ options: t2.options,
1743
+ signal: t2.signal,
1744
+ input: t2.input,
1745
+ output: t2.output,
1746
+ initialValue: t2.initialValue,
1729
1747
  render() {
1730
- const t2 = e.withGuide ?? settings.withGuide, d2 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, n2 = wrapTextWithPrefix(e.output, e.message, s, d2), u4 = `${t2 ? `${styleText2("gray", S_BAR)}
1748
+ const i = t2.withGuide ?? settings.withGuide, m3 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, n2 = wrapTextWithPrefix(t2.output, t2.message, s, m3), u4 = `${i ? `${styleText2("gray", S_BAR)}
1731
1749
  ` : ""}${n2}
1732
1750
  `;
1733
1751
  switch (this.state) {
1734
1752
  case "submit": {
1735
- const r2 = t2 ? `${styleText2("gray", S_BAR)} ` : "", l2 = wrapTextWithPrefix(e.output, a3(this.options[this.cursor], "selected"), r2);
1736
- return `${u4}${l2}`;
1753
+ const r2 = i ? `${styleText2("gray", S_BAR)} ` : "", o2 = wrapTextWithPrefix(t2.output, a3(this.options[this.cursor], "selected"), r2);
1754
+ return `${u4}${o2}`;
1737
1755
  }
1738
1756
  case "cancel": {
1739
- const r2 = t2 ? `${styleText2("gray", S_BAR)} ` : "", l2 = wrapTextWithPrefix(e.output, a3(this.options[this.cursor], "cancelled"), r2);
1740
- return `${u4}${l2}${t2 ? `
1757
+ const r2 = i ? `${styleText2("gray", S_BAR)} ` : "", o2 = wrapTextWithPrefix(t2.output, a3(this.options[this.cursor], "cancelled"), r2);
1758
+ return `${u4}${o2}${i ? `
1741
1759
  ${styleText2("gray", S_BAR)}` : ""}`;
1742
1760
  }
1743
1761
  default: {
1744
- const r2 = t2 ? `${styleText2("cyan", S_BAR)} ` : "", l2 = t2 ? styleText2("cyan", S_BAR_END) : "", g2 = u4.split(`
1745
- `).length, h2 = t2 ? 2 : 1;
1762
+ const r2 = i ? `${styleText2("cyan", S_BAR)} ` : "", o2 = u4.split(`
1763
+ `).length, $ = formatInstructionFooter(SELECT_INSTRUCTIONS, i), h2 = $.join(`
1764
+ `), b2 = $.length + 1;
1746
1765
  return `${u4}${r2}${limitOptions({
1747
- output: e.output,
1766
+ output: t2.output,
1748
1767
  cursor: this.cursor,
1749
1768
  options: this.options,
1750
- maxItems: e.maxItems,
1769
+ maxItems: t2.maxItems,
1751
1770
  columnPadding: r2.length,
1752
- rowPadding: g2 + h2,
1753
- style: (p2, b2) => a3(p2, p2.disabled ? "disabled" : b2 ? "active" : "inactive")
1771
+ rowPadding: o2 + b2,
1772
+ style: (p2, x) => a3(p2, p2.disabled ? "disabled" : x ? "active" : "inactive")
1754
1773
  }).join(`
1755
1774
  ${r2}`)}
1756
- ${l2}
1775
+ ${h2}
1757
1776
  `;
1758
1777
  }
1759
1778
  }
@@ -1775,10 +1794,10 @@ var text = (t2) => new n({
1775
1794
  `, c3 = t2.placeholder ? styleText2("inverse", t2.placeholder[0]) + styleText2("dim", t2.placeholder.slice(1)) : styleText2(["inverse", "hidden"], "_"), o2 = this.userInput ? this.userInputWithCursor : c3, a3 = this.value ?? "";
1776
1795
  switch (this.state) {
1777
1796
  case "error": {
1778
- const n2 = this.error ? ` ${styleText2("yellow", this.error)}` : "", r2 = i2 ? `${styleText2("yellow", S_BAR)} ` : "", d2 = i2 ? styleText2("yellow", S_BAR_END) : "";
1797
+ const n2 = this.error ? ` ${styleText2("yellow", this.error)}` : "", r2 = i2 ? `${styleText2("yellow", S_BAR)} ` : "", d = i2 ? styleText2("yellow", S_BAR_END) : "";
1779
1798
  return `${s.trim()}
1780
1799
  ${r2}${o2}
1781
- ${d2}${n2}
1800
+ ${d}${n2}
1782
1801
  `;
1783
1802
  }
1784
1803
  case "submit": {
@@ -2108,10 +2127,7 @@ var commitTypeOptions = [
2108
2127
  ];
2109
2128
 
2110
2129
  // apps/cli/utils/input.ts
2111
- async function Input({
2112
- message,
2113
- placeholder
2114
- }) {
2130
+ async function Input({ message, placeholder }) {
2115
2131
  const value = await text({
2116
2132
  message,
2117
2133
  placeholder,
@@ -2129,10 +2145,7 @@ async function Input({
2129
2145
  }
2130
2146
 
2131
2147
  // apps/cli/utils/select.ts
2132
- async function Select({
2133
- message,
2134
- options
2135
- }) {
2148
+ async function Select({ message, options }) {
2136
2149
  const selected = await select({
2137
2150
  message,
2138
2151
  options
@@ -2210,11 +2223,7 @@ class SetupAliasesUseCase {
2210
2223
  }
2211
2224
 
2212
2225
  // apps/cli/utils/confirm.ts
2213
- async function Confirm({
2214
- message,
2215
- cancelMessage,
2216
- exitOnCancel = true
2217
- }) {
2226
+ async function Confirm({ message, cancelMessage, exitOnCancel = true }) {
2218
2227
  const confirmed = await confirm({
2219
2228
  message
2220
2229
  });
@@ -2360,12 +2369,7 @@ ${functionLine}
2360
2369
  return path2.join(this.getWindowsDocumentsPath(), "PowerShell", "Microsoft.PowerShell_profile.ps1");
2361
2370
  }
2362
2371
  getWindowsDocumentsPath() {
2363
- const proc = Bun.spawnSync([
2364
- "powershell",
2365
- "-NoProfile",
2366
- "-Command",
2367
- "[Environment]::GetFolderPath('MyDocuments')"
2368
- ], {
2372
+ const proc = Bun.spawnSync(["powershell", "-NoProfile", "-Command", "[Environment]::GetFolderPath('MyDocuments')"], {
2369
2373
  stdout: "pipe",
2370
2374
  stderr: "pipe"
2371
2375
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jannael/glinter",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "description": "A high-performance, transparent Git wrapper with interactive staging",
5
5
  "type": "module",
6
6
  "private": false,
@@ -36,12 +36,12 @@
36
36
  "scripts": {
37
37
  "build": "bun build ./apps/cli/index.ts --outfile=./dist/index.js --target=bun",
38
38
  "prepublishOnly": "bun run build",
39
- "test": "vitest",
40
- "lint": "oxlint",
41
- "lint:fix": "oxlint --fix",
42
- "format": "prettier --write .",
43
- "format:check": "prettier --check .",
39
+ "test": "bun test",
44
40
  "type-check": "bunx tsc --noEmit",
41
+ "lint": "eslint .",
42
+ "lint:fix": "eslint --fix .",
43
+ "fmt": "prettier --write .",
44
+ "fmt:check": "prettier --check .",
45
45
  "prepare": "husky"
46
46
  },
47
47
  "bin": {
@@ -51,27 +51,23 @@
51
51
  "bun": ">=1.0.0"
52
52
  },
53
53
  "devDependencies": {
54
+ "@eslint/js": "10.0.1",
54
55
  "@types/bun": "1.3.14",
56
+ "eslint": "10.5.0",
57
+ "eslint-plugin-astro": "2.0.0",
55
58
  "husky": "9.1.7",
56
- "oxlint": "1.68.0",
57
- "prettier": "3.8.3",
59
+ "lint-staged": "17.0.8",
60
+ "prettier": "3.8.4",
58
61
  "prettier-plugin-astro": "0.14.1",
59
62
  "prettier-plugin-tailwindcss": "0.8.0",
60
- "vitest": "4.1.8",
61
- "lint-staged": "17.0.7",
62
- "wrangler": "4.98.0"
63
+ "typescript-eslint": "8.62.0",
64
+ "wrangler": "4.104.0"
63
65
  },
64
66
  "peerDependencies": {
65
67
  "bun": "1.3.14",
66
68
  "typescript": "6.0.3"
67
69
  },
68
70
  "dependencies": {
69
- "@clack/prompts": "1.5.1"
70
- },
71
- "lint-staged": {
72
- "*.{ts,tsx,js,jsx,astro}": [
73
- "bun run lint:fix",
74
- "bun run format"
75
- ]
71
+ "@clack/prompts": "1.6.0"
76
72
  }
77
73
  }