wip: pushing the code to sync in other branch
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.osal.tmux
|
||||
|
||||
mut t := tmux.new()!
|
||||
|
||||
// if !t.is_running()! {
|
||||
// t.start()!
|
||||
// }
|
||||
// if t.session_exist('main') {
|
||||
// t.session_delete('main')!
|
||||
// }
|
||||
// // Create session first, then create window
|
||||
// mut session := t.session_create(name: 'main')!
|
||||
// session.window_new(name: 'test', cmd: 'mc', reset: true)!
|
||||
|
||||
// // Or use the convenience method
|
||||
// // t.window_new(session_name: 'main', name: 'test', cmd: 'mc', reset: true)!
|
||||
|
||||
println(t)
|
||||
122
examples/tmux/tmux.vsh
Executable file
122
examples/tmux/tmux.vsh
Executable file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.osal.tmux
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import time
|
||||
|
||||
// Constants for display formatting
|
||||
const (
|
||||
bytes_to_mb = 1024.0 * 1024.0
|
||||
cpu_precision = 1
|
||||
memory_precision = 3
|
||||
)
|
||||
|
||||
println('=== Tmux Pane Example ===')
|
||||
|
||||
mut t := tmux.new()!
|
||||
|
||||
if !t.is_running()! {
|
||||
println('Starting tmux server...')
|
||||
t.start()!
|
||||
}
|
||||
|
||||
if t.session_exist('demo') {
|
||||
println('Deleting existing demo session...')
|
||||
t.session_delete('demo')!
|
||||
}
|
||||
|
||||
// Create session and window
|
||||
println('Creating demo session...')
|
||||
mut session := t.session_create(name: 'demo')!
|
||||
|
||||
println('Creating main window with htop...')
|
||||
mut window := session.window_new(name: 'main', cmd: 'htop', reset: true)!
|
||||
|
||||
// Wait a moment for the window to be created
|
||||
time.sleep(500 * time.millisecond)
|
||||
|
||||
// Refresh to get current state
|
||||
t.scan()!
|
||||
|
||||
println('\n=== Current Tmux State ===')
|
||||
println(t)
|
||||
|
||||
// Get the window and demonstrate pane functionality
|
||||
mut main_window := session.window_get(name: 'main')!
|
||||
|
||||
println('\n=== Window Pane Information ===')
|
||||
println('Window: ${main_window.name} (ID: ${main_window.id})')
|
||||
println('Number of panes: ${main_window.panes.len}')
|
||||
|
||||
for i, mut pane in main_window.panes {
|
||||
println('Pane ${i}: ID=%${pane.id}, PID=${pane.pid}, Active=${pane.active}, Cmd="${pane.cmd}"')
|
||||
|
||||
// Get pane stats
|
||||
stats := pane.stats() or {
|
||||
println(' Could not get stats: ${err}')
|
||||
continue
|
||||
}
|
||||
memory_mb := f64(stats.memory_bytes) / bytes_to_mb
|
||||
println(' CPU: ${stats.cpu_percent:.1f}%, Memory: ${stats.memory_percent:.3f}% (${memory_mb:.1f} MB)')
|
||||
}
|
||||
|
||||
// Get the active pane
|
||||
if mut active_pane := main_window.pane_active() {
|
||||
println('\n=== Active Pane Details ===')
|
||||
println('Active pane ID: %${active_pane.id}')
|
||||
println('Process ID: ${active_pane.pid}')
|
||||
println('Command: ${active_pane.cmd}')
|
||||
|
||||
// Get process information
|
||||
process_info := active_pane.processinfo_main() or {
|
||||
println('Could not get process info: ${err}')
|
||||
osal.ProcessInfo{}
|
||||
}
|
||||
if process_info.pid > 0 {
|
||||
println('Process info: PID=${process_info.pid}, Command=${process_info.cmd}')
|
||||
}
|
||||
|
||||
// Get recent logs
|
||||
println('\n=== Recent Pane Output ===')
|
||||
logs := active_pane.logs_all() or {
|
||||
println('Could not get logs: ${err}')
|
||||
''
|
||||
}
|
||||
if logs.len > 0 {
|
||||
lines := logs.split_into_lines()
|
||||
// Show last 5 lines
|
||||
start_idx := if lines.len > 5 { lines.len - 5 } else { 0 }
|
||||
for i in start_idx .. lines.len {
|
||||
if lines[i].trim_space().len > 0 {
|
||||
println(' ${lines[i]}')
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println('No active pane found')
|
||||
}
|
||||
|
||||
println('\n=== Creating Additional Windows ===')
|
||||
|
||||
// Create more windows to demonstrate multiple panes
|
||||
mut monitor_window := session.window_new(name: 'monitor', cmd: 'top', reset: true)!
|
||||
mut logs_window := session.window_new(name: 'logs', cmd: 'tail -f /var/log/system.log', reset: true)!
|
||||
|
||||
time.sleep(500 * time.millisecond)
|
||||
t.scan()!
|
||||
|
||||
println('\n=== Final Tmux State ===')
|
||||
println(t)
|
||||
|
||||
println('\n=== Window Statistics ===')
|
||||
for mut win in session.windows {
|
||||
println('Window: ${win.name}')
|
||||
window_stats := win.stats() or {
|
||||
println(' Could not get window stats: ${err}')
|
||||
continue
|
||||
}
|
||||
memory_mb := f64(window_stats.memory_bytes) / bytes_to_mb
|
||||
println(' Total CPU: ${window_stats.cpu_percent:.1f}%')
|
||||
println(' Total Memory: ${window_stats.memory_percent:.3f}% (${memory_mb:.1f} MB)')
|
||||
println(' Panes: ${win.panes.len}')
|
||||
}
|
||||
143
examples/tmux/tmux_pane_resize.vsh
Executable file
143
examples/tmux/tmux_pane_resize.vsh
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.osal.tmux
|
||||
import time
|
||||
|
||||
println('=== Tmux Pane Resizing Example ===')
|
||||
|
||||
mut t := tmux.new()!
|
||||
|
||||
if !t.is_running()! {
|
||||
println('Starting tmux server...')
|
||||
t.start()!
|
||||
}
|
||||
|
||||
if t.session_exist('resize_demo') {
|
||||
println('Deleting existing resize_demo session...')
|
||||
t.session_delete('resize_demo')!
|
||||
}
|
||||
|
||||
// Create session and window
|
||||
println('Creating resize_demo session...')
|
||||
mut session := t.session_create(name: 'resize_demo')!
|
||||
|
||||
println('Creating main window...')
|
||||
mut window := session.window_new(name: 'main', cmd: 'bash', reset: true)!
|
||||
|
||||
time.sleep(500 * time.millisecond)
|
||||
t.scan()!
|
||||
|
||||
// Create a 2x2 grid of panes
|
||||
println('\n=== Creating 2x2 Grid of Panes ===')
|
||||
|
||||
// Split horizontally first (left | right)
|
||||
mut right_pane := window.pane_split_horizontal('htop')!
|
||||
time.sleep(300 * time.millisecond)
|
||||
|
||||
// Split left pane vertically (top-left, bottom-left)
|
||||
window.scan()!
|
||||
if window.panes.len > 1 {
|
||||
mut left_pane := window.panes[1] // The original bash pane
|
||||
left_pane.select()!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
mut bottom_left_pane := window.pane_split_vertical('top')!
|
||||
time.sleep(300 * time.millisecond)
|
||||
|
||||
// Split right pane vertically (top-right, bottom-right)
|
||||
window.scan()!
|
||||
for mut pane in window.panes {
|
||||
if pane.cmd.contains('htop') {
|
||||
pane.select()!
|
||||
break
|
||||
}
|
||||
}
|
||||
time.sleep(200 * time.millisecond)
|
||||
mut bottom_right_pane := window.pane_split_vertical('tail -f /var/log/system.log')!
|
||||
time.sleep(500 * time.millisecond)
|
||||
|
||||
window.scan()!
|
||||
println('Created 2x2 grid with ${window.panes.len} panes:')
|
||||
for i, pane in window.panes {
|
||||
println(' Pane ${i}: ID=%${pane.id}, Cmd="${pane.cmd}"')
|
||||
}
|
||||
|
||||
// Demonstrate resizing operations
|
||||
println('\n=== Demonstrating Pane Resizing ===')
|
||||
|
||||
// Get references to panes for resizing
|
||||
window.scan()!
|
||||
if window.panes.len >= 4 {
|
||||
mut top_left := window.panes[1] // bash
|
||||
mut top_right := window.panes[0] // htop
|
||||
mut bottom_left := window.panes[2] // top
|
||||
mut bottom_right := window.panes[3] // tail
|
||||
|
||||
println('Resizing top-left pane (bash) to be wider...')
|
||||
top_left.select()!
|
||||
time.sleep(200 * time.millisecond)
|
||||
top_left.resize_right(10)!
|
||||
time.sleep(1000 * time.millisecond)
|
||||
|
||||
println('Resizing top-right pane (htop) to be taller...')
|
||||
top_right.select()!
|
||||
time.sleep(200 * time.millisecond)
|
||||
top_right.resize_down(5)!
|
||||
time.sleep(1000 * time.millisecond)
|
||||
|
||||
println('Resizing bottom-left pane (top) to be narrower...')
|
||||
bottom_left.select()!
|
||||
time.sleep(200 * time.millisecond)
|
||||
bottom_left.resize_left(5)!
|
||||
time.sleep(1000 * time.millisecond)
|
||||
|
||||
println('Resizing bottom-right pane (tail) to be shorter...')
|
||||
bottom_right.select()!
|
||||
time.sleep(200 * time.millisecond)
|
||||
bottom_right.resize_up(3)!
|
||||
time.sleep(1000 * time.millisecond)
|
||||
|
||||
// Demonstrate using the generic resize method
|
||||
println('Using generic resize method to make top-left pane taller...')
|
||||
top_left.select()!
|
||||
time.sleep(200 * time.millisecond)
|
||||
top_left.resize(direction: 'down', cells: 3)!
|
||||
time.sleep(1000 * time.millisecond)
|
||||
}
|
||||
|
||||
// Send some commands to make the panes more interesting
|
||||
println('\n=== Adding Content to Panes ===')
|
||||
window.scan()!
|
||||
if window.panes.len >= 4 {
|
||||
// Send commands to bash pane
|
||||
mut bash_pane := window.panes[1]
|
||||
bash_pane.send_command('echo "=== Bash Pane ==="')!
|
||||
bash_pane.send_command('ls -la')!
|
||||
bash_pane.send_command('pwd')!
|
||||
time.sleep(500 * time.millisecond)
|
||||
|
||||
// Send command to top pane
|
||||
mut top_pane := window.panes[2]
|
||||
top_pane.send_command('echo "=== Top Pane ==="')!
|
||||
time.sleep(500 * time.millisecond)
|
||||
}
|
||||
|
||||
println('\n=== Final Layout ===')
|
||||
t.scan()!
|
||||
println('Session: ${session.name}')
|
||||
println('Window: ${window.name} (${window.panes.len} panes)')
|
||||
for i, pane in window.panes {
|
||||
println(' ${i+1}. Pane %${pane.id} - ${pane.cmd}')
|
||||
}
|
||||
|
||||
println('\n=== Pane Resize Operations Available ===')
|
||||
println('✓ resize_up(cells) - Make pane taller by shrinking pane above')
|
||||
println('✓ resize_down(cells) - Make pane taller by shrinking pane below')
|
||||
println('✓ resize_left(cells) - Make pane wider by shrinking pane to the left')
|
||||
println('✓ resize_right(cells) - Make pane wider by shrinking pane to the right')
|
||||
println('✓ resize(direction: "up/down/left/right", cells: N) - Generic resize method')
|
||||
|
||||
println('\nExample completed! You can attach to the session with:')
|
||||
println(' tmux attach-session -t resize_demo')
|
||||
println('\nThen use Ctrl+B followed by arrow keys to manually resize panes,')
|
||||
println('or Ctrl+B followed by Alt+arrow keys for larger resize steps.')
|
||||
170
examples/tmux/tmux_panes.vsh
Executable file
170
examples/tmux/tmux_panes.vsh
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.osal.tmux
|
||||
import time
|
||||
|
||||
println('=== Tmux Pane Splitting Example ===')
|
||||
|
||||
mut t := tmux.new()!
|
||||
|
||||
if !t.is_running()! {
|
||||
println('Starting tmux server...')
|
||||
t.start()!
|
||||
}
|
||||
|
||||
if t.session_exist('panes_demo') {
|
||||
println('Deleting existing panes_demo session...')
|
||||
t.session_delete('panes_demo')!
|
||||
}
|
||||
|
||||
// Create session and initial window
|
||||
println('Creating panes_demo session...')
|
||||
mut session := t.session_create(name: 'panes_demo')!
|
||||
|
||||
println('Creating main window...')
|
||||
mut window := session.window_new(name: 'main', cmd: 'bash', reset: true)!
|
||||
|
||||
// Wait for initial setup
|
||||
time.sleep(500 * time.millisecond)
|
||||
t.scan()!
|
||||
|
||||
println('\n=== Initial State ===')
|
||||
println('Window: ${window.name} (ID: ${window.id})')
|
||||
println('Number of panes: ${window.panes.len}')
|
||||
|
||||
// Split the window horizontally (side by side)
|
||||
println('\n=== Splitting Horizontally (Side by Side) ===')
|
||||
mut right_pane := window.pane_split_horizontal('htop')!
|
||||
time.sleep(500 * time.millisecond)
|
||||
window.scan()!
|
||||
|
||||
println('After horizontal split:')
|
||||
println('Number of panes: ${window.panes.len}')
|
||||
for i, mut pane in window.panes {
|
||||
println(' Pane ${i}: ID=%${pane.id}, PID=${pane.pid}, Active=${pane.active}, Cmd="${pane.cmd}"')
|
||||
}
|
||||
|
||||
// Split the right pane vertically (top and bottom)
|
||||
println('\n=== Splitting Right Pane Vertically (Top and Bottom) ===')
|
||||
// Get a fresh reference to the right pane after the first split
|
||||
window.scan()!
|
||||
if window.panes.len > 0 {
|
||||
// Find the pane with htop command (the one we just created)
|
||||
mut right_pane_fresh := &window.panes[0]
|
||||
for mut pane in window.panes {
|
||||
if pane.cmd.contains('htop') {
|
||||
right_pane_fresh = pane
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Select the right pane to make it active
|
||||
right_pane_fresh.select()!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
|
||||
mut bottom_pane := window.pane_split_vertical('top')!
|
||||
time.sleep(500 * time.millisecond)
|
||||
window.scan()!
|
||||
|
||||
println('After vertical split of right pane:')
|
||||
println('Number of panes: ${window.panes.len}')
|
||||
for i, mut pane in window.panes {
|
||||
println(' Pane ${i}: ID=%${pane.id}, PID=${pane.pid}, Active=${pane.active}, Cmd="${pane.cmd}"')
|
||||
}
|
||||
|
||||
// Send commands to different panes
|
||||
println('\n=== Sending Commands to Panes ===')
|
||||
|
||||
// Get the first pane (left side) and send some commands
|
||||
if window.panes.len > 0 {
|
||||
mut left_pane := window.panes[0]
|
||||
println('Sending commands to left pane (ID: %${left_pane.id})')
|
||||
|
||||
left_pane.send_command('echo "Hello from left pane!"')!
|
||||
time.sleep(200 * time.millisecond)
|
||||
|
||||
left_pane.send_command('ls -la')!
|
||||
time.sleep(200 * time.millisecond)
|
||||
|
||||
left_pane.send_command('pwd')!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
|
||||
// Send command to bottom pane
|
||||
if window.panes.len > 2 {
|
||||
mut bottom_pane_ref := window.panes[2]
|
||||
println('Sending command to bottom pane (ID: %${bottom_pane_ref.id})')
|
||||
bottom_pane_ref.send_command('echo "Hello from bottom pane!"')!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
|
||||
// Capture output from panes
|
||||
println('\n=== Capturing Pane Output ===')
|
||||
for i, mut pane in window.panes {
|
||||
println('Output from Pane ${i} (ID: %${pane.id}):')
|
||||
logs := pane.logs_all() or {
|
||||
println(' Could not get logs: ${err}')
|
||||
continue
|
||||
}
|
||||
|
||||
if logs.len > 0 {
|
||||
lines := logs.split_into_lines()
|
||||
// Show last 3 lines
|
||||
start_idx := if lines.len > 3 { lines.len - 3 } else { 0 }
|
||||
for j in start_idx .. lines.len {
|
||||
if lines[j].trim_space().len > 0 {
|
||||
println(' ${lines[j]}')
|
||||
}
|
||||
}
|
||||
}
|
||||
println('')
|
||||
}
|
||||
|
||||
// Demonstrate pane selection
|
||||
println('\n=== Demonstrating Pane Selection ===')
|
||||
for i, mut pane in window.panes {
|
||||
println('Selecting pane ${i} (ID: %${pane.id})')
|
||||
pane.select()!
|
||||
time.sleep(300 * time.millisecond)
|
||||
}
|
||||
|
||||
// Final state
|
||||
println('\n=== Final Tmux State ===')
|
||||
t.scan()!
|
||||
println(t)
|
||||
|
||||
println('\n=== Pane Management Summary ===')
|
||||
println('Created ${window.panes.len} panes in window "${window.name}":')
|
||||
for i, pane in window.panes {
|
||||
println(' ${i + 1}. Pane %${pane.id} - PID: ${pane.pid} - Command: ${pane.cmd}')
|
||||
}
|
||||
|
||||
// Demonstrate killing a pane
|
||||
println('\n=== Demonstrating Pane Killing ===')
|
||||
if window.panes.len > 2 {
|
||||
mut pane_to_kill := window.panes[2] // Kill the bottom pane
|
||||
println('Killing pane %${pane_to_kill.id} (${pane_to_kill.cmd})')
|
||||
pane_to_kill.kill()!
|
||||
time.sleep(500 * time.millisecond)
|
||||
window.scan()!
|
||||
|
||||
println('After killing pane:')
|
||||
println('Number of panes: ${window.panes.len}')
|
||||
for i, pane in window.panes {
|
||||
println(' Pane ${i}: ID=%${pane.id}, PID=${pane.pid}, Cmd="${pane.cmd}"')
|
||||
}
|
||||
}
|
||||
|
||||
println('\n=== Available Pane Operations ===')
|
||||
println('✓ Split panes horizontally (side by side)')
|
||||
println('✓ Split panes vertically (top and bottom)')
|
||||
println('✓ Send commands to specific panes')
|
||||
println('✓ Send raw keys to panes')
|
||||
println('✓ Select/activate panes')
|
||||
println('✓ Capture pane output')
|
||||
println('✓ Get pane process information')
|
||||
println('✓ Kill individual panes')
|
||||
|
||||
println('\nExample completed! You can attach to the session with:')
|
||||
println(' tmux attach-session -t panes_demo')
|
||||
@@ -7,6 +7,24 @@ import os
|
||||
import time
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
// Check if error message indicates tmux server is not running
|
||||
fn is_tmux_server_not_running_error(error_msg string) bool {
|
||||
// Common tmux server not running error patterns
|
||||
tmux_not_running_patterns := [
|
||||
'no server running',
|
||||
'error connecting to',
|
||||
'No such file or directory', // when socket doesn't exist
|
||||
]
|
||||
|
||||
error_lower := error_msg.to_lower()
|
||||
for pattern in tmux_not_running_patterns {
|
||||
if error_lower.contains(pattern.to_lower()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@[heap]
|
||||
pub struct Tmux {
|
||||
pub mut:
|
||||
@@ -177,7 +195,7 @@ pub fn (mut t Tmux) windows_get() []&Window {
|
||||
pub fn (mut t Tmux) is_running() !bool {
|
||||
res := os.execute('tmux info')
|
||||
if res.exit_code != 0 {
|
||||
if res.output.contains('no server running') {
|
||||
if is_tmux_server_not_running_error(res.output) {
|
||||
// console.print_debug(" TMUX NOT RUNNING")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -7,6 +7,83 @@ import time
|
||||
import os
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
// Constants for memory calculations
|
||||
const kb_to_bytes_factor = 1024
|
||||
const memory_display_precision = 3
|
||||
const memory_cache_ttl_seconds = 300 // Cache system memory for 5 minutes
|
||||
|
||||
// Global cache for system memory to avoid repeated syscalls
|
||||
struct MemoryCache {
|
||||
mut:
|
||||
total_bytes u64
|
||||
cached_at time.Time
|
||||
}
|
||||
|
||||
__global (
|
||||
memory_cache MemoryCache
|
||||
)
|
||||
|
||||
// Platform-specific memory detection
|
||||
fn get_total_system_memory() !u64 {
|
||||
$if macos {
|
||||
result := osal.execute_silent('sysctl -n hw.memsize') or {
|
||||
return error('Failed to get system memory on macOS: ${err}')
|
||||
}
|
||||
return result.trim_space().u64()
|
||||
} $else $if linux {
|
||||
// Read from /proc/meminfo
|
||||
content := os.read_file('/proc/meminfo') or {
|
||||
return error('Failed to read /proc/meminfo on Linux: ${err}')
|
||||
}
|
||||
for line in content.split_into_lines() {
|
||||
if line.starts_with('MemTotal:') {
|
||||
parts := line.split_any(' \t').filter(it.len > 0)
|
||||
if parts.len >= 2 {
|
||||
kb_value := parts[1].u64()
|
||||
return kb_value * kb_to_bytes_factor
|
||||
}
|
||||
}
|
||||
}
|
||||
return error('Could not parse MemTotal from /proc/meminfo')
|
||||
} $else {
|
||||
return error('Unsupported platform for memory detection')
|
||||
}
|
||||
}
|
||||
|
||||
// Get cached or fresh system memory
|
||||
fn get_system_memory_cached() u64 {
|
||||
now := time.now()
|
||||
|
||||
// Check if cache is valid
|
||||
if memory_cache.total_bytes > 0
|
||||
&& now.unix() - memory_cache.cached_at.unix() < memory_cache_ttl_seconds {
|
||||
return memory_cache.total_bytes
|
||||
}
|
||||
|
||||
// Refresh cache
|
||||
total_memory := get_total_system_memory() or {
|
||||
console.print_debug('Failed to get system memory: ${err}')
|
||||
return 0
|
||||
}
|
||||
|
||||
memory_cache.total_bytes = total_memory
|
||||
memory_cache.cached_at = now
|
||||
|
||||
return total_memory
|
||||
}
|
||||
|
||||
// Calculate accurate memory percentage
|
||||
fn calculate_memory_percentage(memory_bytes u64, ps_fallback_percent f64) f64 {
|
||||
total_memory := get_system_memory_cached()
|
||||
|
||||
if total_memory > 0 {
|
||||
return (f64(memory_bytes) / f64(total_memory)) * 100.0
|
||||
}
|
||||
|
||||
// Fallback to ps value if system memory detection fails
|
||||
return ps_fallback_percent
|
||||
}
|
||||
|
||||
@[heap]
|
||||
struct Pane {
|
||||
pub mut:
|
||||
@@ -22,28 +99,47 @@ pub mut:
|
||||
|
||||
pub fn (mut p Pane) stats() !ProcessStats {
|
||||
if p.pid == 0 {
|
||||
return ProcessStats{}
|
||||
return ProcessStats{
|
||||
cpu_percent: 0.0
|
||||
memory_percent: 0.0
|
||||
memory_bytes: 0
|
||||
}
|
||||
}
|
||||
|
||||
// Use ps command to get CPU and memory stats
|
||||
cmd := 'ps -p ${p.pid} -o %cpu,%mem,rss --no-headers'
|
||||
// Use ps command to get CPU and memory stats (cross-platform compatible)
|
||||
cmd := 'ps -p ${p.pid} -o %cpu,%mem,rss'
|
||||
result := osal.execute_silent(cmd) or {
|
||||
return error('Cannot get stats for PID ${p.pid}: ${err}')
|
||||
}
|
||||
|
||||
if result.trim_space() == '' {
|
||||
lines := result.split_into_lines()
|
||||
if lines.len < 2 {
|
||||
return error('Process ${p.pid} not found')
|
||||
}
|
||||
|
||||
parts := result.trim_space().split_any(' \t').filter(it != '')
|
||||
if parts.len < 3 {
|
||||
return error('Invalid ps output: ${result}')
|
||||
// Skip header line, get data line
|
||||
data_line := lines[1].trim_space()
|
||||
if data_line == '' {
|
||||
return error('Process ${p.pid} not found')
|
||||
}
|
||||
|
||||
parts := data_line.split_any(' \t').filter(it != '')
|
||||
if parts.len < 3 {
|
||||
return error('Invalid ps output: ${data_line}')
|
||||
}
|
||||
|
||||
// Parse values from ps output
|
||||
cpu_percent := parts[0].f64()
|
||||
ps_memory_percent := parts[1].f64()
|
||||
memory_bytes := parts[2].u64() * kb_to_bytes_factor
|
||||
|
||||
// Calculate accurate memory percentage using cached system memory
|
||||
memory_percent := calculate_memory_percentage(memory_bytes, ps_memory_percent)
|
||||
|
||||
return ProcessStats{
|
||||
cpu_percent: parts[0].f64()
|
||||
memory_percent: parts[1].f64()
|
||||
memory_bytes: parts[2].u64() * 1024 // ps returns KB, convert to bytes
|
||||
cpu_percent: cpu_percent
|
||||
memory_percent: memory_percent
|
||||
memory_bytes: memory_bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,3 +243,66 @@ pub fn (mut p Pane) processinfo_main() !osal.ProcessInfo {
|
||||
|
||||
return osal.processinfo_get(p.pid)!
|
||||
}
|
||||
|
||||
// Send a command to this pane
|
||||
pub fn (mut p Pane) send_command(command string) ! {
|
||||
cmd := 'tmux send-keys -t ${p.window.session.name}:@${p.window.id}.%${p.id} "${command}" Enter'
|
||||
osal.execute_silent(cmd) or { return error('Cannot send command to pane %${p.id}: ${err}') }
|
||||
}
|
||||
|
||||
// Send raw keys to this pane (without Enter)
|
||||
pub fn (mut p Pane) send_keys(keys string) ! {
|
||||
cmd := 'tmux send-keys -t ${p.window.session.name}:@${p.window.id}.%${p.id} "${keys}"'
|
||||
osal.execute_silent(cmd) or { return error('Cannot send keys to pane %${p.id}: ${err}') }
|
||||
}
|
||||
|
||||
// Kill this specific pane
|
||||
pub fn (mut p Pane) kill() ! {
|
||||
cmd := 'tmux kill-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id}'
|
||||
osal.execute_silent(cmd) or { return error('Cannot kill pane %${p.id}: ${err}') }
|
||||
}
|
||||
|
||||
// Select/activate this pane
|
||||
pub fn (mut p Pane) select() ! {
|
||||
cmd := 'tmux select-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id}'
|
||||
osal.execute_silent(cmd) or { return error('Cannot select pane %${p.id}: ${err}') }
|
||||
p.active = true
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PaneResizeArgs {
|
||||
pub mut:
|
||||
direction string = 'right' // 'up', 'down', 'left', 'right'
|
||||
cells int = 5 // number of cells to resize by
|
||||
}
|
||||
|
||||
// Resize this pane
|
||||
pub fn (mut p Pane) resize(args PaneResizeArgs) ! {
|
||||
direction_flag := match args.direction.to_lower() {
|
||||
'up', 'u' { '-U' }
|
||||
'down', 'd' { '-D' }
|
||||
'left', 'l' { '-L' }
|
||||
'right', 'r' { '-R' }
|
||||
else { return error('Invalid resize direction: ${args.direction}. Use up, down, left, or right') }
|
||||
}
|
||||
|
||||
cmd := 'tmux resize-pane -t ${p.window.session.name}:@${p.window.id}.%${p.id} ${direction_flag} ${args.cells}'
|
||||
osal.execute_silent(cmd) or { return error('Cannot resize pane %${p.id}: ${err}') }
|
||||
}
|
||||
|
||||
// Convenience methods for resizing
|
||||
pub fn (mut p Pane) resize_up(cells int) ! {
|
||||
p.resize(direction: 'up', cells: cells)!
|
||||
}
|
||||
|
||||
pub fn (mut p Pane) resize_down(cells int) ! {
|
||||
p.resize(direction: 'down', cells: cells)!
|
||||
}
|
||||
|
||||
pub fn (mut p Pane) resize_left(cells int) ! {
|
||||
p.resize(direction: 'left', cells: cells)!
|
||||
}
|
||||
|
||||
pub fn (mut p Pane) resize_right(cells int) ! {
|
||||
p.resize(direction: 'right', cells: cells)!
|
||||
}
|
||||
|
||||
@@ -5,73 +5,99 @@ import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import time
|
||||
|
||||
// Check if error message indicates tmux server is not running
|
||||
fn is_tmux_server_not_running_error(error_msg string) bool {
|
||||
// Common tmux server not running error patterns
|
||||
tmux_not_running_patterns := [
|
||||
'no server running',
|
||||
'error connecting to',
|
||||
'No such file or directory', // when socket doesn't exist
|
||||
]
|
||||
|
||||
error_lower := error_msg.to_lower()
|
||||
for pattern in tmux_not_running_patterns {
|
||||
if error_lower.contains(pattern.to_lower()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (mut t Tmux) scan_add(line string) !&Pane {
|
||||
// Parse the line to get session, window, and pane info
|
||||
line_arr := line.split('|')
|
||||
session_name := line_arr[0]
|
||||
window_name := line_arr[1]
|
||||
window_id := line_arr[2]
|
||||
pane_active := line_arr[3]
|
||||
pane_id := line_arr[4]
|
||||
pane_pid := line_arr[5]
|
||||
pane_start_command := line_arr[6] or { '' }
|
||||
// Parse the line to get session, window, and pane info
|
||||
line_arr := line.split('|')
|
||||
if line_arr.len < 6 {
|
||||
return error('Invalid tmux pane line format: ${line}')
|
||||
}
|
||||
session_name := line_arr[0]
|
||||
window_name := line_arr[1]
|
||||
window_id := line_arr[2]
|
||||
pane_active := line_arr[3]
|
||||
pane_id := line_arr[4]
|
||||
pane_pid := line_arr[5]
|
||||
pane_start_command := line_arr[6] or { '' }
|
||||
|
||||
wid := (window_id.replace('@', '')).int()
|
||||
pid := (pane_id.replace('%', '')).int()
|
||||
// Skip if window name is empty
|
||||
if window_name.len == 0 {
|
||||
return error('Window name is empty in line: ${line}')
|
||||
}
|
||||
|
||||
mut s := t.session_get(session_name)!
|
||||
wid := (window_id.replace('@', '')).int()
|
||||
pid := (pane_id.replace('%', '')).int()
|
||||
|
||||
// Get or create window
|
||||
mut w := if s.window_exist(name: window_name, id: wid) {
|
||||
s.window_get(name: window_name, id: wid)!
|
||||
} else {
|
||||
mut new_w := Window{
|
||||
session: s
|
||||
name: texttools.name_fix(window_name)
|
||||
id: wid
|
||||
panes: []&Pane{}
|
||||
}
|
||||
s.windows << &new_w
|
||||
&new_w
|
||||
}
|
||||
mut s := t.session_get(session_name)!
|
||||
|
||||
// Create or update pane
|
||||
mut p := Pane{
|
||||
window: w
|
||||
id: pid
|
||||
pid: pane_pid.int()
|
||||
active: pane_active == '1'
|
||||
cmd: pane_start_command
|
||||
created_at: time.now()
|
||||
}
|
||||
// Get or create window
|
||||
mut w := if s.window_exist(name: window_name, id: wid) {
|
||||
s.window_get(name: window_name, id: wid)!
|
||||
} else {
|
||||
mut new_w := Window{
|
||||
session: s
|
||||
name: texttools.name_fix(window_name)
|
||||
id: wid
|
||||
panes: []&Pane{}
|
||||
}
|
||||
s.windows << &new_w
|
||||
&new_w
|
||||
}
|
||||
|
||||
// Check if pane already exists
|
||||
mut found := false
|
||||
for mut existing_pane in w.panes {
|
||||
if existing_pane.id == pid {
|
||||
existing_pane.pid = p.pid
|
||||
existing_pane.active = p.active
|
||||
existing_pane.cmd = p.cmd
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// Create or update pane
|
||||
mut p := Pane{
|
||||
window: w
|
||||
id: pid
|
||||
pid: pane_pid.int()
|
||||
active: pane_active == '1'
|
||||
cmd: pane_start_command
|
||||
created_at: time.now()
|
||||
}
|
||||
|
||||
if !found {
|
||||
w.panes << &p
|
||||
}
|
||||
// Check if pane already exists
|
||||
mut found := false
|
||||
for mut existing_pane in w.panes {
|
||||
if existing_pane.id == pid {
|
||||
existing_pane.pid = p.pid
|
||||
existing_pane.active = p.active
|
||||
existing_pane.cmd = p.cmd
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &p
|
||||
if !found {
|
||||
w.panes << &p
|
||||
}
|
||||
|
||||
return &p
|
||||
}
|
||||
|
||||
// scan the system to detect sessions .
|
||||
//TODO needs to be done differently, here only find the sessions, then per session call the scan() which will find the windows, call scan() there as well ...
|
||||
// TODO needs to be done differently, here only find the sessions, then per session call the scan() which will find the windows, call scan() there as well ...
|
||||
pub fn (mut t Tmux) scan() ! {
|
||||
// os.log('TMUX - Scanning ....')
|
||||
|
||||
cmd_list_session := "tmux list-sessions -F '#{session_name}'"
|
||||
exec_list := osal.exec(cmd: cmd_list_session, stdout: false, name: 'tmux_list') or {
|
||||
if err.msg().contains('no server running') {
|
||||
if is_tmux_server_not_running_error(err.msg()) {
|
||||
return
|
||||
}
|
||||
return error('could not execute list sessions.\n${err}')
|
||||
|
||||
@@ -63,8 +63,11 @@ pub fn (mut s Session) scan() ! {
|
||||
for line in result.split_into_lines() {
|
||||
if line.contains('|') {
|
||||
parts := line.split('|')
|
||||
if parts.len >= 2 {
|
||||
if parts.len >= 3 && parts[0].len > 0 && parts[1].len > 0 {
|
||||
window_name := texttools.name_fix(parts[0])
|
||||
if window_name.len == 0 {
|
||||
continue
|
||||
}
|
||||
window_id := parts[1].replace('@', '').int()
|
||||
window_active := parts[2] == '1'
|
||||
|
||||
@@ -73,7 +76,7 @@ pub fn (mut s Session) scan() ! {
|
||||
// Update existing window or create new one
|
||||
mut found := false
|
||||
for mut w in s.windows {
|
||||
if w.name == window_name {
|
||||
if w.name.len > 0 && window_name.len > 0 && w.name == window_name {
|
||||
w.id = window_id
|
||||
w.active = window_active
|
||||
w.scan()! // Scan panes for this window
|
||||
@@ -99,7 +102,7 @@ pub fn (mut s Session) scan() ! {
|
||||
}
|
||||
|
||||
// Remove windows that no longer exist in tmux
|
||||
s.windows = s.windows.filter(current_windows[it.name] == true)
|
||||
s.windows = s.windows.filter(it.name.len > 0 && current_windows[it.name] == true)
|
||||
}
|
||||
|
||||
// window_name is the name of the window in session main (will always be called session main)
|
||||
@@ -211,9 +214,12 @@ fn (mut s Session) window_exist(args_ WindowGetArgs) bool {
|
||||
|
||||
pub fn (mut s Session) window_get(args_ WindowGetArgs) !&Window {
|
||||
mut args := args_
|
||||
if args.name.len == 0 {
|
||||
return error('Window name cannot be empty')
|
||||
}
|
||||
args.name = texttools.name_fix(args.name)
|
||||
for w in s.windows {
|
||||
if w.name == args.name {
|
||||
if w.name.len > 0 && w.name == args.name {
|
||||
if (args.id > 0 && w.id == args.id) || args.id == 0 {
|
||||
return w
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub mut:
|
||||
session &Session @[skip]
|
||||
name string
|
||||
id int
|
||||
panes []&Pane // windows contain multiple panes
|
||||
panes []&Pane // windows contain multiple panes
|
||||
active bool
|
||||
env map[string]string
|
||||
}
|
||||
@@ -22,105 +22,104 @@ pub mut:
|
||||
pub struct PaneNewArgs {
|
||||
pub mut:
|
||||
name string
|
||||
reset bool //means we reset the pane if it already exists
|
||||
reset bool // means we reset the pane if it already exists
|
||||
cmd string
|
||||
env map[string]string
|
||||
env map[string]string
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut w Window) scan() ! {
|
||||
// Get current panes for this window
|
||||
cmd := "tmux list-panes -t ${w.session.name}:@${w.id} -F '#{pane_id}|#{pane_pid}|#{pane_active}|#{pane_start_command}'"
|
||||
result := osal.execute_silent(cmd) or {
|
||||
// Window might not exist anymore
|
||||
return
|
||||
}
|
||||
|
||||
mut current_panes := map[int]bool{}
|
||||
for line in result.split_into_lines() {
|
||||
if line.contains('|') {
|
||||
parts := line.split('|')
|
||||
if parts.len >= 3 {
|
||||
pane_id := parts[0].replace('%', '').int()
|
||||
pane_pid := parts[1].int()
|
||||
pane_active := parts[2] == '1'
|
||||
pane_cmd := if parts.len > 3 { parts[3] } else { '' }
|
||||
|
||||
current_panes[pane_id] = true
|
||||
|
||||
// Update existing pane or create new one
|
||||
mut found := false
|
||||
for mut p in w.panes {
|
||||
if p.id == pane_id {
|
||||
p.pid = pane_pid
|
||||
p.active = pane_active
|
||||
p.cmd = pane_cmd
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
mut new_pane := Pane{
|
||||
window: &w
|
||||
id: pane_id
|
||||
pid: pane_pid
|
||||
active: pane_active
|
||||
cmd: pane_cmd
|
||||
env: map[string]string{}
|
||||
created_at: time.now()
|
||||
last_output_offset: 0
|
||||
}
|
||||
w.panes << &new_pane
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove panes that no longer exist
|
||||
w.panes = w.panes.filter(current_panes[it.id] == true)
|
||||
}
|
||||
// Get current panes for this window
|
||||
cmd := "tmux list-panes -t ${w.session.name}:@${w.id} -F '#{pane_id}|#{pane_pid}|#{pane_active}|#{pane_start_command}'"
|
||||
result := osal.execute_silent(cmd) or {
|
||||
// Window might not exist anymore
|
||||
return
|
||||
}
|
||||
|
||||
mut current_panes := map[int]bool{}
|
||||
for line in result.split_into_lines() {
|
||||
if line.contains('|') {
|
||||
parts := line.split('|')
|
||||
if parts.len >= 3 {
|
||||
pane_id := parts[0].replace('%', '').int()
|
||||
pane_pid := parts[1].int()
|
||||
pane_active := parts[2] == '1'
|
||||
pane_cmd := if parts.len > 3 { parts[3] } else { '' }
|
||||
|
||||
current_panes[pane_id] = true
|
||||
|
||||
// Update existing pane or create new one
|
||||
mut found := false
|
||||
for mut p in w.panes {
|
||||
if p.id == pane_id {
|
||||
p.pid = pane_pid
|
||||
p.active = pane_active
|
||||
p.cmd = pane_cmd
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
mut new_pane := Pane{
|
||||
window: &w
|
||||
id: pane_id
|
||||
pid: pane_pid
|
||||
active: pane_active
|
||||
cmd: pane_cmd
|
||||
env: map[string]string{}
|
||||
created_at: time.now()
|
||||
last_output_offset: 0
|
||||
}
|
||||
w.panes << &new_pane
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove panes that no longer exist
|
||||
w.panes = w.panes.filter(current_panes[it.id] == true)
|
||||
}
|
||||
|
||||
pub fn (mut w Window) stop() ! {
|
||||
w.kill()!
|
||||
w.kill()!
|
||||
}
|
||||
//helper function
|
||||
//TODO env variables are not inserted in pane
|
||||
|
||||
// helper function
|
||||
// TODO env variables are not inserted in pane
|
||||
pub fn (mut w Window) create(cmd_ string) ! {
|
||||
mut final_cmd := cmd_
|
||||
if cmd_.contains('\n') {
|
||||
os.mkdir_all('/tmp/tmux/${w.session.name}')!
|
||||
// Fix: osal.exec_string doesn't exist, use file writing instead
|
||||
script_path := '/tmp/tmux/${w.session.name}/${w.name}.sh'
|
||||
script_content := '#!/bin/bash\n' + cmd_
|
||||
os.write_file(script_path, script_content)!
|
||||
os.chmod(script_path, 0o755)!
|
||||
final_cmd = script_path
|
||||
}
|
||||
mut final_cmd := cmd_
|
||||
if cmd_.contains('\n') {
|
||||
os.mkdir_all('/tmp/tmux/${w.session.name}')!
|
||||
// Fix: osal.exec_string doesn't exist, use file writing instead
|
||||
script_path := '/tmp/tmux/${w.session.name}/${w.name}.sh'
|
||||
script_content := '#!/bin/bash\n' + cmd_
|
||||
os.write_file(script_path, script_content)!
|
||||
os.chmod(script_path, 0o755)!
|
||||
final_cmd = script_path
|
||||
}
|
||||
|
||||
mut newcmd := '/bin/bash -c "${final_cmd}"'
|
||||
if cmd_ == "" {
|
||||
newcmd = '/bin/bash'
|
||||
}
|
||||
mut newcmd := '/bin/bash -c "${final_cmd}"'
|
||||
if cmd_ == '' {
|
||||
newcmd = '/bin/bash'
|
||||
}
|
||||
|
||||
// Build environment arguments
|
||||
mut env_args := ''
|
||||
for key, value in w.env {
|
||||
env_args += ' -e ${key}="${value}"'
|
||||
}
|
||||
// Build environment arguments
|
||||
mut env_args := ''
|
||||
for key, value in w.env {
|
||||
env_args += ' -e ${key}="${value}"'
|
||||
}
|
||||
|
||||
res_opt := "-P -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'"
|
||||
cmd := 'tmux new-window ${res_opt}${env_args} -t ${w.session.name} -n ${w.name} \'${newcmd}\''
|
||||
console.print_debug(cmd)
|
||||
|
||||
res := osal.exec(cmd: cmd, stdout: false, name: 'tmux_window_create') or {
|
||||
return error("Can't create new window ${w.name} \n${cmd}\n${err}")
|
||||
}
|
||||
|
||||
line_arr := res.output.split('|')
|
||||
wid := line_arr[2] or { return error('cannot split line for window create.\n${line_arr}') }
|
||||
w.id = wid.replace('@', '').int()
|
||||
res_opt := "-P -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'"
|
||||
cmd := 'tmux new-window ${res_opt}${env_args} -t ${w.session.name} -n ${w.name} \'${newcmd}\''
|
||||
console.print_debug(cmd)
|
||||
|
||||
res := osal.exec(cmd: cmd, stdout: false, name: 'tmux_window_create') or {
|
||||
return error("Can't create new window ${w.name} \n${cmd}\n${err}")
|
||||
}
|
||||
|
||||
line_arr := res.output.split('|')
|
||||
wid := line_arr[2] or { return error('cannot split line for window create.\n${line_arr}') }
|
||||
w.id = wid.replace('@', '').int()
|
||||
}
|
||||
|
||||
// stop the window
|
||||
@@ -143,14 +142,14 @@ pub fn (window Window) str() string {
|
||||
}
|
||||
|
||||
pub fn (mut w Window) stats() !ProcessStats {
|
||||
mut total := ProcessStats{}
|
||||
for mut pane in w.panes {
|
||||
stats := pane.stats() or { continue }
|
||||
total.cpu_percent += stats.cpu_percent
|
||||
total.memory_bytes += stats.memory_bytes
|
||||
total.memory_percent += stats.memory_percent
|
||||
}
|
||||
return total
|
||||
mut total := ProcessStats{}
|
||||
for mut pane in w.panes {
|
||||
stats := pane.stats() or { continue }
|
||||
total.cpu_percent += stats.cpu_percent
|
||||
total.memory_bytes += stats.memory_bytes
|
||||
total.memory_percent += stats.memory_percent
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// will select the current window so with tmux a we can go there .
|
||||
@@ -169,10 +168,91 @@ pub fn (mut w Window) pane_list() []&Pane {
|
||||
|
||||
// Get active pane in window
|
||||
pub fn (mut w Window) pane_active() ?&Pane {
|
||||
for pane in w.panes {
|
||||
if pane.active {
|
||||
return pane
|
||||
}
|
||||
}
|
||||
return none
|
||||
for pane in w.panes {
|
||||
if pane.active {
|
||||
return pane
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PaneSplitArgs {
|
||||
pub mut:
|
||||
cmd string // command to run in new pane
|
||||
horizontal bool // true for horizontal split, false for vertical
|
||||
env map[string]string // environment variables
|
||||
}
|
||||
|
||||
// Split the active pane horizontally or vertically
|
||||
pub fn (mut w Window) pane_split(args PaneSplitArgs) !&Pane {
|
||||
mut cmd_to_run := args.cmd
|
||||
if cmd_to_run == '' {
|
||||
cmd_to_run = '/bin/bash'
|
||||
}
|
||||
|
||||
// Build environment arguments
|
||||
mut env_args := ''
|
||||
for key, value in args.env {
|
||||
env_args += ' -e ${key}="${value}"'
|
||||
}
|
||||
|
||||
// Choose split direction
|
||||
split_flag := if args.horizontal { '-h' } else { '-v' }
|
||||
|
||||
// Execute tmux split-window command
|
||||
res_opt := "-P -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'"
|
||||
cmd := 'tmux split-window ${split_flag} ${res_opt}${env_args} -t ${w.session.name}:@${w.id} \'${cmd_to_run}\''
|
||||
|
||||
console.print_debug('Splitting pane: ${cmd}')
|
||||
|
||||
res := osal.exec(cmd: cmd, stdout: false, name: 'tmux_pane_split') or {
|
||||
return error("Can't split pane in window ${w.name}: ${err}")
|
||||
}
|
||||
|
||||
// Parse the result to get new pane info
|
||||
line_arr := res.output.split('|')
|
||||
if line_arr.len < 7 {
|
||||
return error('Invalid tmux split-window output: ${res.output}')
|
||||
}
|
||||
|
||||
pane_id := line_arr[4].replace('%', '').int()
|
||||
pane_pid := line_arr[5].int()
|
||||
pane_active := line_arr[3] == '1'
|
||||
pane_cmd := line_arr[6] or { '' }
|
||||
|
||||
// Create new pane object
|
||||
mut new_pane := Pane{
|
||||
window: &w
|
||||
id: pane_id
|
||||
pid: pane_pid
|
||||
active: pane_active
|
||||
cmd: pane_cmd
|
||||
env: args.env
|
||||
created_at: time.now()
|
||||
last_output_offset: 0
|
||||
}
|
||||
|
||||
// Add to window's panes and rescan to get current state
|
||||
w.panes << &new_pane
|
||||
w.scan()!
|
||||
|
||||
// Return reference to the new pane
|
||||
for mut pane in w.panes {
|
||||
if pane.id == pane_id {
|
||||
return pane
|
||||
}
|
||||
}
|
||||
|
||||
return error('Could not find newly created pane with ID ${pane_id}')
|
||||
}
|
||||
|
||||
// Split pane horizontally (side by side)
|
||||
pub fn (mut w Window) pane_split_horizontal(cmd string) !&Pane {
|
||||
return w.pane_split(cmd: cmd, horizontal: true)
|
||||
}
|
||||
|
||||
// Split pane vertically (top and bottom)
|
||||
pub fn (mut w Window) pane_split_vertical(cmd string) !&Pane {
|
||||
return w.pane_split(cmd: cmd, horizontal: false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user