zames_pro 2.89.0 → 2.90.0
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/CHANGELOG.md +47 -0
- package/README.md +32 -585
- package/dist/agent-loop.js +47 -4
- package/dist/ask-retry.js +49 -0
- package/dist/ask-state.js +80 -0
- package/dist/browser-base.js +69 -0
- package/dist/browser.js +170 -41
- package/dist/command-registry.js +58 -0
- package/dist/commands.js +25 -2
- package/dist/completion-check.js +77 -0
- package/dist/config-schema.js +8 -0
- package/dist/config.js +14 -0
- package/dist/dev-commands.js +27 -0
- package/dist/dev-watch.js +96 -0
- package/dist/first-run.js +80 -0
- package/dist/glm-browser.js +133 -32
- package/dist/i18n/common.js +6 -3
- package/dist/i18n/config.js +16 -0
- package/dist/i18n/wizard.js +25 -0
- package/dist/i18n.js +6 -1
- package/dist/index.js +1289 -919
- package/dist/info-commands.js +61 -0
- package/dist/input/layout.js +38 -1
- package/dist/input.js +2 -2
- package/dist/password-store.js +48 -0
- package/dist/prompt-budget.js +23 -0
- package/dist/provider-errors.js +53 -0
- package/dist/provider-fallback.js +46 -0
- package/dist/qwen-browser.js +155 -39
- package/dist/run-task.js +2 -1
- package/dist/scheduler-commands.js +41 -0
- package/dist/send-slot.js +36 -0
- package/dist/session-commands.js +42 -0
- package/dist/spinner.js +3 -3
- package/dist/swallow.js +45 -0
- package/dist/system-prompt.js +2 -2
- package/dist/tree-commands.js +36 -0
- package/dist/tui/FullScreenApp.js +13 -4
- package/dist/tui/InputLine.js +20 -3
- package/dist/tui/LogView.js +11 -5
- package/dist/tui/StatusBar.js +4 -1
- package/dist/tui/TuiApp.js +11 -2
- package/dist/tui/layout.js +14 -1
- package/dist/tui/store.js +2 -1
- package/dist/tui/ui-colors.js +2 -0
- package/dist/width.js +10 -5
- package/llms-full.txt +16 -292
- package/package.json +2 -2
package/dist/swallow.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// N7: ACCOUNTING for swallowed exceptions instead of silent `catch {}`.
|
|
2
|
+
//
|
|
3
|
+
// The browser classes have ~180 empty catches (a selector probe, a best-effort
|
|
4
|
+
// cleanup, an optional DOM read). When a site redesign breaks a selector, such
|
|
5
|
+
// a catch turns the failure into silence: the UI health probe is clean, yet the
|
|
6
|
+
// answer never appears. This helper records the failure by TAG (always) and
|
|
7
|
+
// prints it only in debug mode, so the counters can be surfaced by /doctor and
|
|
8
|
+
// the send telemetry without flooding the terminal.
|
|
9
|
+
//
|
|
10
|
+
// A genuinely expected empty catch (best-effort cleanup, an absent optional
|
|
11
|
+
// element) keeps `swallowSilently()` with a WHY comment — the point is that
|
|
12
|
+
// silence becomes an EXPLICIT choice, not the default.
|
|
13
|
+
const counts = new Map();
|
|
14
|
+
/** Record a swallowed failure. `detail` is only used in debug mode. */
|
|
15
|
+
export function noteSwallowed(tag, err, debug = false) {
|
|
16
|
+
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
17
|
+
if (debug) {
|
|
18
|
+
const msg = err instanceof Error ? err.message : String(err ?? '');
|
|
19
|
+
console.error('[swallowed] ' + tag + (msg ? ': ' + msg : ''));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Run `fn`, swallowing (and counting) any error under `tag`.
|
|
24
|
+
* Returns `fallback` when it failed, so the caller keeps its happy path.
|
|
25
|
+
*/
|
|
26
|
+
export async function swallow(tag, fn, fallback, debug = false) {
|
|
27
|
+
try {
|
|
28
|
+
return await fn();
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
noteSwallowed(tag, e, debug);
|
|
32
|
+
return fallback;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** The swallowed-failure counters, sorted by count (for /doctor). */
|
|
36
|
+
export function swallowedReport(limit = 5) {
|
|
37
|
+
return [...counts.entries()]
|
|
38
|
+
.map(([tag, count]) => ({ tag, count }))
|
|
39
|
+
.sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag))
|
|
40
|
+
.slice(0, limit);
|
|
41
|
+
}
|
|
42
|
+
/** Reset the counters (tests and a fresh session). */
|
|
43
|
+
export function resetSwallowed() {
|
|
44
|
+
counts.clear();
|
|
45
|
+
}
|
package/dist/system-prompt.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { translate } from './i18n.js';
|
|
2
|
+
import { translate, DEFAULT_LOCALE } from './i18n.js';
|
|
3
3
|
// The dev-mode BACKLOG instruction. English (agent-facing) like the rest of the
|
|
4
4
|
// prompt. Deliberately narrow: one short note at most, and only when the idea
|
|
5
5
|
// is OUTSIDE the current task — otherwise the model documents trivia instead
|
|
@@ -204,7 +204,7 @@ function renderToolSection(tools) {
|
|
|
204
204
|
.map((t2) => `### ${t2.name}\n${t2.description}\nParameters: ${JSON.stringify(t2.parameters)}`)
|
|
205
205
|
.join('\n\n');
|
|
206
206
|
}
|
|
207
|
-
export function buildSystemPrompt({ workdir, tools, gitContext = null, locale =
|
|
207
|
+
export function buildSystemPrompt({ workdir, tools, gitContext = null, locale = DEFAULT_LOCALE, attachments = [], context = null, selfImprovement = false, provider, }) {
|
|
208
208
|
const t = translate(locale);
|
|
209
209
|
const toolDescriptions = renderToolSection(tools);
|
|
210
210
|
// Emit the subagent guidance ONLY when the Task tool is actually in the set
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// N2 group (c): the git / undo / rewind commands — /diff, /diffstat, /undo,
|
|
2
|
+
// /undo-list (/history), /rewind-list (/checkpoints), /rewind. All of them
|
|
3
|
+
// drive live stores (git, the undo store, the checkpoint store) and the editor
|
|
4
|
+
// lock, so the work is INJECTED; the registry owns the names, aliases and the
|
|
5
|
+
// argument forwarding.
|
|
6
|
+
/** True when the typed line carries the `--staged` flag (any position). */
|
|
7
|
+
export function hasStagedFlag(line) {
|
|
8
|
+
return /(^|\s)--staged(\s|$)/.test(line);
|
|
9
|
+
}
|
|
10
|
+
export function createTreeCommands(deps) {
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
name: 'diff',
|
|
14
|
+
handler: (ctx) => deps.diff(hasStagedFlag(ctx.input)),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'diffstat',
|
|
18
|
+
handler: (ctx) => deps.diffstat(hasStagedFlag(ctx.input)),
|
|
19
|
+
},
|
|
20
|
+
{ name: 'undo', handler: () => deps.undo() },
|
|
21
|
+
{
|
|
22
|
+
name: 'undo-list',
|
|
23
|
+
aliases: ['history'],
|
|
24
|
+
handler: () => deps.undoList(),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'rewind-list',
|
|
28
|
+
aliases: ['checkpoints'],
|
|
29
|
+
handler: () => deps.rewindList(),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'rewind',
|
|
33
|
+
handler: (ctx) => deps.rewind(ctx.rest.split(/\s+/)[0] || '1', ctx.input),
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
}
|
|
@@ -17,14 +17,17 @@ import { StatusBar } from './StatusBar.js';
|
|
|
17
17
|
import { InputLine } from './InputLine.js';
|
|
18
18
|
import { translate } from '../i18n.js';
|
|
19
19
|
import { suggestRowCount } from '../input/suggest.js';
|
|
20
|
-
import { truncateToWidth, visLen } from '../input/layout.js';
|
|
20
|
+
import { truncateToWidth, visLen, screenGutter, screenInnerCols, } from '../input/layout.js';
|
|
21
21
|
import { fullScreenLayout } from './layout.js';
|
|
22
22
|
import { useMouseWheel } from './useMouseWheel.js';
|
|
23
23
|
import { UI, inputBorderCols } from './ui-colors.js';
|
|
24
24
|
/** The header bar: title, version, working directory. */
|
|
25
25
|
function Header({ store }) {
|
|
26
26
|
const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
27
|
-
|
|
27
|
+
// The header lives INSIDE the gutter, so it must lay out to the pane width:
|
|
28
|
+
// filling the terminal width would wrap the title row and add a row the
|
|
29
|
+
// layout does not reserve.
|
|
30
|
+
const cols = screenInnerCols(useWindowSize().columns || 80);
|
|
28
31
|
const right = [state.provider && '[' + state.provider + ']']
|
|
29
32
|
.filter(Boolean)
|
|
30
33
|
.join(' ');
|
|
@@ -57,7 +60,13 @@ export function FullScreenApp({ store, inputProps, }) {
|
|
|
57
60
|
// caret row can never drift apart (the component used to compute them
|
|
58
61
|
// separately and the caret landed below the input box when the panel filled
|
|
59
62
|
// the terminal).
|
|
60
|
-
|
|
63
|
+
// Gutter on both sides: the operator asked for breathing room instead of a
|
|
64
|
+
// UI pinned to the terminal edges (most visible on a wide screen, where the
|
|
65
|
+
// theme's background made the flush layout look broken). Everything below
|
|
66
|
+
// (header, log wrap, status, input box, hint) uses the SAME pane width.
|
|
67
|
+
const termCols = Math.max(1, useWindowSize().columns || 80);
|
|
68
|
+
const gutter = screenGutter(termCols);
|
|
69
|
+
const cols = screenInnerCols(termCols);
|
|
61
70
|
const fsLayout = fullScreenLayout({
|
|
62
71
|
termRows,
|
|
63
72
|
cols,
|
|
@@ -112,7 +121,7 @@ export function FullScreenApp({ store, inputProps, }) {
|
|
|
112
121
|
const next = cur + delta * 3;
|
|
113
122
|
store.set({ scroll: Math.max(0, Math.min(maxScroll, next)) });
|
|
114
123
|
}, state.mouseScroll);
|
|
115
|
-
return (_jsxs(Box, { flexDirection: "column", height: termRows, children: [_jsx(Header, { store: store }), _jsx(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, minHeight: 1, overflow: "hidden", justifyContent: "flex-end", children: all.length === 0 ? (_jsx(Text, { color: UI.dim, children: translate(state.locale)('editor.empty_hint') })) : (_jsx(LogList, { entries: visible })) }), _jsx(StatusBar, { state: state, divider: false }), _jsx(InputLine, { ...inputProps, store: store,
|
|
124
|
+
return (_jsxs(Box, { flexDirection: "column", height: termRows, paddingLeft: gutter, paddingRight: gutter, children: [_jsx(Header, { store: store }), _jsx(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, minHeight: 1, overflow: "hidden", justifyContent: "flex-end", children: all.length === 0 ? (_jsx(Text, { color: UI.dim, children: translate(state.locale)('editor.empty_hint') })) : (_jsx(LogList, { entries: visible })) }), _jsx(StatusBar, { state: state, divider: false }), _jsx(InputLine, { ...inputProps, store: store,
|
|
116
125
|
// Cursor rows above the input box: header + log viewport + status.
|
|
117
126
|
// The log pane is FLEXIBLE now, so the panel below it starts right
|
|
118
127
|
// after everything above: header + as many log rows as fit + the
|
package/dist/tui/InputLine.js
CHANGED
|
@@ -12,10 +12,12 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
12
12
|
import { Box, Text, useInput, usePaste, useCursor, useWindowSize, useStdin, } from 'ink';
|
|
13
13
|
import { translate } from '../i18n.js';
|
|
14
14
|
import { expandPastes, inputInnerCols, layoutInput, SUGGEST_PAGE, } from '../input/layout.js';
|
|
15
|
+
import { screenInnerCols, screenGutter } from '../input/layout.js';
|
|
15
16
|
import { insertText, insertPaste, insertNewline, backspace, del, moveLeft, moveRight, moveHome, moveEnd, wordLeft, wordRight, deleteWordLeft, deleteWordRight, killToLineEnd, moveVisualRows, pushUndo, undoEdit, historyUp, historyDown, searchStart, searchNext, searchAccept, searchCancel, searchInput, searchBackspace, recordHistory, afterSubmit, rememberDraft, } from './edit.js';
|
|
16
17
|
import { dispatchKey, parseCsiU } from './keys.js';
|
|
17
18
|
import { useSyncExternalStore } from 'react';
|
|
18
19
|
import { matchSlashCommands, suggestWindow, completeSlashCommand, } from '../input/suggest.js';
|
|
20
|
+
import { suggestRowCount } from '../input/suggest.js';
|
|
19
21
|
import { UI, inputBorderStyle } from './ui-colors.js';
|
|
20
22
|
/** Render the visual rows of the buffer with the prompt on the first row. */
|
|
21
23
|
function InputRows({ prompt, buf, cols, }) {
|
|
@@ -23,8 +25,10 @@ function InputRows({ prompt, buf, cols, }) {
|
|
|
23
25
|
return (_jsx(Box, { flexDirection: "column", children: lay.rows.map((r, i) => (_jsxs(Text, { children: [_jsx(Text, { color: UI.accent, bold: true, children: r.prefix }), _jsx(Text, { color: UI.text, children: r.text })] }, i))) }));
|
|
24
26
|
}
|
|
25
27
|
export function InputLine(props) {
|
|
28
|
+
// Pane width (gutter excluded): the box must not be wider than the padded
|
|
29
|
+
// root, or it overflows the row and the caret math drifts.
|
|
26
30
|
const { columns } = useWindowSize();
|
|
27
|
-
const cols = columns || 80;
|
|
31
|
+
const cols = screenInnerCols(columns || 80);
|
|
28
32
|
const { setCursorPosition } = useCursor();
|
|
29
33
|
const [suggestSelected, setSuggestSelected] = useState(-1);
|
|
30
34
|
// The visible page of the suggestion list follows the SELECTION: with more
|
|
@@ -317,8 +321,21 @@ export function InputLine(props) {
|
|
|
317
321
|
// code re-measured `rowText` up to `cursorCol` code points, which
|
|
318
322
|
// DOUBLE-counted wide chars: a CJK buffer put the caret at the line end
|
|
319
323
|
// instead of under the cursor.
|
|
320
|
-
2 +
|
|
321
|
-
|
|
324
|
+
// 2 (box border + inner padding) + the ROOT gutter (paddingLeft of the
|
|
325
|
+
// full-screen/inline root) + the column inside the box. The gutter was
|
|
326
|
+
// forgotten: at 170 columns the root pads 2, so the caret sat exactly 2
|
|
327
|
+
// cells LEFT of the text the operator typed.
|
|
328
|
+
screenGutter(columns || 80) + 2 + lay.cursorCol;
|
|
329
|
+
// The suggestion popup is drawn ABOVE the box in THIS component, so it pushes
|
|
330
|
+
// the input rows DOWN by its own height. `cursorTop` (from the app) counts
|
|
331
|
+
// the rows above the INPUT BOX, not above the popup, so the popup rows are
|
|
332
|
+
// added HERE — exactly where they are rendered. (A caller that folded them
|
|
333
|
+
// in as well double-counted them and the caret landed inside the popup.)
|
|
334
|
+
const popupRows = suggestions.length > 0 ? suggestRowCount(st.buf, props.commands, cols) : 0;
|
|
335
|
+
setCursorPosition({
|
|
336
|
+
x: cursorX,
|
|
337
|
+
y: (props.cursorTop ?? 3) + popupRows + cursorRow,
|
|
338
|
+
});
|
|
322
339
|
const { shown, moreCount } = suggestWindow(suggestions, suggestOffset);
|
|
323
340
|
const t = translate(props.locale);
|
|
324
341
|
return (_jsxs(Box, { flexDirection: "column", children: [suggestions.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [shown.map((c, i) => {
|
package/dist/tui/LogView.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Static, Box, Text } from 'ink';
|
|
3
3
|
import { theme } from '../theme.js';
|
|
4
|
+
import { UI } from './ui-colors.js';
|
|
4
5
|
/** One log entry. `raw` items already carry ANSI colors and are printed
|
|
5
6
|
* verbatim; structured items get a themed prefix. */
|
|
6
7
|
function LogRow({ entry }) {
|
|
@@ -10,10 +11,15 @@ function LogRow({ entry }) {
|
|
|
10
11
|
return _jsx(Text, { children: entry.text });
|
|
11
12
|
}
|
|
12
13
|
if (entry.bar) {
|
|
13
|
-
// An operator turn: `▌` on EVERY visual row.
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
14
|
+
// An operator turn: `▌` on EVERY visual row.
|
|
15
|
+
//
|
|
16
|
+
// A SINGLE <Text> with a colored prefix child is NOT enough: Ink renders a
|
|
17
|
+
// multi-line string as-is, so the prefix appears on the FIRST line only
|
|
18
|
+
// (the old comment claimed the opposite and the bug survived for that
|
|
19
|
+
// reason). The text is already hard-wrapped by the caller
|
|
20
|
+
// (wrapToWidth), so split it back into rows and draw the bar on each one.
|
|
21
|
+
const rows = String(entry.text ?? '').split(String.fromCharCode(10));
|
|
22
|
+
return (_jsx(Box, { flexDirection: "column", children: rows.map((row, i) => (_jsxs(Text, { children: [_jsx(Text, { color: UI.userBar, children: "\u258C " }), _jsx(Text, { color: UI.user, children: row })] }, i))) }));
|
|
17
23
|
}
|
|
18
24
|
switch (entry.kind) {
|
|
19
25
|
case 'user':
|
package/dist/tui/StatusBar.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text, useAnimation, useWindowSize } from 'ink';
|
|
3
3
|
import { translate } from '../i18n.js';
|
|
4
|
+
import { screenInnerCols } from '../input/layout.js';
|
|
4
5
|
import { DOTS, stripEllipsis } from '../spinner.js';
|
|
5
6
|
import { UI } from './ui-colors.js';
|
|
6
7
|
import { truncateToWidth, visLen } from '../input/layout.js';
|
|
@@ -175,7 +176,9 @@ export function StatusBar({ state, divider = true, }) {
|
|
|
175
176
|
// row sat empty; measuring the badges (they are DROPPED whole when the row
|
|
176
177
|
// is tight, the context counter survives longest) is both accurate and
|
|
177
178
|
// keeps the layout stable.
|
|
178
|
-
|
|
179
|
+
// Pane width: the full-screen/inline root keeps a gutter on both sides, so
|
|
180
|
+
// the divider must not run to the terminal edge.
|
|
181
|
+
const cols = screenInnerCols(useWindowSize().columns || 80);
|
|
179
182
|
const badgeW = badgeTextWidth(state);
|
|
180
183
|
const gap = badgeW > 0 ? 2 : 0;
|
|
181
184
|
const leftBudget = Math.max(0, cols - 2 - badgeW - gap);
|
package/dist/tui/TuiApp.js
CHANGED
|
@@ -14,6 +14,7 @@ import { LogView } from './LogView.js';
|
|
|
14
14
|
import { StatusBar } from './StatusBar.js';
|
|
15
15
|
import { InputLine } from './InputLine.js';
|
|
16
16
|
import { suggestRowCount } from '../input/suggest.js';
|
|
17
|
+
import { screenGutter, screenInnerCols } from '../input/layout.js';
|
|
17
18
|
export function TuiApp({ store, inputProps, initialPrintedCount = 0, }) {
|
|
18
19
|
const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
19
20
|
// The watermark is fixed for the lifetime of this mount.
|
|
@@ -23,7 +24,15 @@ export function TuiApp({ store, inputProps, initialPrintedCount = 0, }) {
|
|
|
23
24
|
// + the suggestion popup (which renders above the box and would otherwise
|
|
24
25
|
// shift the caret down by its row count). Without the suggestRows term the
|
|
25
26
|
// caret drifted whenever the «/» list opened.
|
|
26
|
-
|
|
27
|
+
// The INLINE layout needs the same gutter the full-screen one has: the
|
|
28
|
+
// operator's complaint was that switching a theme made the flush edges
|
|
29
|
+
// obvious (the themed background only covers what we paint). The width fed
|
|
30
|
+
// to the suggest-row math must match the padded pane, or the caret drifts.
|
|
31
|
+
const gutter = screenGutter(useWindowSize().columns || 80);
|
|
32
|
+
const cols = screenInnerCols(useWindowSize().columns || 80);
|
|
27
33
|
const suggestRows = suggestRowCount(state.edit.buf, state.commands, cols);
|
|
28
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(LogView, { entries: entries }), _jsx(StatusBar, { state: state }), _jsx(InputLine, { ...inputProps, store: store, prompt: state.prompt, locale: state.locale, commands: state.commands, locked: state.locked,
|
|
34
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: gutter, paddingRight: gutter, children: [_jsx(LogView, { entries: entries }), _jsx(StatusBar, { state: state }), _jsx(InputLine, { ...inputProps, store: store, prompt: state.prompt, locale: state.locale, commands: state.commands, locked: state.locked,
|
|
35
|
+
// 3 = divider + status + the box TOP border. The popup sits ABOVE
|
|
36
|
+
// the box and does not move it, so it must not shift the caret.
|
|
37
|
+
cursorTop: 3 })] }));
|
|
29
38
|
}
|
package/dist/tui/layout.js
CHANGED
|
@@ -12,13 +12,26 @@ const STATUS_ROWS = 1;
|
|
|
12
12
|
export const HINT_ROWS = 1;
|
|
13
13
|
export function fullScreenLayout(input) {
|
|
14
14
|
const termRows = Math.max(1, Math.floor(input.termRows) || 24);
|
|
15
|
+
// `cols` is the WIDTH OF A PANE, not of the terminal: the full-screen app
|
|
16
|
+
// puts a gutter on both sides (screenInnerCols), and the input box wraps to
|
|
17
|
+
// exactly that inner width. Using the terminal width here reserved one row
|
|
18
|
+
// too few the moment a long input filled the pane (the box then wrapped an
|
|
19
|
+
// extra line and the caret drifted), so the caller MUST pass the inner
|
|
20
|
+
// width — and the pure helper stays consistent with what it renders.
|
|
15
21
|
const cols = Math.max(1, Math.floor(input.cols) || 80);
|
|
16
22
|
const box = inputBoxRows(input.prompt, input.buf, input.cursor, cols, input.borderCols ?? 2);
|
|
17
23
|
const suggestRows = Math.max(0, Math.floor(input.suggestRows) || 0);
|
|
18
24
|
const panelRows = HEADER_ROWS + STATUS_ROWS + box + suggestRows + HINT_ROWS;
|
|
19
25
|
const logHeight = Math.max(1, termRows - panelRows);
|
|
20
26
|
// The caret counts the SAME viewport the reservation uses.
|
|
21
|
-
|
|
27
|
+
// The suggestion popup is drawn ABOVE the box and the box does NOT move, so
|
|
28
|
+
// the caret row must NOT include `suggestRows`: adding it put the terminal
|
|
29
|
+
// caret on the popup line (the operator saw the block cursor between
|
|
30
|
+
// `/chats` and the box). The popup only SHRINKS the log viewport — which
|
|
31
|
+
// `logHeight` above already accounts for.
|
|
32
|
+
// `cursorTop` is the row of the input BOX top border (the popup and the
|
|
33
|
+
// buffer rows are added by InputLine, where they are actually rendered).
|
|
34
|
+
const cursorTop = HEADER_ROWS + logHeight + STATUS_ROWS + 1;
|
|
22
35
|
return {
|
|
23
36
|
headerRows: HEADER_ROWS,
|
|
24
37
|
statusRows: STATUS_ROWS,
|
package/dist/tui/store.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// The store keeps the WHOLE app state in one immutable snapshot object so
|
|
14
14
|
// `useSyncExternalStore` gets a stable reference between changes (a new object
|
|
15
15
|
// is produced only on an actual mutation).
|
|
16
|
+
import { DEFAULT_LOCALE } from '../i18n.js';
|
|
16
17
|
import { initialEditState } from './edit.js';
|
|
17
18
|
function initialState() {
|
|
18
19
|
return {
|
|
@@ -34,7 +35,7 @@ function initialState() {
|
|
|
34
35
|
history: [],
|
|
35
36
|
prompt: '> ',
|
|
36
37
|
commands: [],
|
|
37
|
-
locale:
|
|
38
|
+
locale: DEFAULT_LOCALE,
|
|
38
39
|
contextText: null,
|
|
39
40
|
contextLevel: null,
|
|
40
41
|
queueLength: 0,
|
package/dist/tui/ui-colors.js
CHANGED
package/dist/width.js
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
// is printed at the full terminal width while something else stops at 100 --
|
|
5
5
|
// the mismatch looked ragged after a resize.
|
|
6
6
|
//
|
|
7
|
-
// 0 (default) =
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// 0 (default) = the FULL terminal width (minus one autowrap-safety column).
|
|
8
|
+
// The old default capped the content at 100 columns, which on a wide terminal
|
|
9
|
+
// left a wide empty band on the right of every answer; the operator asked for
|
|
10
|
+
// the text to use the whole width. A cap is still available per-run via
|
|
11
|
+
// `ui.answerWidth` when a narrower column is easier to read.
|
|
10
12
|
// > 0 = an explicit cap (still never wider than the terminal).
|
|
11
|
-
const DEFAULT_CONTENT_WIDTH =
|
|
13
|
+
const DEFAULT_CONTENT_WIDTH = 0;
|
|
12
14
|
let configured = 0;
|
|
13
15
|
/** Set the configured content width (0 or a non-positive value = auto). */
|
|
14
16
|
export function setContentWidth(width) {
|
|
@@ -21,5 +23,8 @@ export function setContentWidth(width) {
|
|
|
21
23
|
export function contentWidth() {
|
|
22
24
|
const cols = process.stdout.columns || 80;
|
|
23
25
|
const cap = configured > 0 ? configured : DEFAULT_CONTENT_WIDTH;
|
|
24
|
-
|
|
26
|
+
// A cap of 0 means "no cap beyond the terminal": use every column except
|
|
27
|
+
// the last one (autowrap safety).
|
|
28
|
+
const limit = cap > 0 ? Math.min(cap, cols - 1) : cols - 1;
|
|
29
|
+
return Math.max(20, limit);
|
|
25
30
|
}
|