@askalf/dario 5.4.3 → 5.4.4
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/dist/tui/render.js +9 -1
- package/dist/tui/tabs/config.js +25 -10
- package/package.json +1 -1
package/dist/tui/render.js
CHANGED
|
@@ -143,7 +143,15 @@ export function truncate(text, maxWidth, ellipsis = '…') {
|
|
|
143
143
|
visible++;
|
|
144
144
|
i++;
|
|
145
145
|
}
|
|
146
|
-
|
|
146
|
+
// Flush SGR sequences from the clipped remainder. Without this, a cut
|
|
147
|
+
// that lands inside a dim()/fg() block drops the closing code, leaving
|
|
148
|
+
// the attribute open — it then bleeds into the following line, and past
|
|
149
|
+
// the frame entirely (clearScreen does not reset SGR). Replaying the
|
|
150
|
+
// remainder's own codes rather than appending a blanket reset keeps an
|
|
151
|
+
// enclosing inverse() wrapper intact; clipped openers arrive paired
|
|
152
|
+
// with their closers, so they render as a no-op.
|
|
153
|
+
const tail = text.slice(i).match(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g);
|
|
154
|
+
return out + ellipsis + (tail ? tail.join('') : '');
|
|
147
155
|
}
|
|
148
156
|
/** Pad `text` (right-aligned by default 'left' fills right side) to `width`. */
|
|
149
157
|
export function pad(text, width, align = 'left') {
|
package/dist/tui/tabs/config.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* change. The DarioConfig schema is the source of truth — adding a
|
|
20
20
|
* field there + a row here lights it up.
|
|
21
21
|
*/
|
|
22
|
-
import { fg, dim, brand, inverse, pad } from '../render.js';
|
|
22
|
+
import { fg, dim, brand, inverse, pad, truncate } from '../render.js';
|
|
23
23
|
import { CONFIG_SCHEMA_VERSION, defaultConfig, loadConfig, saveConfig, } from '../../config-file.js';
|
|
24
24
|
/**
|
|
25
25
|
* The visible field registry. Order = display order. New fields just
|
|
@@ -101,40 +101,55 @@ export const ConfigTab = {
|
|
|
101
101
|
const lines = [];
|
|
102
102
|
const w = dimv.cols;
|
|
103
103
|
const labelW = 26;
|
|
104
|
-
|
|
104
|
+
// Fixed value column so the hint keeps room on the same row. Every
|
|
105
|
+
// row is padded/truncated to exactly `w` below: the frame is drawn
|
|
106
|
+
// from the top with no scrollback, so a row that wraps costs a
|
|
107
|
+
// second physical line and pushes the panel head off-screen.
|
|
108
|
+
const valueW = Math.max(6, Math.min(16, w - labelW - 4));
|
|
109
|
+
// Rows that fit between the title (+blank) and the trailing blank
|
|
110
|
+
// + prompt/status pair. Window FIELDS around the selection so the
|
|
111
|
+
// list scrolls instead of overflowing the body region.
|
|
112
|
+
const listRows = Math.max(1, dimv.rows - 5);
|
|
113
|
+
const startIdx = Math.max(0, Math.min(state.selectedIdx - Math.floor(listRows / 3), FIELDS.length - listRows));
|
|
114
|
+
const endIdx = Math.min(startIdx + listRows, FIELDS.length);
|
|
105
115
|
const dirty = isDirty(state);
|
|
106
116
|
const title = dirty
|
|
107
117
|
? brand('Config') + dim(' — ') + fg('yellow', '● unsaved changes')
|
|
108
118
|
: brand('Config');
|
|
109
|
-
|
|
119
|
+
const counter = FIELDS.length > listRows
|
|
120
|
+
? dim(` ${state.selectedIdx + 1} / ${FIELDS.length}`)
|
|
121
|
+
: '';
|
|
122
|
+
lines.push(truncate(' ' + title + counter, w));
|
|
110
123
|
lines.push('');
|
|
111
|
-
for (let i =
|
|
124
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
112
125
|
const field = FIELDS[i];
|
|
113
126
|
const value = getByPath(state.config, field.path);
|
|
114
127
|
const orig = getByPath(state.snapshot, field.path);
|
|
115
128
|
const changed = !Object.is(value, orig);
|
|
116
129
|
const valueRender = renderValue(field, value, changed);
|
|
117
130
|
const hint = field.hint ? ' ' + dim('— ' + field.hint) : '';
|
|
118
|
-
const row = ' ' + pad(field.label + ':', labelW) + pad(valueRender, valueW) + hint;
|
|
131
|
+
const row = pad(truncate(' ' + pad(field.label + ':', labelW) + pad(valueRender, valueW) + hint, w), w);
|
|
119
132
|
lines.push(i === state.selectedIdx ? inverse(row) : row);
|
|
120
133
|
}
|
|
121
134
|
// ── Edit prompt or status line ─────────────────────────────
|
|
122
135
|
lines.push('');
|
|
123
136
|
if (state.editBuffer !== null) {
|
|
124
137
|
const f = FIELDS[state.selectedIdx];
|
|
125
|
-
lines.push(' ' + fg('cyan', `Edit ${f.label}:`) + ' ' + state.editBuffer + fg('cyan', '_'));
|
|
126
|
-
lines.push(' ' + dim('Enter to confirm · Esc to cancel'));
|
|
138
|
+
lines.push(truncate(' ' + fg('cyan', `Edit ${f.label}:`) + ' ' + state.editBuffer + fg('cyan', '_'), w));
|
|
139
|
+
lines.push(truncate(' ' + dim('Enter to confirm · Esc to cancel'), w));
|
|
127
140
|
}
|
|
128
141
|
else if (state.statusMessage) {
|
|
129
142
|
const color = state.statusKind === 'error' ? 'red'
|
|
130
143
|
: state.statusKind === 'success' ? 'green'
|
|
131
144
|
: 'cyan';
|
|
132
|
-
lines.push(' ' + fg(color, state.statusMessage));
|
|
145
|
+
lines.push(truncate(' ' + fg(color, state.statusMessage), w));
|
|
133
146
|
}
|
|
134
147
|
else {
|
|
135
|
-
lines.push(' ' + dim('↑↓ navigate · Enter edit · s save · d discard · r reload'));
|
|
148
|
+
lines.push(truncate(' ' + dim('↑↓ navigate · Enter edit · s save · d discard · r reload'), w));
|
|
136
149
|
}
|
|
137
|
-
|
|
150
|
+
// Hard floor for terminals too short for even one field + prompt:
|
|
151
|
+
// drop trailing rows rather than let the panel run past the body.
|
|
152
|
+
return lines.slice(0, Math.max(1, dimv.rows)).join('\n');
|
|
138
153
|
},
|
|
139
154
|
};
|
|
140
155
|
// ── Helpers ───────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.4",
|
|
4
4
|
"description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|