@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.
@@ -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
- return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Header, { text: project.name, subtitle: "Editing project", color: editColor || 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: "Color (hex): " }), _jsx(TextInput, { value: editColor, onChange: setEditColor, onSubmit: () => saveAndNext('done'), placeholder: "#ff6b6b" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: save project, Esc: cancel (leave blank for default)" }) })] })] }));
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
- id: String(w.index),
314
- label: `[${i + 1}] ${w.name.replace(`${project.name}-`, '')}`,
315
- status: w.active ? 'active' : 'inactive',
316
- meta: getWindowStatus(w),
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.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
  ],