@colmbus72/yeehaw 0.1.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/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +414 -0
- package/dist/components/BarnHeader.d.ts +6 -0
- package/dist/components/BarnHeader.js +21 -0
- package/dist/components/BottomBar.d.ts +16 -0
- package/dist/components/BottomBar.js +7 -0
- package/dist/components/Header.d.ts +8 -0
- package/dist/components/Header.js +83 -0
- package/dist/components/HelpOverlay.d.ts +7 -0
- package/dist/components/HelpOverlay.js +17 -0
- package/dist/components/List.d.ts +17 -0
- package/dist/components/List.js +53 -0
- package/dist/components/Markdown.d.ts +8 -0
- package/dist/components/Markdown.js +23 -0
- package/dist/components/Panel.d.ts +10 -0
- package/dist/components/Panel.js +5 -0
- package/dist/components/PathInput.d.ts +9 -0
- package/dist/components/PathInput.js +141 -0
- package/dist/components/ScrollableMarkdown.d.ts +11 -0
- package/dist/components/ScrollableMarkdown.js +56 -0
- package/dist/components/StatusBar.d.ts +5 -0
- package/dist/components/StatusBar.js +20 -0
- package/dist/components/TextArea.d.ts +17 -0
- package/dist/components/TextArea.js +140 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.js +5 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/useConfig.d.ts +11 -0
- package/dist/hooks/useConfig.js +36 -0
- package/dist/hooks/useRemoteYeehaw.d.ts +13 -0
- package/dist/hooks/useRemoteYeehaw.js +49 -0
- package/dist/hooks/useSessions.d.ts +11 -0
- package/dist/hooks/useSessions.js +46 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +34 -0
- package/dist/lib/config.d.ts +27 -0
- package/dist/lib/config.js +150 -0
- package/dist/lib/detection.d.ts +16 -0
- package/dist/lib/detection.js +41 -0
- package/dist/lib/editor.d.ts +5 -0
- package/dist/lib/editor.js +35 -0
- package/dist/lib/errors.d.ts +28 -0
- package/dist/lib/errors.js +48 -0
- package/dist/lib/git.d.ts +11 -0
- package/dist/lib/git.js +73 -0
- package/dist/lib/github.d.ts +43 -0
- package/dist/lib/github.js +111 -0
- package/dist/lib/hotkeys.d.ts +27 -0
- package/dist/lib/hotkeys.js +92 -0
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.js +10 -0
- package/dist/lib/livestock.d.ts +51 -0
- package/dist/lib/livestock.js +233 -0
- package/dist/lib/mcp-validation.d.ts +33 -0
- package/dist/lib/mcp-validation.js +62 -0
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +28 -0
- package/dist/lib/shell.d.ts +34 -0
- package/dist/lib/shell.js +61 -0
- package/dist/lib/ssh.d.ts +15 -0
- package/dist/lib/ssh.js +77 -0
- package/dist/lib/tmux-config.d.ts +3 -0
- package/dist/lib/tmux-config.js +42 -0
- package/dist/lib/tmux.d.ts +32 -0
- package/dist/lib/tmux.js +397 -0
- package/dist/mcp-server.d.ts +23 -0
- package/dist/mcp-server.js +825 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.js +2 -0
- package/dist/views/BarnContext.d.ts +22 -0
- package/dist/views/BarnContext.js +252 -0
- package/dist/views/GlobalDashboard.d.ts +16 -0
- package/dist/views/GlobalDashboard.js +253 -0
- package/dist/views/Home.d.ts +11 -0
- package/dist/views/Home.js +27 -0
- package/dist/views/IssuesView.d.ts +7 -0
- package/dist/views/IssuesView.js +157 -0
- package/dist/views/LivestockDetailView.d.ts +11 -0
- package/dist/views/LivestockDetailView.js +140 -0
- package/dist/views/LogsView.d.ts +8 -0
- package/dist/views/LogsView.js +84 -0
- package/dist/views/NightSkyView.d.ts +5 -0
- package/dist/views/NightSkyView.js +441 -0
- package/dist/views/ProjectContext.d.ts +18 -0
- package/dist/views/ProjectContext.js +333 -0
- package/dist/views/Projects.d.ts +8 -0
- package/dist/views/Projects.js +20 -0
- package/dist/views/WikiView.d.ts +8 -0
- package/dist/views/WikiView.js +138 -0
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +2 -0
- package/package.json +65 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
|
3
|
+
import { Box, Text, useInput, useStdout } from 'ink';
|
|
4
|
+
// Easing function for smooth transitions (ease-in-out cubic)
|
|
5
|
+
function easeInOutCubic(t) {
|
|
6
|
+
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
7
|
+
}
|
|
8
|
+
// Static star characters (dimmer, always visible)
|
|
9
|
+
const STATIC_STAR_CHARS = ['.', '·', '+'];
|
|
10
|
+
// Test messages for cloud demo
|
|
11
|
+
const TEST_MESSAGES = [
|
|
12
|
+
'Hello from the desert!',
|
|
13
|
+
'New commit pushed',
|
|
14
|
+
'Build succeeded',
|
|
15
|
+
'PR #42 merged',
|
|
16
|
+
'Deploy complete',
|
|
17
|
+
'Tests passing',
|
|
18
|
+
];
|
|
19
|
+
// Cactus templates - block-style saguaros
|
|
20
|
+
const CACTUS_TEMPLATES = [
|
|
21
|
+
// Tall saguaro with both arms
|
|
22
|
+
[
|
|
23
|
+
' █ ',
|
|
24
|
+
'█ █ ',
|
|
25
|
+
' █ █',
|
|
26
|
+
' █ ',
|
|
27
|
+
' █ ',
|
|
28
|
+
],
|
|
29
|
+
// Medium saguaro with left arm
|
|
30
|
+
[
|
|
31
|
+
' █ ',
|
|
32
|
+
'█ █ ',
|
|
33
|
+
' █ ',
|
|
34
|
+
' █ ',
|
|
35
|
+
],
|
|
36
|
+
// Medium saguaro with right arm
|
|
37
|
+
[
|
|
38
|
+
' █ ',
|
|
39
|
+
' █ █',
|
|
40
|
+
' █ ',
|
|
41
|
+
' █ ',
|
|
42
|
+
],
|
|
43
|
+
// Small saguaro
|
|
44
|
+
[
|
|
45
|
+
' █ ',
|
|
46
|
+
' █ ',
|
|
47
|
+
' █ ',
|
|
48
|
+
],
|
|
49
|
+
// Tiny cactus
|
|
50
|
+
[
|
|
51
|
+
' █ ',
|
|
52
|
+
' █ ',
|
|
53
|
+
],
|
|
54
|
+
];
|
|
55
|
+
// Green gradient colors for cacti - all vibrant greens (lighter top to darker bottom)
|
|
56
|
+
const CACTUS_COLORS = ['#5fa33a', '#4a9030', '#3d8028', '#307020'];
|
|
57
|
+
// Animation parameters
|
|
58
|
+
const FRAME_INTERVAL = 50; // 20 FPS
|
|
59
|
+
const TARGET_WINKING_STARS = { min: 5, max: 10 };
|
|
60
|
+
const STATIC_STAR_COUNT = { min: 30, max: 50 };
|
|
61
|
+
const SPAWN_CHANCE = 0.05;
|
|
62
|
+
const LANDSCAPE_HEIGHT = 9; // Taller to fit full saguaros
|
|
63
|
+
function randomFloat(min, max) {
|
|
64
|
+
return Math.random() * (max - min) + min;
|
|
65
|
+
}
|
|
66
|
+
function randomInt(min, max) {
|
|
67
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
68
|
+
}
|
|
69
|
+
function createWinkingStar(width, height, existingStars) {
|
|
70
|
+
const maxAttempts = 20;
|
|
71
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
72
|
+
const x = randomInt(0, width - 1);
|
|
73
|
+
const y = randomInt(0, height - 1);
|
|
74
|
+
const occupied = existingStars.some((s) => s.x === x && s.y === y);
|
|
75
|
+
if (!occupied) {
|
|
76
|
+
return {
|
|
77
|
+
type: 'winking',
|
|
78
|
+
x,
|
|
79
|
+
y,
|
|
80
|
+
progress: 0,
|
|
81
|
+
speed: randomFloat(0.025, 0.06),
|
|
82
|
+
holdDuration: randomFloat(0.3, 0.8),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function createStaticStar(width, height, existingStars) {
|
|
89
|
+
const maxAttempts = 30;
|
|
90
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
91
|
+
const x = randomInt(0, width - 1);
|
|
92
|
+
const y = randomInt(0, height - 1);
|
|
93
|
+
const occupied = existingStars.some((s) => s.x === x && s.y === y);
|
|
94
|
+
if (!occupied) {
|
|
95
|
+
return {
|
|
96
|
+
type: 'static',
|
|
97
|
+
x,
|
|
98
|
+
y,
|
|
99
|
+
char: STATIC_STAR_CHARS[randomInt(0, STATIC_STAR_CHARS.length - 1)],
|
|
100
|
+
baseBrightness: randomFloat(0.2, 0.6),
|
|
101
|
+
pulsePhase: randomFloat(0, Math.PI * 2),
|
|
102
|
+
pulseSpeed: randomFloat(0.04, 0.12),
|
|
103
|
+
pulseAmount: randomFloat(0.05, 0.2),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function createMessageCloud(text, width, skyHeight) {
|
|
110
|
+
const startX = randomInt(5, Math.max(6, width - text.length - 10));
|
|
111
|
+
const startY = randomInt(2, Math.max(3, Math.floor(skyHeight / 2)));
|
|
112
|
+
return {
|
|
113
|
+
text,
|
|
114
|
+
x: startX,
|
|
115
|
+
y: startY,
|
|
116
|
+
targetX: startX + randomInt(-5, 5),
|
|
117
|
+
targetY: startY + randomInt(-2, 2),
|
|
118
|
+
progress: 0,
|
|
119
|
+
speed: 0.012,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function generateCacti(width) {
|
|
123
|
+
const cacti = [];
|
|
124
|
+
let pos = randomInt(15, 30);
|
|
125
|
+
while (pos < width - 15) {
|
|
126
|
+
// Only use the tall saguaro templates (first 3)
|
|
127
|
+
const templateIndex = randomInt(0, 2);
|
|
128
|
+
const template = CACTUS_TEMPLATES[templateIndex];
|
|
129
|
+
const colorIndex = randomInt(0, CACTUS_COLORS.length - 1);
|
|
130
|
+
// Random vertical offset: some cacti sit higher, some lower
|
|
131
|
+
const yOffset = randomInt(-1, 1);
|
|
132
|
+
cacti.push({ x: pos, yOffset, template, colorIndex });
|
|
133
|
+
pos += randomInt(25, 45);
|
|
134
|
+
}
|
|
135
|
+
return cacti;
|
|
136
|
+
}
|
|
137
|
+
function generateGround(width, height) {
|
|
138
|
+
const rows = [];
|
|
139
|
+
const groundLineY = 5; // Ground line - leaves room for tall cacti above
|
|
140
|
+
for (let y = 0; y < height; y++) {
|
|
141
|
+
const row = [];
|
|
142
|
+
for (let x = 0; x < width; x++) {
|
|
143
|
+
if (y === groundLineY) {
|
|
144
|
+
// Ground line - simple texture
|
|
145
|
+
const r = Math.random();
|
|
146
|
+
row.push(r < 0.6 ? '~' : r < 0.8 ? '-' : r < 0.95 ? '_' : '.');
|
|
147
|
+
}
|
|
148
|
+
else if (y > groundLineY) {
|
|
149
|
+
// Below ground line - sparse sand specs
|
|
150
|
+
const r = Math.random();
|
|
151
|
+
row.push(r < 0.03 ? '.' : r < 0.05 ? ',' : ' ');
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Above ground line (cactus area)
|
|
155
|
+
row.push(' ');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
rows.push(row);
|
|
159
|
+
}
|
|
160
|
+
return rows;
|
|
161
|
+
}
|
|
162
|
+
function initializeStars(width, skyHeight) {
|
|
163
|
+
const staticCount = randomInt(STATIC_STAR_COUNT.min, STATIC_STAR_COUNT.max);
|
|
164
|
+
const stars = [];
|
|
165
|
+
for (let i = 0; i < staticCount; i++) {
|
|
166
|
+
const star = createStaticStar(width, skyHeight, stars);
|
|
167
|
+
if (star)
|
|
168
|
+
stars.push(star);
|
|
169
|
+
}
|
|
170
|
+
return stars;
|
|
171
|
+
}
|
|
172
|
+
export function NightSkyView({ onExit }) {
|
|
173
|
+
const { stdout } = useStdout();
|
|
174
|
+
const width = stdout?.columns || 80;
|
|
175
|
+
const height = (stdout?.rows || 24) - 1;
|
|
176
|
+
const skyHeight = height - LANDSCAPE_HEIGHT;
|
|
177
|
+
// Single state object for all animated elements
|
|
178
|
+
const [state, setState] = useState(() => ({
|
|
179
|
+
stars: initializeStars(width, skyHeight),
|
|
180
|
+
clouds: [],
|
|
181
|
+
}));
|
|
182
|
+
const [isPaused, setIsPaused] = useState(false);
|
|
183
|
+
const dimsRef = useRef({ width, skyHeight });
|
|
184
|
+
dimsRef.current = { width, skyHeight };
|
|
185
|
+
// Generate landscape elements once
|
|
186
|
+
const cacti = useMemo(() => generateCacti(width), [width]);
|
|
187
|
+
const groundBase = useMemo(() => generateGround(width, LANDSCAPE_HEIGHT), [width]);
|
|
188
|
+
// Reinitialize stars when dimensions change
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
setState(prev => ({
|
|
191
|
+
...prev,
|
|
192
|
+
stars: initializeStars(width, skyHeight),
|
|
193
|
+
}));
|
|
194
|
+
}, [width, skyHeight]);
|
|
195
|
+
const randomize = useCallback(() => {
|
|
196
|
+
const { width, skyHeight } = dimsRef.current;
|
|
197
|
+
setState({
|
|
198
|
+
stars: initializeStars(width, skyHeight),
|
|
199
|
+
clouds: [],
|
|
200
|
+
});
|
|
201
|
+
}, []);
|
|
202
|
+
const spawnCloud = useCallback(() => {
|
|
203
|
+
const { width, skyHeight } = dimsRef.current;
|
|
204
|
+
const message = TEST_MESSAGES[randomInt(0, TEST_MESSAGES.length - 1)];
|
|
205
|
+
const cloud = createMessageCloud(message, width, skyHeight);
|
|
206
|
+
setState(prev => ({
|
|
207
|
+
...prev,
|
|
208
|
+
clouds: [...prev.clouds, cloud],
|
|
209
|
+
}));
|
|
210
|
+
}, []);
|
|
211
|
+
useInput((input, key) => {
|
|
212
|
+
if (key.escape) {
|
|
213
|
+
onExit();
|
|
214
|
+
}
|
|
215
|
+
else if (input === 'r') {
|
|
216
|
+
randomize();
|
|
217
|
+
}
|
|
218
|
+
else if (input === 'c') {
|
|
219
|
+
spawnCloud();
|
|
220
|
+
}
|
|
221
|
+
else if (input === ' ') {
|
|
222
|
+
setIsPaused(p => !p);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
// Single animation loop with batched state update
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (isPaused)
|
|
228
|
+
return;
|
|
229
|
+
const interval = setInterval(() => {
|
|
230
|
+
const { width, skyHeight } = dimsRef.current;
|
|
231
|
+
setState(prev => {
|
|
232
|
+
// Update stars
|
|
233
|
+
const updatedStars = [];
|
|
234
|
+
for (const star of prev.stars) {
|
|
235
|
+
if (star.type === 'static') {
|
|
236
|
+
updatedStars.push({
|
|
237
|
+
...star,
|
|
238
|
+
pulsePhase: star.pulsePhase + star.pulseSpeed,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
const newProgress = star.progress + star.speed;
|
|
243
|
+
if (newProgress < 2 + star.holdDuration) {
|
|
244
|
+
updatedStars.push({ ...star, progress: newProgress });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Spawn new winking stars
|
|
249
|
+
const winkingCount = updatedStars.filter(s => s.type === 'winking').length;
|
|
250
|
+
if (winkingCount < TARGET_WINKING_STARS.min && Math.random() < SPAWN_CHANCE) {
|
|
251
|
+
const newStar = createWinkingStar(width, skyHeight, updatedStars);
|
|
252
|
+
if (newStar)
|
|
253
|
+
updatedStars.push(newStar);
|
|
254
|
+
}
|
|
255
|
+
// Update clouds
|
|
256
|
+
const updatedClouds = prev.clouds
|
|
257
|
+
.map(cloud => {
|
|
258
|
+
const newProgress = cloud.progress + cloud.speed;
|
|
259
|
+
let newX = cloud.x;
|
|
260
|
+
let newY = cloud.y;
|
|
261
|
+
if (newProgress > 0.5 && newProgress < 2.5) {
|
|
262
|
+
newX += (cloud.targetX - cloud.x) * 0.02;
|
|
263
|
+
newY += (cloud.targetY - cloud.y) * 0.02;
|
|
264
|
+
}
|
|
265
|
+
return { ...cloud, x: newX, y: newY, progress: newProgress };
|
|
266
|
+
})
|
|
267
|
+
.filter(cloud => cloud.progress < 3);
|
|
268
|
+
return {
|
|
269
|
+
stars: updatedStars,
|
|
270
|
+
clouds: updatedClouds,
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
}, FRAME_INTERVAL);
|
|
274
|
+
return () => clearInterval(interval);
|
|
275
|
+
}, [isPaused]);
|
|
276
|
+
// Rendering helpers
|
|
277
|
+
const getWinkingStarDisplay = (star) => {
|
|
278
|
+
const { progress, holdDuration } = star;
|
|
279
|
+
let brightness;
|
|
280
|
+
if (progress < 1) {
|
|
281
|
+
brightness = easeInOutCubic(progress);
|
|
282
|
+
}
|
|
283
|
+
else if (progress < 1 + holdDuration) {
|
|
284
|
+
brightness = 1;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
brightness = 1 - easeInOutCubic(progress - 1 - holdDuration);
|
|
288
|
+
}
|
|
289
|
+
const chars = [' ', '.', '·', '+', '*'];
|
|
290
|
+
return { char: chars[Math.round(brightness * 4)], dim: brightness < 0.5 };
|
|
291
|
+
};
|
|
292
|
+
const getStaticStarDisplay = (star) => {
|
|
293
|
+
const brightness = star.baseBrightness + Math.sin(star.pulsePhase) * star.pulseAmount;
|
|
294
|
+
return { char: star.char, dim: brightness < 0.4 };
|
|
295
|
+
};
|
|
296
|
+
// Cloud display - simple opacity-based fading
|
|
297
|
+
const getCloudOpacity = (cloud) => {
|
|
298
|
+
const { progress } = cloud;
|
|
299
|
+
if (progress < 0.3) {
|
|
300
|
+
// Fading in - dim
|
|
301
|
+
return { dim: true, visible: true };
|
|
302
|
+
}
|
|
303
|
+
else if (progress < 2.7) {
|
|
304
|
+
// Fully visible
|
|
305
|
+
return { dim: false, visible: true };
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
// Fading out - dim
|
|
309
|
+
return { dim: true, visible: true };
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
// Render sky
|
|
313
|
+
const skyRows = [];
|
|
314
|
+
for (let y = 0; y < skyHeight; y++) {
|
|
315
|
+
let rowChars = ' '.repeat(width).split('');
|
|
316
|
+
let rowDims = new Array(width).fill(false);
|
|
317
|
+
let rowColors = new Array(width).fill(undefined);
|
|
318
|
+
// Place stars
|
|
319
|
+
for (const star of state.stars) {
|
|
320
|
+
if (star.y === y && star.x >= 0 && star.x < width) {
|
|
321
|
+
const display = star.type === 'winking' ? getWinkingStarDisplay(star) : getStaticStarDisplay(star);
|
|
322
|
+
if (display.char !== ' ') {
|
|
323
|
+
rowChars[star.x] = display.char;
|
|
324
|
+
rowDims[star.x] = display.dim;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Place clouds
|
|
329
|
+
for (const cloud of state.clouds) {
|
|
330
|
+
const display = getCloudOpacity(cloud);
|
|
331
|
+
if (!display.visible)
|
|
332
|
+
continue;
|
|
333
|
+
const cloudY = Math.round(cloud.y);
|
|
334
|
+
const cloudX = Math.round(cloud.x);
|
|
335
|
+
// Simple box cloud
|
|
336
|
+
const textLen = cloud.text.length + 2;
|
|
337
|
+
const lines = [
|
|
338
|
+
'╭' + '─'.repeat(textLen) + '╮',
|
|
339
|
+
'│ ' + cloud.text + ' │',
|
|
340
|
+
'╰' + '─'.repeat(textLen) + '╯',
|
|
341
|
+
];
|
|
342
|
+
const lineIdx = y - cloudY;
|
|
343
|
+
if (lineIdx >= 0 && lineIdx < 3) {
|
|
344
|
+
const line = lines[lineIdx];
|
|
345
|
+
for (let lx = 0; lx < line.length; lx++) {
|
|
346
|
+
const cellX = cloudX + lx;
|
|
347
|
+
if (cellX >= 0 && cellX < width) {
|
|
348
|
+
rowChars[cellX] = line[lx];
|
|
349
|
+
rowDims[cellX] = display.dim;
|
|
350
|
+
rowColors[cellX] = '#87CEEB';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Build segments for efficient rendering
|
|
356
|
+
const segments = [];
|
|
357
|
+
let seg = { text: rowChars[0], dim: rowDims[0], color: rowColors[0] };
|
|
358
|
+
for (let x = 1; x < width; x++) {
|
|
359
|
+
if (rowDims[x] === seg.dim && rowColors[x] === seg.color) {
|
|
360
|
+
seg.text += rowChars[x];
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
segments.push(seg);
|
|
364
|
+
seg = { text: rowChars[x], dim: rowDims[x], color: rowColors[x] };
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
segments.push(seg);
|
|
368
|
+
skyRows.push(_jsx(Text, { children: segments.map((s, i) => (_jsx(Text, { color: s.color || 'white', dimColor: s.dim, children: s.text }, i))) }, y));
|
|
369
|
+
}
|
|
370
|
+
// Render landscape with cacti and ground
|
|
371
|
+
const renderLandscape = () => {
|
|
372
|
+
const GROUND_LINE_Y = 5; // Ground line position in landscape
|
|
373
|
+
// Create ground grid
|
|
374
|
+
const ground = groundBase.map(row => [...row]);
|
|
375
|
+
// Place cacti - they sit on the ground line with vertical variation
|
|
376
|
+
for (const cactus of cacti) {
|
|
377
|
+
const cactusHeight = cactus.template.length;
|
|
378
|
+
// Cactus bottom aligns with ground line, plus offset for depth variation
|
|
379
|
+
const cactusStartY = GROUND_LINE_Y - cactusHeight + 1 + cactus.yOffset;
|
|
380
|
+
for (let cy = 0; cy < cactusHeight; cy++) {
|
|
381
|
+
const groundY = cactusStartY + cy;
|
|
382
|
+
if (groundY >= 0 && groundY < LANDSCAPE_HEIGHT) {
|
|
383
|
+
const line = cactus.template[cy];
|
|
384
|
+
for (let cx = 0; cx < line.length; cx++) {
|
|
385
|
+
const groundX = cactus.x + cx;
|
|
386
|
+
if (groundX >= 0 && groundX < width && line[cx] !== ' ') {
|
|
387
|
+
ground[groundY][groundX] = line[cx];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// Render ground rows with coloring
|
|
394
|
+
const rows = [];
|
|
395
|
+
for (let y = 0; y < LANDSCAPE_HEIGHT; y++) {
|
|
396
|
+
const segments = [];
|
|
397
|
+
let currentColor = '#C2B280';
|
|
398
|
+
let currentDim = false;
|
|
399
|
+
let currentText = '';
|
|
400
|
+
for (let x = 0; x < width; x++) {
|
|
401
|
+
const char = ground[y][x];
|
|
402
|
+
let charColor = '#C2B280'; // Sand/dirt color
|
|
403
|
+
let charDim = y > GROUND_LINE_Y; // Dim the sand specs below ground
|
|
404
|
+
// Check if this char is part of a cactus (█ character)
|
|
405
|
+
if (char === '█') {
|
|
406
|
+
// Find which cactus this belongs to for gradient
|
|
407
|
+
for (const cactus of cacti) {
|
|
408
|
+
const cactusHeight = cactus.template.length;
|
|
409
|
+
const cactusStartY = GROUND_LINE_Y - cactusHeight + 1 + cactus.yOffset;
|
|
410
|
+
const relY = y - cactusStartY;
|
|
411
|
+
const relX = x - cactus.x;
|
|
412
|
+
if (relY >= 0 && relY < cactusHeight && relX >= 0 && relX < cactus.template[relY].length) {
|
|
413
|
+
if (cactus.template[relY][relX] === '█') {
|
|
414
|
+
// Gradient: lighter at top, darker at bottom
|
|
415
|
+
const gradientIndex = Math.min(CACTUS_COLORS.length - 1, Math.floor(relY / cactusHeight * CACTUS_COLORS.length));
|
|
416
|
+
charColor = CACTUS_COLORS[gradientIndex];
|
|
417
|
+
charDim = false;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (charColor === currentColor && charDim === currentDim) {
|
|
424
|
+
currentText += char;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
if (currentText)
|
|
428
|
+
segments.push({ text: currentText, color: currentColor, dim: currentDim });
|
|
429
|
+
currentText = char;
|
|
430
|
+
currentColor = charColor;
|
|
431
|
+
currentDim = charDim;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (currentText)
|
|
435
|
+
segments.push({ text: currentText, color: currentColor, dim: currentDim });
|
|
436
|
+
rows.push(_jsx(Text, { children: segments.map((s, i) => (_jsx(Text, { color: s.color, dimColor: s.dim, children: s.text }, i))) }, `land-${y}`));
|
|
437
|
+
}
|
|
438
|
+
return rows;
|
|
439
|
+
};
|
|
440
|
+
return (_jsxs(Box, { flexDirection: "column", height: height, children: [_jsx(Box, { flexDirection: "column", children: skyRows }), _jsx(Box, { flexDirection: "column", children: renderLandscape() })] }));
|
|
441
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Project, Barn, Livestock } from '../types.js';
|
|
2
|
+
import { type TmuxWindow } from '../lib/tmux.js';
|
|
3
|
+
interface ProjectContextProps {
|
|
4
|
+
project: Project;
|
|
5
|
+
barns: Barn[];
|
|
6
|
+
windows: TmuxWindow[];
|
|
7
|
+
onBack: () => void;
|
|
8
|
+
onNewClaude: () => void;
|
|
9
|
+
onSelectWindow: (window: TmuxWindow) => void;
|
|
10
|
+
onSelectLivestock: (livestock: Livestock, barn: Barn | null) => void;
|
|
11
|
+
onOpenLivestockSession: (livestock: Livestock, barn: Barn | null) => void;
|
|
12
|
+
onUpdateProject: (project: Project) => void;
|
|
13
|
+
onDeleteProject: (projectName: string) => void;
|
|
14
|
+
onOpenWiki: () => void;
|
|
15
|
+
onOpenIssues: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function ProjectContext({ project, barns, windows, onBack, onNewClaude, onSelectWindow, onSelectLivestock, onOpenLivestockSession, onUpdateProject, onDeleteProject, onOpenWiki, onOpenIssues, }: ProjectContextProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|