tycono 0.1.96-beta.25 → 0.1.96-beta.27
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
package/src/tui/app.tsx
CHANGED
|
@@ -244,7 +244,7 @@ export const App: React.FC = () => {
|
|
|
244
244
|
const roles = api.company?.roles ?? [];
|
|
245
245
|
const statuses = api.execStatus?.statuses ?? {};
|
|
246
246
|
const orgTree = useMemo(() => buildOrgTree(roles, statuses), [roles, statuses]);
|
|
247
|
-
const flatRoleIds = useMemo(() => flattenOrgRoleIds(orgTree), [orgTree]);
|
|
247
|
+
const flatRoleIds = useMemo(() => ['ceo', ...flattenOrgRoleIds(orgTree)], [orgTree]);
|
|
248
248
|
|
|
249
249
|
// Active count
|
|
250
250
|
const activeCount = Object.values(statuses).filter(
|
|
@@ -426,11 +426,11 @@ export const App: React.FC = () => {
|
|
|
426
426
|
focusedWaveId={focusedWaveId}
|
|
427
427
|
portSummary={api.portSummary}
|
|
428
428
|
onMove={(dir) => {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
429
|
+
const nextIdx = dir === 'up'
|
|
430
|
+
? Math.max(0, selectedRoleIndex - 1)
|
|
431
|
+
: Math.min(flatRoleIds.length - 1, selectedRoleIndex + 1);
|
|
432
|
+
setSelectedRoleIndex(nextIdx);
|
|
433
|
+
setSelectedRoleId(flatRoleIds[nextIdx] ?? null);
|
|
434
434
|
}}
|
|
435
435
|
onSelect={() => {
|
|
436
436
|
const roleId = flatRoleIds[selectedRoleIndex] ?? null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OrgTree — left panel showing organization hierarchy with real-time status
|
|
3
|
+
* CEO is now selectable (index 0 in flatRoles)
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import React from 'react';
|
|
@@ -12,6 +13,7 @@ interface OrgTreeProps {
|
|
|
12
13
|
focused: boolean;
|
|
13
14
|
selectedIndex: number;
|
|
14
15
|
flatRoles: string[];
|
|
16
|
+
ceoStatus?: string;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
function statusColor(status: string): string {
|
|
@@ -46,10 +48,10 @@ function flattenTree(nodes: OrgNode[], prefix: string = '', isLast: boolean[] =
|
|
|
46
48
|
|
|
47
49
|
let linePrefix = '';
|
|
48
50
|
for (let j = 0; j < isLast.length; j++) {
|
|
49
|
-
linePrefix += isLast[j] ? ' ' : '
|
|
51
|
+
linePrefix += isLast[j] ? ' ' : '\u2502 ';
|
|
50
52
|
}
|
|
51
53
|
linePrefix += isLast.length > 0 || i > 0 || nodes.length > 1
|
|
52
|
-
? (last ? '
|
|
54
|
+
? (last ? '\u2514\u2500 ' : '\u251C\u2500 ')
|
|
53
55
|
: '';
|
|
54
56
|
|
|
55
57
|
result.push({
|
|
@@ -67,14 +69,24 @@ function flattenTree(nodes: OrgNode[], prefix: string = '', isLast: boolean[] =
|
|
|
67
69
|
return result;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
export const OrgTree: React.FC<OrgTreeProps> = React.memo(({ tree, focused, selectedIndex, flatRoles }) => {
|
|
72
|
+
export const OrgTree: React.FC<OrgTreeProps> = React.memo(({ tree, focused, selectedIndex, flatRoles, ceoStatus }) => {
|
|
71
73
|
const entries = flattenTree(tree);
|
|
74
|
+
const isCeoSelected = focused && flatRoles[selectedIndex] === 'ceo';
|
|
75
|
+
const ceoIcon = statusIcon(ceoStatus ?? 'idle');
|
|
76
|
+
const ceoColor = statusColor(ceoStatus ?? 'idle');
|
|
72
77
|
|
|
73
78
|
return (
|
|
74
79
|
<Box flexDirection="column" paddingX={1}>
|
|
75
|
-
<Text bold color={focused ? 'cyan' : 'gray'}>{'
|
|
80
|
+
<Text bold color={focused ? 'cyan' : 'gray'}>{'\u2500\u2500 Org Tree \u2500\u2500'}</Text>
|
|
76
81
|
<Box marginTop={1}>
|
|
77
|
-
<Text color=
|
|
82
|
+
<Text color={ceoColor} bold={ceoStatus === 'working'}>{ceoIcon} </Text>
|
|
83
|
+
<Text
|
|
84
|
+
color={isCeoSelected ? 'cyan' : 'yellow'}
|
|
85
|
+
bold={isCeoSelected}
|
|
86
|
+
inverse={isCeoSelected}
|
|
87
|
+
>
|
|
88
|
+
CEO
|
|
89
|
+
</Text>
|
|
78
90
|
</Box>
|
|
79
91
|
{entries.map((entry, i) => {
|
|
80
92
|
const isSelected = focused && flatRoles[selectedIndex] === entry.roleId;
|
|
@@ -119,14 +119,6 @@ export const PanelMode: React.FC<PanelModeProps> = ({
|
|
|
119
119
|
? waves.findIndex(w => w.waveId === focusedWaveId) + 1
|
|
120
120
|
: 0;
|
|
121
121
|
|
|
122
|
-
// Count sessions per wave for summary
|
|
123
|
-
const waveSessionCounts = new Map<string, number>();
|
|
124
|
-
for (const s of activeSessions) {
|
|
125
|
-
if (s.waveId) {
|
|
126
|
-
waveSessionCounts.set(s.waveId, (waveSessionCounts.get(s.waveId) ?? 0) + 1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
122
|
return (
|
|
131
123
|
<Box flexDirection="column" flexGrow={1}>
|
|
132
124
|
{/* Main content: Org Tree left | Detail + Stream right */}
|
|
@@ -138,38 +130,8 @@ export const PanelMode: React.FC<PanelModeProps> = ({
|
|
|
138
130
|
focused={true}
|
|
139
131
|
selectedIndex={selectedRoleIndex}
|
|
140
132
|
flatRoles={flatRoles}
|
|
133
|
+
ceoStatus={activeSessions.some(s => s.roleId === 'ceo' && s.status === 'active') ? 'working' : 'idle'}
|
|
141
134
|
/>
|
|
142
|
-
|
|
143
|
-
{/* Resource Summary — below org tree */}
|
|
144
|
-
<Box flexDirection="column" paddingX={1} marginTop={1}>
|
|
145
|
-
<Text color="gray">{'\u2500'.repeat(24)}</Text>
|
|
146
|
-
|
|
147
|
-
{/* Waves */}
|
|
148
|
-
{waves.length > 0 && (
|
|
149
|
-
<Box flexDirection="column">
|
|
150
|
-
{waves.map((w, i) => {
|
|
151
|
-
const isFocused = w.waveId === focusedWaveId;
|
|
152
|
-
const count = waveSessionCounts.get(w.waveId) ?? 0;
|
|
153
|
-
return (
|
|
154
|
-
<Box key={w.waveId}>
|
|
155
|
-
<Text color={isFocused ? 'green' : 'gray'}>
|
|
156
|
-
{isFocused ? '\u25B8' : ' '} W{i + 1}
|
|
157
|
-
</Text>
|
|
158
|
-
<Text color="gray"> {count > 0 ? `${count} agents` : 'idle'}</Text>
|
|
159
|
-
</Box>
|
|
160
|
-
);
|
|
161
|
-
})}
|
|
162
|
-
</Box>
|
|
163
|
-
)}
|
|
164
|
-
|
|
165
|
-
{/* Port summary */}
|
|
166
|
-
{portSummary.totalPorts > 0 && (
|
|
167
|
-
<Box marginTop={0}>
|
|
168
|
-
<Text color="blue">{portSummary.totalPorts} ports</Text>
|
|
169
|
-
<Text color="gray"> allocated</Text>
|
|
170
|
-
</Box>
|
|
171
|
-
)}
|
|
172
|
-
</Box>
|
|
173
135
|
</Box>
|
|
174
136
|
|
|
175
137
|
{/* Vertical separator — memoized to avoid regenerating on every render */}
|