tycono 0.3.14-beta.17 → 0.3.14-beta.19
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 +1 -1
- package/src/tui/components/PanelMode.tsx +54 -14
package/package.json
CHANGED
|
@@ -91,6 +91,7 @@ const PanelModeInner: React.FC<PanelModeProps> = ({
|
|
|
91
91
|
}) => {
|
|
92
92
|
const [termHeight, setTermHeight] = useState(process.stdout.rows || 30);
|
|
93
93
|
const [rightTab, setRightTab] = useState<RightTab>('stream');
|
|
94
|
+
const [docsIndex, setDocsIndex] = useState(0);
|
|
94
95
|
|
|
95
96
|
useEffect(() => {
|
|
96
97
|
const fn = () => setTermHeight(process.stdout.rows || 30);
|
|
@@ -123,9 +124,30 @@ const PanelModeInner: React.FC<PanelModeProps> = ({
|
|
|
123
124
|
if (idx < tabs.length - 1) setRightTab(tabs[idx + 1]);
|
|
124
125
|
return;
|
|
125
126
|
}
|
|
126
|
-
|
|
127
|
-
if (input === '
|
|
128
|
-
|
|
127
|
+
// j/k context-dependent
|
|
128
|
+
if (input === 'k' || key.upArrow) {
|
|
129
|
+
if (rightTab === 'docs') { setDocsIndex(i => Math.max(0, i - 1)); }
|
|
130
|
+
else { onMove('up'); }
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (input === 'j' || key.downArrow) {
|
|
134
|
+
if (rightTab === 'docs') { setDocsIndex(i => i + 1); } // capped later by docsList length
|
|
135
|
+
else { onMove('down'); }
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (key.return) {
|
|
139
|
+
if (rightTab === 'docs' && selectedDocPath) {
|
|
140
|
+
// Open in vim
|
|
141
|
+
try {
|
|
142
|
+
const { execSync } = require('child_process');
|
|
143
|
+
const editor = process.env.EDITOR || 'vim';
|
|
144
|
+
execSync(`${editor} "${selectedDocPath}"`, { stdio: 'inherit' });
|
|
145
|
+
} catch { /* ignore */ }
|
|
146
|
+
} else {
|
|
147
|
+
onSelect();
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
129
151
|
// Wave switch 1-9
|
|
130
152
|
const num = parseInt(input, 10);
|
|
131
153
|
if (num >= 1 && num <= 9 && num <= waves.length) {
|
|
@@ -151,8 +173,9 @@ const PanelModeInner: React.FC<PanelModeProps> = ({
|
|
|
151
173
|
})),
|
|
152
174
|
];
|
|
153
175
|
|
|
154
|
-
// === Build right column: Stream/Info ===
|
|
176
|
+
// === Build right column: Stream/Info/Docs ===
|
|
155
177
|
const rightContentLines: string[] = [];
|
|
178
|
+
let selectedDocPath: string | null = null;
|
|
156
179
|
if (rightTab === 'stream') {
|
|
157
180
|
const maxEv = Math.max(5, termHeight - 10);
|
|
158
181
|
const filtered = selectedRoleId ? events.filter(e => e.roleId === selectedRoleId) : events;
|
|
@@ -162,7 +185,12 @@ const PanelModeInner: React.FC<PanelModeProps> = ({
|
|
|
162
185
|
if (line) rightContentLines.push(line.slice(0, rightWidth));
|
|
163
186
|
}
|
|
164
187
|
if (rightContentLines.length === 0) {
|
|
165
|
-
|
|
188
|
+
if (selectedRoleId && events.length > 0) {
|
|
189
|
+
rightContentLines.push(`No events for ${selectedRoleId} (${events.length} total)`);
|
|
190
|
+
rightContentLines.push('Press Enter to show all roles');
|
|
191
|
+
} else {
|
|
192
|
+
rightContentLines.push(waveId ? `Waiting for events... (${events.length} in buffer)` : 'No active stream. Type a directive to start.');
|
|
193
|
+
}
|
|
166
194
|
}
|
|
167
195
|
} else if (rightTab === 'info') {
|
|
168
196
|
rightContentLines.push(`Wave: ${focusedWave?.waveId ?? 'none'}`);
|
|
@@ -170,29 +198,41 @@ const PanelModeInner: React.FC<PanelModeProps> = ({
|
|
|
170
198
|
rightContentLines.push(`Sessions: ${waveSessionCount} Events: ${events.length}`);
|
|
171
199
|
rightContentLines.push(`Stream: ${streamStatus}`);
|
|
172
200
|
} else if (rightTab === 'docs') {
|
|
173
|
-
// Docs: scan .md files
|
|
201
|
+
// Docs: scan .md files with j/k scroll + Enter to open
|
|
174
202
|
try {
|
|
175
203
|
const skip = new Set(['.git', 'node_modules', '.tycono', '.worktrees', 'dist', '.claude', '.obsidian']);
|
|
176
204
|
const mdFiles: string[] = [];
|
|
205
|
+
const mdPaths: string[] = []; // full paths for vim
|
|
177
206
|
const walk = (dir: string, depth: number) => {
|
|
178
|
-
if (depth > 3 || mdFiles.length >
|
|
207
|
+
if (depth > 3 || mdFiles.length > 200) return;
|
|
179
208
|
try {
|
|
180
209
|
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
181
210
|
if (skip.has(e.name)) continue;
|
|
182
211
|
const full = path.join(dir, e.name);
|
|
183
212
|
if (e.isDirectory()) walk(full, depth + 1);
|
|
184
|
-
else if (e.name.endsWith('.md'))
|
|
213
|
+
else if (e.name.endsWith('.md')) {
|
|
214
|
+
mdFiles.push(full.replace(companyRoot + '/', ''));
|
|
215
|
+
mdPaths.push(full);
|
|
216
|
+
}
|
|
185
217
|
}
|
|
186
218
|
} catch {}
|
|
187
219
|
};
|
|
188
220
|
walk(companyRoot, 0);
|
|
189
221
|
mdFiles.sort();
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
222
|
+
mdPaths.sort();
|
|
223
|
+
// Cap docsIndex
|
|
224
|
+
const cappedIdx = Math.min(docsIndex, mdFiles.length - 1);
|
|
225
|
+
if (cappedIdx !== docsIndex) setDocsIndex(Math.max(0, cappedIdx));
|
|
226
|
+
selectedDocPath = mdPaths[cappedIdx] ?? null;
|
|
227
|
+
|
|
228
|
+
const maxVisible = Math.max(5, termHeight - 12);
|
|
229
|
+
const scrollStart = Math.max(0, Math.min(cappedIdx - 3, mdFiles.length - maxVisible));
|
|
230
|
+
rightContentLines.push(`${mdFiles.length} documents [j/k] browse [Enter] ${process.env.EDITOR || 'vim'}`);
|
|
231
|
+
for (let i = scrollStart; i < Math.min(scrollStart + maxVisible, mdFiles.length); i++) {
|
|
232
|
+
const selected = i === cappedIdx;
|
|
233
|
+
const prefix = selected ? '\u25B6 ' : ' ';
|
|
234
|
+
rightContentLines.push(`${prefix}${mdFiles[i].slice(0, rightWidth - 4)}`);
|
|
194
235
|
}
|
|
195
|
-
if (mdFiles.length > maxDocs) rightContentLines.push(` ... +${mdFiles.length - maxDocs} more`);
|
|
196
236
|
} catch {
|
|
197
237
|
rightContentLines.push('Cannot scan documents');
|
|
198
238
|
}
|
|
@@ -254,7 +294,7 @@ const PanelModeInner: React.FC<PanelModeProps> = ({
|
|
|
254
294
|
<Text color="gray">{sep}</Text>
|
|
255
295
|
<Text>
|
|
256
296
|
{waveTabs ? <Text color="gray">{waveTabs + ' '}</Text> : null}
|
|
257
|
-
<Text color="gray" dimColor>{'[h/l] tab [j/k] role [Enter] filter '}</Text>
|
|
297
|
+
<Text color="gray" dimColor>{rightTab === 'docs' ? '[h/l] tab [j/k] browse [Enter] open ' : '[h/l] tab [j/k] role [Enter] filter '}</Text>
|
|
258
298
|
{waves.length > 1 ? <Text color="gray" dimColor>{'[1-9] wave '}</Text> : null}
|
|
259
299
|
<Text color="gray" dimColor>{'[Esc] back'}</Text>
|
|
260
300
|
</Text>
|