bingocode 1.1.67 → 1.1.69
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 +58 -20
- package/src/manager/CliMenuManager.tsx +13 -7
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>
|
|
@@ -220,6 +220,9 @@ export const CliMenuManager: React.FC = () => {
|
|
|
220
220
|
// Language
|
|
221
221
|
const [lang, setLang] = useState<Lang>('en');
|
|
222
222
|
|
|
223
|
+
// Config ready probe (avoid Logo early read)
|
|
224
|
+
const [configReady, setConfigReady] = useState(false);
|
|
225
|
+
|
|
223
226
|
useEffect(() => {
|
|
224
227
|
if (configReady) {
|
|
225
228
|
try {
|
|
@@ -291,11 +294,9 @@ export const CliMenuManager: React.FC = () => {
|
|
|
291
294
|
|
|
292
295
|
// Compute viewport
|
|
293
296
|
const TOP_H = page === null ? TOP_H_HOME : TOP_H_COMPACT;
|
|
294
|
-
const MID_H = Math.max(5, VIEW_H - TOP_H - BOTTOM_H);
|
|
297
|
+
const MID_H = Math.max(5, VIEW_H - TOP_H - BOTTOM_H - (page === null ? 0 : 2));
|
|
295
298
|
const MSGS_PAGE_SIZE = Math.max(1, MID_H - 2);
|
|
296
299
|
const [expandMsgs, setExpandMsgs] = useState(false);
|
|
297
|
-
// Config ready probe (avoid Logo early read)
|
|
298
|
-
const [configReady, setConfigReady] = useState(false);
|
|
299
300
|
|
|
300
301
|
// Boot/Reuse singleton local server (with retry)
|
|
301
302
|
useEffect(() => {
|
|
@@ -592,9 +593,10 @@ export const CliMenuManager: React.FC = () => {
|
|
|
592
593
|
// History shortcuts
|
|
593
594
|
if (!showHelp && page === 'history') {
|
|
594
595
|
if (historyMenuStage === 'list') {
|
|
596
|
+
const HIST_VISIBLE = Math.max(1, MID_H - 1);
|
|
595
597
|
if (key.downArrow || input === 'j') {
|
|
596
598
|
// Internal SelectInput handles cursor, we just need to track offset for ScrollBar
|
|
597
|
-
setListOffset(o => o + 1);
|
|
599
|
+
setListOffset(o => Math.min(o + 1, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE)));
|
|
598
600
|
}
|
|
599
601
|
if (key.upArrow || input === 'k') {
|
|
600
602
|
setListOffset(o => Math.max(0, o - 1));
|
|
@@ -1042,12 +1044,16 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1042
1044
|
|
|
1043
1045
|
|
|
1044
1046
|
// History List
|
|
1047
|
+
const HIST_VISIBLE = Math.max(1, MID_H - 1);
|
|
1048
|
+
const start = Math.min(listOffset, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE));
|
|
1049
|
+
const slicedItems = groupedHistoryItems.slice(start, start + HIST_VISIBLE);
|
|
1050
|
+
|
|
1045
1051
|
return (
|
|
1046
1052
|
<Box width={VIEW_W} height={MID_H} flexDirection="row">
|
|
1047
1053
|
<Box flexDirection="column" flexGrow={1}>
|
|
1048
1054
|
<SelectInput
|
|
1049
|
-
key={`${historyCursor ?? 'first'}:${
|
|
1050
|
-
items={
|
|
1055
|
+
key={`${historyCursor ?? 'first'}:${slicedItems.length}:${start}`}
|
|
1056
|
+
items={slicedItems}
|
|
1051
1057
|
onSelect={item => {
|
|
1052
1058
|
if (String(item.value).startsWith('__group_')) return; // Ignore group headers
|
|
1053
1059
|
const session = historyList.find(h => h.id === item.value);
|
|
@@ -1069,7 +1075,7 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1069
1075
|
/>
|
|
1070
1076
|
<Hint>{i18nMap[lang].historyHint}</Hint>
|
|
1071
1077
|
</Box>
|
|
1072
|
-
<ScrollBar total={groupedHistoryItems.length} offset={
|
|
1078
|
+
<ScrollBar total={groupedHistoryItems.length} offset={start} height={MID_H} />
|
|
1073
1079
|
</Box>
|
|
1074
1080
|
);
|
|
1075
1081
|
}
|