bingocode 1.1.69 → 1.1.71
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
|
@@ -141,13 +141,13 @@ export const ProviderPanel: React.FC<{
|
|
|
141
141
|
loadPresets();
|
|
142
142
|
}, [loadProviders, loadPresets]);
|
|
143
143
|
|
|
144
|
-
// Key processing for Page Up/Down in scrolling lists
|
|
144
|
+
// Key processing for Page Up/Down and Arrow keys in scrolling lists
|
|
145
145
|
useEffect(() => {
|
|
146
146
|
const handler = (buf: Buffer) => {
|
|
147
147
|
const s = buf.toString();
|
|
148
148
|
if (stage === 'add_select_preset' || stage === 'slot_select_model') {
|
|
149
|
-
if (s === 'j') setListOffset(prev => prev + 1);
|
|
150
|
-
if (s === 'k') setListOffset(prev => Math.max(0, prev - 1));
|
|
149
|
+
if (s === 'j' || s === '\u001b[B') setListOffset(prev => prev + 1); // j or Down
|
|
150
|
+
if (s === 'k' || s === '\u001b[A') setListOffset(prev => Math.max(0, prev - 1)); // k or Up
|
|
151
151
|
}
|
|
152
152
|
};
|
|
153
153
|
process.stdin.on('data', handler);
|
|
@@ -679,7 +679,7 @@ export const ProviderPanel: React.FC<{
|
|
|
679
679
|
: null;
|
|
680
680
|
const modelDisplayName = entry?.label || entry?.modelId || 'Unconfigured';
|
|
681
681
|
const status = entry ? `${providerName} / ${modelDisplayName}` : 'Unconfigured';
|
|
682
|
-
const label = `[${s}] ${safePadEnd(status,
|
|
682
|
+
const label = `[${s}] ${safePadEnd(status, 30)} — ${SLOT_DESCS[s]}`;
|
|
683
683
|
return { label, value: s };
|
|
684
684
|
});
|
|
685
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',
|
|
@@ -593,12 +607,12 @@ export const CliMenuManager: React.FC = () => {
|
|
|
593
607
|
// History shortcuts
|
|
594
608
|
if (!showHelp && page === 'history') {
|
|
595
609
|
if (historyMenuStage === 'list') {
|
|
596
|
-
const HIST_VISIBLE =
|
|
597
|
-
if (key.downArrow || input === 'j') {
|
|
610
|
+
const HIST_VISIBLE = MID_H - 2;
|
|
611
|
+
if (key.downArrow || input === 'j' || input === '\u001b[B') {
|
|
598
612
|
// Internal SelectInput handles cursor, we just need to track offset for ScrollBar
|
|
599
613
|
setListOffset(o => Math.min(o + 1, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE)));
|
|
600
614
|
}
|
|
601
|
-
if (key.upArrow || input === 'k') {
|
|
615
|
+
if (key.upArrow || input === 'k' || input === '\u001b[A') {
|
|
602
616
|
setListOffset(o => Math.max(0, o - 1));
|
|
603
617
|
}
|
|
604
618
|
if (input === 'q') {
|
|
@@ -951,48 +965,45 @@ export const CliMenuManager: React.FC = () => {
|
|
|
951
965
|
if (historyMenuStage === 'deleteConfirm' && selectedHistory) {
|
|
952
966
|
const halfH = Math.floor(MID_H / 2);
|
|
953
967
|
const items = [
|
|
954
|
-
{ label: 'Yes,
|
|
955
|
-
{ label: 'No,
|
|
968
|
+
{ label: 'Yes, Delete', value: '__confirm_delete' },
|
|
969
|
+
{ label: 'No, Back', value: '__cancel_delete' },
|
|
956
970
|
];
|
|
957
971
|
return (
|
|
958
972
|
<Box width={VIEW_W} height={MID_H} flexDirection="column">
|
|
959
|
-
<Box height={halfH} flexDirection="column">
|
|
960
|
-
<Text color="red">
|
|
961
|
-
<Text>
|
|
962
|
-
<Text>
|
|
963
|
-
<Text>
|
|
973
|
+
<Box height={halfH} flexDirection="column" paddingX={1} paddingTop={1}>
|
|
974
|
+
<Text color="red" bold>Confirm Delete?</Text>
|
|
975
|
+
<Text>Title: {selectedHistory.title || 'Untitled'}</Text>
|
|
976
|
+
<Text dimColor>Time: {selectedHistory.createdAt?.replace('T',' ')}</Text>
|
|
977
|
+
<Text dimColor>ID: {selectedHistory.id}</Text>
|
|
964
978
|
</Box>
|
|
965
979
|
<Panel height={MID_H - halfH} borderStyle="round" borderColor="red" paddingX={1}>
|
|
966
|
-
<Text>Confirm Delete</Text>
|
|
967
980
|
<SelectInput
|
|
968
981
|
items={items}
|
|
969
982
|
onSelect={(item) => handleHistoryMenuAction(String(item.value))}
|
|
970
983
|
/>
|
|
971
|
-
<Hint
|
|
984
|
+
<Hint>Enter Confirm · q Cancel</Hint>
|
|
972
985
|
</Panel>
|
|
973
986
|
</Box>
|
|
974
987
|
);
|
|
975
988
|
}
|
|
976
989
|
if (!historyList.length && loadingHist) {
|
|
977
|
-
return <StateDisplay type="loading" message="
|
|
990
|
+
return <StateDisplay type="loading" message="Loading..." />;
|
|
978
991
|
}
|
|
979
992
|
if (!historyList.length) {
|
|
980
993
|
return <StateDisplay type="empty" message={i18nMap[lang].emptyHistory} />;
|
|
981
994
|
}
|
|
982
995
|
|
|
996
|
+
const LIST_H = Math.floor(MID_H * 0.7);
|
|
997
|
+
const ACTIONS_H = MID_H - LIST_H - 1; // -1 for separator/buffer
|
|
998
|
+
|
|
983
999
|
if (historyMenuStage === 'window' && selectedHistory) {
|
|
1000
|
+
// Detailed View with Split
|
|
984
1001
|
const isMarked = markedSessionIds.has(selectedHistory.id);
|
|
1002
|
+
const PREVIEW_H = Math.max(3, LIST_H - 2);
|
|
985
1003
|
|
|
986
|
-
// INFO_H: Title + Metadata + Hint (3 lines)
|
|
987
|
-
const INFO_H = 3;
|
|
988
|
-
const MSGS_H = Math.max(3, MID_H - INFO_H);
|
|
989
|
-
|
|
990
|
-
// Filter messages
|
|
991
1004
|
const displayMsgs = sessionMessages.filter(
|
|
992
1005
|
m => m.type === 'user' || m.type === 'assistant' || m.type === 'system'
|
|
993
1006
|
);
|
|
994
|
-
|
|
995
|
-
// Pagination
|
|
996
1007
|
const totalPages = Math.max(1, Math.ceil(displayMsgs.length / MSGS_PAGE_SIZE));
|
|
997
1008
|
const safePage = Math.min(msgsPage, totalPages - 1);
|
|
998
1009
|
const pageStart = safePage * MSGS_PAGE_SIZE;
|
|
@@ -1000,62 +1011,60 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1000
1011
|
|
|
1001
1012
|
return (
|
|
1002
1013
|
<Box width={VIEW_W} height={MID_H} flexDirection="column">
|
|
1003
|
-
{/*
|
|
1004
|
-
<Box height={
|
|
1005
|
-
<
|
|
1006
|
-
{isMarked ? '
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1014
|
+
{/* Upper Pane: Preview */}
|
|
1015
|
+
<Box height={LIST_H} borderStyle="single" borderColor="gray" flexDirection="column" paddingX={1}>
|
|
1016
|
+
<Box justifyContent="space-between">
|
|
1017
|
+
<Text color={isMarked ? 'yellow' : 'cyan'} bold>
|
|
1018
|
+
{isMarked ? '★ ' : ''}{selectedHistory.title || 'Untitled'}
|
|
1019
|
+
</Text>
|
|
1020
|
+
<Text dimColor>{selectedHistory.createdAt?.slice(0,16).replace('T',' ')}</Text>
|
|
1021
|
+
</Box>
|
|
1022
|
+
|
|
1023
|
+
<Box flexDirection="column" flexGrow={1} marginTop={1}>
|
|
1024
|
+
{loadingMsgs && <Text dimColor>Loading messages...</Text>}
|
|
1025
|
+
{pageMsgs.map((msg) => {
|
|
1026
|
+
const text = extractTextFromContent(msg.content);
|
|
1027
|
+
const roleLabel = msg.type === 'user' ? 'You' : 'Bot';
|
|
1028
|
+
const roleColor = msg.type === 'user' ? 'green' : 'cyan';
|
|
1029
|
+
return (
|
|
1030
|
+
<Box key={msg.id} marginBottom={1}>
|
|
1031
|
+
<Text color={roleColor} bold>{roleLabel}: </Text>
|
|
1032
|
+
<Text>{clampTextLines(text, VIEW_W - 10, 2)}</Text>
|
|
1033
|
+
</Box>
|
|
1034
|
+
);
|
|
1035
|
+
})}
|
|
1036
|
+
</Box>
|
|
1037
|
+
{totalPages > 1 && <Hint alignSelf="center">Page {safePage + 1}/{totalPages} (j/k to scroll)</Hint>}
|
|
1013
1038
|
</Box>
|
|
1014
1039
|
|
|
1015
|
-
{/*
|
|
1016
|
-
<Box height={
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
const isUser = msg.type === 'user';
|
|
1026
|
-
const isSystem = msg.type === 'system';
|
|
1027
|
-
const roleLabel = isUser ? '👤 You' : isSystem ? '⚙ System' : '🤖 Assistant';
|
|
1028
|
-
const roleColor = isUser ? 'green' : isSystem ? 'gray' : 'cyan';
|
|
1029
|
-
return (
|
|
1030
|
-
<Box key={msg.id} flexDirection="column" marginBottom={1}>
|
|
1031
|
-
<Text color={roleColor} bold>{roleLabel}</Text>
|
|
1032
|
-
{isUser ? (
|
|
1033
|
-
<Text>{text}</Text>
|
|
1034
|
-
) : (
|
|
1035
|
-
<Ansi>{applyMarkdown(text, theme)}</Ansi>
|
|
1036
|
-
)}
|
|
1037
|
-
</Box>
|
|
1038
|
-
);
|
|
1039
|
-
})}
|
|
1040
|
+
{/* Lower Pane: Actions */}
|
|
1041
|
+
<Box height={ACTIONS_H} marginTop={1} paddingX={1} flexDirection="column">
|
|
1042
|
+
<Text color="cyan" bold>Session Actions</Text>
|
|
1043
|
+
<Box marginTop={1}>
|
|
1044
|
+
<SelectInput
|
|
1045
|
+
items={secondaryMenu?.items || []}
|
|
1046
|
+
onSelect={secondaryMenu?.onSelect}
|
|
1047
|
+
/>
|
|
1048
|
+
</Box>
|
|
1049
|
+
<Hint>ESC Back · ↑↓ Select Action</Hint>
|
|
1040
1050
|
</Box>
|
|
1041
1051
|
</Box>
|
|
1042
1052
|
);
|
|
1043
1053
|
}
|
|
1044
1054
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
const HIST_VISIBLE = Math.max(1, MID_H - 1);
|
|
1055
|
+
// History List View (Default)
|
|
1056
|
+
const HIST_VISIBLE = MID_H - 2;
|
|
1048
1057
|
const start = Math.min(listOffset, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE));
|
|
1049
1058
|
const slicedItems = groupedHistoryItems.slice(start, start + HIST_VISIBLE);
|
|
1050
1059
|
|
|
1051
1060
|
return (
|
|
1052
1061
|
<Box width={VIEW_W} height={MID_H} flexDirection="row">
|
|
1053
|
-
<Box flexDirection="column" flexGrow={1}>
|
|
1062
|
+
<Box flexDirection="column" flexGrow={1} paddingX={1}>
|
|
1054
1063
|
<SelectInput
|
|
1055
1064
|
key={`${historyCursor ?? 'first'}:${slicedItems.length}:${start}`}
|
|
1056
1065
|
items={slicedItems}
|
|
1057
1066
|
onSelect={item => {
|
|
1058
|
-
if (String(item.value).startsWith('__group_')) return;
|
|
1067
|
+
if (String(item.value).startsWith('__group_')) return;
|
|
1059
1068
|
const session = historyList.find(h => h.id === item.value);
|
|
1060
1069
|
if (session) {
|
|
1061
1070
|
setSelectedHistory(session);
|
|
@@ -1068,12 +1077,14 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1068
1077
|
const color = it?.color;
|
|
1069
1078
|
return (
|
|
1070
1079
|
<Text color={isGroup ? 'gray' : (color ? color : (isSelected ? 'cyan' : undefined))}>
|
|
1071
|
-
{label}
|
|
1080
|
+
{isSelected ? '> ' : ' '}{label}
|
|
1072
1081
|
</Text>
|
|
1073
1082
|
)
|
|
1074
1083
|
}}
|
|
1075
1084
|
/>
|
|
1076
|
-
<
|
|
1085
|
+
<Box marginTop="auto">
|
|
1086
|
+
<Hint>{i18nMap[lang].historyHint}</Hint>
|
|
1087
|
+
</Box>
|
|
1077
1088
|
</Box>
|
|
1078
1089
|
<ScrollBar total={groupedHistoryItems.length} offset={start} height={MID_H} />
|
|
1079
1090
|
</Box>
|
|
@@ -1125,10 +1136,15 @@ export const CliMenuManager: React.FC = () => {
|
|
|
1125
1136
|
if (page === 'about') {
|
|
1126
1137
|
return (
|
|
1127
1138
|
<Box width={VIEW_W} height={MID_H} flexDirection="column">
|
|
1128
|
-
<Text>{i18nMap[lang].about}</Text>
|
|
1129
|
-
<
|
|
1130
|
-
|
|
1131
|
-
</
|
|
1139
|
+
<Text color="cyan" bold>{i18nMap[lang].about}</Text>
|
|
1140
|
+
<Box marginTop={1} flexDirection="column">
|
|
1141
|
+
<Text>{(i18nMap[lang] as any).aboutContent}</Text>
|
|
1142
|
+
</Box>
|
|
1143
|
+
<Box marginTop={1}>
|
|
1144
|
+
<Hint>
|
|
1145
|
+
API Base: {apiUrl}
|
|
1146
|
+
</Hint>
|
|
1147
|
+
</Box>
|
|
1132
1148
|
</Box>
|
|
1133
1149
|
);
|
|
1134
1150
|
}
|