bingocode 1.1.115 → 1.1.117

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.115",
3
+ "version": "1.1.117",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "claude": "bin/claude-win.cjs",
@@ -72,9 +72,9 @@ async function buildSpawnEnv(): Promise<NodeJS.ProcessEnv> {
72
72
  return base;
73
73
  }
74
74
 
75
- // Top height: Home fits LogoV2 + Toolbar, other pages more compact
76
- const TOP_H_HOME = Number(process.env.CLI_TOP_H_HOME || 9);
77
- const TOP_H_COMPACT = Number(process.env.CLI_TOP_H_COMPACT || 6);
75
+ // Top height: Home = Clawd(3 rows) + border(2) = 5; Compact = 1 row + border(2) = 3
76
+ const TOP_H_HOME = Number(process.env.CLI_TOP_H_HOME || 5);
77
+ 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
 
@@ -995,7 +995,7 @@ export const CliMenuManager: React.FC = () => {
995
995
  return <StateDisplay type="empty" message={i18nMap[lang].emptyHistory} />;
996
996
  }
997
997
 
998
- const ACTIONS_H = 6;
998
+ const ACTIONS_H = 7; // Actions title(1) + 4 items + hint(1) + padding(1)
999
999
  const LIST_H = Math.max(2, MID_H - ACTIONS_H - 1);
1000
1000
 
1001
1001
  if (historyMenuStage === 'window' && selectedHistory) {
@@ -1042,7 +1042,7 @@ export const CliMenuManager: React.FC = () => {
1042
1042
  )}
1043
1043
  </Box>
1044
1044
 
1045
- <Box height={1} marginBottom={0}><Text dimColor>{'─'.repeat(VIEW_W - 2)}</Text></Box>
1045
+ <Box height={1} marginBottom={0}><Text dimColor>{'─'.repeat(VIEW_W - 4)}</Text></Box>
1046
1046
 
1047
1047
  {/* Lower Pane: Actions */}
1048
1048
  <Box height={ACTIONS_H} paddingX={1} flexDirection="column" overflow="hidden">
@@ -1060,42 +1060,47 @@ export const CliMenuManager: React.FC = () => {
1060
1060
  }
1061
1061
 
1062
1062
  // History List View (Default)
1063
+ // MID_H - 1 (hint bar at top) - 1 (scrollbar safety) = MID_H - 2 visible items
1063
1064
  const HIST_VISIBLE = MID_H - 2;
1064
1065
  const start = Math.min(listOffset, Math.max(0, groupedHistoryItems.length - HIST_VISIBLE));
1065
1066
  const slicedItems = groupedHistoryItems.slice(start, start + HIST_VISIBLE);
1066
1067
 
1067
1068
  return (
1068
- <Box width={VIEW_W} height={MID_H} flexDirection="row" position="relative">
1069
- <Box flexDirection="column" flexGrow={1} paddingX={1}>
1070
- <SelectInput
1071
- key={`${historyCursor ?? 'first'}:${slicedItems.length}:${start}`}
1072
- items={slicedItems}
1073
- onSelect={item => {
1074
- if (String(item.value).startsWith('__group_')) return;
1075
- const session = historyList.find(h => h.id === item.value);
1076
- if (session) {
1077
- setSelectedHistory(session);
1078
- setHistoryMenuStage('window');
1079
- }
1080
- }}
1081
- itemComponent={({ isSelected, label }) => {
1082
- const it = groupedHistoryItems.find(i => i.label === label);
1083
- const isGroup = it?.isGroup;
1084
- const color = it?.color;
1085
- return (
1086
- <Box height={1} overflow="hidden">
1087
- <Text wrap="truncate" color={isGroup ? 'gray' : (color ? color : (isSelected ? 'cyan' : undefined))}>
1088
- {isSelected ? '> ' : ' '}{label}
1089
- </Text>
1090
- </Box>
1091
- )
1092
- }}
1093
- />
1094
- </Box>
1095
- <ScrollBar total={groupedHistoryItems.length} offset={start} height={MID_H - 2} />
1096
- <Box position="absolute" bottom={0} left={1} width={VIEW_W - 4}>
1069
+ <Box width={VIEW_W} height={MID_H} flexDirection="column">
1070
+ {/* Hint bar — fixed 1 row at top, never overlaps list */}
1071
+ <Box height={1} paddingX={1}>
1097
1072
  <Hint>{i18nMap[lang].historyHint}</Hint>
1098
1073
  </Box>
1074
+ {/* List area — takes the rest of the height */}
1075
+ <Box flexDirection="row" flexGrow={1} position="relative">
1076
+ <Box flexDirection="column" flexGrow={1} paddingX={1}>
1077
+ <SelectInput
1078
+ key={`${historyCursor ?? 'first'}:${slicedItems.length}:${start}`}
1079
+ items={slicedItems}
1080
+ onSelect={item => {
1081
+ if (String(item.value).startsWith('__group_')) return;
1082
+ const session = historyList.find(h => h.id === item.value);
1083
+ if (session) {
1084
+ setSelectedHistory(session);
1085
+ setHistoryMenuStage('window');
1086
+ }
1087
+ }}
1088
+ itemComponent={({ isSelected, label }) => {
1089
+ const it = groupedHistoryItems.find(i => i.label === label);
1090
+ const isGroup = it?.isGroup;
1091
+ const color = it?.color;
1092
+ return (
1093
+ <Box height={1} overflow="hidden">
1094
+ <Text wrap="truncate" color={isGroup ? 'gray' : (color ? color : (isSelected ? 'cyan' : undefined))}>
1095
+ {isSelected ? '> ' : ' '}{label}
1096
+ </Text>
1097
+ </Box>
1098
+ )
1099
+ }}
1100
+ />
1101
+ </Box>
1102
+ <ScrollBar total={groupedHistoryItems.length} offset={start} height={MID_H - 3} />
1103
+ </Box>
1099
1104
  </Box>
1100
1105
  );
1101
1106
  }
@@ -1190,9 +1195,6 @@ export const CliMenuManager: React.FC = () => {
1190
1195
  page={page}
1191
1196
  width={VIEW_W}
1192
1197
  height={TOP_H}
1193
- homeLogo={<LogoV2 />}
1194
- compactLogo={<CondensedLogo />}
1195
- ip={apiUrl ? apiUrl.replace(/^https?:\/\//, '') : undefined}
1196
1198
  toolbar={
1197
1199
  <TopToolbar
1198
1200
  ready={configReady}
@@ -256,23 +256,20 @@ export const ChipRow: React.FC<{ children: React.ReactNode }> = memo(({ children
256
256
  </Box>
257
257
  ));
258
258
 
259
- // TopBar (logo + toolbar horizontal arrangement, with ready state and placeholder)
259
+ // TopBar (full-width toolbar, no separate logo slot)
260
260
  export const TopBar: React.FC<{
261
261
  ready: boolean;
262
262
  page: string | null;
263
263
  width?: number;
264
264
  height?: number;
265
- homeLogo: React.ReactNode;
266
- compactLogo: React.ReactNode;
265
+ homeLogo?: React.ReactNode;
266
+ compactLogo?: React.ReactNode;
267
267
  toolbar?: React.ReactNode;
268
268
  ip?: string;
269
- }> = memo(({ ready, page, width = 80, height = 5, homeLogo, compactLogo, toolbar, ip }) => (
269
+ }> = memo(({ width = 80, height = 5, toolbar }) => (
270
270
  <Panel width={width} height={height} borderStyle="round" paddingX={1} paddingY={0}>
271
- <Box width={width - 2} flexDirection="row" justifyContent="space-between" alignItems="center">
272
- <Box>
273
- {ready ? (page === null ? homeLogo : compactLogo) : <FallbackTop ip={ip} />}
274
- </Box>
275
- {toolbar ? <Box>{toolbar}</Box> : <Box><Hint dim>{ready ? '' : '…'}</Hint></Box>}
271
+ <Box width={width - 4} flexDirection="row" alignItems="flex-start">
272
+ {toolbar ?? null}
276
273
  </Box>
277
274
  </Panel>
278
275
  ));
@@ -1,121 +1,98 @@
1
- //@C:M ID=M.UI.TopToolbar;K=M;V=1.2;P=top toolbar;D=CLI;M=cli;S=ui
2
- import React, { memo, useMemo } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { Chip, ChipRow } from './CliMenuUi.tsx';
5
- import { useTheme } from '../components/design-system/ThemeProvider.js';
6
- import { getGlobalConfig, getCurrentProjectConfig, isPathTrusted, checkHasTrustDialogAccepted } from '../utils/config.ts';
7
- import { getCwd } from '../utils/cwd.js';
8
- // Update: Import respectively according to the new interface
9
- import type { ClawdPose } from '../components/LogoV2/Clawd.tsx';
10
- import { Clawd } from '../components/LogoV2/Clawd.tsx';
11
- import { AnimatedClawd } from '../components/LogoV2/AnimatedClawd.tsx';
12
-
13
- type Props = {
14
- ready: boolean;
15
- page: string | null;
16
- animEnabled: boolean;
17
- tipsEnabled: boolean;
18
- ip?: string;
19
- };
20
-
21
- function basename(p: string) {
22
- if (!p) return '';
23
- const parts = p.split(/[/\\]/).filter(Boolean);
24
- return parts[parts.length - 1] || p;
25
- }
26
- function ellipsisPath(p: string, keep = 2) {
27
- if (!p) return '';
28
- const parts = p.split(/[/\\]/).filter(Boolean);
29
- if (parts.length <= keep) return p;
30
- return '…/' + parts.slice(-keep).join('/');
31
- }
32
-
33
- //@C:F ID=F.UI.TopToolbar;K=F;V=1.3;P=toolbar;D=CLI;M=cli;S=ui;In=Props;Out=JSX.Element
34
- export const TopToolbar: React.FC<Props> = memo(({ ready, page, animEnabled, tipsEnabled, ip }) => {
35
- const [theme] = useTheme();
36
-
37
- // Only read config and trust status when ready
38
- const { cwd, trustAccepted, trustedPath, projectName } = useMemo(() => {
39
- if (!ready) {
40
- return { cwd: '', trustAccepted: undefined as undefined|boolean, trustedPath: undefined as undefined|boolean, projectName: '' };
41
- }
42
- const _cwd = getCwd();
43
- const _trustAccepted = checkHasTrustDialogAccepted();
44
- const _trustedPath = isPathTrusted(_cwd);
45
- let _projectName = '';
46
- try {
47
- const prj = getCurrentProjectConfig();
48
- _projectName = (prj && (prj.name || prj.projectName || prj.id)) || basename(_cwd);
49
- } catch {
50
- _projectName = basename(_cwd);
51
- }
52
- return { cwd: _cwd, trustAccepted: _trustAccepted, trustedPath: _trustedPath, projectName: _projectName };
53
- }, [ready]);
54
-
55
- const compact = page !== null;
56
- const cwdShort = useMemo(() => ellipsisPath(cwd, compact ? 2 : 3), [cwd, compact]);
57
-
58
- // Theme name
59
- const themeLabel = String(theme || (ready ? (getGlobalConfig()?.theme ?? 'system') : '…'));
60
-
61
- // Static Clawd pose
62
- const clawdPose: ClawdPose = useMemo(() => {
63
- if (!ready) return 'default';
64
- if (page === null) return animEnabled ? 'arms-up' : 'default';
65
- return tipsEnabled ? 'look-left' : 'look-right';
66
- }, [ready, page, animEnabled, tipsEnabled]);
67
-
68
- const uiTone = (String(theme) === 'dark') ? 'accent' : (String(theme) === 'highContrast' ? 'warning' : 'info');
69
-
70
- // Trust chip: only show when there's meaningful content
71
- const trustChip = useMemo(() => {
72
- if (trustedPath === undefined || trustAccepted === undefined) return null;
73
- if (trustedPath && trustAccepted) return <Chip label="Trust" value="✓ Trusted" tone="success" />;
74
- return <Chip label="Trust" value="Untrusted" tone="warning" />;
75
- }, [trustedPath, trustAccepted]);
76
-
77
- return (
78
- <Box flexDirection="column">
79
- {/* Row 1: Clawd + Theme + Project(if set) + CWD(if set) + Trust(if meaningful) */}
80
- <ChipRow>
81
- <Box>
82
- <Box marginRight={1}>
83
- {animEnabled ? <AnimatedClawd /> : <Clawd pose={clawdPose} />}
84
- </Box>
85
- <Chip label="Theme" value={themeLabel} tone="accent" />
86
- {projectName ? <Chip label="Project" value={projectName} tone="info" /> : null}
87
- {cwdShort ? <Chip label="CWD" value={cwdShort} tone="subtle" /> : null}
88
- {trustChip}
89
- </Box>
90
- </ChipRow>
91
-
92
- {/* Row 2: IP/server info (left) + UI chips (right) */}
93
- <Box flexDirection="row" justifyContent="space-between" alignItems="center">
94
- <Box>
95
- {ip ? (
96
- <Text color="green" dimColor>IP: {ip}</Text>
97
- ) : (
98
- <Text color="yellow" dimColor>{ready ? 'Server ready' : 'Starting server…'}</Text>
99
- )}
100
- </Box>
101
- <Box>
102
- <Chip
103
- label="UI"
104
- value={`Anim ${animEnabled ? 'On' : 'Off'} · Tips ${tipsEnabled ? 'On' : 'Off'}`}
105
- tone={uiTone as any}
106
- />
107
- </Box>
108
- </Box>
109
-
110
- {!compact && (
111
- <ChipRow>
112
- <Box>
113
- <Chip label="Shortcuts" value="N New · R Resume · P Provider · G Theme · ? Help" tone="subtle" />
114
- </Box>
115
- </ChipRow>
116
- )}
117
- </Box>
118
- );
119
- });
120
-
121
- export default TopToolbar;
1
+ //@C:M ID=M.UI.TopToolbar;K=M;V=1.3;P=top toolbar;D=CLI;M=cli;S=ui
2
+ import React, { memo, useMemo } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ import { Chip } from './CliMenuUi.tsx';
5
+ import { useTheme } from '../components/design-system/ThemeProvider.js';
6
+ import { getGlobalConfig } from '../utils/config.ts';
7
+ import type { ClawdPose } from '../components/LogoV2/Clawd.tsx';
8
+ import { Clawd } from '../components/LogoV2/Clawd.tsx';
9
+ import { AnimatedClawd } from '../components/LogoV2/AnimatedClawd.tsx';
10
+
11
+ type Props = {
12
+ ready: boolean;
13
+ page: string | null;
14
+ animEnabled: boolean;
15
+ tipsEnabled: boolean;
16
+ ip?: string;
17
+ };
18
+
19
+ //@C:F ID=F.UI.TopToolbar;K=F;V=1.4;P=toolbar;D=CLI;M=cli;S=ui;In=Props;Out=JSX.Element
20
+ export const TopToolbar: React.FC<Props> = memo(({ ready, page, animEnabled, tipsEnabled, ip }) => {
21
+ const [theme] = useTheme();
22
+
23
+ const { version } = useMemo(() => {
24
+ try {
25
+ const cfg = getGlobalConfig();
26
+ return { version: (cfg as any)?.version ?? '' };
27
+ } catch {
28
+ return { version: '' };
29
+ }
30
+ }, []);
31
+
32
+ const themeLabel = String(theme || (ready ? (getGlobalConfig()?.theme ?? 'system') : '…'));
33
+ const uiChipValue = `Anim ${animEnabled ? 'On' : 'Off'} · Tips ${tipsEnabled ? 'On' : 'Off'}`;
34
+ const uiTone = (String(theme) === 'dark') ? 'accent' : (String(theme) === 'highContrast' ? 'warning' : 'info');
35
+
36
+ const clawdPose: ClawdPose = useMemo(() => {
37
+ if (!ready) return 'default';
38
+ if (page === null) return animEnabled ? 'arms-up' : 'default';
39
+ return tipsEnabled ? 'look-left' : 'look-right';
40
+ }, [ready, page, animEnabled, tipsEnabled]);
41
+
42
+ // ── Compact mode (page !== null): single line, no logo ──────────────
43
+ if (page !== null) {
44
+ return (
45
+ <Box flexDirection="row" alignItems="center">
46
+ <Text bold>Bingo Code</Text>
47
+ <Box marginLeft={1}>
48
+ <Chip label="Theme" value={themeLabel} tone="accent" />
49
+ </Box>
50
+ <Chip label="UI" value={uiChipValue} tone={uiTone as any} />
51
+ {ip ? (
52
+ <Text color="green" dimColor> · IP: {ip}</Text>
53
+ ) : (
54
+ <Text color="yellow" dimColor> · {ready ? 'Server ready' : 'Starting…'}</Text>
55
+ )}
56
+ </Box>
57
+ );
58
+ }
59
+
60
+ // ── Home mode (page === null): Clawd left + 3-row right column ───────
61
+ return (
62
+ <Box flexDirection="row" alignItems="flex-start">
63
+ {/* Left: Clawd sprite */}
64
+ <Box marginRight={2}>
65
+ {animEnabled ? <AnimatedClawd /> : <Clawd pose={clawdPose} />}
66
+ </Box>
67
+
68
+ {/* Right: 3 rows */}
69
+ <Box flexDirection="column">
70
+ {/* Row 1: brand + version + chips */}
71
+ <Box flexDirection="row" alignItems="center">
72
+ <Text bold>Welcome to Bingo Code</Text>
73
+ {version ? <Text dimColor> v{version}</Text> : null}
74
+ <Box marginLeft={1}>
75
+ <Chip label="Theme" value={themeLabel} tone="accent" />
76
+ </Box>
77
+ <Chip label="UI" value={uiChipValue} tone={uiTone as any} />
78
+ </Box>
79
+
80
+ {/* Row 2: IP / server status */}
81
+ <Box>
82
+ {ip ? (
83
+ <Text color="green" dimColor>IP: {ip}</Text>
84
+ ) : (
85
+ <Text color="yellow" dimColor>{ready ? 'Server ready' : 'Starting server…'}</Text>
86
+ )}
87
+ </Box>
88
+
89
+ {/* Row 3: keyboard shortcuts */}
90
+ <Box>
91
+ <Text dimColor>N New · R Resume · P Provider · G Theme · ? Help</Text>
92
+ </Box>
93
+ </Box>
94
+ </Box>
95
+ );
96
+ });
97
+
98
+ export default TopToolbar;