bingocode 1.1.70 → 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bingocode",
3
- "version": "1.1.70",
3
+ "version": "1.1.71",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "claude": "bin/claude-win.cjs",
@@ -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);
@@ -607,12 +607,12 @@ export const CliMenuManager: React.FC = () => {
607
607
  // History shortcuts
608
608
  if (!showHelp && page === 'history') {
609
609
  if (historyMenuStage === 'list') {
610
- const HIST_VISIBLE = Math.max(1, MID_H - 1);
611
- if (key.downArrow || input === 'j') {
610
+ const HIST_VISIBLE = MID_H - 2;
611
+ if (key.downArrow || input === 'j' || input === '\u001b[B') {
612
612
  // Internal SelectInput handles cursor, we just need to track offset for ScrollBar
613
613
  setListOffset(o => Math.min(o + 1, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE)));
614
614
  }
615
- if (key.upArrow || input === 'k') {
615
+ if (key.upArrow || input === 'k' || input === '\u001b[A') {
616
616
  setListOffset(o => Math.max(0, o - 1));
617
617
  }
618
618
  if (input === 'q') {
@@ -965,48 +965,45 @@ export const CliMenuManager: React.FC = () => {
965
965
  if (historyMenuStage === 'deleteConfirm' && selectedHistory) {
966
966
  const halfH = Math.floor(MID_H / 2);
967
967
  const items = [
968
- { label: 'Yes, delete', value: '__confirm_delete' },
969
- { label: 'No, back', value: '__cancel_delete' },
968
+ { label: 'Yes, Delete', value: '__confirm_delete' },
969
+ { label: 'No, Back', value: '__cancel_delete' },
970
970
  ];
971
971
  return (
972
972
  <Box width={VIEW_W} height={MID_H} flexDirection="column">
973
- <Box height={halfH} flexDirection="column">
974
- <Text color="red">{i18nMap[lang].deleting}</Text>
975
- <Text>id: {selectedHistory.id}</Text>
976
- <Text>Title: {selectedHistory.title}</Text>
977
- <Text>Created At: {selectedHistory.createdAt}</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>
978
978
  </Box>
979
979
  <Panel height={MID_H - halfH} borderStyle="round" borderColor="red" paddingX={1}>
980
- <Text>Confirm Delete</Text>
981
980
  <SelectInput
982
981
  items={items}
983
982
  onSelect={(item) => handleHistoryMenuAction(String(item.value))}
984
983
  />
985
- <Hint>↩ Enter · q Back</Hint>
984
+ <Hint>Enter Confirm · q Cancel</Hint>
986
985
  </Panel>
987
986
  </Box>
988
987
  );
989
988
  }
990
989
  if (!historyList.length && loadingHist) {
991
- return <StateDisplay type="loading" message="Fetching history..." />;
990
+ return <StateDisplay type="loading" message="Loading..." />;
992
991
  }
993
992
  if (!historyList.length) {
994
993
  return <StateDisplay type="empty" message={i18nMap[lang].emptyHistory} />;
995
994
  }
996
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
+
997
999
  if (historyMenuStage === 'window' && selectedHistory) {
1000
+ // Detailed View with Split
998
1001
  const isMarked = markedSessionIds.has(selectedHistory.id);
1002
+ const PREVIEW_H = Math.max(3, LIST_H - 2);
999
1003
 
1000
- // INFO_H: Title + Metadata + Hint (3 lines)
1001
- const INFO_H = 3;
1002
- const MSGS_H = Math.max(3, MID_H - INFO_H);
1003
-
1004
- // Filter messages
1005
1004
  const displayMsgs = sessionMessages.filter(
1006
1005
  m => m.type === 'user' || m.type === 'assistant' || m.type === 'system'
1007
1006
  );
1008
-
1009
- // Pagination
1010
1007
  const totalPages = Math.max(1, Math.ceil(displayMsgs.length / MSGS_PAGE_SIZE));
1011
1008
  const safePage = Math.min(msgsPage, totalPages - 1);
1012
1009
  const pageStart = safePage * MSGS_PAGE_SIZE;
@@ -1014,62 +1011,60 @@ export const CliMenuManager: React.FC = () => {
1014
1011
 
1015
1012
  return (
1016
1013
  <Box width={VIEW_W} height={MID_H} flexDirection="column">
1017
- {/* Top Info Bar */}
1018
- <Box height={INFO_H} flexDirection="column">
1019
- <Text color={isMarked ? 'yellow' : 'cyan'}>
1020
- {isMarked ? '' : ''}{selectedHistory.title || 'Untitled'}
1021
- <Text dimColor> {selectedHistory.createdAt?.slice(0, 16).replace('T', ' ') || ''} · {displayMsgs.length} msgs</Text>
1022
- </Text>
1023
- <Hint>
1024
- j/↓ Down · k/↑ Up · m Mark · c Continue · d Delete · q Back
1025
- {displayMsgs.length > MSGS_PAGE_SIZE ? ` [${safePage + 1}/${totalPages}]` : ''}
1026
- </Hint>
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>}
1027
1038
  </Box>
1028
1039
 
1029
- {/* Message Area */}
1030
- <Box height={MSGS_H} flexDirection="column">
1031
- {loadingMsgs && <StateDisplay type="loading" message="Loading messages..." />}
1032
- {msgsErr && <StateDisplay type="error" message={`Error: ${msgsErr}`} />}
1033
- {!loadingMsgs && !msgsErr && displayMsgs.length === 0 && (
1034
- <StateDisplay type="empty" message="No message history" />
1035
- )}
1036
- {pageMsgs.map((msg) => {
1037
- const text = extractTextFromContent(msg.content);
1038
- if (!text.trim()) return null;
1039
- const isUser = msg.type === 'user';
1040
- const isSystem = msg.type === 'system';
1041
- const roleLabel = isUser ? '👤 You' : isSystem ? '⚙ System' : '🤖 Assistant';
1042
- const roleColor = isUser ? 'green' : isSystem ? 'gray' : 'cyan';
1043
- return (
1044
- <Box key={msg.id} flexDirection="column" marginBottom={1}>
1045
- <Text color={roleColor} bold>{roleLabel}</Text>
1046
- {isUser ? (
1047
- <Text>{text}</Text>
1048
- ) : (
1049
- <Ansi>{applyMarkdown(text, theme)}</Ansi>
1050
- )}
1051
- </Box>
1052
- );
1053
- })}
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>
1054
1050
  </Box>
1055
1051
  </Box>
1056
1052
  );
1057
1053
  }
1058
1054
 
1059
-
1060
- // History List
1061
- const HIST_VISIBLE = Math.max(1, MID_H - 1);
1055
+ // History List View (Default)
1056
+ const HIST_VISIBLE = MID_H - 2;
1062
1057
  const start = Math.min(listOffset, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE));
1063
1058
  const slicedItems = groupedHistoryItems.slice(start, start + HIST_VISIBLE);
1064
1059
 
1065
1060
  return (
1066
1061
  <Box width={VIEW_W} height={MID_H} flexDirection="row">
1067
- <Box flexDirection="column" flexGrow={1}>
1062
+ <Box flexDirection="column" flexGrow={1} paddingX={1}>
1068
1063
  <SelectInput
1069
1064
  key={`${historyCursor ?? 'first'}:${slicedItems.length}:${start}`}
1070
1065
  items={slicedItems}
1071
1066
  onSelect={item => {
1072
- if (String(item.value).startsWith('__group_')) return; // Ignore group headers
1067
+ if (String(item.value).startsWith('__group_')) return;
1073
1068
  const session = historyList.find(h => h.id === item.value);
1074
1069
  if (session) {
1075
1070
  setSelectedHistory(session);
@@ -1082,12 +1077,14 @@ export const CliMenuManager: React.FC = () => {
1082
1077
  const color = it?.color;
1083
1078
  return (
1084
1079
  <Text color={isGroup ? 'gray' : (color ? color : (isSelected ? 'cyan' : undefined))}>
1085
- {label}
1080
+ {isSelected ? '> ' : ' '}{label}
1086
1081
  </Text>
1087
1082
  )
1088
1083
  }}
1089
1084
  />
1090
- <Hint>{i18nMap[lang].historyHint}</Hint>
1085
+ <Box marginTop="auto">
1086
+ <Hint>{i18nMap[lang].historyHint}</Hint>
1087
+ </Box>
1091
1088
  </Box>
1092
1089
  <ScrollBar total={groupedHistoryItems.length} offset={start} height={MID_H} />
1093
1090
  </Box>