@colmbus72/yeehaw 0.2.0 → 0.4.0
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/claude-plugin/.claude-plugin/plugin.json +7 -0
- package/claude-plugin/skills/yeehaw-project-setup/SKILL.md +129 -0
- package/claude-plugin/skills/yeehaw-project-setup/references/color-discovery.md +170 -0
- package/claude-plugin/skills/yeehaw-project-setup/references/wiki-templates.md +266 -0
- package/dist/app.js +66 -56
- package/dist/components/Header.d.ts +8 -1
- package/dist/components/Header.js +33 -23
- package/dist/components/LivestockHeader.d.ts +7 -0
- package/dist/components/LivestockHeader.js +122 -0
- package/dist/components/PathInput.js +81 -19
- package/dist/components/SplashScreen.d.ts +5 -0
- package/dist/components/SplashScreen.js +178 -0
- package/dist/index.js +2 -5
- package/dist/lib/tmux-config.js +11 -0
- package/dist/lib/tmux.d.ts +5 -0
- package/dist/lib/tmux.js +57 -7
- package/dist/lib/update-check.d.ts +8 -0
- package/dist/lib/update-check.js +12 -0
- package/dist/mcp-server.js +0 -0
- package/dist/types.d.ts +6 -0
- package/dist/views/BarnContext.js +0 -4
- package/dist/views/GlobalDashboard.d.ts +5 -1
- package/dist/views/GlobalDashboard.js +59 -32
- package/dist/views/IssuesView.js +1 -1
- package/dist/views/LivestockDetailView.d.ts +8 -3
- package/dist/views/LivestockDetailView.js +56 -54
- package/dist/views/LogsView.js +1 -1
- package/dist/views/ProjectContext.js +142 -24
- package/dist/views/WikiView.js +0 -4
- package/package.json +2 -1
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import TextInput from 'ink-text-input';
|
|
5
5
|
import { Header } from '../components/Header.js';
|
|
6
|
+
import { LivestockHeader } from '../components/LivestockHeader.js';
|
|
7
|
+
import { List } from '../components/List.js';
|
|
6
8
|
import { Panel } from '../components/Panel.js';
|
|
7
9
|
import { PathInput } from '../components/PathInput.js';
|
|
8
10
|
import { loadBarn } from '../lib/config.js';
|
|
9
|
-
|
|
11
|
+
import { getWindowStatus } from '../lib/tmux.js';
|
|
12
|
+
export function LivestockDetailView({ project, livestock, source, sourceBarn, windows, onBack, onOpenLogs, onOpenSession, onSelectWindow, onUpdateLivestock, }) {
|
|
10
13
|
const [mode, setMode] = useState('normal');
|
|
11
14
|
// Edit form state
|
|
12
15
|
const [editName, setEditName] = useState(livestock.name);
|
|
@@ -17,6 +20,15 @@ export function LivestockDetailView({ project, livestock, onBack, onOpenLogs, on
|
|
|
17
20
|
const [editEnvPath, setEditEnvPath] = useState(livestock.env_path || '');
|
|
18
21
|
// Get barn info if remote
|
|
19
22
|
const barn = livestock.barn ? loadBarn(livestock.barn) : null;
|
|
23
|
+
// Sync form state when livestock prop changes (e.g., after save)
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setEditName(livestock.name);
|
|
26
|
+
setEditPath(livestock.path);
|
|
27
|
+
setEditRepo(livestock.repo || '');
|
|
28
|
+
setEditBranch(livestock.branch || '');
|
|
29
|
+
setEditLogPath(livestock.log_path || '');
|
|
30
|
+
setEditEnvPath(livestock.env_path || '');
|
|
31
|
+
}, [livestock]);
|
|
20
32
|
const resetForm = () => {
|
|
21
33
|
setEditName(livestock.name);
|
|
22
34
|
setEditPath(livestock.path);
|
|
@@ -25,29 +37,18 @@ export function LivestockDetailView({ project, livestock, onBack, onOpenLogs, on
|
|
|
25
37
|
setEditLogPath(livestock.log_path || '');
|
|
26
38
|
setEditEnvPath(livestock.env_path || '');
|
|
27
39
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
case 'branch':
|
|
41
|
-
updated.branch = value || undefined;
|
|
42
|
-
break;
|
|
43
|
-
case 'log_path':
|
|
44
|
-
updated.log_path = value || undefined;
|
|
45
|
-
break;
|
|
46
|
-
case 'env_path':
|
|
47
|
-
updated.env_path = value || undefined;
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
onUpdateLivestock(updated);
|
|
40
|
+
// Save all pending changes at once
|
|
41
|
+
const saveAllChanges = () => {
|
|
42
|
+
const updated = {
|
|
43
|
+
...livestock,
|
|
44
|
+
name: editName.trim() || livestock.name,
|
|
45
|
+
path: editPath.trim() || livestock.path,
|
|
46
|
+
repo: editRepo.trim() || undefined,
|
|
47
|
+
branch: editBranch.trim() || undefined,
|
|
48
|
+
log_path: editLogPath.trim() || undefined,
|
|
49
|
+
env_path: editEnvPath.trim() || undefined,
|
|
50
|
+
};
|
|
51
|
+
onUpdateLivestock(livestock, updated);
|
|
51
52
|
setMode('normal');
|
|
52
53
|
};
|
|
53
54
|
useInput((input, key) => {
|
|
@@ -62,13 +63,17 @@ export function LivestockDetailView({ project, livestock, onBack, onOpenLogs, on
|
|
|
62
63
|
}
|
|
63
64
|
return;
|
|
64
65
|
}
|
|
66
|
+
// Handle Ctrl+S to save and exit from any edit mode
|
|
67
|
+
// Note: Ctrl+S sends ASCII 19 (\x13), not 's'
|
|
68
|
+
if ((key.ctrl && input === 's') || input === '\x13') {
|
|
69
|
+
if (mode !== 'normal') {
|
|
70
|
+
saveAllChanges();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
65
74
|
// Only process these in normal mode
|
|
66
75
|
if (mode !== 'normal')
|
|
67
76
|
return;
|
|
68
|
-
if (input === 'q') {
|
|
69
|
-
onBack();
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
77
|
if (input === 's') {
|
|
73
78
|
onOpenSession();
|
|
74
79
|
return;
|
|
@@ -91,50 +96,47 @@ export function LivestockDetailView({ project, livestock, onBack, onOpenLogs, on
|
|
|
91
96
|
if (mode === 'edit-name') {
|
|
92
97
|
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Name: " }), _jsx(TextInput, { value: editName, onChange: setEditName, onSubmit: () => {
|
|
93
98
|
if (editName.trim()) {
|
|
94
|
-
saveEdit('name', editName.trim());
|
|
95
99
|
setMode('edit-path');
|
|
96
100
|
}
|
|
97
|
-
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel" }) })] })] }));
|
|
101
|
+
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Ctrl+S: save & exit, Esc: cancel" }) })] })] }));
|
|
98
102
|
}
|
|
99
103
|
// Edit path
|
|
100
104
|
if (mode === 'edit-path') {
|
|
101
105
|
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Path: " }), _jsx(PathInput, { value: editPath, onChange: setEditPath, onSubmit: () => {
|
|
102
106
|
if (editPath.trim()) {
|
|
103
|
-
saveEdit('path', editPath.trim());
|
|
104
107
|
setMode('edit-repo');
|
|
105
108
|
}
|
|
106
|
-
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next
|
|
109
|
+
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next, Tab: autocomplete, Ctrl+S: save & exit, Esc: cancel" }) })] })] }));
|
|
107
110
|
}
|
|
108
111
|
// Edit repo
|
|
109
112
|
if (mode === 'edit-repo') {
|
|
110
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Git Repo (optional): " }), _jsx(TextInput, { value: editRepo, onChange: setEditRepo, onSubmit: () => {
|
|
111
|
-
saveEdit('repo', editRepo.trim());
|
|
112
|
-
setMode('edit-branch');
|
|
113
|
-
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel" }) })] })] }));
|
|
113
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Git Repo (optional): " }), _jsx(TextInput, { value: editRepo, onChange: setEditRepo, onSubmit: () => setMode('edit-branch') })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Ctrl+S: save & exit, Esc: cancel" }) })] })] }));
|
|
114
114
|
}
|
|
115
115
|
// Edit branch
|
|
116
116
|
if (mode === 'edit-branch') {
|
|
117
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Git Branch (optional): " }), _jsx(TextInput, { value: editBranch, onChange: setEditBranch, onSubmit: () => {
|
|
118
|
-
saveEdit('branch', editBranch.trim());
|
|
119
|
-
setMode('edit-log-path');
|
|
120
|
-
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel" }) })] })] }));
|
|
117
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Git Branch (optional): " }), _jsx(TextInput, { value: editBranch, onChange: setEditBranch, onSubmit: () => setMode('edit-log-path') })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Ctrl+S: save & exit, Esc: cancel" }) })] })] }));
|
|
121
118
|
}
|
|
122
119
|
// Edit log path
|
|
123
120
|
if (mode === 'edit-log-path') {
|
|
124
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Log Path (optional, relative): " }), _jsx(TextInput, { value: editLogPath, onChange: setEditLogPath, onSubmit: () => {
|
|
125
|
-
saveEdit('log_path', editLogPath.trim());
|
|
126
|
-
setMode('edit-env-path');
|
|
127
|
-
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel" }) })] })] }));
|
|
121
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Log Path (optional, relative): " }), _jsx(TextInput, { value: editLogPath, onChange: setEditLogPath, onSubmit: () => setMode('edit-env-path') })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Ctrl+S: save & exit, Esc: cancel" }) })] })] }));
|
|
128
122
|
}
|
|
129
|
-
// Edit env path
|
|
123
|
+
// Edit env path (last field - saves all changes)
|
|
130
124
|
if (mode === 'edit-env-path') {
|
|
131
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Env Path (optional, relative): " }), _jsx(TextInput, { value: editEnvPath, onChange: setEditEnvPath, onSubmit: ()
|
|
132
|
-
saveEdit('env_path', editEnvPath.trim());
|
|
133
|
-
// All done - return to normal mode
|
|
134
|
-
setMode('normal');
|
|
135
|
-
} })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: save & finish, Esc: cancel" }) })] })] }));
|
|
125
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: `Edit: ${livestock.name}`, color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Livestock" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Env Path (optional, relative): " }), _jsx(TextInput, { value: editEnvPath, onChange: setEditEnvPath, onSubmit: saveAllChanges })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: save & finish, Ctrl+S: save & exit, Esc: cancel" }) })] })] }));
|
|
136
126
|
}
|
|
137
|
-
//
|
|
138
|
-
const
|
|
139
|
-
|
|
127
|
+
// Filter windows to this livestock (match pattern: projectname-livestockname)
|
|
128
|
+
const livestockWindowName = `${project.name}-${livestock.name}`;
|
|
129
|
+
const livestockWindows = windows.filter(w => w.name === livestockWindowName || w.name.startsWith(`${livestockWindowName}-`));
|
|
130
|
+
const sessionItems = livestockWindows.map((w, i) => ({
|
|
131
|
+
id: String(w.index),
|
|
132
|
+
label: `[${i + 1}] shell`,
|
|
133
|
+
status: w.active ? 'active' : 'inactive',
|
|
134
|
+
meta: getWindowStatus(w),
|
|
135
|
+
}));
|
|
136
|
+
// Normal view - show livestock info inline with sessions
|
|
137
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(LivestockHeader, { project: project, livestock: livestock }), _jsxs(Box, { paddingX: 2, gap: 3, children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "path:" }), " ", livestock.path] }), barn && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "barn:" }), " ", barn.name, " ", _jsxs(Text, { dimColor: true, children: ["(", barn.host, ")"] })] }))] }), _jsxs(Box, { paddingX: 2, gap: 3, marginBottom: 1, children: [livestock.repo && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "repo:" }), " ", livestock.repo] })), livestock.log_path && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "logs:" }), " ", livestock.log_path] })), livestock.env_path && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "env:" }), " ", livestock.env_path] }))] }), _jsx(Box, { paddingX: 1, flexGrow: 1, children: _jsx(Panel, { title: "Sessions", focused: true, hints: `[s] shell ${livestock.log_path ? '[l] logs ' : ''}[e] edit`, children: sessionItems.length > 0 ? (_jsx(List, { items: sessionItems, focused: true, onSelect: (item) => {
|
|
138
|
+
const window = livestockWindows.find(w => String(w.index) === item.id);
|
|
139
|
+
if (window)
|
|
140
|
+
onSelectWindow(window);
|
|
141
|
+
} })) : (_jsx(Text, { dimColor: true, italic: true, children: "No active sessions. Press [s] to start a shell." })) }) })] }));
|
|
140
142
|
}
|
package/dist/views/LogsView.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useState, useCallback } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import TextInput from 'ink-text-input';
|
|
5
5
|
import { Header } from '../components/Header.js';
|
|
@@ -9,6 +9,70 @@ import { PathInput } from '../components/PathInput.js';
|
|
|
9
9
|
import { getWindowStatus } from '../lib/tmux.js';
|
|
10
10
|
import { detectGitInfo, detectRemoteGitInfo } from '../lib/git.js';
|
|
11
11
|
import { detectLivestockConfig } from '../lib/livestock.js';
|
|
12
|
+
import { isLocalBarn } from '../lib/config.js';
|
|
13
|
+
// HSL color utilities for color picker
|
|
14
|
+
function hexToHsl(hex) {
|
|
15
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
16
|
+
if (!result)
|
|
17
|
+
return null;
|
|
18
|
+
const r = parseInt(result[1], 16) / 255;
|
|
19
|
+
const g = parseInt(result[2], 16) / 255;
|
|
20
|
+
const b = parseInt(result[3], 16) / 255;
|
|
21
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
22
|
+
let h = 0, s = 0;
|
|
23
|
+
const l = (max + min) / 2;
|
|
24
|
+
if (max !== min) {
|
|
25
|
+
const d = max - min;
|
|
26
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
27
|
+
switch (max) {
|
|
28
|
+
case r:
|
|
29
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
30
|
+
break;
|
|
31
|
+
case g:
|
|
32
|
+
h = ((b - r) / d + 2) / 6;
|
|
33
|
+
break;
|
|
34
|
+
case b:
|
|
35
|
+
h = ((r - g) / d + 4) / 6;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { h: h * 360, s: s * 100, l: l * 100 };
|
|
40
|
+
}
|
|
41
|
+
function hslToHex(h, s, l) {
|
|
42
|
+
h = ((h % 360) + 360) % 360;
|
|
43
|
+
s = Math.max(0, Math.min(100, s)) / 100;
|
|
44
|
+
l = Math.max(0, Math.min(100, l)) / 100;
|
|
45
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
46
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
47
|
+
const m = l - c / 2;
|
|
48
|
+
let r = 0, g = 0, b = 0;
|
|
49
|
+
if (h < 60) {
|
|
50
|
+
r = c;
|
|
51
|
+
g = x;
|
|
52
|
+
}
|
|
53
|
+
else if (h < 120) {
|
|
54
|
+
r = x;
|
|
55
|
+
g = c;
|
|
56
|
+
}
|
|
57
|
+
else if (h < 180) {
|
|
58
|
+
g = c;
|
|
59
|
+
b = x;
|
|
60
|
+
}
|
|
61
|
+
else if (h < 240) {
|
|
62
|
+
g = x;
|
|
63
|
+
b = c;
|
|
64
|
+
}
|
|
65
|
+
else if (h < 300) {
|
|
66
|
+
r = x;
|
|
67
|
+
b = c;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
r = c;
|
|
71
|
+
b = x;
|
|
72
|
+
}
|
|
73
|
+
const toHex = (n) => Math.round((n + m) * 255).toString(16).padStart(2, '0');
|
|
74
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
75
|
+
}
|
|
12
76
|
export function ProjectContext({ project, barns, windows, onBack, onNewClaude, onSelectWindow, onSelectLivestock, onOpenLivestockSession, onUpdateProject, onDeleteProject, onOpenWiki, onOpenIssues, }) {
|
|
13
77
|
const [focusedPanel, setFocusedPanel] = useState('livestock');
|
|
14
78
|
const [mode, setMode] = useState('normal');
|
|
@@ -17,6 +81,8 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
17
81
|
const [editPath, setEditPath] = useState(project.path);
|
|
18
82
|
const [editSummary, setEditSummary] = useState(project.summary || '');
|
|
19
83
|
const [editColor, setEditColor] = useState(project.color || '');
|
|
84
|
+
const [editGradientSpread, setEditGradientSpread] = useState(project.gradientSpread ?? 5);
|
|
85
|
+
const [editGradientInverted, setEditGradientInverted] = useState(project.gradientInverted ?? false);
|
|
20
86
|
// New livestock form state
|
|
21
87
|
const [newLivestockName, setNewLivestockName] = useState('');
|
|
22
88
|
const [newLivestockPath, setNewLivestockPath] = useState('');
|
|
@@ -33,6 +99,40 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
33
99
|
const [detectedGit, setDetectedGit] = useState(null);
|
|
34
100
|
// Framework detection for config suggestions
|
|
35
101
|
const [detectedConfig, setDetectedConfig] = useState(null);
|
|
102
|
+
// Color picker keyboard handlers
|
|
103
|
+
const shiftHue = useCallback((delta) => {
|
|
104
|
+
const currentColor = editColor || '#f0c040';
|
|
105
|
+
const hsl = hexToHsl(currentColor);
|
|
106
|
+
if (hsl) {
|
|
107
|
+
const newHex = hslToHex(hsl.h + delta, hsl.s, hsl.l);
|
|
108
|
+
setEditColor(newHex);
|
|
109
|
+
}
|
|
110
|
+
else if (!editColor) {
|
|
111
|
+
setEditColor(hslToHex(delta > 0 ? 10 : 350, 70, 50));
|
|
112
|
+
}
|
|
113
|
+
}, [editColor]);
|
|
114
|
+
const adjustSpread = useCallback((delta) => {
|
|
115
|
+
setEditGradientSpread((s) => Math.max(0, Math.min(10, s + delta)));
|
|
116
|
+
}, []);
|
|
117
|
+
const toggleInvert = useCallback(() => {
|
|
118
|
+
setEditGradientInverted((i) => !i);
|
|
119
|
+
}, []);
|
|
120
|
+
// Handle color picker inputs (must be before any conditional returns)
|
|
121
|
+
// Using [ ] for hue shift and ! for invert to avoid conflicts with text input
|
|
122
|
+
useInput((input, key) => {
|
|
123
|
+
if (mode !== 'edit-color')
|
|
124
|
+
return;
|
|
125
|
+
if (input === '[')
|
|
126
|
+
shiftHue(-10);
|
|
127
|
+
else if (input === ']')
|
|
128
|
+
shiftHue(10);
|
|
129
|
+
else if (key.upArrow)
|
|
130
|
+
adjustSpread(1);
|
|
131
|
+
else if (key.downArrow)
|
|
132
|
+
adjustSpread(-1);
|
|
133
|
+
else if (input === '!')
|
|
134
|
+
toggleInvert();
|
|
135
|
+
}, { isActive: mode === 'edit-color' });
|
|
36
136
|
// Filter windows to this project (by name prefix)
|
|
37
137
|
const projectWindows = windows.filter((w) => w.index > 0 && w.name.startsWith(project.name));
|
|
38
138
|
// Create a map from display number (1-9) to window for quick access
|
|
@@ -47,6 +147,8 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
47
147
|
setEditPath(project.path);
|
|
48
148
|
setEditSummary(project.summary || '');
|
|
49
149
|
setEditColor(project.color || '');
|
|
150
|
+
setEditGradientSpread(project.gradientSpread ?? 5);
|
|
151
|
+
setEditGradientInverted(project.gradientInverted ?? false);
|
|
50
152
|
setMode('edit-name');
|
|
51
153
|
};
|
|
52
154
|
const cancelEdit = () => {
|
|
@@ -61,6 +163,8 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
61
163
|
path: editPath,
|
|
62
164
|
summary: editSummary || undefined,
|
|
63
165
|
color: editColor || undefined,
|
|
166
|
+
gradientSpread: editGradientSpread,
|
|
167
|
+
gradientInverted: editGradientInverted,
|
|
64
168
|
});
|
|
65
169
|
setMode('normal');
|
|
66
170
|
}
|
|
@@ -159,10 +263,6 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
159
263
|
}
|
|
160
264
|
if (mode !== 'normal')
|
|
161
265
|
return;
|
|
162
|
-
if (input === 'q') {
|
|
163
|
-
onBack();
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
266
|
if (key.tab) {
|
|
167
267
|
setFocusedPanel((p) => (p === 'livestock' ? 'sessions' : 'livestock'));
|
|
168
268
|
return;
|
|
@@ -225,37 +325,44 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
225
325
|
});
|
|
226
326
|
// Edit mode screens
|
|
227
327
|
if (mode === 'edit-name') {
|
|
228
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Project" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Name: " }), _jsx(TextInput, { value: editName, onChange: setEditName, onSubmit: () => saveAndNext('edit-path') })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel" }) })] })] }));
|
|
328
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "yellow", children: "Edit Project" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Name: " }), _jsx(TextInput, { value: editName, onChange: setEditName, onSubmit: () => saveAndNext('edit-path') })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel" }) })] })] }));
|
|
229
329
|
}
|
|
230
330
|
if (mode === 'edit-path') {
|
|
231
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "yellow", children: ["Edit Project: ", editName] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Path: " }), _jsx(PathInput, { value: editPath, onChange: setEditPath, onSubmit: () => saveAndNext('edit-summary') })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Tab: autocomplete, Enter: next field, Esc: cancel" }) })] })] }));
|
|
331
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "yellow", children: ["Edit Project: ", editName] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Path: " }), _jsx(PathInput, { value: editPath, onChange: setEditPath, onSubmit: () => saveAndNext('edit-summary') })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Tab: autocomplete, Enter: next field, Esc: cancel" }) })] })] }));
|
|
232
332
|
}
|
|
233
333
|
if (mode === 'edit-summary') {
|
|
234
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "yellow", children: ["Edit Project: ", editName] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Summary: " }), _jsx(TextInput, { value: editSummary, onChange: setEditSummary, onSubmit: () => saveAndNext('edit-color'), placeholder: "Short description..." })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel (leave blank to skip)" }) })] })] }));
|
|
334
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "yellow", children: ["Edit Project: ", editName] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Summary: " }), _jsx(TextInput, { value: editSummary, onChange: setEditSummary, onSubmit: () => saveAndNext('edit-color'), placeholder: "Short description..." })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next field, Esc: cancel (leave blank to skip)" }) })] })] }));
|
|
235
335
|
}
|
|
236
336
|
if (mode === 'edit-color') {
|
|
237
|
-
|
|
337
|
+
const displayColor = editColor || '#f0c040';
|
|
338
|
+
// Filter out special keys used for color picker controls
|
|
339
|
+
const handleColorChange = (value) => {
|
|
340
|
+
const filtered = value.replace(/[\[\]!]/g, '');
|
|
341
|
+
setEditColor(filtered);
|
|
342
|
+
};
|
|
343
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: displayColor, gradientSpread: editGradientSpread, gradientInverted: editGradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "yellow", children: ["Edit Project: ", editName] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Color (hex): " }), _jsx(TextInput, { value: editColor, onChange: handleColorChange, onSubmit: () => saveAndNext('done'), placeholder: "#ff6b6b" })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Spread: ", editGradientSpread, "/10 ", editGradientInverted ? '(inverted)' : ''] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: displayColor, children: "\u2588\u2588\u2588\u2588" }), _jsx(Text, { children: " Preview" })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "[ ] shift color \u2191/\u2193 gradient spread ! invert" }), _jsx(Text, { dimColor: true, children: "Enter: save project, Esc: cancel" })] })] })] }));
|
|
238
344
|
}
|
|
239
345
|
// Add livestock flow: name → barn → path (with auto-detect)
|
|
240
346
|
if (mode === 'add-livestock-name') {
|
|
241
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "green", children: "Add Livestock" }), _jsx(Text, { dimColor: true, children: "Livestock are deployed instances of your app" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Name: " }), _jsx(TextInput, { value: newLivestockName, onChange: setNewLivestockName, onSubmit: () => {
|
|
347
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "green", children: "Add Livestock" }), _jsx(Text, { dimColor: true, children: "Livestock are deployed instances of your app" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Name: " }), _jsx(TextInput, { value: newLivestockName, onChange: setNewLivestockName, onSubmit: () => {
|
|
242
348
|
if (newLivestockName.trim()) {
|
|
243
349
|
setMode('add-livestock-barn');
|
|
244
350
|
}
|
|
245
351
|
}, placeholder: "local, dev, production..." })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: next, Esc: cancel" }) })] })] }));
|
|
246
352
|
}
|
|
247
353
|
if (mode === 'add-livestock-barn') {
|
|
248
|
-
// Build barn options including "Local" option
|
|
354
|
+
// Build barn options including "Local" option (filter out local barn from array to avoid duplicates)
|
|
355
|
+
const remoteBarns = barns.filter((b) => !isLocalBarn(b));
|
|
249
356
|
const barnOptions = [
|
|
250
357
|
{ id: '__local__', label: 'Local (this machine)', status: 'active' },
|
|
251
|
-
...
|
|
358
|
+
...remoteBarns.map((b) => ({
|
|
252
359
|
id: b.name,
|
|
253
360
|
label: b.name,
|
|
254
361
|
status: 'active',
|
|
255
362
|
meta: `${b.user}@${b.host}`,
|
|
256
363
|
})),
|
|
257
364
|
];
|
|
258
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsx(Text, { dimColor: true, children: "Where is this livestock deployed?" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(List, { items: barnOptions, focused: true, onSelect: (item) => {
|
|
365
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsx(Text, { dimColor: true, children: "Where is this livestock deployed?" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(List, { items: barnOptions, focused: true, onSelect: (item) => {
|
|
259
366
|
if (item.id === '__local__') {
|
|
260
367
|
setSelectedBarn(null);
|
|
261
368
|
}
|
|
@@ -270,7 +377,7 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
270
377
|
const locationLabel = selectedBarn
|
|
271
378
|
? `on ${selectedBarn.name} (${selectedBarn.host})`
|
|
272
379
|
: 'local';
|
|
273
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsxs(Text, { dimColor: true, children: ["Location: ", locationLabel] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Path: " }), _jsx(PathInput, { value: newLivestockPath, onChange: setNewLivestockPath, onSubmit: handlePathSubmit, barn: selectedBarn || undefined })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Tab: autocomplete, Enter: next, Esc: cancel" }) })] })] }));
|
|
380
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsxs(Text, { dimColor: true, children: ["Location: ", locationLabel] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Path: " }), _jsx(PathInput, { value: newLivestockPath, onChange: setNewLivestockPath, onSubmit: handlePathSubmit, barn: selectedBarn || undefined })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Tab: autocomplete, Enter: next, Esc: cancel" }) })] })] }));
|
|
274
381
|
}
|
|
275
382
|
if (mode === 'add-livestock-log-path') {
|
|
276
383
|
const locationLabel = selectedBarn
|
|
@@ -279,13 +386,13 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
279
386
|
const frameworkLabel = detectedConfig?.framework && detectedConfig.framework !== 'unknown'
|
|
280
387
|
? ` (${detectedConfig.framework} detected)`
|
|
281
388
|
: '';
|
|
282
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsxs(Text, { dimColor: true, children: ["Location: ", locationLabel, " \u2022 Path: ", newLivestockPath] }), frameworkLabel && _jsx(Text, { color: "cyan", children: frameworkLabel }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Log path (optional): " }), _jsx(TextInput, { value: newLivestockLogPath, onChange: setNewLivestockLogPath, onSubmit: () => setMode('add-livestock-env-path'), placeholder: "storage/logs/ or logs/" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Relative to livestock path. Enter: next (leave blank to skip), Esc: cancel" }) })] })] }));
|
|
389
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsxs(Text, { dimColor: true, children: ["Location: ", locationLabel, " \u2022 Path: ", newLivestockPath] }), frameworkLabel && _jsx(Text, { color: "cyan", children: frameworkLabel }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Log path (optional): " }), _jsx(TextInput, { value: newLivestockLogPath, onChange: setNewLivestockLogPath, onSubmit: () => setMode('add-livestock-env-path'), placeholder: "storage/logs/ or logs/" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Relative to livestock path. Enter: next (leave blank to skip), Esc: cancel" }) })] })] }));
|
|
283
390
|
}
|
|
284
391
|
if (mode === 'add-livestock-env-path') {
|
|
285
392
|
const locationLabel = selectedBarn
|
|
286
393
|
? `on ${selectedBarn.name} (${selectedBarn.host})`
|
|
287
394
|
: 'local';
|
|
288
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsxs(Text, { dimColor: true, children: ["Location: ", locationLabel, " \u2022 Path: ", newLivestockPath] }), newLivestockLogPath && _jsxs(Text, { dimColor: true, children: ["Log path: ", newLivestockLogPath] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Env path (optional): " }), _jsx(TextInput, { value: newLivestockEnvPath, onChange: setNewLivestockEnvPath, onSubmit: saveLivestock, placeholder: ".env" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Relative to livestock path. Enter: save livestock, Esc: cancel" }) })] })] }));
|
|
395
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Adding livestock", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsxs(Text, { bold: true, color: "green", children: ["Add Livestock: ", newLivestockName] }), _jsxs(Text, { dimColor: true, children: ["Location: ", locationLabel, " \u2022 Path: ", newLivestockPath] }), newLivestockLogPath && _jsxs(Text, { dimColor: true, children: ["Log path: ", newLivestockLogPath] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Env path (optional): " }), _jsx(TextInput, { value: newLivestockEnvPath, onChange: setNewLivestockEnvPath, onSubmit: saveLivestock, placeholder: ".env" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Relative to livestock path. Enter: save livestock, Esc: cancel" }) })] })] }));
|
|
289
396
|
}
|
|
290
397
|
if (mode === 'delete-project-confirm') {
|
|
291
398
|
const handleDeleteConfirm = () => {
|
|
@@ -297,7 +404,7 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
297
404
|
}
|
|
298
405
|
// Delete livestock confirmation
|
|
299
406
|
if (mode === 'delete-livestock-confirm' && deleteLivestockTarget) {
|
|
300
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Remove livestock", color: project.color }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "red", children: "Remove Livestock" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Remove \"", deleteLivestockTarget.name, "\" from this project?"] }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Path: ", deleteLivestockTarget.path] }) }), _jsxs(Box, { marginTop: 1, gap: 2, children: [_jsx(Text, { color: "red", bold: true, children: "[y] Yes, remove" }), _jsx(Text, { dimColor: true, children: "[n/Esc] Cancel" })] })] })] }));
|
|
407
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Remove livestock", color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { bold: true, color: "red", children: "Remove Livestock" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Remove \"", deleteLivestockTarget.name, "\" from this project?"] }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Path: ", deleteLivestockTarget.path] }) }), _jsxs(Box, { marginTop: 1, gap: 2, children: [_jsx(Text, { color: "red", bold: true, children: "[y] Yes, remove" }), _jsx(Text, { dimColor: true, children: "[n/Esc] Cancel" })] })] })] }));
|
|
301
408
|
}
|
|
302
409
|
// Get livestock with resolved barn info
|
|
303
410
|
const livestockWithBarns = (project.livestock || []).map((livestock) => ({
|
|
@@ -310,17 +417,28 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
310
417
|
status: 'active', // TODO: actual health check
|
|
311
418
|
meta: livestock.path,
|
|
312
419
|
}));
|
|
420
|
+
// Parse session name for type hint (consistent with GlobalDashboard)
|
|
421
|
+
const getSessionTypeHint = (name) => {
|
|
422
|
+
if (name.endsWith('-claude'))
|
|
423
|
+
return 'claude';
|
|
424
|
+
return 'shell';
|
|
425
|
+
};
|
|
313
426
|
// Use display numbers (1-9) instead of tmux window index
|
|
314
|
-
const sessionItems = projectWindows.map((w, i) =>
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
427
|
+
const sessionItems = projectWindows.map((w, i) => {
|
|
428
|
+
const sessionName = w.name.replace(`${project.name}-`, '');
|
|
429
|
+
const typeHint = getSessionTypeHint(w.name);
|
|
430
|
+
const displayName = sessionName.replace('-claude', '');
|
|
431
|
+
return {
|
|
432
|
+
id: String(w.index),
|
|
433
|
+
label: `[${i + 1}] ${displayName}`,
|
|
434
|
+
status: w.active ? 'active' : 'inactive',
|
|
435
|
+
meta: `${typeHint} · ${getWindowStatus(w)}`,
|
|
436
|
+
};
|
|
437
|
+
});
|
|
320
438
|
// Panel-specific hints (page-level hotkeys like c/w/i are in BottomBar)
|
|
321
439
|
const livestockHints = '[s] shell [n] new [d] delete';
|
|
322
440
|
const sessionHints = '1-9 switch';
|
|
323
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: project.path, summary: project.summary, color: project.color }), _jsxs(Box, { flexGrow: 1, marginY: 1, paddingX: 1, gap: 2, children: [_jsx(Panel, { title: "Livestock", focused: focusedPanel === 'livestock', width: "50%", hints: livestockHints, children: livestockItems.length > 0 ? (_jsx(List, { items: livestockItems, focused: focusedPanel === 'livestock', selectedIndex: selectedLivestockIndex, onSelectionChange: setSelectedLivestockIndex, onSelect: (item) => {
|
|
441
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: project.path, summary: project.summary, color: project.color, gradientSpread: project.gradientSpread, gradientInverted: project.gradientInverted }), _jsxs(Box, { flexGrow: 1, marginY: 1, paddingX: 1, gap: 2, children: [_jsx(Panel, { title: "Livestock", focused: focusedPanel === 'livestock', width: "50%", hints: livestockHints, children: livestockItems.length > 0 ? (_jsx(List, { items: livestockItems, focused: focusedPanel === 'livestock', selectedIndex: selectedLivestockIndex, onSelectionChange: setSelectedLivestockIndex, onSelect: (item) => {
|
|
324
442
|
const found = livestockWithBarns.find((l) => l.livestock.name === item.id);
|
|
325
443
|
if (found) {
|
|
326
444
|
onSelectLivestock(found.livestock, found.barn);
|
package/dist/views/WikiView.js
CHANGED
|
@@ -65,10 +65,6 @@ export function WikiView({ project, onBack, onUpdateProject }) {
|
|
|
65
65
|
// Only process these in normal mode
|
|
66
66
|
if (mode !== 'normal')
|
|
67
67
|
return;
|
|
68
|
-
if (input === 'q') {
|
|
69
|
-
onBack();
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
68
|
// Tab to switch focus between panels
|
|
73
69
|
if (key.tab) {
|
|
74
70
|
setFocusedPanel((prev) => (prev === 'sections' ? 'content' : 'sections'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colmbus72/yeehaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Terminal dashboard for managing projects, servers, and deployments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
+
"claude-plugin",
|
|
13
14
|
"README.md",
|
|
14
15
|
"LICENSE"
|
|
15
16
|
],
|