@dui-toolkit/plugin-tui 0.1.1-next.3 → 0.1.1-next.5

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 (2) hide show
  1. package/dist/index.mjs +30 -17
  2. package/package.json +5 -2
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { stripAnsi, truncateByCells, visibleLength } from "@bdocs/dui";
1
+ import { splitGraphemes, stripAnsi, truncateByCells, visibleLength } from "@bdocs/dui";
2
2
  import { readFileSync } from "node:fs";
3
3
  //#region src/widget.ts
4
4
  var BaseWidget = class {
@@ -72,8 +72,11 @@ var TextInput = class extends BaseWidget {
72
72
  /** Set the text value programmatically. */
73
73
  setValue(value) {
74
74
  const max = this.data.maxLength;
75
- this.data.value = max > 0 ? value.slice(0, max) : value;
76
- this.cursorPos = this.data.value.length;
75
+ if (max > 0) {
76
+ const g = splitGraphemes(value);
77
+ this.data.value = g.slice(0, max).join("");
78
+ } else this.data.value = value;
79
+ this.cursorPos = splitGraphemes(this.data.value).length;
77
80
  this.data.onChange?.(this.data.value);
78
81
  }
79
82
  /** Get the current text value. */
@@ -86,16 +89,18 @@ var TextInput = class extends BaseWidget {
86
89
  const value = this.data.mask ? this.data.mask.repeat(this.data.value.length) : this.data.value;
87
90
  const display = value || this.data.placeholder;
88
91
  const isEmpty = !value;
89
- const cursorInDisplay = this.cursorPos;
90
- let before = display.slice(0, cursorInDisplay);
91
- let cursor = display[cursorInDisplay] || " ";
92
- let after = display.slice(cursorInDisplay + 1);
92
+ const graphemes = splitGraphemes(display);
93
+ const cursorInDisplay = Math.min(this.cursorPos, graphemes.length);
94
+ let before = graphemes.slice(0, cursorInDisplay).join("");
95
+ let cursor = graphemes[cursorInDisplay] || " ";
96
+ let after = graphemes.slice(cursorInDisplay + 1).join("");
97
+ graphemes.slice(cursorInDisplay + 1);
93
98
  const maxVisible = width - 4;
94
99
  if (visibleLength(display) > maxVisible) {
95
100
  const start = Math.max(0, cursorInDisplay - Math.floor(maxVisible / 2));
96
- before = display.slice(start, cursorInDisplay);
97
- cursor = display[cursorInDisplay] || " ";
98
- after = display.slice(cursorInDisplay + 1, start + maxVisible);
101
+ before = graphemes.slice(start, cursorInDisplay).join("");
102
+ cursor = graphemes[cursorInDisplay] || " ";
103
+ after = graphemes.slice(cursorInDisplay + 1, start + maxVisible).join("");
99
104
  }
100
105
  const lines = [];
101
106
  lines.push(`┌${"─".repeat(width - 2)}┐`);
@@ -138,14 +143,18 @@ var TextInput = class extends BaseWidget {
138
143
  return true;
139
144
  case "Backspace":
140
145
  if (this.cursorPos > 0) {
141
- this.data.value = this.data.value.slice(0, this.cursorPos - 1) + this.data.value.slice(this.cursorPos);
142
- this.cursorPos--;
146
+ const g = splitGraphemes(this.data.value);
147
+ const pos = Math.min(this.cursorPos, g.length);
148
+ this.data.value = g.slice(0, pos - 1).join("") + g.slice(pos).join("");
149
+ this.cursorPos = pos - 1;
143
150
  this.data.onChange?.(this.data.value);
144
151
  }
145
152
  return true;
146
153
  case "Delete":
147
154
  if (this.cursorPos < this.data.value.length) {
148
- this.data.value = this.data.value.slice(0, this.cursorPos) + this.data.value.slice(this.cursorPos + 1);
155
+ const g = splitGraphemes(this.data.value);
156
+ const pos = Math.min(this.cursorPos, g.length);
157
+ this.data.value = g.slice(0, pos).join("") + g.slice(pos + 1).join("");
149
158
  this.data.onChange?.(this.data.value);
150
159
  }
151
160
  return true;
@@ -175,9 +184,13 @@ var TextInput = class extends BaseWidget {
175
184
  }
176
185
  if (char && char.length === 1 && char >= " ") {
177
186
  const max = this.data.maxLength;
178
- if (max > 0 && this.data.value.length >= max) return true;
179
- this.data.value = this.data.value.slice(0, this.cursorPos) + char + this.data.value.slice(this.cursorPos);
180
- this.cursorPos++;
187
+ if (max > 0) {
188
+ if (splitGraphemes(this.data.value).length >= max) return true;
189
+ }
190
+ const g = splitGraphemes(this.data.value);
191
+ const pos = Math.min(this.cursorPos, g.length);
192
+ this.data.value = g.slice(0, pos).join("") + char + g.slice(pos).join("");
193
+ this.cursorPos = pos + 1;
181
194
  this.data.onChange?.(this.data.value);
182
195
  return true;
183
196
  }
@@ -280,7 +293,7 @@ var SelectList = class extends BaseWidget {
280
293
  if (!this.focused) return false;
281
294
  const { key, char, ctrl } = event;
282
295
  const items = this.getFilteredItems();
283
- const maxVisible = Math.max(1, this.viewportHeight - 2);
296
+ const maxVisible = Math.max(1, this.viewportHeight - (this.data.filterActive ? 4 : 2));
284
297
  if (this.data.filterActive) {
285
298
  if (key === "Escape") {
286
299
  this.data.filterActive = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dui-toolkit/plugin-tui",
3
- "version": "0.1.1-next.3",
3
+ "version": "0.1.1-next.5",
4
4
  "description": "TUI widget toolkit plugin for @bdocs/dui — text inputs, select lists, modals, status bars, and more.",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -28,7 +28,7 @@
28
28
  "license": "MIT",
29
29
  "type": "module",
30
30
  "dependencies": {
31
- "@bdocs/dui": "0.7.0-next.5"
31
+ "@bdocs/dui": "0.7.0-next.7"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^22.0.0",
@@ -36,6 +36,9 @@
36
36
  "typescript": "^5.9.3",
37
37
  "vitest": "^3.0.0"
38
38
  },
39
+ "peerDependencies": {
40
+ "@bdocs/dui": "0.7.0-next.7"
41
+ },
39
42
  "scripts": {
40
43
  "build": "tsdown --config-loader unrun",
41
44
  "dev": "tsdown --watch --config-loader unrun",