@colmbus72/yeehaw 0.3.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 +20 -8
- package/dist/components/Header.d.ts +4 -1
- package/dist/components/Header.js +33 -23
- package/dist/components/LivestockHeader.js +6 -6
- 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.js +7 -2
- package/dist/types.d.ts +2 -0
- package/dist/views/GlobalDashboard.js +16 -16
- package/dist/views/LivestockDetailView.d.ts +1 -1
- package/dist/views/LivestockDetailView.js +27 -45
- package/dist/views/ProjectContext.js +138 -18
- package/package.json +2 -1
|
@@ -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';
|
|
@@ -10,6 +10,69 @@ import { getWindowStatus } from '../lib/tmux.js';
|
|
|
10
10
|
import { detectGitInfo, detectRemoteGitInfo } from '../lib/git.js';
|
|
11
11
|
import { detectLivestockConfig } from '../lib/livestock.js';
|
|
12
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
|
+
}
|
|
13
76
|
export function ProjectContext({ project, barns, windows, onBack, onNewClaude, onSelectWindow, onSelectLivestock, onOpenLivestockSession, onUpdateProject, onDeleteProject, onOpenWiki, onOpenIssues, }) {
|
|
14
77
|
const [focusedPanel, setFocusedPanel] = useState('livestock');
|
|
15
78
|
const [mode, setMode] = useState('normal');
|
|
@@ -18,6 +81,8 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
18
81
|
const [editPath, setEditPath] = useState(project.path);
|
|
19
82
|
const [editSummary, setEditSummary] = useState(project.summary || '');
|
|
20
83
|
const [editColor, setEditColor] = useState(project.color || '');
|
|
84
|
+
const [editGradientSpread, setEditGradientSpread] = useState(project.gradientSpread ?? 5);
|
|
85
|
+
const [editGradientInverted, setEditGradientInverted] = useState(project.gradientInverted ?? false);
|
|
21
86
|
// New livestock form state
|
|
22
87
|
const [newLivestockName, setNewLivestockName] = useState('');
|
|
23
88
|
const [newLivestockPath, setNewLivestockPath] = useState('');
|
|
@@ -34,6 +99,40 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
34
99
|
const [detectedGit, setDetectedGit] = useState(null);
|
|
35
100
|
// Framework detection for config suggestions
|
|
36
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' });
|
|
37
136
|
// Filter windows to this project (by name prefix)
|
|
38
137
|
const projectWindows = windows.filter((w) => w.index > 0 && w.name.startsWith(project.name));
|
|
39
138
|
// Create a map from display number (1-9) to window for quick access
|
|
@@ -48,6 +147,8 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
48
147
|
setEditPath(project.path);
|
|
49
148
|
setEditSummary(project.summary || '');
|
|
50
149
|
setEditColor(project.color || '');
|
|
150
|
+
setEditGradientSpread(project.gradientSpread ?? 5);
|
|
151
|
+
setEditGradientInverted(project.gradientInverted ?? false);
|
|
51
152
|
setMode('edit-name');
|
|
52
153
|
};
|
|
53
154
|
const cancelEdit = () => {
|
|
@@ -62,6 +163,8 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
62
163
|
path: editPath,
|
|
63
164
|
summary: editSummary || undefined,
|
|
64
165
|
color: editColor || undefined,
|
|
166
|
+
gradientSpread: editGradientSpread,
|
|
167
|
+
gradientInverted: editGradientInverted,
|
|
65
168
|
});
|
|
66
169
|
setMode('normal');
|
|
67
170
|
}
|
|
@@ -222,20 +325,26 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
222
325
|
});
|
|
223
326
|
// Edit mode screens
|
|
224
327
|
if (mode === 'edit-name') {
|
|
225
|
-
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" }) })] })] }));
|
|
226
329
|
}
|
|
227
330
|
if (mode === 'edit-path') {
|
|
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: [_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" }) })] })] }));
|
|
229
332
|
}
|
|
230
333
|
if (mode === 'edit-summary') {
|
|
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: "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)" }) })] })] }));
|
|
232
335
|
}
|
|
233
336
|
if (mode === 'edit-color') {
|
|
234
|
-
|
|
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" })] })] })] }));
|
|
235
344
|
}
|
|
236
345
|
// Add livestock flow: name → barn → path (with auto-detect)
|
|
237
346
|
if (mode === 'add-livestock-name') {
|
|
238
|
-
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: () => {
|
|
239
348
|
if (newLivestockName.trim()) {
|
|
240
349
|
setMode('add-livestock-barn');
|
|
241
350
|
}
|
|
@@ -253,7 +362,7 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
253
362
|
meta: `${b.user}@${b.host}`,
|
|
254
363
|
})),
|
|
255
364
|
];
|
|
256
|
-
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) => {
|
|
257
366
|
if (item.id === '__local__') {
|
|
258
367
|
setSelectedBarn(null);
|
|
259
368
|
}
|
|
@@ -268,7 +377,7 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
268
377
|
const locationLabel = selectedBarn
|
|
269
378
|
? `on ${selectedBarn.name} (${selectedBarn.host})`
|
|
270
379
|
: 'local';
|
|
271
|
-
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" }) })] })] }));
|
|
272
381
|
}
|
|
273
382
|
if (mode === 'add-livestock-log-path') {
|
|
274
383
|
const locationLabel = selectedBarn
|
|
@@ -277,13 +386,13 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
277
386
|
const frameworkLabel = detectedConfig?.framework && detectedConfig.framework !== 'unknown'
|
|
278
387
|
? ` (${detectedConfig.framework} detected)`
|
|
279
388
|
: '';
|
|
280
|
-
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" }) })] })] }));
|
|
281
390
|
}
|
|
282
391
|
if (mode === 'add-livestock-env-path') {
|
|
283
392
|
const locationLabel = selectedBarn
|
|
284
393
|
? `on ${selectedBarn.name} (${selectedBarn.host})`
|
|
285
394
|
: 'local';
|
|
286
|
-
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" }) })] })] }));
|
|
287
396
|
}
|
|
288
397
|
if (mode === 'delete-project-confirm') {
|
|
289
398
|
const handleDeleteConfirm = () => {
|
|
@@ -295,7 +404,7 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
295
404
|
}
|
|
296
405
|
// Delete livestock confirmation
|
|
297
406
|
if (mode === 'delete-livestock-confirm' && deleteLivestockTarget) {
|
|
298
|
-
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" })] })] })] }));
|
|
299
408
|
}
|
|
300
409
|
// Get livestock with resolved barn info
|
|
301
410
|
const livestockWithBarns = (project.livestock || []).map((livestock) => ({
|
|
@@ -308,17 +417,28 @@ export function ProjectContext({ project, barns, windows, onBack, onNewClaude, o
|
|
|
308
417
|
status: 'active', // TODO: actual health check
|
|
309
418
|
meta: livestock.path,
|
|
310
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
|
+
};
|
|
311
426
|
// Use display numbers (1-9) instead of tmux window index
|
|
312
|
-
const sessionItems = projectWindows.map((w, i) =>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
+
});
|
|
318
438
|
// Panel-specific hints (page-level hotkeys like c/w/i are in BottomBar)
|
|
319
439
|
const livestockHints = '[s] shell [n] new [d] delete';
|
|
320
440
|
const sessionHints = '1-9 switch';
|
|
321
|
-
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) => {
|
|
322
442
|
const found = livestockWithBarns.find((l) => l.livestock.name === item.id);
|
|
323
443
|
if (found) {
|
|
324
444
|
onSelectLivestock(found.livestock, found.barn);
|
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
|
],
|