lazyclaw 5.3.2 → 5.3.3

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/package.json +2 -1
  2. package/tui/editor.mjs +47 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazyclaw",
3
- "version": "5.3.2",
3
+ "version": "5.3.3",
4
4
  "description": "Lazy, elegant terminal CLI for chatting with Claude / OpenAI / Gemini / Ollama, orchestrating multi-step LLM workflows, and running multi-agent Slack teams with cross-task memory. Banner-on-launch, slash-command ghost autocomplete, persistent sessions, local HTTP gateway.",
5
5
  "keywords": [
6
6
  "claude",
@@ -87,6 +87,7 @@
87
87
  "better-sqlite3": "^11.10.0",
88
88
  "chalk": "^5.3.0",
89
89
  "ink": "^5.0.1",
90
+ "ink-testing-library": "^4.0.0",
90
91
  "node-ssh": "^13.2.0",
91
92
  "string-width": "^7.2.0",
92
93
  "undici": "^6.21.0"
package/tui/editor.mjs CHANGED
@@ -217,6 +217,49 @@ export function Editor({
217
217
  }, [state.lastSubmit]);
218
218
 
219
219
  const lines = state.buffer.split('\n');
220
+ // Manual cell-aware wrapping. Ink's <Text wrap="wrap"> uses wrap-ansi,
221
+ // which IS string-width aware, but the box's width:'100%' resolves
222
+ // against ink-testing-library's stdout shim (and some real terminals
223
+ // when columns is reset by SIGWINCH late) at 100 cols regardless of
224
+ // the actual viewport — so wide CJK buffers visibly bleed past the
225
+ // box right edge in narrow terminals. Pre-wrap to the actual cell
226
+ // budget so Ink never has to guess.
227
+ const TERM = Math.max(20, process.stdout.columns || 80);
228
+ // Box overhead: 1 border + 1 padX on each side = 4 cells; first row
229
+ // also reserves PROMPT_WIDTH; continuation rows reserve CONTINUATION_WIDTH.
230
+ function wrapToBudget(text, firstBudget, contBudget) {
231
+ if (!text) return [''];
232
+ const out = [];
233
+ let line = '';
234
+ let lineW = 0;
235
+ let budget = firstBudget;
236
+ for (const ch of text) {
237
+ const w = stringWidth(ch);
238
+ if (lineW + w > budget) {
239
+ out.push(line);
240
+ line = ch;
241
+ lineW = w;
242
+ budget = contBudget;
243
+ } else {
244
+ line += ch;
245
+ lineW += w;
246
+ }
247
+ }
248
+ out.push(line);
249
+ return out;
250
+ }
251
+ const innerCells = Math.max(8, TERM - 4); // 2 border + 2 padX
252
+ const renderedLines = [];
253
+ for (let li = 0; li < lines.length; li++) {
254
+ const wrapped = wrapToBudget(lines[li], innerCells - PROMPT_WIDTH, innerCells - CONTINUATION_WIDTH);
255
+ for (let wi = 0; wi < wrapped.length; wi++) {
256
+ const isFirstLogical = li === 0 && wi === 0;
257
+ renderedLines.push({
258
+ prefix: isFirstLogical ? theme.accent(PROMPT_PREFIX) : CONTINUATION_GUTTER,
259
+ text: wrapped[wi],
260
+ });
261
+ }
262
+ }
220
263
  return React.createElement(
221
264
  Box,
222
265
  {
@@ -225,17 +268,12 @@ export function Editor({
225
268
  paddingX: 1,
226
269
  flexDirection: 'column',
227
270
  flexShrink: 0,
228
- // Pin to full terminal width. Ink's wrap-ansi (which is
229
- // string-width aware) then has the correct cell budget for
230
- // wrapping long CJK buffers — fixes the right-edge truncation
231
- // perceived on Hangul / Han input. See `displayWidth`/
232
- // `cursorDisplayCol` above for the public width helpers.
233
- width: '100%',
271
+ width: TERM,
234
272
  },
235
- lines.map((ln, i) => React.createElement(
273
+ renderedLines.map((row, i) => React.createElement(
236
274
  Text,
237
- { key: i },
238
- i === 0 ? theme.accent(PROMPT_PREFIX) + ln : CONTINUATION_GUTTER + ln,
275
+ { key: i, wrap: 'truncate' },
276
+ row.prefix + row.text,
239
277
  )),
240
278
  );
241
279
  }