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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bingocode",
3
- "version": "1.1.68",
3
+ "version": "1.1.70",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "claude": "bin/claude-win.cjs",
@@ -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
- <StateDisplay type="error" message="No presets available. Please ensure the backend is running." />
387
+ <Title color="cyan">Select Preset</Title>
381
388
  <SelectInput
382
- items={[{ label: '← Back', value: 'back' }]}
383
- onSelect={() => setStage('list')}
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, items.length - MAX_VISIBLE));
391
- const sliced = items.slice(start, start + MAX_VISIBLE);
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
- const preset = presets.find(p => p.id === (it.value as string));
402
- const fields: ProviderField[] =
403
- preset?.fields && preset.fields.length > 0
404
- ? preset.fields
405
- : [
406
- { key: 'name', label: 'Provider Nickname', required: true },
407
- { key: 'apiKey', label: 'API Key', required: true, secret: true },
408
- ];
409
- setSelectedPresetId(it.value as string);
410
- setAddFields(fields);
411
- setAddFieldValues({});
412
- setAddFieldIndex(0);
413
- setListOffset(0);
414
- setStage('add_input_fields');
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={items.length} offset={start} height={MAX_VISIBLE} />
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, 28)} — ${SLOT_DESCS[s]}`;
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: 'Session 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 Terminal - Version Info & About',
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'}:${groupedHistoryItems.length}`}
1051
- items={groupedHistoryItems}
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={listOffset} height={MID_H} />
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
- <Hint>
1125
- API Base: {apiUrl}
1126
- </Hint>
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
  }