bingocode 1.1.68 → 1.1.70
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/package.json +1 -1
- package/src/cli/ProviderPanel.tsx +59 -21
- package/src/manager/CliMenuManager.tsx +35 -11
package/package.json
CHANGED
|
@@ -374,21 +374,45 @@ export const ProviderPanel: React.FC<{
|
|
|
374
374
|
: `${pr.label || pr.name || pr.id}`,
|
|
375
375
|
value: pr.id
|
|
376
376
|
}));
|
|
377
|
+
|
|
378
|
+
// Add Custom option at the beginning
|
|
379
|
+
const finalItems = [
|
|
380
|
+
{ label: 'Custom (OpenAI Compatible)', value: 'custom' },
|
|
381
|
+
...items
|
|
382
|
+
];
|
|
383
|
+
|
|
377
384
|
if (!items.length) {
|
|
378
385
|
return (
|
|
379
386
|
<Box flexDirection="column" flexGrow={1}>
|
|
380
|
-
<
|
|
387
|
+
<Title color="cyan">Select Preset</Title>
|
|
381
388
|
<SelectInput
|
|
382
|
-
items={
|
|
383
|
-
onSelect={
|
|
389
|
+
items={finalItems}
|
|
390
|
+
onSelect={it => {
|
|
391
|
+
if (it.value === 'custom') {
|
|
392
|
+
const fields: ProviderField[] = [
|
|
393
|
+
{ key: 'name', label: 'Provider Nickname', required: true, placeholder: 'My Custom API' },
|
|
394
|
+
{ key: 'baseUrl', label: 'Base URL', required: true, placeholder: 'https://api.example.com/v1' },
|
|
395
|
+
{ key: 'apiKey', label: 'API Key', required: true, secret: true },
|
|
396
|
+
];
|
|
397
|
+
setSelectedPresetId('custom');
|
|
398
|
+
setAddFields(fields);
|
|
399
|
+
setAddFieldValues({});
|
|
400
|
+
setAddFieldIndex(0);
|
|
401
|
+
setListOffset(0);
|
|
402
|
+
setStage('add_input_fields');
|
|
403
|
+
} else {
|
|
404
|
+
setStage('list');
|
|
405
|
+
}
|
|
406
|
+
}}
|
|
384
407
|
/>
|
|
408
|
+
{!items.length && <Hint dimColor>No presets found from server, only Custom available.</Hint>}
|
|
385
409
|
</Box>
|
|
386
410
|
);
|
|
387
411
|
}
|
|
388
412
|
|
|
389
413
|
const MAX_VISIBLE = 8;
|
|
390
|
-
const start = Math.min(listOffset, Math.max(0,
|
|
391
|
-
const sliced =
|
|
414
|
+
const start = Math.min(listOffset, Math.max(0, finalItems.length - MAX_VISIBLE));
|
|
415
|
+
const sliced = finalItems.slice(start, start + MAX_VISIBLE);
|
|
392
416
|
|
|
393
417
|
return (
|
|
394
418
|
<Box flexDirection="column" flexGrow={1}>
|
|
@@ -398,24 +422,38 @@ export const ProviderPanel: React.FC<{
|
|
|
398
422
|
<SelectInput
|
|
399
423
|
items={sliced}
|
|
400
424
|
onSelect={it => {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
:
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
425
|
+
if (it.value === 'custom') {
|
|
426
|
+
const fields: ProviderField[] = [
|
|
427
|
+
{ key: 'name', label: 'Provider Nickname', required: true, placeholder: 'My Custom API' },
|
|
428
|
+
{ key: 'baseUrl', label: 'Base URL', required: true, placeholder: 'https://api.example.com/v1' },
|
|
429
|
+
{ key: 'apiKey', label: 'API Key', required: true, secret: true },
|
|
430
|
+
];
|
|
431
|
+
setSelectedPresetId('custom');
|
|
432
|
+
setAddFields(fields);
|
|
433
|
+
setAddFieldValues({});
|
|
434
|
+
setAddFieldIndex(0);
|
|
435
|
+
setListOffset(0);
|
|
436
|
+
setStage('add_input_fields');
|
|
437
|
+
} else {
|
|
438
|
+
const preset = presets.find(p => p.id === (it.value as string));
|
|
439
|
+
const fields: ProviderField[] =
|
|
440
|
+
preset?.fields && preset.fields.length > 0
|
|
441
|
+
? preset.fields
|
|
442
|
+
: [
|
|
443
|
+
{ key: 'name', label: 'Provider Nickname', required: true },
|
|
444
|
+
{ key: 'apiKey', label: 'API Key', required: true, secret: true },
|
|
445
|
+
];
|
|
446
|
+
setSelectedPresetId(it.value as string);
|
|
447
|
+
setAddFields(fields);
|
|
448
|
+
setAddFieldValues({});
|
|
449
|
+
setAddFieldIndex(0);
|
|
450
|
+
setListOffset(0);
|
|
451
|
+
setStage('add_input_fields');
|
|
452
|
+
}
|
|
415
453
|
}}
|
|
416
454
|
/>
|
|
417
455
|
</Box>
|
|
418
|
-
<ScrollBar total={
|
|
456
|
+
<ScrollBar total={finalItems.length} offset={start} height={MAX_VISIBLE} />
|
|
419
457
|
</Box>
|
|
420
458
|
<Hint>↑↓: Select · j Next Page · k Prev Page · ESC: Back</Hint>
|
|
421
459
|
</Box>
|
|
@@ -641,7 +679,7 @@ export const ProviderPanel: React.FC<{
|
|
|
641
679
|
: null;
|
|
642
680
|
const modelDisplayName = entry?.label || entry?.modelId || 'Unconfigured';
|
|
643
681
|
const status = entry ? `${providerName} / ${modelDisplayName}` : 'Unconfigured';
|
|
644
|
-
const label = `[${s}] ${safePadEnd(status,
|
|
682
|
+
const label = `[${s}] ${safePadEnd(status, 30)} — ${SLOT_DESCS[s]}`;
|
|
645
683
|
return { label, value: s };
|
|
646
684
|
});
|
|
647
685
|
return (
|
|
@@ -82,13 +82,20 @@ const i18nMap = {
|
|
|
82
82
|
zh: {
|
|
83
83
|
menu: {
|
|
84
84
|
newSession: 'New Session',
|
|
85
|
-
history: '
|
|
85
|
+
history: 'History',
|
|
86
86
|
provider: 'API Config',
|
|
87
87
|
settings: 'Settings',
|
|
88
88
|
about: 'About',
|
|
89
89
|
exit: 'Exit',
|
|
90
90
|
},
|
|
91
|
-
about: 'Bingo CLI
|
|
91
|
+
about: 'Bingo CLI - Version Info & About',
|
|
92
|
+
aboutContent: [
|
|
93
|
+
'Bingo is an AI assistant terminal client.',
|
|
94
|
+
'1. API Config: Press "P" or select "API Config" to set up your keys.',
|
|
95
|
+
'2. Model Slots: Configure specific models in the Provider panel.',
|
|
96
|
+
'3. Background Service: Bingo runs a local server to manage sessions.',
|
|
97
|
+
'4. Start Chat: Run `bingocode` or `claude` in any terminal to start.',
|
|
98
|
+
].join('\n'),
|
|
92
99
|
mark: '→ Mark Session',
|
|
93
100
|
unmark: '→ Unmark Session',
|
|
94
101
|
tipsSimple: 'L Lang | ESC Back | ←→ Menu | ↩ Enter | ? Help',
|
|
@@ -108,6 +115,13 @@ const i18nMap = {
|
|
|
108
115
|
exit: 'Exit',
|
|
109
116
|
},
|
|
110
117
|
about: 'Bingo CLI Terminal - Version Info & About',
|
|
118
|
+
aboutContent: [
|
|
119
|
+
'Bingo is an AI assistant terminal client.',
|
|
120
|
+
'1. API Config: Press "P" or select "API Config" to set up your keys.',
|
|
121
|
+
'2. Model Slots: Configure specific models in the Provider panel.',
|
|
122
|
+
'3. Background Service: Bingo runs a local server to manage sessions.',
|
|
123
|
+
'4. Start Chat: Run `bingocode` or `claude` in any terminal to start.',
|
|
124
|
+
].join('\n'),
|
|
111
125
|
mark: '→ Mark Session',
|
|
112
126
|
unmark: '→ Unmark Session',
|
|
113
127
|
tipsSimple: 'L Lang | ESC Back | ←→ Menu | ↩ Enter | ? Help',
|
|
@@ -294,7 +308,7 @@ export const CliMenuManager: React.FC = () => {
|
|
|
294
308
|
|
|
295
309
|
// Compute viewport
|
|
296
310
|
const TOP_H = page === null ? TOP_H_HOME : TOP_H_COMPACT;
|
|
297
|
-
const MID_H = Math.max(5, VIEW_H - TOP_H - BOTTOM_H);
|
|
311
|
+
const MID_H = Math.max(5, VIEW_H - TOP_H - BOTTOM_H - (page === null ? 0 : 2));
|
|
298
312
|
const MSGS_PAGE_SIZE = Math.max(1, MID_H - 2);
|
|
299
313
|
const [expandMsgs, setExpandMsgs] = useState(false);
|
|
300
314
|
|
|
@@ -593,9 +607,10 @@ export const CliMenuManager: React.FC = () => {
|
|
|
593
607
|
// History shortcuts
|
|
594
608
|
if (!showHelp && page === 'history') {
|
|
595
609
|
if (historyMenuStage === 'list') {
|
|
610
|
+
const HIST_VISIBLE = Math.max(1, MID_H - 1);
|
|
596
611
|
if (key.downArrow || input === 'j') {
|
|
597
612
|
// Internal SelectInput handles cursor, we just need to track offset for ScrollBar
|
|
598
|
-
setListOffset(o => o + 1);
|
|
613
|
+
setListOffset(o => Math.min(o + 1, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE)));
|
|
599
614
|
}
|
|
600
615
|
if (key.upArrow || input === 'k') {
|
|
601
616
|
setListOffset(o => Math.max(0, o - 1));
|
|
@@ -1043,12 +1058,16 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1043
1058
|
|
|
1044
1059
|
|
|
1045
1060
|
// History List
|
|
1061
|
+
const HIST_VISIBLE = Math.max(1, MID_H - 1);
|
|
1062
|
+
const start = Math.min(listOffset, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE));
|
|
1063
|
+
const slicedItems = groupedHistoryItems.slice(start, start + HIST_VISIBLE);
|
|
1064
|
+
|
|
1046
1065
|
return (
|
|
1047
1066
|
<Box width={VIEW_W} height={MID_H} flexDirection="row">
|
|
1048
1067
|
<Box flexDirection="column" flexGrow={1}>
|
|
1049
1068
|
<SelectInput
|
|
1050
|
-
key={`${historyCursor ?? 'first'}:${
|
|
1051
|
-
items={
|
|
1069
|
+
key={`${historyCursor ?? 'first'}:${slicedItems.length}:${start}`}
|
|
1070
|
+
items={slicedItems}
|
|
1052
1071
|
onSelect={item => {
|
|
1053
1072
|
if (String(item.value).startsWith('__group_')) return; // Ignore group headers
|
|
1054
1073
|
const session = historyList.find(h => h.id === item.value);
|
|
@@ -1070,7 +1089,7 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1070
1089
|
/>
|
|
1071
1090
|
<Hint>{i18nMap[lang].historyHint}</Hint>
|
|
1072
1091
|
</Box>
|
|
1073
|
-
<ScrollBar total={groupedHistoryItems.length} offset={
|
|
1092
|
+
<ScrollBar total={groupedHistoryItems.length} offset={start} height={MID_H} />
|
|
1074
1093
|
</Box>
|
|
1075
1094
|
);
|
|
1076
1095
|
}
|
|
@@ -1120,10 +1139,15 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1120
1139
|
if (page === 'about') {
|
|
1121
1140
|
return (
|
|
1122
1141
|
<Box width={VIEW_W} height={MID_H} flexDirection="column">
|
|
1123
|
-
<Text>{i18nMap[lang].about}</Text>
|
|
1124
|
-
<
|
|
1125
|
-
|
|
1126
|
-
</
|
|
1142
|
+
<Text color="cyan" bold>{i18nMap[lang].about}</Text>
|
|
1143
|
+
<Box marginTop={1} flexDirection="column">
|
|
1144
|
+
<Text>{(i18nMap[lang] as any).aboutContent}</Text>
|
|
1145
|
+
</Box>
|
|
1146
|
+
<Box marginTop={1}>
|
|
1147
|
+
<Hint>
|
|
1148
|
+
API Base: {apiUrl}
|
|
1149
|
+
</Hint>
|
|
1150
|
+
</Box>
|
|
1127
1151
|
</Box>
|
|
1128
1152
|
);
|
|
1129
1153
|
}
|