bingocode 1.1.132 → 1.1.134

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.
@@ -2,7 +2,9 @@
2
2
  "permissions": {
3
3
  "allow": [
4
4
  "Read(//c/Users/qi.lin/.claude/**)",
5
- "Bash(dir \"F:\\\\Leanchy\\\\VirtuosAgent\\\\BingoCode\\\\src\\\\server\\\\proxy\")"
5
+ "Bash(dir \"F:\\\\Leanchy\\\\VirtuosAgent\\\\BingoCode\\\\src\\\\server\\\\proxy\")",
6
+ "Bash(ls src/**/)",
7
+ "Bash(cat config/*.json)"
6
8
  ]
7
9
  }
8
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bingocode",
3
- "version": "1.1.132",
3
+ "version": "1.1.134",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "claude": "bin/claude-win.cjs",
@@ -78,34 +78,46 @@ const TOP_H_COMPACT = Number(process.env.CLI_TOP_H_COMPACT || 3);
78
78
  // Bottom bar height
79
79
  const BOTTOM_H = Number(process.env.CLI_BOTTOM_H || 3);
80
80
 
81
+ const LANG_OPTIONS = [
82
+ { label: 'English', value: 'en' as const },
83
+ { label: '中文', value: 'zh' as const },
84
+ { label: '日本語', value: 'ja' as const },
85
+ ];
86
+
81
87
  const i18nMap = {
82
- zh: {
83
- menu: {
84
- newSession: 'New Session',
85
- history: 'History',
86
- provider: 'API Config',
87
- settings: 'Settings',
88
- about: 'About',
89
- exit: 'Exit',
90
- },
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'),
99
- aboutFooter: 'Author: leanchy (leanchy07@outlook.com) · github.com/leanchy/bingo-claude-code-offline-installer',
100
- mark: '→ Mark Session',
101
- unmark: '→ Unmark Session',
102
- tipsSimple: 'L Lang | ESC Back | ←→ Menu | ↩ Enter | ? Help',
103
- noData: 'No data',
104
- emptyHistory: 'Nothing here yet. Start a new session?',
105
- deleting: 'Delete this session? (Irreversible)',
106
- historyHint: 'Enter to open · j next · k first · q back',
107
- helpTitle: 'Shortcuts',
88
+ zh: {
89
+ menu: {
90
+ newSession: '新建会话',
91
+ history: '会话历史',
92
+ provider: 'API 配置',
93
+ settings: '设置',
94
+ about: '关于',
95
+ exit: '退出',
108
96
  },
97
+ about: 'Bingo CLI 终端 - 版本信息与关于',
98
+ aboutContent: [
99
+ 'Bingo 是一款 AI 助手终端客户端。',
100
+ '1. API 配置:按 "P" 或选择「API 配置」来设置你的密钥。',
101
+ '2. 模型槽:在 Provider 面板中配置各模型。',
102
+ '3. 后台服务:Bingo 会运行一个本地服务器来管理会话。',
103
+ '4. 开始聊天:在任意终端中运行 `bingocode` 或 `claude`。',
104
+ ].join('\n'),
105
+ aboutFooter: '作者: leanchy (leanchy07@outlook.com) · github.com/leanchy/claude-code-bingo',
106
+ mark: '→ 标记会话',
107
+ unmark: '→ 取消标记',
108
+ tipsSimple: 'L 语言 | ESC 返回 | ←→ 菜单 | ↩ 确认 | ? 帮助',
109
+ noData: '暂无数据',
110
+ emptyHistory: '还没有会话,要新建一个吗?',
111
+ deleting: '确定删除此会话?(不可恢复)',
112
+ historyHint: '↩ 打开 · j 下一页 · k 首页 · q 返回',
113
+ helpTitle: '快捷键',
114
+ // Settings page
115
+ settingsTitle: '设置',
116
+ langLabel: '语言',
117
+ langPickerTitle: '选择语言',
118
+ settingsHint: '↑/k ↓/j 滚动 · ↩ 编辑 · ESC 返回',
119
+ langOptions: LANG_OPTIONS,
120
+ },
109
121
  en: {
110
122
  menu: {
111
123
  newSession: 'New Session',
@@ -123,7 +135,7 @@ const i18nMap = {
123
135
  '3. Background Service: Bingo runs a local server to manage sessions.',
124
136
  '4. Start Chat: Run `bingocode` or `claude` in any terminal to start.',
125
137
  ].join('\n'),
126
- aboutFooter: 'Author: leanchy (leanchy07@outlook.com) · github.com/leanchy/bingo-claude-code-offline-installer',
138
+ aboutFooter: 'Author: leanchy (leanchy07@outlook.com) · github.com/leanchy/claude-code-bingo',
127
139
  mark: '→ Mark Session',
128
140
  unmark: '→ Unmark Session',
129
141
  tipsSimple: 'L Lang | ESC Back | ←→ Menu | ↩ Enter | ? Help',
@@ -132,7 +144,46 @@ const i18nMap = {
132
144
  deleting: 'Delete this session? (Irreversible)',
133
145
  historyHint: 'Enter to open · j next · k first · q back',
134
146
  helpTitle: 'Shortcuts',
135
- }
147
+ // Settings page
148
+ settingsTitle: 'Settings',
149
+ langLabel: 'Language',
150
+ langPickerTitle: 'Select Language',
151
+ settingsHint: '↑/k ↓/j scroll · ↩ edit · ESC back',
152
+ langOptions: LANG_OPTIONS,
153
+ },
154
+ ja: {
155
+ menu: {
156
+ newSession: '新規セッション',
157
+ history: 'セッション履歴',
158
+ provider: 'API設定',
159
+ settings: '設定',
160
+ about: 'について',
161
+ exit: '終了',
162
+ },
163
+ about: 'Bingo CLI ターミナル - バージョン情報',
164
+ aboutContent: [
165
+ 'BingoはAIアシスタントのターミナルクライアントです。',
166
+ '1. API設定: "P"キーまたは「API設定」を選択してキーを設定。',
167
+ '2. モデルスロット: Providerパネルで各モデルを設定。',
168
+ '3. バックグラウンドサービス: セッション管理用ローカルサーバーを起動。',
169
+ '4. チャット開始: 任意のターミナルで `bingocode` または `claude` を実行。',
170
+ ].join('\n'),
171
+ aboutFooter: '作者: leanchy (leanchy07@outlook.com) · github.com/leanchy/claude-code-bingo',
172
+ mark: '→ セッションをマーク',
173
+ unmark: '→ マークを解除',
174
+ tipsSimple: 'L 言語 | ESC 戻る | ←→ メニュー | ↩ 決定 | ? ヘルプ',
175
+ noData: 'データなし',
176
+ emptyHistory: 'まだセッションがありません。新規作成しますか?',
177
+ deleting: 'このセッションを削除しますか?(元に戻せません)',
178
+ historyHint: '↩ 開く · j 次へ · k 最初へ · q 戻る',
179
+ helpTitle: 'ショートカット',
180
+ // Settings page
181
+ settingsTitle: '設定',
182
+ langLabel: '言語',
183
+ langPickerTitle: '言語を選択',
184
+ settingsHint: '↑/k ↓/j スクロール · ↩ 編集 · ESC 戻る',
185
+ langOptions: LANG_OPTIONS,
186
+ },
136
187
  };
137
188
 
138
189
  const menuKeys = [
@@ -243,7 +294,7 @@ export const CliMenuManager: React.FC = () => {
243
294
  if (configReady) {
244
295
  try {
245
296
  const cfg = getGlobalConfig();
246
- if (cfg.language && (cfg.language === 'en' || cfg.language === 'zh')) {
297
+ if (cfg.language && (cfg.language === 'en' || cfg.language === 'zh' || cfg.language === 'ja')) {
247
298
  setLang(cfg.language as Lang);
248
299
  }
249
300
  } catch (e) {
@@ -294,6 +345,8 @@ export const CliMenuManager: React.FC = () => {
294
345
  const [settingData, setSettingData] = useState<any>(null);
295
346
  const [loadingSetting, setLoadingSetting] = useState(false);
296
347
  const [setErr, setSetErr] = useState<string | null>(null);
348
+ const [settingsStage, setSettingsStage] = useState<'list' | 'langPicker'>('list');
349
+ const [settingsCursor, setSettingsCursor] = useState(0);
297
350
 
298
351
  // Top toolbar state
299
352
  const [animEnabled, setAnimEnabled] = useState(true);
@@ -373,6 +426,8 @@ export const CliMenuManager: React.FC = () => {
373
426
  }
374
427
  if (page !== 'settings') {
375
428
  setSettingsOffset(0);
429
+ setSettingsStage('list');
430
+ setSettingsCursor(0);
376
431
  }
377
432
  // Close help overlay
378
433
  setShowHelp(false);
@@ -515,9 +570,10 @@ export const CliMenuManager: React.FC = () => {
515
570
 
516
571
  // Keyboard interactions
517
572
  useInput((input, key) => {
518
- // Language toggle
573
+ // Language toggle (en → zh → ja → en)
519
574
  if (input === 'l' || input === 'L') {
520
- const nextLang = lang === 'zh' ? 'en' : 'zh';
575
+ const langOrder: Lang[] = ['en', 'zh', 'ja'];
576
+ const nextLang = langOrder[(langOrder.indexOf(lang) + 1) % langOrder.length];
521
577
  setLang(nextLang);
522
578
  try {
523
579
  const cfg = getGlobalConfig();
@@ -563,6 +619,10 @@ export const CliMenuManager: React.FC = () => {
563
619
  if (key.escape) {
564
620
  if (showHelp) { setShowHelp(false); return; }
565
621
  if (page === 'provider') return; // Handled internally
622
+ // Settings: langPicker → back to list; list → back to main menu
623
+ if (page === 'settings') {
624
+ if (settingsStage === 'langPicker') { setSettingsStage('list'); return; }
625
+ }
566
626
  setPage(null);
567
627
  setHistoryMenuStage('list');
568
628
  setSelectedHistory(null);
@@ -670,18 +730,30 @@ export const CliMenuManager: React.FC = () => {
670
730
  }
671
731
  }
672
732
 
673
- // Settings scrolling
674
- if (!showHelp && page === 'settings' && settingData && typeof settingData === 'object') {
675
- const total = Object.keys(settingData).length;
676
- const visible = Math.max(1, MID_H - 1);
677
- if (key.downArrow || input === 'j') {
678
- setSettingsOffset(o => Math.min(Math.max(0, total - visible), o + 1));
679
- }
680
- if (key.upArrow || input === 'k') {
681
- setSettingsOffset(o => Math.max(0, o - 1));
733
+ // Settings interactions
734
+ if (!showHelp && page === 'settings') {
735
+ if (settingsStage === 'list') {
736
+ // +1 for the fixed Language row prepended before settingData entries
737
+ const totalRows = 1 + (settingData && typeof settingData === 'object' ? Object.keys(settingData).length : 0);
738
+ const visible = Math.max(1, MID_H - 2);
739
+ if (key.downArrow || input === 'j') {
740
+ setSettingsCursor(c => Math.min(totalRows - 1, c + 1));
741
+ setSettingsOffset(o => Math.min(Math.max(0, totalRows - visible), o + 1));
742
+ }
743
+ if (key.upArrow || input === 'k') {
744
+ setSettingsCursor(c => Math.max(0, c - 1));
745
+ setSettingsOffset(o => Math.max(0, o - 1));
746
+ }
747
+ if (key.return) {
748
+ // Row 0 is the interactive Language row
749
+ if (settingsCursor === 0) {
750
+ setSettingsStage('langPicker');
751
+ }
752
+ }
682
753
  }
754
+ // langPicker stage: ESC handled above; selection via SelectInput onSelect
683
755
  }
684
- }, [menuItems, page, historyMenuStage, historyList, historyHasMore, navIndex, sessionMessages, settingData, MID_H, MSGS_PAGE_SIZE, showHelp, theme]);
756
+ }, [menuItems, page, historyMenuStage, historyList, historyHasMore, navIndex, sessionMessages, settingData, MID_H, MSGS_PAGE_SIZE, showHelp, theme, settingsStage, settingsCursor]);
685
757
 
686
758
  function cleanText(text: string): string {
687
759
  return String(text ?? '').replace(/[\n\r]+/g, ' ').replace(/\u001b\[[0-9;]*m/g, '').trim();
@@ -915,7 +987,7 @@ export const CliMenuManager: React.FC = () => {
915
987
  <Text color="cyan">R</Text><Text> Quick Resume</Text>
916
988
  <Text color="cyan">P</Text><Text> Open Provider Config</Text>
917
989
  <Text color="cyan">G</Text><Text> Toggle Theme (light/dark/highContrast)</Text>
918
- <Text color="cyan">L</Text><Text> Toggle Language (en/zh)</Text>
990
+ <Text color="cyan">L</Text><Text> Toggle Language (enzh → ja)</Text>
919
991
  <Text color="cyan">O</Text><Text> Toggle Top Animation</Text>
920
992
  <Text color="cyan">T</Text><Text> Toggle Top Tips</Text>
921
993
  <Text color="cyan">?</Text><Text> Toggle Help</Text>
@@ -1130,18 +1202,84 @@ export const CliMenuManager: React.FC = () => {
1130
1202
  if (page === 'settings') {
1131
1203
  if (loadingSetting) return <StateDisplay type="loading" message="Loading settings..." />;
1132
1204
  if (setErr) return <StateDisplay type="error" message={setErr} />;
1133
- if (!settingData || typeof settingData !== 'object') return <StateDisplay type="empty" message="No settings found" />;
1134
- const entries = Object.entries(settingData);
1135
- const visible = Math.max(1, MID_H - 1);
1136
- const start = Math.min(settingsOffset, Math.max(0, entries.length - visible));
1137
- const sliced = entries.slice(start, start + visible);
1205
+
1206
+ const tS = i18nMap[lang];
1207
+ const currentLangLabel = LANG_OPTIONS.find(o => o.value === lang)?.label ?? lang;
1208
+
1209
+ // --- langPicker sub-menu ---
1210
+ if (settingsStage === 'langPicker') {
1211
+ return (
1212
+ <Box width={VIEW_W} height={MID_H} flexDirection="column">
1213
+ <Box paddingX={1} marginBottom={1}>
1214
+ <Text color="magenta" bold>{tS.langPickerTitle}</Text>
1215
+ </Box>
1216
+ <Box paddingX={2} flexGrow={1} flexDirection="column">
1217
+ <SelectInput
1218
+ items={tS.langOptions}
1219
+ initialIndex={tS.langOptions.findIndex(o => o.value === lang)}
1220
+ onSelect={(item: { label: string; value: Lang }) => {
1221
+ setLang(item.value);
1222
+ try {
1223
+ const cfg = getGlobalConfig();
1224
+ cfg.language = item.value;
1225
+ saveGlobalConfig(cfg);
1226
+ } catch {}
1227
+ setSettingsStage('list');
1228
+ }}
1229
+ />
1230
+ </Box>
1231
+ <Box paddingX={1}>
1232
+ <Hint>↩ confirm · ESC back</Hint>
1233
+ </Box>
1234
+ </Box>
1235
+ );
1236
+ }
1237
+
1238
+ // --- settings list ---
1239
+ type SettingRow = { key: string; label: string; value: string; interactive: boolean };
1240
+ const fixedRows: SettingRow[] = [
1241
+ { key: '__lang', label: tS.langLabel, value: currentLangLabel, interactive: true },
1242
+ ];
1243
+ const dataEntries = settingData && typeof settingData === 'object' ? Object.entries(settingData) : [];
1244
+ const dataRows: SettingRow[] = dataEntries.map(([k, v]) => ({
1245
+ key: k,
1246
+ label: k,
1247
+ value: typeof v === 'object' ? JSON.stringify(v) : String(v),
1248
+ interactive: false,
1249
+ }));
1250
+ const allRows: SettingRow[] = [...fixedRows, ...dataRows];
1251
+ const visible = Math.max(1, MID_H - 2);
1252
+ const start = Math.min(settingsOffset, Math.max(0, allRows.length - visible));
1253
+ const sliced = allRows.slice(start, start + visible);
1254
+
1138
1255
  return (
1139
1256
  <Box width={VIEW_W} height={MID_H} flexDirection="column">
1140
- <ScrollBar total={entries.length} offset={start} height={visible - 1} />
1141
- {sliced.map(([k, v]) => <Text key={k}>{k}: {typeof v === 'object' ? JSON.stringify(v) : String(v)}</Text>)}
1142
- <Hint>
1143
- ↑/k and ↓/j scroll · {start+1}-{Math.min(start+visible, entries.length)}/{entries.length}
1144
- </Hint>
1257
+ <Box flexDirection="row" position="relative" flexGrow={1}>
1258
+ <Box flexDirection="column" flexGrow={1} paddingX={1} overflow="hidden">
1259
+ {sliced.map((row, idx) => {
1260
+ const absIdx = start + idx;
1261
+ const isCursor = absIdx === settingsCursor;
1262
+ const prefix = isCursor ? '>' : ' ';
1263
+ const labelColor = isCursor ? 'cyan' : (row.interactive ? 'white' : 'gray');
1264
+ const valueColor = row.interactive ? 'green' : undefined;
1265
+ return (
1266
+ <Box key={row.key} height={1}>
1267
+ <Text color={labelColor}>
1268
+ {prefix} {row.label}:{' '}
1269
+ <Text color={valueColor ?? (isCursor ? 'white' : 'gray')}>
1270
+ {row.value}
1271
+ {row.interactive ? ' ↩' : ''}
1272
+ </Text>
1273
+ </Text>
1274
+ </Box>
1275
+ );
1276
+ })}
1277
+ </Box>
1278
+ <ScrollBar total={allRows.length} offset={start} height={visible - 1} />
1279
+ </Box>
1280
+ <Box paddingX={1}>
1281
+ <Hint>{tS.settingsHint} · {start + 1}-{Math.min(start + visible, allRows.length)}/{allRows.length}</Hint>
1282
+ </Box>
1145
1283
  </Box>
1146
1284
  );
1147
1285
  }
@@ -242,7 +242,7 @@ export type GlobalConfig = {
242
242
  hasUsedBackgroundTask?: boolean // Whether the user has backgrounded a task (Ctrl+B)
243
243
  queuedCommandUpHintCount?: number // Counter for how many times the user has seen the queued command up hint
244
244
  diffTool?: DiffTool // Which tool to use for displaying diffs (terminal or vscode)
245
- language: 'en' | 'zh' // User's preferred language for CLI menus
245
+ language: 'en' | 'zh' | 'ja' // User's preferred language for CLI menus
246
246
 
247
247
  // Terminal setup state tracking
248
248
  iterm2SetupInProgress?: boolean