@alcyone-labs/arg-parser 2.10.0 → 2.10.2

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/README.md CHANGED
@@ -77,6 +77,8 @@ A modern, type-safe command line argument parser with built-in MCP (Model Contex
77
77
  - [Typical Errors](#typical-errors)
78
78
  - [System Flags & Configuration](#system-flags--configuration)
79
79
  - [Changelog](#changelog)
80
+ - [v2.10.2](#v2102)
81
+ - [v2.10.1](#v2101)
80
82
  - [v2.10.0](#v2100)
81
83
  - [v2.8.2](#v282)
82
84
  - [v2.8.1](#v281)
@@ -117,15 +119,64 @@ A modern, type-safe command line argument parser with built-in MCP (Model Contex
117
119
 
118
120
  ## OpenTUI: Reactive Rich Terminal Interfaces
119
121
 
120
- ArgParser now includes **OpenTUI**, a standardized framework for building deep, multi-layered terminal applications with minimal boilerplate. It features a stack-based navigation system (`StackNavigator`), mouse wheel support, and a reactive theming engine.
122
+ ArgParser includes **OpenTUI**, a standardized framework for building deep, multi-layered terminal applications with minimal boilerplate. It features a stack-based navigation system (`StackNavigator`), mouse wheel support, and a reactive theming engine.
121
123
 
122
124
  ### Core TUI Features
123
125
 
124
126
  - **Standardized Navigation**: `Enter` / `Right` to dive into details, `Esc` / `Left` to go back.
125
- - **Reactive Theming**: Cycle through built-in themes (`Default`, `Ocean`, `Monokai`) or create your own using semantic color variables.
126
- - **Mouse & Scroll Performance**: Built-in SGR mouse reporting support with smooth scrolling and left-aligned thematic scrollbars.
127
+ - **Reactive Theming**: Cycle through built-in themes (`Default`, `Ocean`, `Monokai`) or create your own.
128
+ - **Mouse & Scroll Performance**: Built-in SGR mouse reporting support with smooth scrolling and high-performance rendering.
127
129
  - **Component Based**: Reusable `List`, `ScrollArea`, `Input`, and `SplitLayout` components.
128
130
 
131
+ ### Component Reference
132
+
133
+ #### `App`
134
+
135
+ The main entry point for TUI applications. It handles TTY raw mode, mouse reporting, and ensures clean terminal restoration on exit or crash.
136
+
137
+ ```typescript
138
+ const app = new UI.App();
139
+ app.run(rootComponent);
140
+ // Exit manually
141
+ app.stop();
142
+ ```
143
+
144
+ #### `ScrollArea`
145
+
146
+ A container for long text blocks with automatic scrollbars and mouse-wheel support.
147
+
148
+ - `content`: The text to display (supports ANSI colors).
149
+ - `wrapText`: (New in v2.10.2) If true, automatically wraps long lines to fit the area width.
150
+
151
+ ```typescript
152
+ const details = new UI.ScrollArea({
153
+ content: "Long text...",
154
+ wrapText: true,
155
+ });
156
+ ```
157
+
158
+ #### `StackNavigator`
159
+
160
+ Manages a stack of components, perfect for "Drill-down" interfaces.
161
+
162
+ - Handles `Esc` and `Left Arrow` automatically to return to the previous view.
163
+ - Resizes restore components to the correct dimensions.
164
+
165
+ ```typescript
166
+ const nav = new UI.StackNavigator({ initialComponent: homeList });
167
+ nav.push(detailsView); // Drill down
168
+ ```
169
+
170
+ #### `ThemeManager`
171
+
172
+ Allows real-time theme switching without affecting application logic.
173
+
174
+ ```typescript
175
+ UI.ThemeManager.setTheme("Ocean");
176
+ const t = UI.ThemeManager.current;
177
+ console.log(t.accent("Statically colored text"));
178
+ ```
179
+
129
180
  ### Quick TUI Example
130
181
 
131
182
  ```typescript
@@ -135,14 +186,18 @@ const app = new UI.App();
135
186
 
136
187
  // Create components
137
188
  const list = new UI.List({
138
- items: [{ label: "Help", value: "help" }, { label: "Exit", value: "exit" }],
189
+ items: [
190
+ { label: "Help", value: "help" },
191
+ { label: "Exit", value: "exit" },
192
+ ],
139
193
  onSubmit: (item) => {
140
194
  if (item.value === "exit") app.stop();
141
- }
195
+ },
142
196
  });
143
197
 
144
- const details = new UI.ScrollArea({
145
- content: "Select an item to see more..."
198
+ const details = new UI.ScrollArea({
199
+ content: "Select an item to see more...",
200
+ wrapText: true,
146
201
  });
147
202
 
148
203
  // Build layout
@@ -150,10 +205,9 @@ const layout = new UI.SplitLayout({
150
205
  direction: "horizontal",
151
206
  first: list,
152
207
  second: details,
153
- ratio: 0.3
208
+ splitRatio: 0.3,
154
209
  });
155
210
 
156
- // Run with automatic TTY cleanup
157
211
  app.run(layout);
158
212
  ```
159
213
 
@@ -635,20 +689,23 @@ interface DxtOptions {
635
689
  ArgParser provides universal support for environment variables across all commands.
636
690
 
637
691
  **Features:**
692
+
638
693
  1. **Automatic Fallback**: If a flag is not provided via CLI, ArgParser looks for configured environment variables.
639
694
  2. **Priority Handling**: `CLI Flag` > `Environment Variable` > `Default Value`.
640
695
  3. **Reverse Sync**: Once a flag value is resolved (whether from CLI or Env), it is automatically written back to `process.env`. This ensures downstream code accessing `process.env` sees the consistent, final value.
641
696
  4. **Array Support**: You can specify multiple env vars for a single flag; the first one found is used.
642
697
 
643
698
  **Example:**
699
+
644
700
  ```typescript
645
701
  parser.addFlag({
646
702
  name: "apiKey",
647
703
  type: "string",
648
704
  env: ["MY_APP_API_KEY", "LEGACY_API_KEY"], // First match wins
649
- defaultValue: "dev-key"
705
+ defaultValue: "dev-key",
650
706
  });
651
707
  ```
708
+
652
709
  - If passed `--api-key val`: `apiKey` is "val", and `process.env.MY_APP_API_KEY` becomes "val".
653
710
  - If not passed, but `MY_APP_API_KEY` exists: `apiKey` uses the env value.
654
711
  - If neither: `apiKey` is "dev-key", and `process.env.MY_APP_API_KEY` is set to "dev-key".
@@ -2203,6 +2260,17 @@ ArgParser includes built-in `--s-*` flags for development, debugging, and config
2203
2260
 
2204
2261
  ## Changelog
2205
2262
 
2263
+ ### v2.10.2
2264
+
2265
+ - **OpenTUI Improvements**:
2266
+ - **Soft Wrapping**: Added `wrapText` (boolean) to `ScrollArea` component. When enabled, text automatically reflows to fit the container width (preventing clipping).
2267
+ - **ANSI Preservation**: Soft-wrapping logic is ANSI-aware; color and style states are correctly carried over to wrapped lines.
2268
+
2269
+ ### v2.10.1
2270
+
2271
+ - **Bug Fixes**:
2272
+ - Fixed a crash in `Terminal.moveCursor` when running in certain environments where `node:readline` utilities were inaccessible. Switched to direct ANSI escape codes for cursor positioning.
2273
+
2206
2274
  ### v2.10.0 - OpenTUI integration + IFlag "env" property now first-class citizen
2207
2275
 
2208
2276
  #### OpenTUI Integration
package/dist/index.cjs CHANGED
@@ -9018,7 +9018,7 @@ class Terminal {
9018
9018
  process.stdout.write("\x1B[?25h");
9019
9019
  }
9020
9020
  moveCursor(x, y) {
9021
- (void 0)(process.stdout, x, y);
9021
+ process.stdout.write(`\x1B[${y + 1};${x + 1}H`);
9022
9022
  }
9023
9023
  enableRawMode() {
9024
9024
  if (process.stdin.isTTY) {
@@ -9343,12 +9343,61 @@ class List extends Component {
9343
9343
  }
9344
9344
  }
9345
9345
  }
9346
+ function stripAnsi(str) {
9347
+ return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
9348
+ }
9349
+ function visualLength(str) {
9350
+ return stripAnsi(str).length;
9351
+ }
9352
+ function wrapText(text, width) {
9353
+ if (width <= 0) return [text];
9354
+ const result = [];
9355
+ const lines = text.split("\n");
9356
+ for (const line of lines) {
9357
+ if (visualLength(line) <= width) {
9358
+ result.push(line);
9359
+ continue;
9360
+ }
9361
+ let currentLine = "";
9362
+ let currentVisibleLength = 0;
9363
+ let activeAnsiCodes = [];
9364
+ const tokenRegex = /(\u001b\[(?:\d{1,3}(?:;\d{1,3})*)?[mK])|([\s\S])/g;
9365
+ let match;
9366
+ while ((match = tokenRegex.exec(line)) !== null) {
9367
+ const ansiCode = match[1];
9368
+ const char = match[2];
9369
+ if (ansiCode) {
9370
+ currentLine += ansiCode;
9371
+ if (ansiCode === "\x1B[0m") {
9372
+ activeAnsiCodes = [];
9373
+ } else if (ansiCode.endsWith("m")) {
9374
+ activeAnsiCodes.push(ansiCode);
9375
+ }
9376
+ } else if (char) {
9377
+ if (currentVisibleLength >= width) {
9378
+ result.push(currentLine + "\x1B[0m");
9379
+ currentLine = activeAnsiCodes.join("") + char;
9380
+ currentVisibleLength = 1;
9381
+ } else {
9382
+ currentLine += char;
9383
+ currentVisibleLength++;
9384
+ }
9385
+ }
9386
+ }
9387
+ if (currentLine.length > 0) {
9388
+ result.push(currentLine + "\x1B[0m");
9389
+ }
9390
+ }
9391
+ return result;
9392
+ }
9346
9393
  class ScrollArea extends Component {
9347
9394
  constructor(config) {
9348
9395
  super(config);
9349
9396
  this.contentLines = [];
9350
9397
  this.scrollOffset = 0;
9398
+ this.wrapText = false;
9351
9399
  this.content = config.content;
9400
+ this.wrapText = !!config.wrapText;
9352
9401
  this.updateContentLines();
9353
9402
  }
9354
9403
  setContent(content) {
@@ -9356,8 +9405,18 @@ class ScrollArea extends Component {
9356
9405
  this.scrollOffset = 0;
9357
9406
  this.updateContentLines();
9358
9407
  }
9408
+ resize(x, y, width, height) {
9409
+ super.resize(x, y, width, height);
9410
+ if (this.wrapText) {
9411
+ this.updateContentLines();
9412
+ }
9413
+ }
9359
9414
  updateContentLines() {
9360
- this.contentLines = this.content.split("\n");
9415
+ if (this.wrapText && this.width > 0) {
9416
+ this.contentLines = wrapText(this.content, Math.max(1, this.width - 2));
9417
+ } else {
9418
+ this.contentLines = this.content.split("\n");
9419
+ }
9361
9420
  }
9362
9421
  render() {
9363
9422
  const lines = [];
@@ -9372,7 +9431,6 @@ class ScrollArea extends Component {
9372
9431
  const maxTop = this.height - scrollbarHeight;
9373
9432
  scrollbarTop = Math.floor(scrollPercent * maxTop);
9374
9433
  }
9375
- const stripAnsi = (str) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
9376
9434
  for (let i = 0; i < this.height; i++) {
9377
9435
  const lineContent = visibleLines[i] || "";
9378
9436
  let prefix = "";