koneck 2.128.72 → 2.128.74
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/config-store.d.ts +2 -0
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +8 -2
- package/dist/config-store.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/handoff.d.ts.map +1 -1
- package/dist/handoff.js +14 -2
- package/dist/handoff.js.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/ink-chat.d.ts +2 -0
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +123 -25
- package/dist/ink-chat.js.map +1 -1
- package/dist/plan.d.ts +9 -2
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +20 -4
- package/dist/plan.js.map +1 -1
- package/dist/provider-delete.d.ts +37 -0
- package/dist/provider-delete.d.ts.map +1 -0
- package/dist/provider-delete.js +76 -0
- package/dist/provider-delete.js.map +1 -0
- package/dist/run-progress.d.ts.map +1 -1
- package/dist/run-progress.js +11 -5
- package/dist/run-progress.js.map +1 -1
- package/dist/session-changes.d.ts +6 -0
- package/dist/session-changes.d.ts.map +1 -1
- package/dist/session-changes.js +10 -0
- package/dist/session-changes.js.map +1 -1
- package/dist/tools.d.ts +10 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +57 -1
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +14 -9
- package/dist/web/api.js.map +1 -1
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +21 -11
- package/dist/web/ui-client.js.map +1 -1
- package/package.json +1 -1
package/dist/ink-chat.js
CHANGED
|
@@ -10,7 +10,8 @@ import { estimateCost, formatCost, rateText } from './pricing.js';
|
|
|
10
10
|
import { generateSessionId, saveSession, listSessions, loadSession, relativeAge, renameSession, forkSession, archiveSession, findSession, } from './session.js';
|
|
11
11
|
import { loadMemory } from './memory.js';
|
|
12
12
|
import { loadKoneckConfig as loadConfigAt, saveKoneckConfig as saveConfigAt, setConfigKey as setConfigKeyAt, configKeyDescriptions, CONFIG_FILE, CONFIG_SCHEMA, stepValue, displayValue, settingsWindow } from './config-store.js';
|
|
13
|
-
import {
|
|
13
|
+
import { deleteProvider, deletable, describeDeleted } from './provider-delete.js';
|
|
14
|
+
import { PROVIDERS, resolveProvider, getApiKey, addUserProvider, userProvidersPath, keyEnvFor, BUILT_INS as BUILT_IN_PROVIDERS, servesLocally, servesOwnWeights, endpointForProvider } from './providers.js';
|
|
14
15
|
import { normaliseEndpoint, withEndpoint, savedEndpoints, shortSource, resolveModelChoice, withModel } from './endpoints.js';
|
|
15
16
|
import { loadProjectConfig, loadGlobalConfig, saveGlobalConfig, withoutStoredApiKey } from './config.js';
|
|
16
17
|
import { rememberKey, forgetAllKeys } from './credentials.js';
|
|
@@ -347,6 +348,11 @@ export function wrapPreserving(text, width) {
|
|
|
347
348
|
* file, which every earlier grep had failed to match for that reason.
|
|
348
349
|
*/
|
|
349
350
|
export const ADD_PROVIDER = '\u0000add-provider';
|
|
351
|
+
/** The provider picker's delete row, and the rows of the two pickers it leads to. */
|
|
352
|
+
export const DELETE_PROVIDER = '\u0000delete-provider';
|
|
353
|
+
const DELETE_ONE = '\u0000delete:';
|
|
354
|
+
const DELETE_CONFIRMED = '\u0000delete-confirmed:';
|
|
355
|
+
const DELETE_KEPT = '\u0000delete-kept';
|
|
350
356
|
/**
|
|
351
357
|
* Checks one field and says what is wrong with it, or nothing.
|
|
352
358
|
*
|
|
@@ -751,7 +757,7 @@ const COMMANDS = [
|
|
|
751
757
|
{ cmd: '/key', desc: 'Enter an API key for the current provider' },
|
|
752
758
|
{ cmd: '/logout', desc: 'Forget Koneck-stored API credentials; keep preferences' },
|
|
753
759
|
{ cmd: '/model', desc: 'Show or set the model by name' },
|
|
754
|
-
{ cmd: '/provider', desc: 'Show or
|
|
760
|
+
{ cmd: '/provider', desc: 'Show, switch, add or delete a provider' },
|
|
755
761
|
{ cmd: '/status', desc: 'Provider, model, mode, tokens, cost, workspace' },
|
|
756
762
|
{ cmd: '/diff', desc: 'Open the live diff panel; /diff off closes it' },
|
|
757
763
|
{ cmd: '/permissions', desc: 'Show or set approval posture: on or off' },
|
|
@@ -2539,6 +2545,16 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
2539
2545
|
* been chosen either way (/mouse, or `mouse` in the config), which is then what happens.
|
|
2540
2546
|
*/
|
|
2541
2547
|
const [sidePanelShown, setSidePanelShown] = useState(false);
|
|
2548
|
+
/*
|
|
2549
|
+
* The bell, for when KONECK needs the person: a long turn finished, or a question is waiting.
|
|
2550
|
+
* A terminal in another tab or window marks it — Windows Terminal flashes the tab and the
|
|
2551
|
+
* taskbar — so a finished hour-long run is noticed without watching for it. `bell: false` stops it.
|
|
2552
|
+
*/
|
|
2553
|
+
const ring = useCallback(() => {
|
|
2554
|
+
if (cfg.bell === false || !process.stdout.isTTY)
|
|
2555
|
+
return;
|
|
2556
|
+
process.stdout.write('\u0007');
|
|
2557
|
+
}, [cfg.bell]);
|
|
2542
2558
|
const mouseChosenRef = useRef(cfg.mouse !== undefined);
|
|
2543
2559
|
const wheelTracked = mouse || (sidePanelShown && !mouseChosenRef.current);
|
|
2544
2560
|
/*
|
|
@@ -2931,6 +2947,10 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
2931
2947
|
* all — only in the browser, and there as plain text.
|
|
2932
2948
|
*/
|
|
2933
2949
|
const [userQ, setUserQ] = useState(null);
|
|
2950
|
+
// A question waiting on the person rings once, as it appears: see `ring`.
|
|
2951
|
+
const waitingOnPerson = !!ask || !!planAsk || !!userQ;
|
|
2952
|
+
useEffect(() => { if (waitingOnPerson)
|
|
2953
|
+
ring(); }, [waitingOnPerson, ring]);
|
|
2934
2954
|
/** The picker's rows as last drawn, for a click to be matched against. */
|
|
2935
2955
|
const pickerTargetsRef = useRef([]);
|
|
2936
2956
|
const [updatePane, setUpdatePane] = useState(null);
|
|
@@ -4097,6 +4117,11 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
4097
4117
|
return [
|
|
4098
4118
|
{ value: ADD_PROVIDER, label: '+ add a provider',
|
|
4099
4119
|
desc: 'an OpenAI-compatible endpoint KONECK does not ship with' },
|
|
4120
|
+
// Only when there is something to delete, so the row never leads to an empty list.
|
|
4121
|
+
...(Object.keys(PROVIDERS).some(n => deletable(n))
|
|
4122
|
+
? [{ value: DELETE_PROVIDER, label: '- delete one',
|
|
4123
|
+
desc: 'delete a provider you added, or the key you stored for one' }]
|
|
4124
|
+
: []),
|
|
4100
4125
|
...Object.values(PROVIDERS).map(p => ({
|
|
4101
4126
|
value: p.name,
|
|
4102
4127
|
label: p.name,
|
|
@@ -4105,6 +4130,46 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
4105
4130
|
})),
|
|
4106
4131
|
];
|
|
4107
4132
|
}
|
|
4133
|
+
/** What can be deleted, as picker rows: yours first, then built-ins with something stored. */
|
|
4134
|
+
function deleteProviderItems() {
|
|
4135
|
+
return Object.values(PROVIDERS).filter(p => deletable(p.name))
|
|
4136
|
+
.sort((a, b) => Number(!!b.userDefined) - Number(!!a.userDefined))
|
|
4137
|
+
.map(p => ({
|
|
4138
|
+
value: DELETE_ONE + p.name, label: p.name,
|
|
4139
|
+
desc: p.userDefined ? `yours - ${p.baseURL}` : 'built in - forgets the key and settings you stored',
|
|
4140
|
+
current: p.name === cfg.provider,
|
|
4141
|
+
}));
|
|
4142
|
+
}
|
|
4143
|
+
/**
|
|
4144
|
+
* Deletes a provider and says what went. The provider in use loses its key in this session too,
|
|
4145
|
+
* and when it is gone altogether the picker opens to choose another — the next message would
|
|
4146
|
+
* otherwise fail against a provider that no longer exists.
|
|
4147
|
+
*/
|
|
4148
|
+
async function deleteProviderNamed(name) {
|
|
4149
|
+
const wasCurrent = name === cfg.provider;
|
|
4150
|
+
const done = await deleteProvider(name);
|
|
4151
|
+
if (!done) {
|
|
4152
|
+
addSystem(PROVIDERS[name]
|
|
4153
|
+
? `Nothing is stored for "${name}" — it ships with KONECK, with no key or settings of yours to delete.`
|
|
4154
|
+
: `No provider called "${name}".`);
|
|
4155
|
+
return;
|
|
4156
|
+
}
|
|
4157
|
+
(await import('./model-catalog.js')).forgetCatalog(name);
|
|
4158
|
+
if (!wasCurrent) {
|
|
4159
|
+
addSystem(describeDeleted(done));
|
|
4160
|
+
return;
|
|
4161
|
+
}
|
|
4162
|
+
if (PROVIDERS[name]) {
|
|
4163
|
+
const cleared = { ...cfg, apiKey: undefined };
|
|
4164
|
+
cfgRef.current = cleared;
|
|
4165
|
+
setCfg(cleared);
|
|
4166
|
+
await resetSession(cleared);
|
|
4167
|
+
addSystem(describeDeleted(done) + ' This session was using it, and has been reset.');
|
|
4168
|
+
return;
|
|
4169
|
+
}
|
|
4170
|
+
addSystem(describeDeleted(done) + ' This session was using it — choose another below.');
|
|
4171
|
+
openPicker('provider', providerItems());
|
|
4172
|
+
}
|
|
4108
4173
|
/** Opens the declare-a-provider prompt, optionally with fields already known. */
|
|
4109
4174
|
function startAddProvider(seed = {}) {
|
|
4110
4175
|
setAddProv({ step: 'name', name: '', url: '', model: '', value: '', ...seed });
|
|
@@ -4322,6 +4387,28 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
4322
4387
|
startAddProvider();
|
|
4323
4388
|
return;
|
|
4324
4389
|
}
|
|
4390
|
+
// Deleting: choose which, then say yes. The key cannot be got back from KONECK once it is gone.
|
|
4391
|
+
if (kind === 'provider' && item.value === DELETE_PROVIDER) {
|
|
4392
|
+
openPicker('provider', deleteProviderItems());
|
|
4393
|
+
return;
|
|
4394
|
+
}
|
|
4395
|
+
if (kind === 'provider' && item.value.startsWith(DELETE_ONE)) {
|
|
4396
|
+
const name = item.value.slice(DELETE_ONE.length);
|
|
4397
|
+
openPicker('provider', [
|
|
4398
|
+
{ value: DELETE_CONFIRMED + name, label: `yes, delete ${name}`,
|
|
4399
|
+
desc: PROVIDERS[name]?.userDefined ? 'its declaration, stored key and saved settings' : 'its stored key and saved settings' },
|
|
4400
|
+
{ value: DELETE_KEPT, label: 'no, keep it', desc: '' },
|
|
4401
|
+
]);
|
|
4402
|
+
return;
|
|
4403
|
+
}
|
|
4404
|
+
if (kind === 'provider' && item.value.startsWith(DELETE_CONFIRMED)) {
|
|
4405
|
+
await deleteProviderNamed(item.value.slice(DELETE_CONFIRMED.length));
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
4408
|
+
if (kind === 'provider' && item.value === DELETE_KEPT) {
|
|
4409
|
+
addSystem('Kept.');
|
|
4410
|
+
return;
|
|
4411
|
+
}
|
|
4325
4412
|
if (kind === 'provider') {
|
|
4326
4413
|
const def = PROVIDERS[item.value];
|
|
4327
4414
|
// The endpoint saved for the provider being switched to, not its built-in address. Passing
|
|
@@ -4792,25 +4879,19 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
4792
4879
|
});
|
|
4793
4880
|
return;
|
|
4794
4881
|
}
|
|
4795
|
-
if (
|
|
4882
|
+
if (['remove', 'delete', 'forget'].includes(verb?.toLowerCase() ?? '')) {
|
|
4796
4883
|
const target = (restArgs[0] ?? '').toLowerCase();
|
|
4797
4884
|
if (!target) {
|
|
4798
|
-
const
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
const wasShadowing = BUILT_IN_PROVIDERS[target] !== undefined;
|
|
4805
|
-
if (!removeUserProvider(target)) {
|
|
4806
|
-
addSystem(PROVIDERS[target]
|
|
4807
|
-
? `"${target}" ships with KONECK — it was not declared by you, so there is nothing to remove.`
|
|
4808
|
-
: `No declared provider called "${target}".`);
|
|
4885
|
+
const items = deleteProviderItems();
|
|
4886
|
+
if (!items.length) {
|
|
4887
|
+
addSystem('Nothing to delete: you have added no providers and stored no keys.');
|
|
4888
|
+
return;
|
|
4889
|
+
}
|
|
4890
|
+
openPicker('provider', items);
|
|
4809
4891
|
return;
|
|
4810
4892
|
}
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
: `Removed "${target}".`);
|
|
4893
|
+
// Typed out in full, the name is the confirmation; the picker asks, because a row is one key away.
|
|
4894
|
+
await deleteProviderNamed(target);
|
|
4814
4895
|
return;
|
|
4815
4896
|
}
|
|
4816
4897
|
if (!arg) {
|
|
@@ -4830,7 +4911,7 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
4830
4911
|
+ '\nSwitch: /provider <name>, or pick one below\n'
|
|
4831
4912
|
+ 'Add: /provider add — asks for a name, an endpoint and a default model\n'
|
|
4832
4913
|
+ ' /provider add <name> <url> [model] if you have them to hand\n'
|
|
4833
|
-
+ '
|
|
4914
|
+
+ 'Delete: /provider delete [name] — one you added, or the key you stored for one');
|
|
4834
4915
|
openPicker('provider', providerItems());
|
|
4835
4916
|
return;
|
|
4836
4917
|
}
|
|
@@ -7035,6 +7116,9 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
7035
7116
|
// whichever of those three happened last, which for anyone who closes the terminal
|
|
7036
7117
|
// normally was an old snapshot — the reported "always resumes the old one".
|
|
7037
7118
|
void doSave().catch(() => { });
|
|
7119
|
+
// A turn worth walking away from is worth being called back for.
|
|
7120
|
+
if (elapsed >= 30_000)
|
|
7121
|
+
ring();
|
|
7038
7122
|
// A run that stopped short says so, rather than letting the footer imply it finished.
|
|
7039
7123
|
if (incompleteRef.current) {
|
|
7040
7124
|
addSystem(incompleteRef.current);
|
|
@@ -7303,7 +7387,7 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
7303
7387
|
// content, so the right edge stepped in and out; and a box inside a box left
|
|
7304
7388
|
// markdown tables needlessly narrow. A marker plus the content reads better and
|
|
7305
7389
|
// cannot misalign, which is how Claude Code presents its own answers.
|
|
7306
|
-
_jsxs(Box, { flexDirection: "column", marginTop: row.continued ? 0 : 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: CYAN, bold: true, children: row.continued ? ' ' : G.brand }), _jsx(Box, { flexDirection: "column", children: _jsx(Markdown, { text: row.text ?? '', width: barWidth - 3 }) })] }), row.tokens != null && (_jsxs(Text, { color: MUTED, children: [
|
|
7390
|
+
_jsxs(Box, { flexDirection: "column", marginTop: row.continued ? 0 : 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: CYAN, bold: true, children: row.continued ? ' ' : G.brand }), _jsx(Box, { flexDirection: "column", children: _jsx(Markdown, { text: row.text ?? '', width: barWidth - 3 }) })] }), row.tokens != null && (_jsxs(Text, { color: MUTED, children: [_jsxs(Text, { color: GREEN, bold: true, children: [G.ok, " Done"] }), ` in ${fmtElapsed(row.elapsed ?? 0)}`, ' | ', row.tokens, " tokens", ' | ', cfg.provider, " ", modelShort] }))] }, index)));
|
|
7307
7391
|
/**
|
|
7308
7392
|
* A row that has stopped changing, and so never needs rendering twice.
|
|
7309
7393
|
*
|
|
@@ -7808,7 +7892,7 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
7808
7892
|
})(), keyPrompt && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AMBER, paddingX: 1, marginTop: 1, width: barWidth, children: [_jsxs(Text, { color: AMBER, bold: true, children: ["API key for ", keyPrompt.provider] }), _jsx(Text, { color: MUTED, children: "Paste it and press enter, or ctrl+v to read the clipboard. Held in memory for this session only; esc to cancel." }), keyPrompt.note && _jsx(Text, { color: CYAN, children: keyPrompt.note }), _jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: MUTED, children: [keyPrompt.env, ": "] }), _jsx(Text, { color: INK, children: '*'.repeat(Math.min(keyPrompt.value.length, 48)) }), _jsx(Cursor, {}), keyPrompt.value.length > 0 && (_jsxs(Text, { color: DIM, children: [" ", keyPrompt.value.length, " characters"] }))] })] })), themePick && (() => {
|
|
7809
7893
|
const w = Math.min(barWidth, 84);
|
|
7810
7894
|
const labelWidth = Math.max(...THEMES.map(t => t.label.length));
|
|
7811
|
-
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderColor: CYAN, paddingX: 1, width: w, children: [_jsx(Text, { color: INK, bold: true, children: "Theme" }), _jsx(Text, { color: MUTED, children: "Sets KONECK's colours, not your terminal's background." }), _jsx(Text, { children: " " }), THEMES.map((t, i) => {
|
|
7895
|
+
return (_jsxs(Box, { marginTop: 1, flexShrink: 0, flexDirection: "column", borderStyle: "round", borderColor: CYAN, paddingX: 1, width: w, children: [_jsx(Text, { color: INK, bold: true, children: "Theme" }), _jsx(Text, { color: MUTED, children: "Sets KONECK's colours, not your terminal's background." }), _jsx(Text, { children: " " }), THEMES.map((t, i) => {
|
|
7812
7896
|
const on = i === themePick.at;
|
|
7813
7897
|
return (_jsxs(Text, { wrap: "truncate", ...(on ? { backgroundColor: CYAN, color: ON_ACCENT } : { color: INK }), children: [on ? ' › ' : ' ', _jsx(Text, { bold: true, children: i + 1 }), ' ', t.label.padEnd(labelWidth), _jsx(Text, { ...(on ? {} : { color: DIM }), children: ' ' + t.note }), t.name === themePick.was ? ' (current)' : '', ' '] }, t.name));
|
|
7814
7898
|
}), _jsx(Text, { children: " " }), _jsx(ThemePreview, { theme: THEMES[themePick.at].name, width: w - 4 }), _jsxs(Text, { color: DIM, children: ["\u2191\u2193 or 1\u2013", THEMES.length, " to try \u00B7 enter to keep \u00B7 esc to go back"] })] }));
|
|
@@ -7820,7 +7904,15 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
7820
7904
|
'',
|
|
7821
7905
|
'Press a number, or up/down and enter, or click one — or type your own answer. Esc hides this.',
|
|
7822
7906
|
];
|
|
7823
|
-
|
|
7907
|
+
/*
|
|
7908
|
+
* Never shrunk: the transcript above gives way instead.
|
|
7909
|
+
*
|
|
7910
|
+
* Reported with a screenshot: on the alternate screen, after a long reply, only the last
|
|
7911
|
+
* line of this panel showed — "minor bug fixes" in an amber box — and the question and the
|
|
7912
|
+
* other options were clipped away, so it looked like a stray message rather than a
|
|
7913
|
+
* question. The transcript is the box meant to give ground; this one must not.
|
|
7914
|
+
*/
|
|
7915
|
+
return (_jsx(Box, { marginTop: 1, flexShrink: 0, children: _jsx(Panel, { width: Math.min(barWidth, 84), color: AMBER, title: "The model is asking", children: lines }) }));
|
|
7824
7916
|
})(), planAsk && (() => {
|
|
7825
7917
|
/*
|
|
7826
7918
|
* The question, straight after the plan rather than instead of it.
|
|
@@ -7837,7 +7929,7 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
7837
7929
|
'Up/down and enter, or press the number. Esc leaves it in plan mode.',
|
|
7838
7930
|
...(planAsk.file ? [`Saved to \`${planAsk.file}\``] : ['This plan could not be written to disk.']),
|
|
7839
7931
|
];
|
|
7840
|
-
return (_jsx(Box, { marginTop: 1, children: _jsx(Panel, { width: Math.min(barWidth, 84), color: CYAN, title: "Plan ready", children: lines }) }));
|
|
7932
|
+
return (_jsx(Box, { marginTop: 1, flexShrink: 0, children: _jsx(Panel, { width: Math.min(barWidth, 84), color: CYAN, title: "Plan ready", children: lines }) }));
|
|
7841
7933
|
})(), ask && (() => {
|
|
7842
7934
|
let detail = '';
|
|
7843
7935
|
let isCommand = false;
|
|
@@ -7955,9 +8047,6 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
7955
8047
|
const notices = [];
|
|
7956
8048
|
if (agentState !== 'ready')
|
|
7957
8049
|
notices.push({ text: statusLabel.toLowerCase(), color: statusColor });
|
|
7958
|
-
const planNote = plan ? planSummary(plan) : '';
|
|
7959
|
-
if (planNote)
|
|
7960
|
-
notices.push({ text: `plan ${planNote}`, color: CYAN });
|
|
7961
8050
|
if (effort !== 'medium')
|
|
7962
8051
|
notices.push({ text: `effort ${effort}`, color: AMBER });
|
|
7963
8052
|
if (autoUpdated)
|
|
@@ -7970,6 +8059,15 @@ export function App({ config: initialConfig, clearFrame, synchronizedFrames = fa
|
|
|
7970
8059
|
notices.push({ text: `${pasteCount} paste${pasteCount === 1 ? '' : 's'} held, sent in full`,
|
|
7971
8060
|
color: AMBER });
|
|
7972
8061
|
}
|
|
8062
|
+
// The plan last, given only the room the rest leaves: its step is the one notice of no
|
|
8063
|
+
// fixed length, and the screen edge used to cut it mid-word ("…critical journeys; r").
|
|
8064
|
+
if (plan) {
|
|
8065
|
+
const used = displayWidth(ml.title) + 2 + displayWidth(ml.hint)
|
|
8066
|
+
+ notices.reduce((w, n) => w + 5 + displayWidth(n.text), 0) + 5 + displayWidth('plan ');
|
|
8067
|
+
const planNote = planSummary(plan, Math.max(0, uiWidth - 9 - used));
|
|
8068
|
+
if (planNote)
|
|
8069
|
+
notices.push({ text: `plan ${planNote}`, color: CYAN });
|
|
8070
|
+
}
|
|
7973
8071
|
return (_jsx(Box, { marginTop: 1, paddingX: 2, width: uiWidth - 5, flexShrink: 0, children: _jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: TONE[ml.tone], bold: true, children: ml.title }), _jsxs(Text, { color: MUTED, children: [' ', ml.hint] }), notices.map((n, i) => (_jsxs(Text, { children: [_jsx(Text, { color: DIM, children: ' · ' }), _jsx(Text, { color: n.color, children: n.text })] }, i)))] }, `m${notices.length}`) }));
|
|
7974
8072
|
})(), _jsx(Box, { borderStyle: "single", borderLeft: false, borderRight: false, borderColor: DIM, paddingX: 2, width: uiWidth - 5, flexShrink: 0, children: _jsxs(Text, { wrap: "wrap", children: [_jsxs(Text, { color: busy ? DIM : AMBER, bold: true, children: [G.caret, " "] }), (() => {
|
|
7975
8073
|
const sel = selectionRange(selAnchor, caret, draft.length);
|