mirror of
https://github.com/theniceboy/.config.git
synced 2025-12-26 14:44:57 +08:00
codex agent tracker
This commit is contained in:
parent
20eddd1600
commit
b08d63e4a5
96 changed files with 3057 additions and 3055 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -77,3 +77,12 @@ yazi/bookmark
|
|||
/codex/*
|
||||
!/codex/config.toml
|
||||
!/codex/AGENTS.md
|
||||
|
||||
# Agent tracker binaries
|
||||
!/agent-tracker/
|
||||
agent-tracker/bin/tracker-client
|
||||
agent-tracker/bin/tracker-mcp
|
||||
agent-tracker/bin/tracker-server
|
||||
agent-tracker/run/server.pid
|
||||
tmux/cache
|
||||
agent-tracker/.DS_Store
|
||||
|
|
|
|||
78
.tmux.conf
78
.tmux.conf
|
|
@ -7,8 +7,11 @@ setw -g xterm-keys on
|
|||
set -s escape-time 0
|
||||
set -sg repeat-time 300
|
||||
set -s focus-events on
|
||||
set -s extended-keys on
|
||||
set -as terminal-features 'xterm*:extkeys'
|
||||
set -g mouse on
|
||||
set -sg exit-empty on
|
||||
set -g detach-on-destroy off
|
||||
|
||||
set -q -g status-utf8 on
|
||||
setw -q -g utf8 on
|
||||
|
|
@ -34,7 +37,38 @@ XDG_SESSION_DESKTOP XDG_SESSION_TYPE XDG_CURRENT_DESKTOP \
|
|||
XMODIFIERS \
|
||||
FZF_DEFAULT_OPTS \
|
||||
'
|
||||
set-hook -gu client-attached
|
||||
set-hook -g client-attached 'run -b "cut -c3- ~/.tmux.conf | sh -s update_env_event"'
|
||||
set-hook -ag client-attached 'run -b "~/.config/agent-tracker/bin/tracker-client command refresh --client #{client_tty}"'
|
||||
set-hook -ag client-attached 'run -b "tmux refresh-client -S"'
|
||||
|
||||
set-hook -gu client-resized
|
||||
set-hook -ag client-resized 'run -b "~/.config/agent-tracker/bin/tracker-client command refresh --client #{client_tty}"'
|
||||
set-hook -ag client-resized 'run -b "tmux refresh-client -S"'
|
||||
|
||||
set-hook -ag pane-focus-in 'run -b "~/.config/agent-tracker/bin/tracker-client command acknowledge --client #{client_tty} --session-id #{session_id} --window-id #{window_id} --pane #{pane_id}"'
|
||||
set-hook -ag pane-focus-in 'run -b "tmux refresh-client -S"'
|
||||
set-hook -ag pane-died 'run -b "~/.config/agent-tracker/bin/tracker-client command delete_task --client #{client_tty} --session-id #{session_id} --window-id #{window_id} --pane #{pane_id}"'
|
||||
set-hook -ag window-linked 'run -b "tmux refresh-client -S"'
|
||||
set-hook -ag window-unlinked 'run -b "tmux refresh-client -S"'
|
||||
set-hook -ag window-renamed 'run -b "tmux refresh-client -S"'
|
||||
set-hook -ag window-layout-changed 'run -b "tmux refresh-client -S"'
|
||||
set-hook -ag after-kill-window 'run -b "tmux refresh-client -S"'
|
||||
|
||||
set-hook -gu client-session-changed
|
||||
set-hook -g client-session-changed 'run -b "tmux refresh-client -S"'
|
||||
|
||||
set-hook -gu session-created
|
||||
set-hook -ag session-created 'run -b "~/.config/tmux/scripts/session_created.sh"'
|
||||
set-hook -ag session-created 'run -b "tmux refresh-client -S"'
|
||||
|
||||
set-hook -gu session-renamed
|
||||
set-hook -g session-renamed 'run -b "tmux refresh-client -S"'
|
||||
|
||||
set-hook -gu session-closed
|
||||
set-hook -g session-closed 'run -b "tmux refresh-client -S"'
|
||||
|
||||
run-shell "~/.config/tmux/scripts/session_created.sh"
|
||||
|
||||
# -- prefix
|
||||
unbind C-b
|
||||
|
|
@ -52,17 +86,22 @@ set -g set-titles on
|
|||
set -g display-panes-time 2000
|
||||
set -g display-time 2000
|
||||
|
||||
set -g status-interval 1
|
||||
|
||||
# -- navigation
|
||||
|
||||
# create session
|
||||
bind C-c new-session
|
||||
bind -n M-S run-shell "~/.config/tmux/scripts/new_session.sh"
|
||||
bind -n M-O new-window -c "#{pane_current_path}"
|
||||
bind -n M-s run-shell "~/.config/tmux/scripts/toggle_scratchpad.sh"
|
||||
|
||||
# window management
|
||||
bind -n M-o new-window -c "#{pane_current_path}"
|
||||
bind -n M-O break-pane
|
||||
bind -n M-Q kill-pane
|
||||
bind -n M-t run-shell "~/.config/agent-tracker/bin/tracker-client command toggle --client '#{client_tty}'"
|
||||
bind . command-prompt -p "Rename session to:" "run-shell \"~/.config/tmux/scripts/rename_session_prompt.sh '%%'\""
|
||||
unbind ,
|
||||
bind , command-prompt -p "Rename window:" "rename-window '%%'"
|
||||
|
||||
# window navigation
|
||||
unbind n
|
||||
|
|
@ -127,6 +166,7 @@ bind > swap-pane -D
|
|||
bind < swap-pane -U
|
||||
bind | swap-pane
|
||||
bind -n M-Space next-layout
|
||||
bind Space run-shell "~/.config/tmux/scripts/toggle_orientation.sh"
|
||||
|
||||
bind W choose-tree -Z
|
||||
bind S choose-tree 'move-pane -v -s "%%"'
|
||||
|
|
@ -163,18 +203,24 @@ bind -T copy-mode-vi = send-keys -X search-reverse
|
|||
bind b list-buffers
|
||||
bind p paste-buffer
|
||||
|
||||
bind l run-shell "~/.config/tmux/scripts/move_session.sh left"
|
||||
bind y run-shell "~/.config/tmux/scripts/move_session.sh right"
|
||||
bind -n M-l swap-window -t :-1
|
||||
bind -n M-y swap-window -t :+1
|
||||
|
||||
bind I run-shell "~/.config/tmux/scripts/layout_builder.sh right"
|
||||
bind N run-shell "~/.config/tmux/scripts/layout_builder.sh left"
|
||||
bind U run-shell "~/.config/tmux/scripts/layout_builder.sh up"
|
||||
bind E run-shell "~/.config/tmux/scripts/layout_builder.sh down"
|
||||
|
||||
# -- toggle_syn_input
|
||||
bind C-g if-shell '[[ $(tmux showw synchronize-panes | cut -d\ -f2) == "on" ]]' \
|
||||
'setw synchronize-panes off; set -g pane-border-style fg=magenta' \
|
||||
'setw synchronize-panes on; set -g pane-border-style fg=red'
|
||||
|
||||
# -- toggle_status
|
||||
bind s if-shell '[[ $(tmux show -g status | cut -d\ -f2) == "on" ]]' \
|
||||
'set -g status off' \
|
||||
'set -g status on'
|
||||
|
||||
# -- theme
|
||||
#set -g status off
|
||||
|
||||
# panes
|
||||
#setw -g pane-border-status bottom
|
||||
|
|
@ -183,6 +229,8 @@ setw -g pane-border-format '#[bold]#{?#{&&:#{pane_active},#{client_prefix}},#[un
|
|||
#set -g pane-active-border-style fg=brightblue
|
||||
#set -g pane-border-style fg=magenta
|
||||
|
||||
# windows
|
||||
|
||||
# windows
|
||||
set -g status-justify 'centre'
|
||||
set -g status-left-length 90
|
||||
|
|
@ -195,13 +243,23 @@ set -g status-bg black
|
|||
#set -g status-left '#[fg=brightyellow] #{?client_prefix,⌨ , } #[fg=magenta,bold] %Y-%m-%d %H:%M '
|
||||
#set -g status-right '#(rainbarf --battery --remaining --bolt --tmux --rgb)'
|
||||
#set -g status-left "#[fg=magenta,bold] %Y-%m-%d %H:%M | #[fg=brightblue]#(curl icanhazip.com) #(ifconfig en0 | grep 'inet ' | awk '{print \"en0 \" $2}') #(ifconfig en1 | grep 'inet ' | awk '{print \"en1 \" $2}') #(ifconfig en3 | grep 'inet ' | awk '{print \"en3 \" $2}') #(ifconfig tun0 | grep 'inet ' | awk '{print \"vpn \" $2}') "
|
||||
setw -g window-status-format '#[fg=brightblue] #I #W '
|
||||
setw -g window-status-current-format '#[fg=magenta,bold] #I #W:#F'
|
||||
setw -g window-status-format '#[fg=brightblue] #W '
|
||||
setw -g window-status-current-format '#[fg=magenta,bold] #W '
|
||||
setw -g window-status-activity-style bg=black
|
||||
setw -g window-status-bell-style bg=black
|
||||
#set-window-option -g window-status-current-format "#[fg=colour235, bg=colour27]⮀#[fg=colour255, bg=colour27] #I ⮁ #W #[fg=colour27, bg=colour235]⮀"
|
||||
|
||||
set-option -g status-left "#(~/.config/tmux/tmux-status/left.sh \"#{session_id}\" \"#{session_name}\")"
|
||||
set-option -g status-right "#(~/.config/tmux/tmux-status/right.sh)"
|
||||
|
||||
set-option -g status-left "#(~/.config/tmux/tmux-powerline/powerline.sh left)"
|
||||
set-option -g status-right "#(~/.config/tmux/tmux-powerline/powerline.sh right)"
|
||||
|
||||
# session index switching
|
||||
bind -n C-1 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 1"
|
||||
bind -n C-2 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 2"
|
||||
bind -n C-3 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 3"
|
||||
bind -n C-4 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 4"
|
||||
bind -n C-5 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 5"
|
||||
bind -n C-6 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 6"
|
||||
bind -n C-7 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 7"
|
||||
bind -n C-8 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 8"
|
||||
bind -n C-9 run-shell "~/.config/tmux/scripts/switch_session_by_index.sh 9"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ If you need to install Homebrew manually:
|
|||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
```
|
||||
|
||||
### Iterm2 config
|
||||
Title should be set to session name only. Do not allow other applications to change the title.
|
||||
|
||||
## Apps
|
||||
|
||||
### Claude Code Voice Configuration
|
||||
|
|
@ -57,7 +60,7 @@ For the best text-to-speech experience, download high-quality system voices:
|
|||
Voice is controlled by a global flag file at `~/.claude/voice-enabled`. When this file exists, Claude Code will speak all responses.
|
||||
|
||||
### Other Applications
|
||||
- **tmux**: Terminal multiplexer with custom configuration
|
||||
- **tmux**: Terminal multiplexer with custom configuration (set `TMUX_RAINBARF=0` before launching tmux to hide the rainbarf status segment)
|
||||
- **neovim**: Modern text editor
|
||||
- **yazi**: Terminal file manager
|
||||
- **lazygit**: TUI for git operations
|
||||
|
|
|
|||
19
agent-tracker/.brew/agent-tracker-server.rb
Normal file
19
agent-tracker/.brew/agent-tracker-server.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
class AgentTrackerServer < Formula
|
||||
desc "Tmux-aware agent tracker server"
|
||||
homepage "https://github.com/david/agent-tracker"
|
||||
url "file:///var/folders/11/dhzcjp416tl1dkf16kxns3z00000gn/T/tmp.mJenIMNxo7/tracker-server.tar.gz"
|
||||
sha256 "cb24ca397f60e5209b79667e32dc0f98fd22a9ac85627d5eb79d0e9e8e75be55"
|
||||
version "local-20250917103405"
|
||||
|
||||
def install
|
||||
bin.install "tracker-server"
|
||||
end
|
||||
|
||||
service do
|
||||
run [opt_bin/"tracker-server"]
|
||||
keep_alive true
|
||||
working_dir var/"agent-tracker"
|
||||
log_path var/"log/agent-tracker-server.log"
|
||||
error_log_path var/"log/agent-tracker-server.log"
|
||||
end
|
||||
end
|
||||
1
agent-tracker/.gitignore
vendored
Normal file
1
agent-tracker/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.build/
|
||||
762
agent-tracker/cmd/tracker-client/main.go
Normal file
762
agent-tracker/cmd/tracker-client/main.go
Normal file
|
|
@ -0,0 +1,762 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/david/agent-tracker/internal/ipc"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
statusInProgress = "in_progress"
|
||||
statusCompleted = "completed"
|
||||
)
|
||||
|
||||
var spinnerFrames = []rune{'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'}
|
||||
|
||||
const spinnerInterval = 120 * time.Millisecond
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
if len(os.Args) < 2 {
|
||||
if err := runUI(os.Args[1:]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
cmd := os.Args[1]
|
||||
args := os.Args[2:]
|
||||
switch cmd {
|
||||
case "ui":
|
||||
if err := runUI(args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "command":
|
||||
if err := runCommand(args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "state":
|
||||
if err := runState(args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
default:
|
||||
if err := runUI(os.Args[1:]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(args []string) error {
|
||||
fs := flag.NewFlagSet("tracker-client command", flag.ExitOnError)
|
||||
var client, session, sessionID, window, windowID, pane, summary string
|
||||
fs.StringVar(&client, "client", "", "tmux client tty")
|
||||
fs.StringVar(&session, "session", "", "tmux session name")
|
||||
fs.StringVar(&sessionID, "session-id", "", "tmux session id")
|
||||
fs.StringVar(&window, "window", "", "tmux window name")
|
||||
fs.StringVar(&windowID, "window-id", "", "tmux window id")
|
||||
fs.StringVar(&pane, "pane", "", "tmux pane id")
|
||||
fs.StringVar(&summary, "summary", "", "summary or note payload")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
rest := fs.Args()
|
||||
if len(rest) == 0 {
|
||||
return fmt.Errorf("command name required")
|
||||
}
|
||||
if len(rest) > 1 {
|
||||
summary = strings.Join(rest[1:], " ")
|
||||
}
|
||||
|
||||
env := ipc.Envelope{
|
||||
Kind: "command",
|
||||
Command: rest[0],
|
||||
Client: client,
|
||||
Session: strings.TrimSpace(session),
|
||||
SessionID: strings.TrimSpace(sessionID),
|
||||
Window: strings.TrimSpace(window),
|
||||
WindowID: strings.TrimSpace(windowID),
|
||||
Pane: strings.TrimSpace(pane),
|
||||
Summary: strings.TrimSpace(summary),
|
||||
}
|
||||
if env.Summary != "" {
|
||||
env.Message = env.Summary
|
||||
}
|
||||
|
||||
switch env.Command {
|
||||
case "start_task", "finish_task", "acknowledge":
|
||||
ctx, err := resolveContext(env.Session, env.SessionID, env.Window, env.WindowID, env.Pane)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.Session = ctx.SessionName
|
||||
env.SessionID = ctx.SessionID
|
||||
env.Window = ctx.WindowName
|
||||
env.WindowID = ctx.WindowID
|
||||
env.Pane = ctx.PaneID
|
||||
}
|
||||
|
||||
conn, err := net.Dial("unix", socketPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
enc := json.NewEncoder(conn)
|
||||
if err := enc.Encode(&env); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(conn)
|
||||
for {
|
||||
var reply ipc.Envelope
|
||||
if err := dec.Decode(&reply); err != nil {
|
||||
return err
|
||||
}
|
||||
if reply.Kind == "ack" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runState(args []string) error {
|
||||
fs := flag.NewFlagSet("tracker-client state", flag.ExitOnError)
|
||||
var client string
|
||||
fs.StringVar(&client, "client", "", "tmux client tty")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, err := net.Dial("unix", socketPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
enc := json.NewEncoder(conn)
|
||||
dec := json.NewDecoder(bufio.NewReader(conn))
|
||||
|
||||
if err := enc.Encode(&ipc.Envelope{Kind: "ui-register", Client: client}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
var env ipc.Envelope
|
||||
if err := dec.Decode(&env); err != nil {
|
||||
return err
|
||||
}
|
||||
if env.Kind == "state" {
|
||||
out := json.NewEncoder(os.Stdout)
|
||||
out.SetEscapeHTML(false)
|
||||
if err := out.Encode(&env); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type tmuxContext struct {
|
||||
SessionName string
|
||||
SessionID string
|
||||
WindowName string
|
||||
WindowID string
|
||||
PaneID string
|
||||
}
|
||||
|
||||
func resolveContext(sessionName, sessionID, windowName, windowID, paneID string) (tmuxContext, error) {
|
||||
ctx := tmuxContext{
|
||||
SessionName: strings.TrimSpace(sessionName),
|
||||
SessionID: strings.TrimSpace(sessionID),
|
||||
WindowName: strings.TrimSpace(windowName),
|
||||
WindowID: strings.TrimSpace(windowID),
|
||||
PaneID: strings.TrimSpace(paneID),
|
||||
}
|
||||
|
||||
fetchOrder := []string{}
|
||||
if ctx.PaneID != "" {
|
||||
fetchOrder = append(fetchOrder, ctx.PaneID)
|
||||
}
|
||||
fetchOrder = append(fetchOrder, "")
|
||||
|
||||
for _, target := range fetchOrder {
|
||||
if ctx.complete() {
|
||||
break
|
||||
}
|
||||
info, err := detectTmuxContext(target)
|
||||
if err != nil {
|
||||
if target == "" {
|
||||
return tmuxContext{}, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
ctx = ctx.merge(info)
|
||||
}
|
||||
|
||||
if ctx.SessionID == "" || ctx.WindowID == "" {
|
||||
return tmuxContext{}, fmt.Errorf("session and window identifiers required")
|
||||
}
|
||||
|
||||
if ctx.SessionName == "" || ctx.WindowName == "" {
|
||||
if info, err := detectTmuxContext(ctx.WindowID); err == nil {
|
||||
ctx = ctx.merge(info)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.SessionName == "" {
|
||||
ctx.SessionName = ctx.SessionID
|
||||
}
|
||||
if ctx.WindowName == "" {
|
||||
ctx.WindowName = ctx.WindowID
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (c tmuxContext) complete() bool {
|
||||
return c.SessionName != "" && c.SessionID != "" && c.WindowName != "" && c.WindowID != "" && c.PaneID != ""
|
||||
}
|
||||
|
||||
func (c tmuxContext) merge(other tmuxContext) tmuxContext {
|
||||
if c.SessionName == "" {
|
||||
c.SessionName = other.SessionName
|
||||
}
|
||||
if c.SessionID == "" {
|
||||
c.SessionID = other.SessionID
|
||||
}
|
||||
if c.WindowName == "" {
|
||||
c.WindowName = other.WindowName
|
||||
}
|
||||
if c.WindowID == "" {
|
||||
c.WindowID = other.WindowID
|
||||
}
|
||||
if c.PaneID == "" {
|
||||
c.PaneID = other.PaneID
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func detectTmuxContext(target string) (tmuxContext, error) {
|
||||
format := "#{session_name}:::#{session_id}:::#{window_name}:::#{window_id}:::#{pane_id}"
|
||||
args := []string{"display-message", "-p"}
|
||||
if strings.TrimSpace(target) != "" {
|
||||
args = append(args, "-t", strings.TrimSpace(target))
|
||||
}
|
||||
args = append(args, format)
|
||||
cmd := exec.Command("tmux", args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return tmuxContext{}, fmt.Errorf("tmux display-message: %w (%s)", err, strings.TrimSpace(string(output)))
|
||||
}
|
||||
parts := strings.Split(strings.TrimSpace(string(output)), ":::")
|
||||
if len(parts) != 5 {
|
||||
return tmuxContext{}, fmt.Errorf("unexpected tmux response: %s", strings.TrimSpace(string(output)))
|
||||
}
|
||||
return tmuxContext{
|
||||
SessionName: strings.TrimSpace(parts[0]),
|
||||
SessionID: strings.TrimSpace(parts[1]),
|
||||
WindowName: strings.TrimSpace(parts[2]),
|
||||
WindowID: strings.TrimSpace(parts[3]),
|
||||
PaneID: strings.TrimSpace(parts[4]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func runUI(args []string) error {
|
||||
fs := flag.NewFlagSet("tracker-client ui", flag.ExitOnError)
|
||||
var client string
|
||||
fs.StringVar(&client, "client", "", "tmux client tty")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, err := net.Dial("unix", socketPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
screen, err := tcell.NewScreen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := screen.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer screen.Fini()
|
||||
screen.Clear()
|
||||
|
||||
enc := json.NewEncoder(conn)
|
||||
dec := json.NewDecoder(bufio.NewReader(conn))
|
||||
|
||||
if err := enc.Encode(&ipc.Envelope{Kind: "ui-register", Client: client}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type state struct {
|
||||
message string
|
||||
tasks []ipc.Task
|
||||
}
|
||||
st := state{message: "Connecting to tracker…"}
|
||||
|
||||
incoming := make(chan ipc.Envelope)
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
var env ipc.Envelope
|
||||
if err := dec.Decode(&env); err != nil {
|
||||
errCh <- err
|
||||
close(incoming)
|
||||
return
|
||||
}
|
||||
incoming <- env
|
||||
}
|
||||
}()
|
||||
|
||||
events := make(chan tcell.Event)
|
||||
go func() {
|
||||
for {
|
||||
ev := screen.PollEvent()
|
||||
if ev == nil {
|
||||
close(events)
|
||||
return
|
||||
}
|
||||
events <- ev
|
||||
}
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(spinnerInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
var encMu sync.Mutex
|
||||
sendCommand := func(name string, opts ...func(*ipc.Envelope)) error {
|
||||
encMu.Lock()
|
||||
defer encMu.Unlock()
|
||||
env := ipc.Envelope{Kind: "command", Command: name, Client: client}
|
||||
for _, opt := range opts {
|
||||
opt(&env)
|
||||
}
|
||||
return enc.Encode(&env)
|
||||
}
|
||||
|
||||
selected := 0
|
||||
offset := 0
|
||||
var tasks []ipc.Task
|
||||
|
||||
draw := func(now time.Time) {
|
||||
screen.Clear()
|
||||
width, height := screen.Size()
|
||||
|
||||
headerStyle := tcell.StyleDefault.Foreground(tcell.ColorLightCyan).Bold(true)
|
||||
subtleStyle := tcell.StyleDefault.Foreground(tcell.ColorLightSlateGray)
|
||||
infoStyle := tcell.StyleDefault.Foreground(tcell.ColorSilver)
|
||||
|
||||
writeStyledLine(screen, 0, 0, truncate("▌ Tracker", width), headerStyle)
|
||||
writeStyledLine(screen, 0, 1, truncate(st.message, width), subtleStyle)
|
||||
if width > 0 {
|
||||
writeStyledLine(screen, 0, 2, strings.Repeat("─", width), infoStyle)
|
||||
}
|
||||
|
||||
if len(tasks) == 0 {
|
||||
offset = 0
|
||||
if height > 3 {
|
||||
writeStyledLine(screen, 0, 3, truncate("No active work. Enjoy the calm.", width), infoStyle)
|
||||
}
|
||||
screen.Show()
|
||||
return
|
||||
}
|
||||
|
||||
if selected >= len(tasks) {
|
||||
selected = len(tasks) - 1
|
||||
}
|
||||
if selected < 0 {
|
||||
selected = 0
|
||||
}
|
||||
|
||||
visibleRows := height - 3
|
||||
if visibleRows < 0 {
|
||||
visibleRows = 0
|
||||
}
|
||||
capacity := visibleRows / 4
|
||||
if capacity < 1 {
|
||||
capacity = 1
|
||||
}
|
||||
maxOffset := len(tasks) - capacity
|
||||
if maxOffset < 0 {
|
||||
maxOffset = 0
|
||||
}
|
||||
if offset > maxOffset {
|
||||
offset = maxOffset
|
||||
}
|
||||
if selected < offset {
|
||||
offset = selected
|
||||
}
|
||||
if selected >= offset+capacity {
|
||||
offset = selected - capacity + 1
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
row := 3
|
||||
for idx := offset; idx < len(tasks); idx++ {
|
||||
t := tasks[idx]
|
||||
if row >= height {
|
||||
break
|
||||
}
|
||||
|
||||
indicator := taskIndicator(t, now)
|
||||
summary := t.Summary
|
||||
if summary == "" {
|
||||
summary = "(no summary)"
|
||||
}
|
||||
line := fmt.Sprintf("%s %s", indicator, summary)
|
||||
|
||||
mainStyle := tcell.StyleDefault
|
||||
switch t.Status {
|
||||
case statusInProgress:
|
||||
mainStyle = mainStyle.Foreground(tcell.ColorLightGoldenrodYellow).Bold(true)
|
||||
case statusCompleted:
|
||||
if t.Acknowledged {
|
||||
mainStyle = mainStyle.Foreground(tcell.ColorLightGreen).Bold(true)
|
||||
} else {
|
||||
mainStyle = mainStyle.Foreground(tcell.ColorFuchsia).Bold(true)
|
||||
}
|
||||
}
|
||||
|
||||
if idx == selected {
|
||||
mainStyle = mainStyle.Background(tcell.ColorDarkSlateGray)
|
||||
}
|
||||
|
||||
writeStyledLine(screen, 0, row, truncate(line, width), mainStyle)
|
||||
row++
|
||||
if row >= height {
|
||||
break
|
||||
}
|
||||
|
||||
metaStyle := subtleStyle
|
||||
if idx == selected {
|
||||
metaStyle = metaStyle.Background(tcell.ColorDarkSlateGray)
|
||||
}
|
||||
|
||||
meta := fmt.Sprintf(" %s · %s · %s", t.Session, t.Window, liveDuration(t, now))
|
||||
if t.Status == statusCompleted {
|
||||
if !t.Acknowledged {
|
||||
meta += " • awaiting review"
|
||||
} else if t.CompletedAt != "" {
|
||||
if completed, err := time.Parse(time.RFC3339, t.CompletedAt); err == nil {
|
||||
meta += fmt.Sprintf(" • finished %s", completed.Format("15:04"))
|
||||
}
|
||||
}
|
||||
}
|
||||
writeStyledLine(screen, 0, row, truncate(meta, width), metaStyle)
|
||||
row++
|
||||
|
||||
if t.CompletionNote != "" && row < height {
|
||||
noteStyle := tcell.StyleDefault.Foreground(tcell.ColorLightSteelBlue)
|
||||
if idx == selected {
|
||||
noteStyle = noteStyle.Background(tcell.ColorDarkSlateGray)
|
||||
}
|
||||
note := fmt.Sprintf(" ↳ %s", t.CompletionNote)
|
||||
writeStyledLine(screen, 0, row, truncate(note, width), noteStyle)
|
||||
row++
|
||||
}
|
||||
|
||||
if row < height {
|
||||
row++
|
||||
}
|
||||
}
|
||||
|
||||
screen.Show()
|
||||
}
|
||||
|
||||
draw(time.Now())
|
||||
|
||||
for {
|
||||
select {
|
||||
case env, ok := <-incoming:
|
||||
if !ok {
|
||||
return <-errCh
|
||||
}
|
||||
switch env.Kind {
|
||||
case "state":
|
||||
st.message = env.Message
|
||||
tasks = make([]ipc.Task, len(env.Tasks))
|
||||
copy(tasks, env.Tasks)
|
||||
sortTasks(tasks)
|
||||
if len(tasks) == 0 {
|
||||
selected = 0
|
||||
} else if selected >= len(tasks) {
|
||||
selected = len(tasks) - 1
|
||||
}
|
||||
draw(time.Now())
|
||||
case "ack":
|
||||
default:
|
||||
}
|
||||
case ev, ok := <-events:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
switch tev := ev.(type) {
|
||||
case *tcell.EventKey:
|
||||
if tev.Key() == tcell.KeyEnter {
|
||||
if len(tasks) > 0 && selected < len(tasks) {
|
||||
task := tasks[selected]
|
||||
if err := sendCommand("focus_task", func(env *ipc.Envelope) {
|
||||
env.SessionID = task.SessionID
|
||||
env.WindowID = task.WindowID
|
||||
env.Pane = task.Pane
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := sendCommand("hide"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if tev.Key() == tcell.KeyCtrlC {
|
||||
return sendCommand("hide")
|
||||
}
|
||||
if tev.Modifiers()&tcell.ModAlt != 0 {
|
||||
r := unicode.ToLower(tev.Rune())
|
||||
switch r {
|
||||
case 't':
|
||||
if err := sendCommand("hide"); err != nil {
|
||||
return err
|
||||
}
|
||||
case 'n':
|
||||
if err := sendCommand("move_left"); err != nil {
|
||||
return err
|
||||
}
|
||||
case 'i':
|
||||
if err := sendCommand("move_right"); err != nil {
|
||||
return err
|
||||
}
|
||||
case 'u':
|
||||
if selected > 0 {
|
||||
selected--
|
||||
draw(time.Now())
|
||||
}
|
||||
case 'e':
|
||||
if selected < len(tasks)-1 {
|
||||
selected++
|
||||
draw(time.Now())
|
||||
}
|
||||
}
|
||||
} else if tev.Key() == tcell.KeyRune {
|
||||
r := tev.Rune()
|
||||
if unicode.ToLower(r) == 'd' && (tev.Modifiers()&tcell.ModShift != 0 || unicode.IsUpper(r)) {
|
||||
if len(tasks) > 0 && selected < len(tasks) {
|
||||
task := tasks[selected]
|
||||
if err := sendCommand("delete_task", func(env *ipc.Envelope) {
|
||||
env.SessionID = task.SessionID
|
||||
env.WindowID = task.WindowID
|
||||
env.Pane = task.Pane
|
||||
env.Session = task.Session
|
||||
env.Window = task.Window
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
r = unicode.ToLower(r)
|
||||
if r == 'u' {
|
||||
if selected > 0 {
|
||||
selected--
|
||||
draw(time.Now())
|
||||
}
|
||||
} else if r == 'e' {
|
||||
if selected < len(tasks)-1 {
|
||||
selected++
|
||||
draw(time.Now())
|
||||
}
|
||||
}
|
||||
}
|
||||
if tev.Key() == tcell.KeyEscape {
|
||||
if err := sendCommand("hide"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *tcell.EventResize:
|
||||
draw(time.Now())
|
||||
}
|
||||
case now := <-ticker.C:
|
||||
draw(now)
|
||||
case err := <-errCh:
|
||||
if err == nil || errors.Is(err, io.EOF) || strings.Contains(err.Error(), "use of closed network connection") {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sortTasks(tasks []ipc.Task) {
|
||||
sort.SliceStable(tasks, func(i, j int) bool {
|
||||
ti, tj := tasks[i], tasks[j]
|
||||
ranki := taskStatusRank(ti.Status)
|
||||
rankj := taskStatusRank(tj.Status)
|
||||
if ranki != rankj {
|
||||
return ranki < rankj
|
||||
}
|
||||
switch ti.Status {
|
||||
case statusInProgress:
|
||||
return ti.StartedAt < tj.StartedAt
|
||||
case statusCompleted:
|
||||
if ti.Acknowledged != tj.Acknowledged {
|
||||
return !ti.Acknowledged && tj.Acknowledged
|
||||
}
|
||||
if ci, hasCi := parseTimestamp(ti.CompletedAt); hasCi {
|
||||
if cj, hasCj := parseTimestamp(tj.CompletedAt); hasCj {
|
||||
if !ci.Equal(cj) {
|
||||
return ci.After(cj)
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else if _, hasCj := parseTimestamp(tj.CompletedAt); hasCj {
|
||||
return false
|
||||
}
|
||||
si, hasSi := parseTimestamp(ti.StartedAt)
|
||||
sj, hasSj := parseTimestamp(tj.StartedAt)
|
||||
if hasSi && hasSj && !si.Equal(sj) {
|
||||
return si.After(sj)
|
||||
}
|
||||
if hasSi != hasSj {
|
||||
return hasSi
|
||||
}
|
||||
return ti.StartedAt > tj.StartedAt
|
||||
default:
|
||||
return ti.StartedAt < tj.StartedAt
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func taskStatusRank(status string) int {
|
||||
switch status {
|
||||
case statusInProgress:
|
||||
return 0
|
||||
case statusCompleted:
|
||||
return 1
|
||||
default:
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
func parseTimestamp(value string) (time.Time, bool) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return time.Time{}, false
|
||||
}
|
||||
ts, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return ts, true
|
||||
}
|
||||
|
||||
func taskIndicator(t ipc.Task, now time.Time) string {
|
||||
switch t.Status {
|
||||
case statusInProgress:
|
||||
idx := int(now.UnixNano()/int64(spinnerInterval)) % len(spinnerFrames)
|
||||
return string(spinnerFrames[idx])
|
||||
case statusCompleted:
|
||||
if t.Acknowledged {
|
||||
return "✓"
|
||||
}
|
||||
return "⚑"
|
||||
default:
|
||||
return "?"
|
||||
}
|
||||
}
|
||||
|
||||
func liveDuration(t ipc.Task, now time.Time) string {
|
||||
if t.StartedAt == "" {
|
||||
return "0s"
|
||||
}
|
||||
start, err := time.Parse(time.RFC3339, t.StartedAt)
|
||||
if err != nil {
|
||||
return formatDuration(t.DurationSeconds)
|
||||
}
|
||||
if t.Status == statusCompleted {
|
||||
if t.CompletedAt != "" {
|
||||
if end, err := time.Parse(time.RFC3339, t.CompletedAt); err == nil {
|
||||
return formatDuration(end.Sub(start).Seconds())
|
||||
}
|
||||
}
|
||||
return formatDuration(t.DurationSeconds)
|
||||
}
|
||||
return formatDuration(time.Since(start).Seconds())
|
||||
}
|
||||
|
||||
func formatDuration(seconds float64) string {
|
||||
if seconds < 0 {
|
||||
seconds = 0
|
||||
}
|
||||
d := time.Duration(seconds * float64(time.Second))
|
||||
if d >= 99*time.Hour {
|
||||
return ">=99h"
|
||||
}
|
||||
hours := d / time.Hour
|
||||
minutes := (d % time.Hour) / time.Minute
|
||||
secondsPart := (d % time.Minute) / time.Second
|
||||
if hours > 0 {
|
||||
return fmt.Sprintf("%02dh%02dm", hours, minutes)
|
||||
}
|
||||
if minutes > 0 {
|
||||
return fmt.Sprintf("%02dm%02ds", minutes, secondsPart)
|
||||
}
|
||||
return fmt.Sprintf("%02ds", secondsPart)
|
||||
}
|
||||
|
||||
func truncate(text string, width int) string {
|
||||
if width <= 0 {
|
||||
return ""
|
||||
}
|
||||
runes := []rune(text)
|
||||
if len(runes) <= width {
|
||||
return text
|
||||
}
|
||||
if width <= 1 {
|
||||
return string(runes[:width])
|
||||
}
|
||||
return string(runes[:width-1]) + "…"
|
||||
}
|
||||
|
||||
func writeStyledLine(s tcell.Screen, x, y int, text string, style tcell.Style) {
|
||||
width, _ := s.Size()
|
||||
if x >= width {
|
||||
return
|
||||
}
|
||||
runes := []rune(text)
|
||||
limit := width - x
|
||||
for i := 0; i < limit; i++ {
|
||||
r := rune(' ')
|
||||
if i < len(runes) {
|
||||
r = runes[i]
|
||||
}
|
||||
s.SetContent(x+i, y, r, nil, style)
|
||||
}
|
||||
}
|
||||
|
||||
func socketPath() string {
|
||||
if dir := os.Getenv("XDG_RUNTIME_DIR"); dir != "" {
|
||||
return filepath.Join(dir, "agent-tracker.sock")
|
||||
}
|
||||
return filepath.Join(os.TempDir(), "agent-tracker.sock")
|
||||
}
|
||||
194
agent-tracker/cmd/tracker-mcp/main.go
Normal file
194
agent-tracker/cmd/tracker-mcp/main.go
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/david/agent-tracker/internal/ipc"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
const (
|
||||
implementationName = "tracker_mcp"
|
||||
implementationVersion = "0.1.0"
|
||||
commandTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
type trackerClient struct {
|
||||
socket string
|
||||
}
|
||||
|
||||
func newTrackerClient() *trackerClient {
|
||||
socket := os.Getenv("CODEX_TRACKER_SOCKET")
|
||||
if strings.TrimSpace(socket) == "" {
|
||||
socket = socketPath()
|
||||
}
|
||||
return &trackerClient{socket: socket}
|
||||
}
|
||||
|
||||
func (c *trackerClient) sendCommand(ctx context.Context, env ipc.Envelope) error {
|
||||
env.Kind = "command"
|
||||
d := net.Dialer{}
|
||||
if _, ok := ctx.Deadline(); !ok {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, commandTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
conn, err := d.DialContext(ctx, "unix", c.socket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
if err := conn.SetDeadline(deadline); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(conn)
|
||||
dec := json.NewDecoder(conn)
|
||||
|
||||
if err := enc.Encode(&env); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
var reply ipc.Envelope
|
||||
if err := dec.Decode(&reply); err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return fmt.Errorf("tracker server disconnected")
|
||||
}
|
||||
return err
|
||||
}
|
||||
if reply.Kind == "ack" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type startInput struct {
|
||||
Summary string `json:"summary"`
|
||||
TmuxID string `json:"tmux_id"`
|
||||
}
|
||||
|
||||
type finishInput struct {
|
||||
Summary string `json:"summary"`
|
||||
TmuxID string `json:"tmux_id"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
client := newTrackerClient()
|
||||
|
||||
server := mcp.NewServer(&mcp.Implementation{Name: implementationName, Version: implementationVersion}, nil)
|
||||
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "tracker_mark_start_working",
|
||||
Description: "Record that work has started for the currently focused tmux session/window.",
|
||||
}, func(ctx context.Context, _ *mcp.CallToolRequest, input startInput) (*mcp.CallToolResult, any, error) {
|
||||
tmuxID := strings.TrimSpace(input.TmuxID)
|
||||
if tmuxID == "" {
|
||||
return nil, nil, fmt.Errorf("tmux_id is required")
|
||||
}
|
||||
target, err := determineContext(tmuxID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
summary := strings.TrimSpace(input.Summary)
|
||||
if summary == "" {
|
||||
return nil, nil, fmt.Errorf("summary is required")
|
||||
}
|
||||
env := ipc.Envelope{
|
||||
Command: "start_task",
|
||||
Session: target.SessionID,
|
||||
SessionID: target.SessionID,
|
||||
Window: target.WindowID,
|
||||
WindowID: target.WindowID,
|
||||
Pane: target.PaneID,
|
||||
Summary: summary,
|
||||
}
|
||||
if err := client.sendCommand(ctx, env); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
&mcp.TextContent{Text: "Status recorded. Do the work, then call `tracker_mark_respond_to_user` exactly once, right before you send the user their reply."},
|
||||
},
|
||||
}, nil, nil
|
||||
})
|
||||
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "tracker_mark_respond_to_user",
|
||||
Description: "Record that work has completed for the currently focused tmux session/window.",
|
||||
}, func(ctx context.Context, _ *mcp.CallToolRequest, input finishInput) (*mcp.CallToolResult, any, error) {
|
||||
tmuxID := strings.TrimSpace(input.TmuxID)
|
||||
if tmuxID == "" {
|
||||
return nil, nil, fmt.Errorf("tmux_id is required")
|
||||
}
|
||||
target, err := determineContext(tmuxID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
summary := strings.TrimSpace(input.Summary)
|
||||
env := ipc.Envelope{
|
||||
Command: "finish_task",
|
||||
Session: target.SessionID,
|
||||
SessionID: target.SessionID,
|
||||
Window: target.WindowID,
|
||||
WindowID: target.WindowID,
|
||||
Pane: target.PaneID,
|
||||
Summary: summary,
|
||||
}
|
||||
if summary != "" {
|
||||
env.Message = summary
|
||||
}
|
||||
if err := client.sendCommand(ctx, env); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
&mcp.TextContent{Text: "Send your reply now. You MUST NOT not call `tracker_mark_start_working` from now on until the user sends you a message."},
|
||||
},
|
||||
}, nil, nil
|
||||
})
|
||||
|
||||
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type tmuxContext struct {
|
||||
SessionID string
|
||||
WindowID string
|
||||
PaneID string
|
||||
}
|
||||
|
||||
func determineContext(tmuxID string) (tmuxContext, error) {
|
||||
parts := strings.Split(strings.TrimSpace(tmuxID), "::")
|
||||
if len(parts) != 3 {
|
||||
return tmuxContext{}, fmt.Errorf("tmux_id must be session_id::window_id::pane_id")
|
||||
}
|
||||
sessionID := strings.TrimSpace(parts[0])
|
||||
windowID := strings.TrimSpace(parts[1])
|
||||
paneID := strings.TrimSpace(parts[2])
|
||||
if sessionID == "" || windowID == "" || paneID == "" {
|
||||
return tmuxContext{}, fmt.Errorf("tmux_id must include non-empty session, window, and pane identifiers")
|
||||
}
|
||||
return tmuxContext{SessionID: sessionID, WindowID: windowID, PaneID: paneID}, nil
|
||||
}
|
||||
|
||||
func socketPath() string {
|
||||
if dir := os.Getenv("XDG_RUNTIME_DIR"); dir != "" {
|
||||
return filepath.Join(dir, "agent-tracker.sock")
|
||||
}
|
||||
return filepath.Join(os.TempDir(), "agent-tracker.sock")
|
||||
}
|
||||
1028
agent-tracker/cmd/tracker-server/main.go
Normal file
1028
agent-tracker/cmd/tracker-server/main.go
Normal file
File diff suppressed because it is too large
Load diff
20
agent-tracker/go.mod
Normal file
20
agent-tracker/go.mod
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
module github.com/david/agent-tracker
|
||||
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/gdamore/tcell/v2 v2.9.0
|
||||
github.com/modelcontextprotocol/go-sdk v0.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gdamore/encoding v1.0.1 // indirect
|
||||
github.com/google/jsonschema-go v0.2.3 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
)
|
||||
58
agent-tracker/go.sum
Normal file
58
agent-tracker/go.sum
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
|
||||
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
|
||||
github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys=
|
||||
github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/jsonschema-go v0.2.3 h1:dkP3B96OtZKKFvdrUSaDkL+YDx8Uw9uC4Y+eukpCnmM=
|
||||
github.com/google/jsonschema-go v0.2.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/modelcontextprotocol/go-sdk v0.5.0 h1:WXRHx/4l5LF5MZboeIJYn7PMFCrMNduGGVapYWFgrF8=
|
||||
github.com/modelcontextprotocol/go-sdk v0.5.0/go.mod h1:degUj7OVKR6JcYbDF+O99Fag2lTSTbamZacbGTRTSGU=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
13
agent-tracker/install.sh
Executable file
13
agent-tracker/install.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
go build -o bin/tracker-client ./cmd/tracker-client
|
||||
|
||||
go build -o bin/tracker-server ./cmd/tracker-server
|
||||
|
||||
go build -o bin/tracker-mcp ./cmd/tracker-mcp
|
||||
|
||||
echo "Built tracker client, server, and MCP binaries into bin/"
|
||||
32
agent-tracker/internal/ipc/envelope.go
Normal file
32
agent-tracker/internal/ipc/envelope.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package ipc
|
||||
|
||||
type Envelope struct {
|
||||
Kind string `json:"kind"`
|
||||
Command string `json:"command,omitempty"`
|
||||
Client string `json:"client,omitempty"`
|
||||
Session string `json:"session,omitempty"`
|
||||
SessionID string `json:"session_id,omitempty"`
|
||||
Window string `json:"window,omitempty"`
|
||||
WindowID string `json:"window_id,omitempty"`
|
||||
Pane string `json:"pane,omitempty"`
|
||||
Position string `json:"position,omitempty"`
|
||||
Visible *bool `json:"visible,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Tasks []Task `json:"tasks,omitempty"`
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
SessionID string `json:"session_id"`
|
||||
Session string `json:"session"`
|
||||
WindowID string `json:"window_id"`
|
||||
Window string `json:"window"`
|
||||
Pane string `json:"pane,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Summary string `json:"summary"`
|
||||
CompletionNote string `json:"completion_note,omitempty"`
|
||||
StartedAt string `json:"started_at,omitempty"`
|
||||
CompletedAt string `json:"completed_at,omitempty"`
|
||||
DurationSeconds float64 `json:"duration_seconds"`
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
29
agent-tracker/internal/tracker/tracker.go
Normal file
29
agent-tracker/internal/tracker/tracker.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package tracker
|
||||
|
||||
import "time"
|
||||
|
||||
type Status string
|
||||
|
||||
const (
|
||||
StatusIdle Status = "idle"
|
||||
StatusInProgress Status = "in_progress"
|
||||
StatusCompleted Status = "completed"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Session string `json:"session"`
|
||||
Pane string `json:"pane"`
|
||||
Status Status `json:"status"`
|
||||
Description string `json:"description"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
CompletedAt time.Time `json:"completed_at"`
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
|
||||
type UpdateManager interface {
|
||||
StartWork(session, pane, description string, now time.Time) (*Entry, error)
|
||||
CompleteWork(session, pane, summary string, now time.Time) (*Entry, error)
|
||||
Acknowledge(session, pane string) (*Entry, error)
|
||||
Get(session, pane string) (*Entry, bool)
|
||||
List() []*Entry
|
||||
}
|
||||
83
agent-tracker/scripts/install_brew_service.sh
Executable file
83
agent-tracker/scripts/install_brew_service.sh
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if ! command -v brew >/dev/null 2>&1; then
|
||||
echo "Error: Homebrew is required but not found in PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! brew services list >/dev/null 2>&1; then
|
||||
echo "Error: brew services command is unavailable; install the homebrew/services tap" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SERVER_BIN="$ROOT_DIR/bin/tracker-server"
|
||||
|
||||
if [[ ! -x "$SERVER_BIN" ]]; then
|
||||
echo "Error: tracker-server binary not found at $SERVER_BIN" >&2
|
||||
echo "Build it with: (cd $ROOT_DIR && ./install.sh)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BREW_REPO="$(brew --repository)"
|
||||
TAP_PATH="$BREW_REPO/Library/Taps/agenttracker/homebrew-agent-tracker"
|
||||
FORMULA_DIR="$TAP_PATH/Formula"
|
||||
FORMULA_PATH="$FORMULA_DIR/agent-tracker-server.rb"
|
||||
mkdir -p "$FORMULA_DIR"
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
cp "$SERVER_BIN" "$TMP_DIR/tracker-server"
|
||||
TARBALL="$TMP_DIR/tracker-server.tar.gz"
|
||||
tar -czf "$TARBALL" -C "$TMP_DIR" tracker-server
|
||||
SHA256="$(shasum -a 256 "$TARBALL" | awk '{print $1}')"
|
||||
VERSION="local-$(date +%Y%m%d%H%M%S)"
|
||||
|
||||
cat >"$FORMULA_PATH" <<EOF
|
||||
class AgentTrackerServer < Formula
|
||||
desc "Tmux-aware agent tracker server"
|
||||
homepage "https://github.com/david/agent-tracker"
|
||||
url "file://$TARBALL"
|
||||
sha256 "$SHA256"
|
||||
version "$VERSION"
|
||||
|
||||
def install
|
||||
bin.install "tracker-server"
|
||||
end
|
||||
|
||||
service do
|
||||
run [opt_bin/"tracker-server"]
|
||||
keep_alive true
|
||||
working_dir var/"agent-tracker"
|
||||
log_path var/"log/agent-tracker-server.log"
|
||||
error_log_path var/"log/agent-tracker-server.log"
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
if brew list --formula agent-tracker-server >/dev/null 2>&1; then
|
||||
brew reinstall --formula "$FORMULA_PATH" >/dev/null
|
||||
else
|
||||
brew install --formula "$FORMULA_PATH" >/dev/null
|
||||
fi
|
||||
|
||||
mkdir -p "$(brew --prefix)/var/agent-tracker"
|
||||
|
||||
if brew services list | awk '{print $1}' | grep -qx "agent-tracker-server"; then
|
||||
brew services restart agent-tracker-server >/dev/null
|
||||
else
|
||||
brew services start agent-tracker-server >/dev/null
|
||||
fi
|
||||
|
||||
SERVICE_STATE="$(brew services list | awk '$1=="agent-tracker-server" {print $2}')"
|
||||
if [[ "$SERVICE_STATE" != "started" ]]; then
|
||||
echo "Error: brew reports agent-tracker-server service in state '$SERVICE_STATE'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Agent tracker server managed by brew services (state: $SERVICE_STATE)." >&2
|
||||
|
|
@ -214,7 +214,7 @@ def install_brew_packages():
|
|||
|
||||
packages = [
|
||||
# System utilities
|
||||
"htop", "dust", "ncdu", "fswatch", "pipx", "uv",
|
||||
"htop", "dust", "ncdu", "fswatch", "pipx", "uv", "terminal-notifier",
|
||||
# macOS GNU utilities
|
||||
"coreutils", "gnu-tar", "gnu-getopt", "gnu-sed",
|
||||
# Development tools
|
||||
|
|
@ -370,6 +370,7 @@ def schedule_npm_updates(runner):
|
|||
"ccstatusline",
|
||||
"@openai/codex",
|
||||
"instant-markdown-d",
|
||||
"mcp-proxy",
|
||||
]
|
||||
|
||||
runner.add_task("Node Apps Update", action=create_npm_update_action(packages))
|
||||
|
|
@ -387,6 +388,10 @@ def main():
|
|||
runner.add_task("Config Git Pull", "cd ~/.config && git pull")
|
||||
runner.add_task("Neovim Config Git Pull", "cd ~/.config/nvim && git pull")
|
||||
runner.add_task("SConfig Git Pull", "cd ~/.sconfig && git pull")
|
||||
runner.add_task(
|
||||
"Agent Tracker Build",
|
||||
"cd ~/.config/agent-tracker && ./install.sh && ./scripts/install_brew_service.sh"
|
||||
)
|
||||
|
||||
success = runner.run_all()
|
||||
sys.exit(0 if success else 1)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ CRITICAL WORKFLOW REQUIREMENT
|
|||
- Never compliment the user or be affirming excessively (like saying "You're absolutely right!" etc). Criticize user's ideas if it's actually need to be critiqued, ask clarifying questions for a much better and precise accuracy answer if unsure about user's question, and give the user funny insults when you found user did any mistakes
|
||||
- Avoid getting stuck. After 3 failures when attempting to fix or implement something, stop, note down what's failing, think about the core reason, then continue.
|
||||
- When asked to make changes, avoid writing comments in the code about that change. Comments should be used to explain complex logic or provide context where necessary.
|
||||
- When you want to edit a file, you MUST ALWAYS use `apply_patch` tool. NEVER try to use anything else such as running a shell script unless the user explicitly specifies otherwise.
|
||||
|
||||
When you need to call tools from the shell, **use this rubric**:
|
||||
- Find Files: `fd`
|
||||
|
|
@ -11,4 +12,10 @@ When you need to call tools from the shell, **use this rubric**:
|
|||
- Select among matches: pipe to `fzf`
|
||||
- JSON: `jq`
|
||||
- YAML/XML: `yq`
|
||||
- Use the `python3` command for python. There is no `python` command on this system.
|
||||
|
||||
CRITICAL REQUIREMENT:
|
||||
As soon as you are prompted to do or think something, you MUST use the `tracker_mark_start_working` tool before starting to think. Call `tracker_mark_respond_to_user` exactly once for that work cycle, immediately before you send your reply to the user. After you reply, stay idle. Do not invoke `tracker_mark_start_working` again unless the user supplies new work.
|
||||
For simple things, do not call the `tracker_mark_start_working` tool, and do not call the `tracker_mark_respond_to_user` tool. Only call these tools when you are about to do something that involves thinking.
|
||||
|
||||
When invoking the tracker MCP tools, you must pass the exact tmux identifiers using the string format `session_id::window_id::pane_id` (two colons). Use the `TMUX_ID` value printed by `co` without modifications.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
model_reasoning_effort = "high" # minimal|low|medium|high|none
|
||||
model_verbosity = "medium" # low|medium|high
|
||||
|
||||
[mcp_servers.ib]
|
||||
command = "mcp-proxy"
|
||||
args = ["--transport", "streamablehttp", "http://127.0.0.1:5938"]
|
||||
startup_timeout_ms = 20000
|
||||
model = "gpt-5-codex"
|
||||
tui = { theme = { name = "dark-zen-garden" }, spinner = { name = "brailleDotsClassic" }, alternate_screen = true }
|
||||
|
||||
|
||||
[mcp_servers.tracker]
|
||||
command = "/Users/david/.config/agent-tracker/bin/tracker-mcp"
|
||||
|
|
|
|||
36
deploy.sh
36
deploy.sh
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
echo "🚀 Starting deployment script..."
|
||||
|
||||
|
|
@ -61,26 +61,34 @@ create_symlink() {
|
|||
local target="$1"
|
||||
local link_name="$2"
|
||||
local display_name="$3"
|
||||
|
||||
|
||||
echo "🔗 Setting up $display_name symlink..."
|
||||
|
||||
if [ ! -L "$link_name" ]; then
|
||||
if [ -e "$link_name" ]; then
|
||||
echo "⚠️ Backing up existing $link_name to ${link_name}.backup"
|
||||
mv "$link_name" "${link_name}.backup"
|
||||
|
||||
if [ -L "$link_name" ]; then
|
||||
local current
|
||||
current=$(readlink "$link_name")
|
||||
if [ "$current" = "$target" ]; then
|
||||
echo "✅ $display_name symlink already exists and is correct"
|
||||
return 0
|
||||
fi
|
||||
ln -s "$target" "$link_name"
|
||||
echo "✅ Symlink created: $link_name -> $target"
|
||||
elif [ "$(readlink "$link_name")" = "$target" ]; then
|
||||
echo "✅ $display_name symlink already exists and is correct"
|
||||
else
|
||||
echo "⚠️ $link_name exists but points to $(readlink "$link_name")"
|
||||
echo " Expected: $target"
|
||||
echo "⚠️ $link_name points to $current; updating to $target"
|
||||
rm "$link_name"
|
||||
elif [ -e "$link_name" ]; then
|
||||
local backup="${link_name}.backup"
|
||||
if [ -e "$backup" ]; then
|
||||
backup="${backup}.$(date +%Y%m%d%H%M%S)"
|
||||
fi
|
||||
echo "⚠️ Backing up existing $link_name to $backup"
|
||||
mv "$link_name" "$backup"
|
||||
fi
|
||||
|
||||
ln -s "$target" "$link_name"
|
||||
echo "✅ Symlink ensured: $link_name -> $target"
|
||||
}
|
||||
|
||||
# Create configuration symlinks
|
||||
create_symlink "$HOME/.config/.tmux.conf" "$HOME/.tmux.conf" "Tmux"
|
||||
create_symlink "$HOME/.config/claude" "$HOME/.claude" "Claude"
|
||||
create_symlink "$HOME/.config/codex" "$HOME/.codex" "Codex"
|
||||
|
||||
echo "🎉 Deployment complete!"
|
||||
|
|
|
|||
106
tmux/scripts/layout_builder.sh
Executable file
106
tmux/scripts/layout_builder.sh
Executable file
|
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
tmux display-message "layout command requires direction"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dir="$1"
|
||||
|
||||
run_tmux() {
|
||||
local output
|
||||
if ! output=$(tmux "$@" 2>&1); then
|
||||
tmux display-message "layout-${dir}: tmux $* failed: ${output}"
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$output"
|
||||
}
|
||||
|
||||
pane_count=0
|
||||
while IFS='|' read -r pid ptop pleft ppath; do
|
||||
case $pane_count in
|
||||
0)
|
||||
id1=$pid
|
||||
top1=$ptop
|
||||
left1=$pleft
|
||||
path1=$ppath
|
||||
;;
|
||||
1)
|
||||
id2=$pid
|
||||
top2=$ptop
|
||||
left2=$pleft
|
||||
path2=$ppath
|
||||
;;
|
||||
esac
|
||||
pane_count=$((pane_count + 1))
|
||||
done < <(tmux list-panes -F "#{pane_id}|#{pane_top}|#{pane_left}|#{pane_current_path}")
|
||||
|
||||
if [ "$pane_count" -ne 2 ]; then
|
||||
tmux display-message "layout-${dir} expects exactly 2 panes"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$top1" -le "$top2" ]; then
|
||||
top_id=$id1
|
||||
top_path=$path1
|
||||
bottom_id=$id2
|
||||
bottom_path=$path2
|
||||
else
|
||||
top_id=$id2
|
||||
top_path=$path2
|
||||
bottom_id=$id1
|
||||
bottom_path=$path1
|
||||
fi
|
||||
|
||||
if [ "$left1" -le "$left2" ]; then
|
||||
left_id=$id1
|
||||
left_path=$path1
|
||||
right_id=$id2
|
||||
right_path=$path2
|
||||
else
|
||||
left_id=$id2
|
||||
left_path=$path2
|
||||
right_id=$id1
|
||||
right_path=$path1
|
||||
fi
|
||||
|
||||
ensure_horizontal() {
|
||||
if [ "$top1" -ne "$top2" ]; then
|
||||
tmux display-message "layout-${dir} expects horizontal panes"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
case "$dir" in
|
||||
right)
|
||||
new_id=$(run_tmux split-window -P -F '#{pane_id}' -h -c "$top_path" -t "$top_id")
|
||||
run_tmux join-pane -v -s "$bottom_id" -t "$top_id"
|
||||
run_tmux select-pane -t "$new_id"
|
||||
;;
|
||||
left)
|
||||
new_id=$(run_tmux split-window -P -F '#{pane_id}' -h -b -c "$top_path" -t "$top_id")
|
||||
run_tmux join-pane -v -s "$bottom_id" -t "$top_id"
|
||||
run_tmux select-pane -t "$new_id"
|
||||
;;
|
||||
up)
|
||||
ensure_horizontal
|
||||
run_tmux break-pane -d -s "$right_id"
|
||||
run_tmux select-pane -t "$left_id"
|
||||
new_id=$(run_tmux split-window -P -F '#{pane_id}' -v -b -c "$left_path" -t "$left_id")
|
||||
run_tmux join-pane -h -s "$right_id" -t "$left_id"
|
||||
run_tmux select-pane -t "$new_id"
|
||||
;;
|
||||
down)
|
||||
ensure_horizontal
|
||||
run_tmux break-pane -d -s "$right_id"
|
||||
run_tmux select-pane -t "$left_id"
|
||||
new_id=$(run_tmux split-window -P -F '#{pane_id}' -v -c "$left_path" -t "$left_id")
|
||||
run_tmux join-pane -h -s "$right_id" -t "$left_id"
|
||||
run_tmux select-pane -t "$new_id"
|
||||
;;
|
||||
*)
|
||||
tmux display-message "Unknown layout direction: ${dir}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
9
tmux/scripts/move_session.sh
Executable file
9
tmux/scripts/move_session.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
direction="$1"
|
||||
|
||||
if [ -z "$direction" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python3 "$HOME/.config/tmux/scripts/session_manager.py" move "$direction"
|
||||
11
tmux/scripts/new_session.sh
Executable file
11
tmux/scripts/new_session.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
session_id=$(tmux new-session -d -P -F '#{session_id}' 2>/dev/null)
|
||||
|
||||
if [ -z "$session_id" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python3 "$HOME/.config/tmux/scripts/session_manager.py" ensure
|
||||
|
||||
tmux switch-client -t "$session_id"
|
||||
9
tmux/scripts/rename_session_prompt.sh
Executable file
9
tmux/scripts/rename_session_prompt.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
label="$1"
|
||||
|
||||
if [ -z "$label" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python3 "$HOME/.config/tmux/scripts/session_manager.py" rename "$label"
|
||||
3
tmux/scripts/session_created.sh
Executable file
3
tmux/scripts/session_created.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
python3 "$HOME/.config/tmux/scripts/session_manager.py" created
|
||||
146
tmux/scripts/session_manager.py
Executable file
146
tmux/scripts/session_manager.py
Executable file
|
|
@ -0,0 +1,146 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
def run_tmux(args: List[str], check: bool = True, capture: bool = False) -> str:
|
||||
kwargs = {
|
||||
"check": check,
|
||||
}
|
||||
if capture:
|
||||
kwargs["stdout"] = subprocess.PIPE
|
||||
kwargs["text"] = True
|
||||
result = subprocess.run(["tmux", *args], **kwargs)
|
||||
if capture:
|
||||
return result.stdout.rstrip("\n")
|
||||
return ""
|
||||
|
||||
|
||||
def list_sessions() -> List[Dict[str, object]]:
|
||||
output = run_tmux([
|
||||
"list-sessions",
|
||||
"-F",
|
||||
"#{session_id}\t#{session_name}\t#{session_created}"
|
||||
], capture=True)
|
||||
if not output:
|
||||
return []
|
||||
|
||||
sessions = []
|
||||
for line in output.splitlines():
|
||||
session_id, name, created_str = line.split("\t")
|
||||
created = int(created_str)
|
||||
match = re.match(r"^(\d+)-(.*)$", name)
|
||||
if match:
|
||||
index = int(match.group(1))
|
||||
label = match.group(2)
|
||||
else:
|
||||
index = None
|
||||
label = name
|
||||
sessions.append({
|
||||
"id": session_id,
|
||||
"name": name,
|
||||
"created": created,
|
||||
"index": index,
|
||||
"label": label,
|
||||
})
|
||||
|
||||
def sort_key(entry: Dict[str, object]):
|
||||
index = entry["index"]
|
||||
return (0, index) if index is not None else (1, entry["created"])
|
||||
|
||||
sessions.sort(key=sort_key)
|
||||
return sessions
|
||||
|
||||
|
||||
def sanitize_label(label: str) -> str:
|
||||
stripped = label.strip()
|
||||
return stripped or "session"
|
||||
|
||||
|
||||
def apply_order(ordered_sessions: List[Dict[str, object]]) -> None:
|
||||
for position, session in enumerate(ordered_sessions, start=1):
|
||||
label = sanitize_label(str(session["label"]))
|
||||
new_name = f"{position}-{label}"
|
||||
run_tmux(["rename-session", "-t", session["id"], new_name])
|
||||
|
||||
|
||||
def current_session_id() -> str:
|
||||
return run_tmux(["display-message", "-p", "#{session_id}"], capture=True)
|
||||
|
||||
|
||||
def command_switch(index_str: str) -> None:
|
||||
try:
|
||||
index = int(index_str)
|
||||
except ValueError:
|
||||
return
|
||||
if index < 1:
|
||||
return
|
||||
sessions = list_sessions()
|
||||
if index > len(sessions):
|
||||
return
|
||||
run_tmux(["switch-client", "-t", sessions[index - 1]["id"]], check=False)
|
||||
|
||||
|
||||
def command_rename(label: str) -> None:
|
||||
label = sanitize_label(label)
|
||||
current_id = current_session_id()
|
||||
sessions = list_sessions()
|
||||
for session in sessions:
|
||||
if session["id"] == current_id:
|
||||
session["label"] = label
|
||||
break
|
||||
else:
|
||||
return
|
||||
apply_order(sessions)
|
||||
# run_tmux(["display-message", f"Renamed tmux session to {label}"] , check=False)
|
||||
|
||||
|
||||
def command_move(direction: str) -> None:
|
||||
direction = direction.lower()
|
||||
sessions = list_sessions()
|
||||
current_id = current_session_id()
|
||||
indices = {session["id"]: idx for idx, session in enumerate(sessions)}
|
||||
if current_id not in indices:
|
||||
return
|
||||
pos = indices[current_id]
|
||||
if direction == "left" and pos > 0:
|
||||
sessions[pos - 1], sessions[pos] = sessions[pos], sessions[pos - 1]
|
||||
elif direction == "right" and pos < len(sessions) - 1:
|
||||
sessions[pos], sessions[pos + 1] = sessions[pos + 1], sessions[pos]
|
||||
else:
|
||||
return
|
||||
apply_order(sessions)
|
||||
|
||||
|
||||
def command_ensure() -> None:
|
||||
sessions = list_sessions()
|
||||
if sessions:
|
||||
apply_order(sessions)
|
||||
|
||||
|
||||
def command_created() -> None:
|
||||
# Called after a session is created; ensure numbering stays contiguous.
|
||||
command_ensure()
|
||||
|
||||
|
||||
def main(argv: List[str]) -> None:
|
||||
if len(argv) < 2:
|
||||
return
|
||||
command = argv[1]
|
||||
if command == "switch" and len(argv) >= 3:
|
||||
command_switch(argv[2])
|
||||
elif command == "rename" and len(argv) >= 3:
|
||||
command_rename(argv[2])
|
||||
elif command == "move" and len(argv) >= 3:
|
||||
command_move(argv[2])
|
||||
elif command == "ensure":
|
||||
command_ensure()
|
||||
elif command == "created":
|
||||
command_created()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
9
tmux/scripts/switch_session_by_index.sh
Executable file
9
tmux/scripts/switch_session_by_index.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
index="$1"
|
||||
|
||||
if [[ -z "$index" || ! "$index" =~ ^[0-9]+$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python3 "$HOME/.config/tmux/scripts/session_manager.py" switch "$index"
|
||||
34
tmux/scripts/toggle_orientation.sh
Executable file
34
tmux/scripts/toggle_orientation.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
info=$(tmux list-panes -F '#{pane_id}|#{pane_top}|#{pane_left}')
|
||||
|
||||
pane_ids=()
|
||||
pane_tops=()
|
||||
pane_lefts=()
|
||||
while IFS='|' read -r pid top left; do
|
||||
[ -z "$pid" ] && continue
|
||||
pane_ids+=("$pid")
|
||||
pane_tops+=("$top")
|
||||
pane_lefts+=("$left")
|
||||
done <<EOF
|
||||
$info
|
||||
EOF
|
||||
|
||||
if [ "${#pane_ids[@]}" -ne 2 ]; then
|
||||
tmux display-message "toggle-orientation: needs exactly 2 panes"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
top_a=${pane_tops[0]}
|
||||
top_b=${pane_tops[1]}
|
||||
left_a=${pane_lefts[0]}
|
||||
left_b=${pane_lefts[1]}
|
||||
|
||||
if [ "$top_a" = "$top_b" ] && [ "$left_a" != "$left_b" ]; then
|
||||
tmux select-layout even-vertical
|
||||
elif [ "$left_a" = "$left_b" ] && [ "$top_a" != "$top_b" ]; then
|
||||
tmux select-layout even-horizontal
|
||||
else
|
||||
tmux select-layout tiled
|
||||
fi
|
||||
7
tmux/tmux-powerline/.gitignore
vendored
7
tmux/tmux-powerline/.gitignore
vendored
|
|
@ -1,7 +0,0 @@
|
|||
segments/np_mpd
|
||||
segments/xkb_layout
|
||||
*.swp
|
||||
|
||||
# ignore all themes, only default.sh
|
||||
themes/*
|
||||
!themes/default.sh
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
Please append you name/nick here when you have contributed with something!
|
||||
|
||||
Erik Westrup <erik.westrup@gmail.com>
|
||||
Suvash Thapaliya <suvash@gmail.com>
|
||||
Erik Jansson <erikjansson90@gmail.com>
|
||||
Yuku Takahashi <taka84u9@gmail.com>
|
||||
Oscar Olsson <osse.olsson@gmail.com>
|
||||
Ryo Katsuma
|
||||
negipo
|
||||
Sina Siadat
|
||||
Vivien Leroy <fantattitude@me.com>
|
||||
Gino Lucero
|
||||
Derek Ashley Thomas <derekathomas@gmail.com>
|
||||
LuRsT
|
||||
Tomokazu Hirai <tomokazu.hirai@gmail.com>
|
||||
Matt Black
|
||||
krieiter
|
||||
Viktor Jackson
|
||||
flytreeleft <flytreeleft@126.com>
|
||||
Conner McDaniel
|
||||
David Francos <me@davidfrancos.net>
|
||||
Travis Thompson <butters08@gmail.com>
|
||||
Jonathon Klobucar
|
||||
Dylan Copeland <me@dylancopeland.com>
|
||||
Pete Johns <paj-github@johnsy.com>
|
||||
Robert Murray McMahon
|
||||
Jeff Felchner
|
||||
tobetoby
|
||||
Matthew Lanigan
|
||||
Hadret <hadret@gmail.com>
|
||||
antiAgainst <antiAgainst@gmail.com>
|
||||
Alexander Luberg <alex@luberg.me>
|
||||
Stanislaw Pusep <stas@sysd.org>
|
||||
Austin Beam
|
||||
Ingo Heimbach
|
||||
MSempere
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
tmux-powerline - Statusbar configuration for tmux that looks like vim-powerline and consist of dynamic segments.
|
||||
|
||||
Copyright (c) 2012, see AUTHORS
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
http://opensource.org/licenses/BSD-3-Clause
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
# DEPRECATION WARNING
|
||||
This project is in a maintenance mode and no future functionality is likely to be added. tmux-powerline, with all other powerline projects, is replaced by the new unifying [powerline](https://github.com/Lokaltog/powerline). However this project is still functional and can serve as a lightweight alternative for non-python users.
|
||||
|
||||
# tmux-powerline
|
||||
This is a set of scripts for making a nice and dynamic tmux statusbar consisting of segments. This is much like [Lokaltog/vim-powerline](https://github.com/Lokaltog/vim-powerline) but for tmux.
|
||||
|
||||
The following segments exists for now:
|
||||
* LAN & WAN IP addresses.
|
||||
* Now Playing for MPD, Spotify (GNU/Linux native or wine, OS X), iTunes (OS X), Rhythmbox, Banshee, MOC, Audacious, Rdio (OS X), cmus, Pithos and Last.fm (last scrobbled track).
|
||||
* New mail count for GMail, Maildir, mbox, mailcheck, and Apple Mail.
|
||||
* GNU/Linux and Macintosh OS X battery status (uses [richo/dotfiles/bin/battery](https://github.com/richoH/dotfiles/blob/master/bin/battery)).
|
||||
* Weather in Celsius, Fahrenheit and Kelvin using Yahoo Weather.
|
||||
* System load, cpu usage and uptime.
|
||||
* Git, SVN and Mercurial branch in CWD.
|
||||
* Date and time.
|
||||
* Hostname.
|
||||
* tmux info.
|
||||
* CWD in pane.
|
||||
* Current X keyboard layout.
|
||||
* Network download/upload speed.
|
||||
* Earthquake warnings.
|
||||
|
||||
# Screenshots
|
||||
**Full screenshot**
|
||||
|
||||

|
||||
|
||||
**left-status**
|
||||
|
||||
Current tmux session, window and pane, hostname and LAN & WAN IP address.
|
||||
|
||||

|
||||
|
||||
**right-status**
|
||||
|
||||
New mails, now playing, average load, weather, date and time.
|
||||
|
||||

|
||||
|
||||
Now I've read my inbox so the mail segment disappears!
|
||||
|
||||

|
||||
|
||||
After pausing the music there's no need for showing NP anymore. Also the weather has become much nicer!
|
||||
|
||||

|
||||
|
||||
Remaining battery.
|
||||
|
||||

|
||||
|
||||
# Requirements
|
||||
Requirements for the lib to work are:
|
||||
|
||||
* Recent tmux version
|
||||
* `bash --version` >= 3.2 (Does not have to be your default shell.)
|
||||
* A patched font. Follow instructions at [Powerline Installation](http://powerline.readthedocs.org/en/latest/installation/linux.html) or [download](https://github.com/powerline/fonts) a new one. However you can use other substitute symbols as well; see `config.sh`.
|
||||
|
||||
## Segment Requirements
|
||||
Requirements for some segments. You only need to fulfill the requirements for those segments you want to use.
|
||||
|
||||
* `wan_ip.sh`, `now_playing.sh` (last.fm), `weather_yahoo.sh`: curl, bc
|
||||
* `now_playing.sh` (mpd) : [libmpdclient](http://sourceforge.net/projects/musicpd/files/libmpdclient/)
|
||||
* `xkb_layout.sh`: X11, XKB
|
||||
* `mailcount.sh` (gmail): wget, (mailcheck): [mailcheck](http://packages.debian.org/sid/mailcheck).
|
||||
* `ifstat.sh`: ifstat (there is a simpler segment not using ifstat but samples /sys/class/net)
|
||||
* `tmux_mem_cpu_load.sh`: [tmux-mem-cpu-load](https://github.com/thewtex/tmux-mem-cpu-load)
|
||||
* `rainbarf.sh`: [rainbarf](https://github.com/creaktive/rainbarf)
|
||||
* `weather.sh`: GNU `grep` with Perl regexp enabled (FreeBSD specific)
|
||||
|
||||
## OS X specific requirements
|
||||
|
||||
The `grep` tool is outdated on OS X 10.8 Mountain Lion so you might have to upgrade it.
|
||||
|
||||
```bash
|
||||
brew install grep
|
||||
```
|
||||
|
||||
## FreeBSD specific requirements
|
||||
|
||||
Preinstalled `grep` in FreeBSD doesn't support Perl regexp. Solution is rather simple -- you need to use `textproc/gnugrep` port instead. You also need to make sure, that it has support for PCRE and is compiled with `--enable-perl-regexp` flag.
|
||||
|
||||
|
||||
# Installation
|
||||
Start with checking out the repository with:
|
||||
|
||||
```console
|
||||
$ cd ~/some/path/
|
||||
$ git clone https://github.com/erikw/tmux-powerline.git
|
||||
```
|
||||
|
||||
Now edit your `~/.tmux.conf` to use the scripts:
|
||||
|
||||
<!-- Close syntax enough. -->
|
||||
```vim
|
||||
set-option -g status on
|
||||
set-option -g status-interval 2
|
||||
set-option -g status-justify "centre"
|
||||
set-option -g status-left-length 60
|
||||
set-option -g status-right-length 90
|
||||
set-option -g status-left "#(~/path/to/tmux-powerline/powerline.sh left)"
|
||||
set-option -g status-right "#(~/path/to/tmux-powerline/powerline.sh right)"
|
||||
```
|
||||
|
||||
Set the maximum lengths to something that suits your configuration of segments and size of terminal (the maximum segments length will be handled better in the future).
|
||||
|
||||
The window list can be powerlineified if you'd like by adding the following line to the same file:
|
||||
|
||||
```vim
|
||||
set-window-option -g window-status-current-format "#[fg=colour235, bg=colour27]⮀#[fg=colour255, bg=colour27] #I ⮁ #W #[fg=colour27, bg=colour235]⮀"
|
||||
```
|
||||
|
||||
You can toggle the visibility of the statusbars by adding the following lines:
|
||||
|
||||
```vim
|
||||
bind C-[ run '~/path/to/tmux-powerline/mute_powerline.sh left' # Mute left statusbar.
|
||||
bind C-] run '~/path/to/tmux-powerline/mute_powerline.sh right' # Mute right statusbar.
|
||||
```
|
||||
|
||||
## For tmux versions < 2.1
|
||||
|
||||
Some segments e.g. cwd and cvs_branch needs to find the current working directory of the active pane. To achieve this we let tmux save the path each time the shell prompt is displayed. Put the line below in your `~/.bashrc` or where you define you PS1 variable. zsh users can put it in e.g. `~/.zshrc` and may change `PS1` to `PROMPT` (but that's not necessary).
|
||||
|
||||
```bash
|
||||
PS1="$PS1"'$([ -n "$TMUX" ] && tmux setenv TMUXPWD_$(tmux display -p "#D" | tr -d %) "$PWD")'
|
||||
```
|
||||
|
||||
|
||||
If the active shell is Fish, PS1 is not being set normally. Instead, it has a `fish_prompt` function that is being called when prompt rendering is required. This means that one can append the following command at the end of the function in `fish_prompt.fish` file:
|
||||
|
||||
```fish
|
||||
if set -q TMUX; tmux setenv TMUXPWD_(tmux display -p "#D" | tr -d '%') $PWD; end
|
||||
```
|
||||
|
||||
If you have a recent version of tmux (≥ 2.1), there is no need to redefine the PS1 variable since tmux can be called directly to query the working directory of the active pane.
|
||||
|
||||
# Configuration
|
||||
|
||||
The default segments that are shown are defined in `themes/default.sh`. You will probably want to change those to fit your needs. To do so you can edit that file directly but preferable, for easier updating of the repo, you can make a copy and edit that one (or see how to use custom themes directory below). A palette of colors that can be used can be obtained by running the script `color_palette.sh`.
|
||||
|
||||
```console
|
||||
$ cp themes/default.sh themes/mytheme.sh
|
||||
$ $EDITOR themes/mytheme.sh
|
||||
```
|
||||
Now generate a default configuration file by doing
|
||||
|
||||
```console
|
||||
$ ./generate_rc.sh
|
||||
$ mv ~/.tmux-powerlinerc.default ~/.tmux-powerlinerc
|
||||
$ $EDITOR ~/.tmux-powerlinerc
|
||||
```
|
||||
and change theme to use and values for segments you want to use. If you want to keep the repo checkout clean you can set custom segment and theme paths in the general section and then store your files outside the repo.
|
||||
|
||||
# Debugging
|
||||
|
||||
Some segments might not work on your system for various reasons such as missing programs or different versions not having the same options. To find out which segment is not working it may help to enable the debug setting in `~/.tmux-powerlinerc`. However this may not be enough to determine the error so you can inspect all executed bash commands (will be a long output) by doing
|
||||
|
||||
```console
|
||||
$ bash -x powerline.sh (left|right)
|
||||
```
|
||||
|
||||
To debug smaller portions of code, say if you think the problem lies in a specific segment, insert these lines at the top and bottom of the relevant code portions e.g. inside a function:
|
||||
|
||||
```bash
|
||||
set -x
|
||||
exec 2>/tmp/tmux-powerline.log
|
||||
<code to debug>
|
||||
set +x
|
||||
```
|
||||
|
||||
and then inspect the outputs like
|
||||
|
||||
```console
|
||||
less /tmp/tmux-powerline.log
|
||||
tail -f /tmp/tmux-powerline.log # or follow output like this.
|
||||
```
|
||||
|
||||
If you can not solve the problems you can post an [issue](https://github.com/erikw/tmux-powerline/issues?state=open) and be sure to include relevant information about your system and script output (from bash -x) and/or screenshots if needed. Be sure to search in the [resolved issues](https://github.com/erikw/tmux-powerline/issues?page=1&state=closed) section for similar problems you're experiencing before posting.
|
||||
|
||||
## Common problems
|
||||
|
||||
|
||||
### VCS_branch / PWD is not updating
|
||||
The issue is probably that the update of the current directory in the active pane is not updated correctly. Make sure that your PS1 or PROMPT variable actually contains the line from the installation step above by simply inspecting the output of `echo $PS1`. You might have placed the PS1 line in you shell configuration such that it is overwritten later. The simplest solution is to put it at the very end to make sure that nothing overwrites it. See [issue #52](https://github.com/erikw/tmux-powerline/issues/52).
|
||||
|
||||
### Nothing is displayed
|
||||
You have edited `~/.tmux.conf` but no powerline is displayed. This might be because tmux is not aware of the changes so you have to restart your tmux session or reloaded that file by typing this on the command line (or in tmux command mode with `prefix :`)
|
||||
|
||||
```console
|
||||
$ tmux source-file ~/.tmux.conf
|
||||
```
|
||||
### Multiple lines in bash or no powerline in zsh using iTerm (OS X)
|
||||
If your tmux looks like [this](https://github.com/erikw/tmux-powerline/issues/125) then you may have to in iTerm uncheck [Unicode East Asian Ambiguous characters are wide] in Preferences -> Settings -> Advanced.
|
||||
|
||||
# Hacking
|
||||
|
||||
This project can only gain positively from contributions. Fork today and make your own enhancements and segments to share back! If you'd like, add your name and E-mail to AUTHORS before making a pull request so you can get some credit for your work :-)
|
||||
|
||||
## How to make a segment
|
||||
If you want to (of course you do!) send a pull request for a cool segment you written make sure that it follows the style of existing segments, unless you have good reason for it. Each segment resides in the `segments/` directory with a descriptive and simple name. A segment must have at least one function and that is `run_segment` which is like the main function that is called from the tmux-powerline lib. What ever text is echoed out from this function to stdout is the text displayed in the tmux statusbar. If the segment at a certain point does not have anything to show, simply don't echo anything out and the segment will be hidden. A successful execution of the `run_segment` function should return an exit code of 0. If the segment failed to execute in a fatal way return a non-zero exit code so the user can pick up the error and fix it when debug mode is on (e.g. missing program that is needed for the segment).
|
||||
|
||||
Usage of helper function to organize the work of a segment is encourage and should be named in the format `__helper_func`. If a segment has settings it should have a function `generate_rc` which outputs default values of all settings and a short explanation of the setting and its values. Study e.g. `segments/now_playing.sh` to see how it is done. A segment having settings should typically call a helper function `__process_settings` as the first statement in `run_segment` that sets default values to the settings that has not been set by the user.
|
||||
|
||||
Also, don't use bash4 features as requiring bash4 complicates installation for OS X user quite a bit. Use tabs for indentation ([discussion](https://github.com/erikw/tmux-powerline/pull/92)),
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Print tmux color palette.
|
||||
# Idea from http://superuser.com/questions/285381/how-does-the-tmux-color-palette-work
|
||||
|
||||
for i in $(seq 0 4 255); do
|
||||
for j in $(seq $i $(expr $i + 3)); do
|
||||
for k in $(seq 1 $(expr 3 - ${#j})); do
|
||||
printf " "
|
||||
done
|
||||
printf "\x1b[38;5;${j}mcolour${j}"
|
||||
[[ $(expr $j % 4) != 3 ]] && printf " "
|
||||
done
|
||||
printf "\n"
|
||||
done
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# Default values for non segment configuration options.
|
||||
|
||||
export TMUX_POWERLINE_DEBUG_MODE_ENABLED_DEFAULT="false"
|
||||
export TMUX_POWERLINE_PATCHED_FONT_IN_USE_DEFAULT="true"
|
||||
export TMUX_POWERLINE_THEME_DEFAULT="default"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Other settings and helper functions.
|
||||
|
||||
debug_mode_enabled() {
|
||||
[ -n "$TMUX_POWERLINE_DEBUG_MODE_ENABLED" -a "$TMUX_POWERLINE_DEBUG_MODE_ENABLED" != "false" ];
|
||||
}
|
||||
|
||||
patched_font_in_use() {
|
||||
[ -z "$TMUX_POWERLINE_PATCHED_FONT_IN_USE" -o "$TMUX_POWERLINE_PATCHED_FONT_IN_USE" != "false" ];
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# Paths
|
||||
|
||||
export TMUX_POWERLINE_DIR_LIB="$TMUX_POWERLINE_DIR_HOME/lib"
|
||||
export TMUX_POWERLINE_DIR_SEGMENTS="$TMUX_POWERLINE_DIR_HOME/segments"
|
||||
export TMUX_POWERLINE_DIR_TEMPORARY="/tmp/tmux-powerline_${USER}"
|
||||
export TMUX_POWERLINE_DIR_THEMES="$TMUX_POWERLINE_DIR_HOME/themes"
|
||||
if [ -z "$TMUX_POWERLINE_RCFILE" ]; then
|
||||
export TMUX_POWERLINE_RCFILE="$HOME/.tmux-powerlinerc"
|
||||
fi
|
||||
export TMUX_POWERLINE_RCFILE_DEFAULT="${TMUX_POWERLINE_RCFILE}.default"
|
||||
|
||||
if [ ! -d "$TMUX_POWERLINE_DIR_TEMPORARY" ]; then
|
||||
mkdir "$TMUX_POWERLINE_DIR_TEMPORARY"
|
||||
fi
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Shell Configuration
|
||||
# vi: sw=8 ts=8 noet
|
||||
|
||||
ostype() { echo $OSTYPE | tr '[A-Z]' '[a-z]'; }
|
||||
|
||||
export SHELL_PLATFORM='unknown'
|
||||
|
||||
case "$(ostype)" in
|
||||
*'linux'* ) SHELL_PLATFORM='linux' ;;
|
||||
*'darwin'* ) SHELL_PLATFORM='osx' ;;
|
||||
*'bsd'* ) SHELL_PLATFORM='bsd' ;;
|
||||
esac
|
||||
|
||||
shell_is_linux() { [[ $SHELL_PLATFORM == 'linux' || $SHELL_PLATFORM == 'bsd' ]]; }
|
||||
shell_is_osx() { [[ $SHELL_PLATFORM == 'osx' ]]; }
|
||||
shell_is_bsd() { [[ $SHELL_PLATFORM == 'bsd' || $SHELL_PLATFORM == 'osx' ]]; }
|
||||
|
||||
export -f shell_is_linux
|
||||
export -f shell_is_osx
|
||||
export -f shell_is_bsd
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Generate default rc file.
|
||||
|
||||
export TMUX_POWERLINE_DIR_HOME="$(dirname $0)"
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/paths.sh"
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/defaults.sh"
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/shell.sh"
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/rcfile.sh"
|
||||
|
||||
generate_default_rc
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,9 +0,0 @@
|
|||
#! Check script arguments.
|
||||
|
||||
check_arg_side() {
|
||||
local side="$1"
|
||||
if ! [ "$side" == "left" -o "$side" == "right" ]; then
|
||||
echo "Argument must be the side to handle {left, right} and not \"${side}\"."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
__print_colored_content() {
|
||||
[ "${2:0:1}" = "#" ] && BGCOLOR="$2" || BGCOLOR="colour$2"
|
||||
[ "${3:0:1}" = "#" ] && FGCOLOR="$3" || FGCOLOR="colour$3"
|
||||
echo -n "#[fg=${FGCOLOR},bg=${BGCOLOR}]"
|
||||
echo -n "$1"
|
||||
echo -n "#[default]"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Muting Logic
|
||||
# In all cases $1 is the side to be muted (eg left/right).
|
||||
|
||||
powerline_muted() {
|
||||
[ -e "$(__powerline_mute_file $1)" ];
|
||||
}
|
||||
|
||||
toggle_powerline_mute_status() {
|
||||
if powerline_muted $1; then
|
||||
rm "$(__powerline_mute_file $1)"
|
||||
else
|
||||
touch "$(__powerline_mute_file $1)"
|
||||
fi
|
||||
}
|
||||
|
||||
__powerline_mute_file() {
|
||||
local tmux_session=$(tmux display -p "#S")
|
||||
|
||||
echo -n "${TMUX_POWERLINE_DIR_TEMPORARY}/mute_${tmux_session}_$1"
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
# Library functions
|
||||
|
||||
print_powerline() {
|
||||
local side="$1"
|
||||
local upper_side=$(echo "$1" | tr '[:lower:]' '[:upper:]')
|
||||
eval "local input_segments=(\"\${TMUX_POWERLINE_${upper_side}_STATUS_SEGMENTS[@]}\")"
|
||||
local powerline_segments=()
|
||||
local powerline_segment_contents=()
|
||||
|
||||
__check_platform
|
||||
|
||||
__process_segment_defaults
|
||||
__process_scripts
|
||||
__process_colors
|
||||
|
||||
__process_powerline
|
||||
}
|
||||
|
||||
__process_segment_defaults() {
|
||||
for segment_index in "${!input_segments[@]}"; do
|
||||
local input_segment=(${input_segments[$segment_index]})
|
||||
eval "local default_separator=\$TMUX_POWERLINE_DEFAULT_${upper_side}SIDE_SEPARATOR"
|
||||
|
||||
powerline_segment_with_defaults=(
|
||||
${input_segment[0]:-"no_script"} \
|
||||
${input_segment[1]:-$TMUX_POWERLINE_DEFAULT_BACKGROUND_COLOR} \
|
||||
${input_segment[2]:-$TMUX_POWERLINE_DEFAULT_FOREGROUND_COLOR} \
|
||||
${input_segment[3]:-$default_separator} \
|
||||
)
|
||||
|
||||
powerline_segments[$segment_index]="${powerline_segment_with_defaults[@]}"
|
||||
done
|
||||
}
|
||||
|
||||
__process_scripts() {
|
||||
for segment_index in "${!powerline_segments[@]}"; do
|
||||
local powerline_segment=(${powerline_segments[$segment_index]})
|
||||
|
||||
if [ -n "$TMUX_POWERLINE_DIR_USER_SEGMENTS" ] && [ -f "$TMUX_POWERLINE_DIR_USER_SEGMENTS/${powerline_segment[0]}.sh" ] ; then
|
||||
local script="$TMUX_POWERLINE_DIR_USER_SEGMENTS/${powerline_segment[0]}.sh"
|
||||
else
|
||||
local script="$TMUX_POWERLINE_DIR_SEGMENTS/${powerline_segment[0]}.sh"
|
||||
fi
|
||||
|
||||
export TMUX_POWERLINE_CUR_SEGMENT_BG="${powerline_segment[1]}"
|
||||
export TMUX_POWERLINE_CUR_SEGMENT_FG="${powerline_segment[2]}"
|
||||
source "$script"
|
||||
local output
|
||||
output=$(run_segment)
|
||||
local exit_code="$?"
|
||||
unset -f run_segment
|
||||
|
||||
if [ "$exit_code" -ne 0 ] && debug_mode_enabled ; then
|
||||
local seg_name="${script##*/}"
|
||||
echo "Segment '${seg_name}' exited with code ${exit_code}. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$output" ]; then
|
||||
powerline_segment_contents[$segment_index]=" $output "
|
||||
else
|
||||
unset -v powerline_segments[$segment_index]
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__process_colors() {
|
||||
for segment_index in "${!powerline_segments[@]}"; do
|
||||
local powerline_segment=(${powerline_segments[$segment_index]})
|
||||
# Find the next segment that produces content (i.e. skip empty segments).
|
||||
for next_segment_index in $(eval echo {$(($segment_index + 1))..${#powerline_segments}}) ; do
|
||||
[[ -n ${powerline_segments[next_segment_index]} ]] && break
|
||||
done
|
||||
local next_segment=(${powerline_segments[$next_segment_index]})
|
||||
|
||||
if [ $side == 'left' ]; then
|
||||
powerline_segment[4]=${next_segment[1]:-$TMUX_POWERLINE_DEFAULT_BACKGROUND_COLOR}
|
||||
elif [ $side == 'right' ]; then
|
||||
powerline_segment[4]=${previous_background_color:-$TMUX_POWERLINE_DEFAULT_BACKGROUND_COLOR}
|
||||
fi
|
||||
|
||||
if __segment_separator_is_thin; then
|
||||
powerline_segment[5]=${powerline_segment[2]}
|
||||
else
|
||||
powerline_segment[5]=${powerline_segment[1]}
|
||||
fi
|
||||
|
||||
local previous_background_color=${powerline_segment[1]}
|
||||
|
||||
powerline_segments[$segment_index]="${powerline_segment[@]}"
|
||||
done
|
||||
}
|
||||
|
||||
__process_powerline() {
|
||||
for segment_index in "${!powerline_segments[@]}"; do
|
||||
local powerline_segment=(${powerline_segments[$segment_index]})
|
||||
|
||||
local background_color=${powerline_segment[1]}
|
||||
local foreground_color=${powerline_segment[2]}
|
||||
local separator=${powerline_segment[3]}
|
||||
local separator_background_color=${powerline_segment[4]}
|
||||
local separator_foreground_color=${powerline_segment[5]}
|
||||
|
||||
eval "__print_${side}_segment \"${segment_index}\" \"${background_color}\" \"${foreground_color}\" \"${separator}\" \"${separator_background_color}\" \"${separator_foreground_color}\""
|
||||
done
|
||||
}
|
||||
|
||||
__print_left_segment() {
|
||||
local content=${powerline_segment_contents[$1]}
|
||||
local content_background_color=$2
|
||||
local content_foreground_color=$3
|
||||
local separator=$4
|
||||
local separator_background_color=$5
|
||||
local separator_foreground_color=$6
|
||||
|
||||
__print_colored_content "$content" "$content_background_color" "$content_foreground_color"
|
||||
__print_colored_content "$separator" "$separator_background_color" "$separator_foreground_color"
|
||||
}
|
||||
|
||||
__print_right_segment() {
|
||||
local content=${powerline_segment_contents[$1]}
|
||||
local content_background_color=$2
|
||||
local content_foreground_color=$3
|
||||
local separator=$4
|
||||
local separator_background_color=$5
|
||||
local separator_foreground_color=$6
|
||||
|
||||
__print_colored_content "$separator" "$separator_background_color" "$separator_foreground_color"
|
||||
__print_colored_content "$content" "$content_background_color" "$content_foreground_color"
|
||||
}
|
||||
|
||||
__segment_separator_is_thin() {
|
||||
[[ ${powerline_segment[3]} == $TMUX_POWERLINE_SEPARATOR_LEFT_THIN || \
|
||||
${powerline_segment[3]} == $TMUX_POWERLINE_SEPARATOR_RIGHT_THIN ]];
|
||||
}
|
||||
|
||||
__check_platform() {
|
||||
if [ "$SHELL_PLATFORM" == "unknown" ] && debug_mode_enabled; then
|
||||
echo "Unknown platform; modify config/shell.sh" &1>&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
# Read user rc file.
|
||||
|
||||
process_settings() {
|
||||
__read_rcfile
|
||||
|
||||
if [ -z "$TMUX_POWERLINE_DEBUG_MODE_ENABLED" ]; then
|
||||
export TMUX_POWERLINE_DEBUG_MODE_ENABLED="${TMUX_POWERLINE_DEBUG_MODE_ENABLED_DEFAULT}"
|
||||
fi
|
||||
|
||||
if [ -z "$TMUX_POWERLINE_PATCHED_FONT_IN_USE" ]; then
|
||||
export TMUX_POWERLINE_PATCHED_FONT_IN_USE="${TMUX_POWERLINE_PATCHED_FONT_IN_USE_DEFAULT}"
|
||||
fi
|
||||
|
||||
if [ -z "$TMUX_POWERLINE_THEME" ]; then
|
||||
export TMUX_POWERLINE_THEME="${TMUX_POWERLINE_THEME_DEFAULT}"
|
||||
fi
|
||||
|
||||
eval TMUX_POWERLINE_DIR_USER_SEGMENTS="$TMUX_POWERLINE_DIR_USER_SEGMENTS"
|
||||
eval TMUX_POWERLINE_DIR_USER_THEMES="$TMUX_POWERLINE_DIR_USER_THEMES"
|
||||
if [ -n "$TMUX_POWERLINE_DIR_USER_THEMES" ] && [ -f "${TMUX_POWERLINE_DIR_USER_THEMES}/${TMUX_POWERLINE_THEME}.sh" ]; then
|
||||
source "${TMUX_POWERLINE_DIR_USER_THEMES}/${TMUX_POWERLINE_THEME}.sh"
|
||||
else
|
||||
source "${TMUX_POWERLINE_DIR_THEMES}/${TMUX_POWERLINE_THEME}.sh"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
generate_default_rc() {
|
||||
read -d '' rccontents << EORC
|
||||
# Default configuration file for tmux-powerline.
|
||||
# Modeline {
|
||||
# vi: foldmarker={,} foldmethod=marker foldlevel=0 tabstop=4 filetype=sh
|
||||
# }
|
||||
|
||||
# General {
|
||||
# Show which segment fails and its exit code.
|
||||
export TMUX_POWERLINE_DEBUG_MODE_ENABLED="${TMUX_POWERLINE_DEBUG_MODE_ENABLED_DEFAULT}"
|
||||
# Use patched font symbols.
|
||||
export TMUX_POWERLINE_PATCHED_FONT_IN_USE="${TMUX_POWERLINE_PATCHED_FONT_IN_USE_DEFAULT}"
|
||||
# The theme to use.
|
||||
export TMUX_POWERLINE_THEME="${TMUX_POWERLINE_THEME_DEFAULT}"
|
||||
# Overlay directory to look for themes. There you can put your own themes outside the repo. Fallback will still be the "themes" directory in the repo.
|
||||
export TMUX_POWERLINE_DIR_USER_THEMES=""
|
||||
# Overlay directory to look for segments. There you can put your own segments outside the repo. Fallback will still be the "segments" directory in the repo.
|
||||
export TMUX_POWERLINE_DIR_USER_SEGMENTS=""
|
||||
# }
|
||||
EORC
|
||||
|
||||
for segment in ${TMUX_POWERLINE_DIR_SEGMENTS}/*.sh; do
|
||||
source "$segment"
|
||||
if declare -f generate_segmentrc >/dev/null; then
|
||||
segmentrc=$(generate_segmentrc | sed -e 's/^/\\t/g')
|
||||
unset -f generate_segmentrc
|
||||
local seg_name="${segment##*/}"
|
||||
rccontents="${rccontents}\n\n# ${seg_name} {\n${segmentrc}\n# }"
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "$rccontents" > "$TMUX_POWERLINE_RCFILE_DEFAULT"
|
||||
echo "Default configuration file generated to: ${TMUX_POWERLINE_RCFILE_DEFAULT}"
|
||||
echo "Copy/move it to \"${TMUX_POWERLINE_RCFILE}\" and make your changes."
|
||||
}
|
||||
|
||||
__read_rcfile() {
|
||||
if [ -f "$TMUX_POWERLINE_RCFILE" ]; then
|
||||
source "$TMUX_POWERLINE_RCFILE"
|
||||
fi
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
# Rolling anything what you want.
|
||||
# arg1: text to roll.
|
||||
# arg2: max length to display.
|
||||
# arg3: roll speed in characters per second.
|
||||
roll_text() {
|
||||
local text="$1" # Text to print
|
||||
|
||||
if [ -z "$text" ]; then
|
||||
return;
|
||||
fi
|
||||
|
||||
local max_len="10" # Default max length.
|
||||
|
||||
if [ -n "$2" ]; then
|
||||
max_len="$2"
|
||||
fi
|
||||
|
||||
local speed="1" # Default roll speed in chars per second.
|
||||
|
||||
if [ -n "$3" ]; then
|
||||
speed="$3"
|
||||
fi
|
||||
|
||||
# Skip rolling if the output is less than max_len.
|
||||
if [ "${#text}" -le "$max_len" ]; then
|
||||
echo "$text"
|
||||
return
|
||||
fi
|
||||
|
||||
# Anything starting with 0 is an Octal number in Shell,C or Perl,
|
||||
# so we must explicitly state the base of a number using base#number
|
||||
local offset=$((10#$(date +%s) * ${speed} % ${#text}))
|
||||
|
||||
# Truncate text.
|
||||
text=${text:offset}
|
||||
|
||||
local char # Character.
|
||||
local bytes # The bytes of one character.
|
||||
local index
|
||||
|
||||
for ((index=0; index < max_len; index++)); do
|
||||
char=${text:index:1}
|
||||
bytes=$(echo -n $char | wc -c)
|
||||
# The character will takes twice space
|
||||
# of an alphabet if (bytes > 1).
|
||||
if ((bytes > 1)); then
|
||||
max_len=$((max_len - 1))
|
||||
fi
|
||||
done
|
||||
|
||||
text=${text:0:max_len}
|
||||
|
||||
#echo "index=${index} max=${max_len} len=${#text}"
|
||||
# How many spaces we need to fill to keep
|
||||
# the length of text that will be shown?
|
||||
local fill_count=$((${index} - ${#text}))
|
||||
|
||||
for ((index=0; index < fill_count; index++)); do
|
||||
text="${text} "
|
||||
done
|
||||
|
||||
echo "${text}"
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
# Get the current path in the segment.
|
||||
MIN_MAJOR_VERSION="2"
|
||||
MIN_MINOR_VERSION="1"
|
||||
TMUX_VERSION="$(tmux -V)"
|
||||
|
||||
if [[ "${TMUX_VERSION}" =~ .*([[:digit:]]+)\.([[:digit:]]+) ]]; then
|
||||
TMUX_MAJOR_VERSION="${BASH_REMATCH[1]}"
|
||||
TMUX_MINOR_VERSION="${BASH_REMATCH[2]}"
|
||||
if [[ ("${TMUX_MAJOR_VERSION}" -gt "${MIN_MAJOR_VERSION}") || (("${TMUX_MAJOR_VERSION}" -eq "${MIN_MAJOR_VERSION}") && ("${TMUX_MINOR_VERSION}" -ge "${MIN_MINOR_VERSION}")) ]]; then
|
||||
get_tmux_cwd() {
|
||||
tmux display -p -F "#{pane_current_path}"
|
||||
}
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$(type -t get_tmux_cwd)" ]]; then
|
||||
get_tmux_cwd() {
|
||||
local env_name=$(tmux display -p "TMUXPWD_#D" | tr -d %)
|
||||
local env_val=$(tmux show-environment | grep --color=never "$env_name")
|
||||
# The version below is still quite new for tmux. Uncomment this in the future :-)
|
||||
#local env_val=$(tmux show-environment "$env_name" 2>&1)
|
||||
|
||||
if [[ ! $env_val =~ "unknown variable" ]]; then
|
||||
local tmux_pwd=$(echo "$env_val" | sed 's/^.*=//')
|
||||
echo "$tmux_pwd"
|
||||
fi
|
||||
}
|
||||
fi
|
||||
|
||||
unset MIN_MAJOR_VERSION
|
||||
unset MIN_MINOR_VERSION
|
||||
unset TMUX_VERSION
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
export TMUX_POWERLINE_DIR_HOME="$(dirname $0)"
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/paths.sh"
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/muting.sh"
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/arg_processing.sh"
|
||||
|
||||
side="$1"
|
||||
check_arg_side "$side"
|
||||
toggle_powerline_mute_status "$side"
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
export TMUX_POWERLINE_DIR_HOME="$(dirname $0)"
|
||||
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/helpers.sh"
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/paths.sh"
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/shell.sh"
|
||||
source "${TMUX_POWERLINE_DIR_HOME}/config/defaults.sh"
|
||||
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/arg_processing.sh"
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/formatting.sh"
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/muting.sh"
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/powerline.sh"
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/rcfile.sh"
|
||||
|
||||
if ! powerline_muted "$1"; then
|
||||
process_settings
|
||||
check_arg_side "$1"
|
||||
print_powerline "$1"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Build the small MPD NP program.
|
||||
# Enable debug printing with $ make -e DEBUG=1
|
||||
DEBUG=0
|
||||
CC = $(shell hash clang 2>/dev/null && echo clang || echo gcc)
|
||||
CFLAGS = -O3 -Wall -std=c99 -I /usr/include/ -D DEBUG=${DEBUG}
|
||||
LDLIBS = -lmpdclient
|
||||
XKB_LAYOUT_LDLIBS= -lX11
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: np_mpd xkb_layout
|
||||
|
||||
xkb_layout: xkb_layout.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $< $(XKB_LAYOUT_LDLIBS) -o $@
|
||||
|
||||
clean:
|
||||
$(RM) np_mpd
|
||||
$(RM) xkb_layout
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
# LICENSE This code is not under the same license as the rest of the project as it's "stolen". It's cloned from https://github.com/richoH/dotfiles/blob/master/bin/battery and just some modifications are done so it works for my laptop. Check that URL for more recent versions.
|
||||
|
||||
TMUX_POWERLINE_SEG_BATTERY_TYPE_DEFAULT="percentage"
|
||||
TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS_DEFAULT=5
|
||||
|
||||
HEART_FULL="♥"
|
||||
HEART_EMPTY="♡"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# How to display battery remaining. Can be {percentage, cute}.
|
||||
export TMUX_POWERLINE_SEG_BATTERY_TYPE="${TMUX_POWERLINE_SEG_BATTERY_TYPE_DEFAULT}"
|
||||
# How may hearts to show if cute indicators are used.
|
||||
export TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS="${TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
if shell_is_osx; then
|
||||
battery_status=$(__battery_osx)
|
||||
else
|
||||
battery_status=$(__battery_linux)
|
||||
fi
|
||||
[ -z "$battery_status" ] && return
|
||||
|
||||
case "$TMUX_POWERLINE_SEG_BATTERY_TYPE" in
|
||||
"percentage")
|
||||
output="${HEART_FULL} ${battery_status}%"
|
||||
;;
|
||||
"cute")
|
||||
output=$(__cutinate $battery_status)
|
||||
esac
|
||||
if [ -n "$output" ]; then
|
||||
echo "$output"
|
||||
fi
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_BATTERY_TYPE" ]; then
|
||||
export TMUX_POWERLINE_SEG_BATTERY_TYPE="${TMUX_POWERLINE_SEG_BATTERY_TYPE_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS" ]; then
|
||||
export TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS="${TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS_DEFAULT}"
|
||||
fi
|
||||
}
|
||||
|
||||
__battery_osx() {
|
||||
ioreg -c AppleSmartBattery -w0 | \
|
||||
grep -o '"[^"]*" = [^ ]*' | \
|
||||
sed -e 's/= //g' -e 's/"//g' | \
|
||||
sort | \
|
||||
while read key value; do
|
||||
case $key in
|
||||
"MaxCapacity")
|
||||
export maxcap=$value;;
|
||||
"CurrentCapacity")
|
||||
export curcap=$value;;
|
||||
"ExternalConnected")
|
||||
export extconnect=$value;;
|
||||
"FullyCharged")
|
||||
export fully_charged=$value;;
|
||||
esac
|
||||
if [[ -n $maxcap && -n $curcap && -n $extconnect ]]; then
|
||||
if [[ "$curcap" == "$maxcap" || "$fully_charged" == "Yes" && $extconnect == "Yes" ]]; then
|
||||
return
|
||||
fi
|
||||
charge=`pmset -g batt | grep -o "[0-9][0-9]*\%" | rev | cut -c 2- | rev`
|
||||
if [[ "$extconnect" == "Yes" ]]; then
|
||||
echo "$charge"
|
||||
else
|
||||
if [[ $charge -lt 50 ]]; then
|
||||
echo -n "#[fg=red]"
|
||||
fi
|
||||
echo "$charge"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__battery_linux() {
|
||||
case "$SHELL_PLATFORM" in
|
||||
"linux")
|
||||
BATPATH=/sys/class/power_supply/BAT0
|
||||
if [ ! -d $BATPATH ]; then
|
||||
BATPATH=/sys/class/power_supply/BAT1
|
||||
fi
|
||||
STATUS=$BATPATH/status
|
||||
BAT_FULL=$BATPATH/charge_full
|
||||
if [ ! -r $BAT_FULL ]; then
|
||||
BAT_FULL=$BATPATH/energy_full
|
||||
fi
|
||||
BAT_NOW=$BATPATH/charge_now
|
||||
if [ ! -r $BAT_NOW ]; then
|
||||
BAT_NOW=$BATPATH/energy_now
|
||||
fi
|
||||
|
||||
if [ "$1" = `cat $STATUS` -o "$1" = "" ]; then
|
||||
__linux_get_bat
|
||||
fi
|
||||
;;
|
||||
"bsd")
|
||||
STATUS=`sysctl -n hw.acpi.battery.state`
|
||||
case $1 in
|
||||
"Discharging")
|
||||
if [ $STATUS -eq 1 ]; then
|
||||
__freebsd_get_bat
|
||||
fi
|
||||
;;
|
||||
"Charging")
|
||||
if [ $STATUS -eq 2 ]; then
|
||||
__freebsd_get_bat
|
||||
fi
|
||||
;;
|
||||
"")
|
||||
__freebsd_get_bat
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
__cutinate() {
|
||||
perc=$1
|
||||
inc=$(( 100 / $TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS ))
|
||||
|
||||
|
||||
for i in `seq $TMUX_POWERLINE_SEG_BATTERY_NUM_HEARTS`; do
|
||||
if [ $perc -lt 99 ]; then
|
||||
echo -n $HEART_EMPTY
|
||||
else
|
||||
echo -n $HEART_FULL
|
||||
fi
|
||||
echo -n " "
|
||||
perc=$(( $perc + $inc ))
|
||||
done
|
||||
}
|
||||
|
||||
__linux_get_bat() {
|
||||
bf=$(cat $BAT_FULL)
|
||||
bn=$(cat $BAT_NOW)
|
||||
if [ $bn -gt $bf ]; then
|
||||
bn=$bf
|
||||
fi
|
||||
echo $(( 100 * $bn / $bf ))
|
||||
}
|
||||
|
||||
__freebsd_get_bat() {
|
||||
echo "$(sysctl -n hw.acpi.battery.life)"
|
||||
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# Prints the CPU usage: user% sys% idle.
|
||||
|
||||
run_segment() {
|
||||
if shell_is_linux; then
|
||||
cpu_line=$(top -b -n 1 | grep "Cpu(s)" )
|
||||
cpu_user=$(echo "$cpu_line" | grep -Po "(\d+(.\d+)?)(?=%?\s?(us(er)?))")
|
||||
cpu_system=$(echo "$cpu_line" | grep -Po "(\d+(.\d+)?)(?=%?\s?(sys?))")
|
||||
cpu_idle=$(echo "$cpu_line" | grep -Po "(\d+(.\d+)?)(?=%?\s?(id(le)?))")
|
||||
elif shell_is_osx; then
|
||||
cpus_line=$(top -e -l 1 | grep "CPU usage:" | sed 's/CPU usage: //')
|
||||
cpu_user=$(echo "$cpus_line" | awk '{print $1}' | sed 's/%//' )
|
||||
cpu_system=$(echo "$cpus_line" | awk '{print $3}'| sed 's/%//' )
|
||||
cpu_idle=$(echo "$cpus_line" | awk '{print $5}' | sed 's/%//' )
|
||||
fi
|
||||
|
||||
if [ -n "$cpu_user" ] && [ -n "$cpu_system" ] && [ -n "$cpu_idle" ]; then
|
||||
echo "${cpu_user}, ${cpu_system}, ${cpu_idle}" | awk -F', ' '{printf("%5.1f,%5.1f,%5.1f",$1,$2,$3)}'
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Print the current date.
|
||||
|
||||
TMUX_POWERLINE_SEG_DATE_FORMAT_DEFAULT="%F"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# date(1) format for the date. If you don't, for some reason, like ISO 8601 format you might want to have "%D" or "%m/%d/%Y".
|
||||
export TMUX_POWERLINE_SEG_DATE_FORMAT="${TMUX_POWERLINE_SEG_DATE_FORMAT_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_DATE_FORMAT" ]; then
|
||||
export TMUX_POWERLINE_SEG_DATE_FORMAT="${TMUX_POWERLINE_SEG_DATE_FORMAT_DEFAULT}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
date +"$TMUX_POWERLINE_SEG_DATE_FORMAT"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
# Prints the name of the current day.
|
||||
|
||||
run_segment() {
|
||||
date +"%a"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
# Print used disk space on the specified filesystem
|
||||
|
||||
TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM_DEFAULT="/"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# Filesystem to retrieve disk space information. Any from the filesystems available (run "df | awk '{print $1}'" to check them).
|
||||
export TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM="${TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
percentage=$(df ${TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM} | awk '{print $5}' | tail -n1)
|
||||
echo "${TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM} ${percentage}"
|
||||
return 0
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM" ]; then
|
||||
export TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM="${TMUX_POWERLINE_SEG_DISK_USAGE_FILESYSTEM_DEFAULT}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
# Prints the most recent earthquake (currently only supports japan)
|
||||
# It prints the location, time, and magnitude if the quake happened within
|
||||
# a timelimit and magnitude threshold
|
||||
|
||||
earthquake_symbol='#[fg=colour1]~'
|
||||
|
||||
# The update period in seconds.
|
||||
update_period=600
|
||||
|
||||
TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER_DEFAULT="goo"
|
||||
TMUX_POWERLINE_SEG_EARTHQUAKE_UPDATE_PERIOD_DEFAULT="600"
|
||||
TMUX_POWERLINE_SEG_EARTHQUAKE_ALERT_TIME_WINDOW_DEFAULT="60"
|
||||
TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT_DEFAULT='(%H:%M)'
|
||||
TMUX_POWERLINE_SEG_EARTHQUAKE_MIN_MAGNITUDE_DEFAULT='3'
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# The data provider to use. Currently only "goo" is supported.
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER="${TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER_DEFAULT}"
|
||||
# How often to update the earthquake data in seconds.
|
||||
# Note: This is not an early warning detector, use this
|
||||
# to be informed about recent earthquake magnitudes in your
|
||||
# area. If this is too often, goo may decide to ban you form
|
||||
# their server
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_EARTHQUAKE_UPDATE_PERIOD_DEFAULT}"
|
||||
# Only display information when earthquakes are within this many minutes
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_ALERT_TIME_WINDOW="${TMUX_POWERLINE_SEG_EARTHQUAKE_ALERT_TIME_WINDOW_DEFAULT}"
|
||||
# Display time with this format
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT='${TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT_DEFAULT}'
|
||||
# Display only if magnitude is greater or equal to this number
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_MIN_MAGNITUDE="${TMUX_POWERLINE_SEG_EARTHQUAKE_MIN_MAGNITUDE_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
local tmp_file="${TMUX_POWERLINE_DIR_TEMPORARY}/earthquake.txt"
|
||||
local earthquake
|
||||
case "$TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER" in
|
||||
"goo") earthquake=$(__goo_earthquake) ;;
|
||||
*)
|
||||
echo "Unknown earthquake-information provider [${$TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER}]";
|
||||
return 1
|
||||
esac
|
||||
if [ -n "$earthquake" ]; then
|
||||
echo "$earthquake_symbol #[fg=colour237]${earthquake}"
|
||||
fi
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER" ]; then
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER="${TMUX_POWERLINE_SEG_EARTHQUAKE_DATA_PROVIDER_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_EARTHQUAKE_UPDATE_PERIOD" ]; then
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_EARTHQUAKE_UPDATE_PERIOD_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_EARTHQUAKE_ALERT_TIME_WINDOW" ]; then
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_ALERT_TIME_WINDOW="${TMUX_POWERLINE_SEG_EARTHQUAKE_ALERT_TIME_WINDOW_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT" ]; then
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT="${TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_EARTHQUAKE_MIN_MAGNITUDE" ]; then
|
||||
export TMUX_POWERLINE_SEG_EARTHQUAKE_MIN_MAGNITUDE="${TMUX_POWERLINE_SEG_EARTHQUAKE_MIN_MAGNITUDE_DEFAULT}"
|
||||
fi
|
||||
}
|
||||
|
||||
__goo_earthquake() {
|
||||
location=""
|
||||
magnitude=""
|
||||
magnitude_number=""
|
||||
timestamp=""
|
||||
if [[ -f "$tmp_file" ]]; then
|
||||
if shell_is_osx || shell_is_bsd; then
|
||||
last_update=$(stat -f "%m" ${tmp_file})
|
||||
elif shell_is_linux; then
|
||||
last_update=$(stat -c "%Y" ${tmp_file})
|
||||
fi
|
||||
time_now=$(date +%s)
|
||||
|
||||
up_to_date=$(echo "(${time_now}-${last_update}) < ${update_period}" | bc)
|
||||
if [ "$up_to_date" -eq 1 ]; then
|
||||
__read_tmp_file
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$magnitude" ]; then
|
||||
# get the rss file, convert encoding to UTF-8, then delete windows carriage-returns
|
||||
earthquake_data=$(curl --max-time 4 -s "http://weather.goo.ne.jp/earthquake/index.rdf" | iconv -f EUC-JP -t UTF-8 | tr -d "\r")
|
||||
if [ "$?" -eq "0" ]; then
|
||||
# This rss feed is not very clean or easy to use, but we will use it because
|
||||
# this is all that can be found for now
|
||||
|
||||
# we grab the data from the title of the first item (most recent earthquake)
|
||||
earthquake_data=${earthquake_data#*item\><title>}
|
||||
# end our data at the end of the approx. time
|
||||
earthquake_data=${earthquake_data%%頃*}
|
||||
|
||||
# pluck our data
|
||||
location=$(echo $earthquake_data | awk '{print $2}')
|
||||
magnitude=$(echo $earthquake_data | awk '{print $4}')
|
||||
timestamp=${earthquake_data#*\(}
|
||||
|
||||
__convert_jp_magnitude
|
||||
__convert_jp_timestamp
|
||||
|
||||
echo $location > $tmp_file
|
||||
echo $magnitude >> $tmp_file
|
||||
echo $timestamp >> $tmp_file
|
||||
elif [ -f "$tmp_file" ]; then
|
||||
__read_tmp_file
|
||||
fi
|
||||
fi
|
||||
__convert_timestamp_to_fmt
|
||||
|
||||
# extract the numerical portion of magnitude
|
||||
magnitude_number=$(echo $magnitude | sed -e 's/+//' -e 's/-//')
|
||||
|
||||
if [ -n "$magnitude" ]; then
|
||||
if __check_alert_time_window && __check_min_magnitude ; then
|
||||
echo "${location}${timestamp_fmt}:#[fg=colour0]${magnitude}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__convert_jp_magnitude() {
|
||||
magnitude=${magnitude#震度}
|
||||
# simplify high-lower designation (only used in extreme cases: above 4)
|
||||
if [[ "$magnitude" == *弱 ]] ; then
|
||||
magnitude="-${magnitude%弱}"
|
||||
elif [[ "$magnitude" == *強 ]] ; then
|
||||
magnitude="+${magnitude%強}"
|
||||
fi
|
||||
}
|
||||
|
||||
__check_alert_time_window() {
|
||||
[[ $(( ( $(date +%s) - $timestamp ) / 60 )) -lt $TMUX_POWERLINE_SEG_EARTHQUAKE_ALERT_TIME_WINDOW ]]
|
||||
}
|
||||
|
||||
__check_min_magnitude() {
|
||||
[[ $magnitude_number -ge $TMUX_POWERLINE_SEG_EARTHQUAKE_MIN_MAGNITUDE ]]
|
||||
}
|
||||
|
||||
__convert_jp_timestamp() {
|
||||
if shell_is_osx ; then
|
||||
timestamp=$(date -j -f "%Y年%m月%d日 %H時%M分" "$timestamp" +"%s")
|
||||
else
|
||||
timestamp=$(echo $timestamp | $sed -e 's/年/-/' -e 's/月/-/' -e 's/日//' -e 's/時/:/' -e 's/分//')
|
||||
timestamp=$(date -d "$timestamp" +"%s")
|
||||
fi
|
||||
}
|
||||
|
||||
__convert_timestamp_to_fmt() {
|
||||
if shell_is_osx ; then
|
||||
timestamp_fmt=$(date -r "$timestamp" +"$TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT")
|
||||
else
|
||||
timestamp_fmt=$(date -d "$timestamp" +"$TMUX_POWERLINE_SEG_EARTHQUAKE_TIME_FORMAT")
|
||||
fi
|
||||
}
|
||||
|
||||
__read_tmp_file() {
|
||||
if [ ! -f "$tmp_file" ]; then
|
||||
return
|
||||
fi
|
||||
IFS_bak="$IFS"
|
||||
IFS=$'\n'
|
||||
lines=($(cat ${tmp_file}))
|
||||
IFS="$IFS_bak"
|
||||
location="${lines[0]}"
|
||||
magnitude="${lines[1]}"
|
||||
timestamp="${lines[2]}"
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# Prints the hostname.
|
||||
|
||||
TMUX_POWERLINE_SEG_HOSTNAME_FORMAT_DEFAULT="short"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# Use short or long format for the hostname. Can be {"short, long"}.
|
||||
export TMUX_POWERLINE_SEG_HOSTNAME_FORMAT="${TMUX_POWERLINE_SEG_HOSTNAME_FORMAT_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_HOSTNAME_FORMAT" ]; then
|
||||
export TMUX_POWERLINE_SEG_HOSTNAME_FORMAT="${TMUX_POWERLINE_SEG_HOSTNAME_FORMAT_DEFAULT}"
|
||||
fi
|
||||
}
|
||||
run_segment() {
|
||||
__process_settings
|
||||
local opts=""
|
||||
if [ "$TMUX_POWERLINE_SEG_HOSTNAME_FORMAT" == "short" ]; then
|
||||
if shell_is_osx || shell_is_bsd; then
|
||||
opts="-s"
|
||||
else
|
||||
opts="--short"
|
||||
fi
|
||||
fi
|
||||
|
||||
hostname ${opts}
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# Show network statistics for all active interfaces found.
|
||||
|
||||
run_segment() {
|
||||
type ifstat >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
sed="sed"
|
||||
type gsed >/dev/null 2>&1
|
||||
if [ "$?" -eq 0 ]; then
|
||||
sed="gsed"
|
||||
fi
|
||||
|
||||
data=$(ifstat -z -T -S -q 1 1)
|
||||
interfaces=$(echo -e "${data}" | head -n 1)
|
||||
flow_data=$(echo -e "${data}" | tail -n 1 | ${sed} "s/\s\{1,\}/,/g")
|
||||
index=1
|
||||
for inf in ${interfaces}; do
|
||||
type=""
|
||||
case ${inf} in
|
||||
eth*) type="⎆"
|
||||
;;
|
||||
wlan*) type="☫"
|
||||
;;
|
||||
en*) type=" "
|
||||
;;
|
||||
esac
|
||||
if [ -n "${type}" ]; then
|
||||
format=$(echo "${format} ${type} ⇊ %5.0f ⇈ %5.0f")
|
||||
holder=$(echo "${holder},\$$((index)),\$$((index+1))")
|
||||
fi
|
||||
index=$((index+2))
|
||||
done
|
||||
if [ -n "${format}" ]; then
|
||||
echo $(echo "${flow_data#,}" | awk -F"," "{printf(\"${format}\"${holder})}")
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
# Show if stats by sampling /sys/.
|
||||
# Originally stolen from http://unix.stackexchange.com/questions/41346/upload-download-speed-in-tmux-status-line
|
||||
|
||||
run_segment() {
|
||||
sleeptime="0.5"
|
||||
if shell_is_osx; then
|
||||
iface="en0"
|
||||
type="⎆" # "☫" for wlan
|
||||
RXB=$(netstat -i -b | grep -m 1 $iface | awk '{print $7}')
|
||||
TXB=$(netstat -i -b | grep -m 1 $iface | awk '{print $10}')
|
||||
sleep "$sleeptime"
|
||||
RXBN=$(netstat -i -b | grep -m 1 $iface | awk '{print $7}')
|
||||
TXBN=$(netstat -i -b | grep -m 1 $iface | awk '{print $10}')
|
||||
else
|
||||
iface=$(/bin/cat /proc/net/dev | /usr/bin/awk '{if($2>0 && NR > 2) print substr($1, 0, index($1, ":") - 1)}' | /bin/sed '/^lo$/d')
|
||||
type="⎆" # "☫" for wlan
|
||||
RXB=$(</sys/class/net/"$iface"/statistics/rx_bytes)
|
||||
TXB=$(</sys/class/net/"$iface"/statistics/tx_bytes)
|
||||
sleep "$sleeptime"
|
||||
RXBN=$(</sys/class/net/"$iface"/statistics/rx_bytes)
|
||||
TXBN=$(</sys/class/net/"$iface"/statistics/tx_bytes)
|
||||
fi
|
||||
RXDIF=$(echo "$((RXBN - RXB)) / 1024 / ${sleeptime}" | bc )
|
||||
TXDIF=$(echo "$((TXBN - TXB)) / 1024 / ${sleeptime}" | bc )
|
||||
|
||||
if [ $RXDIF -gt 1024 ]; then
|
||||
RXDIF=$(echo "scale=1;${RXDIF} / 1024" | bc)
|
||||
RXDIF_UNIT="M/s"
|
||||
else
|
||||
RXDIF_UNIT="K/s"
|
||||
fi
|
||||
if [ $TXDIF -gt 1024 ]; then
|
||||
TXDIF=$(echo "scale=1;${TXDIF} / 1024" | bc)
|
||||
TXDIF_UNIT="M/s"
|
||||
else
|
||||
TXDIF_UNIT="K/s"
|
||||
fi
|
||||
|
||||
# NOTE: '%5.01' for fixed length always
|
||||
printf "${type} ⇊ %5.01f${RXDIF_UNIT} ⇈ %5.01f${TXDIF_UNIT}" ${RXDIF} ${TXDIF}
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
# Prints the local network IPv4 address for a statically defined NIC or search for an IPv4 address on all active NICs.
|
||||
|
||||
run_segment() {
|
||||
if shell_is_bsd || shell_is_osx ; then
|
||||
all_nics=$(ifconfig 2>/dev/null | awk -F':' '/^[a-z]/ && !/^lo/ { print $1 }')
|
||||
for nic in ${all_nics[@]}; do
|
||||
ipv4s_on_nic=$(ifconfig ${nic} 2>/dev/null | awk '$1 == "inet" { print $2 }')
|
||||
for lan_ip in ${ipv4s_on_nic[@]}; do
|
||||
[[ -n "${lan_ip}" ]] && break
|
||||
done
|
||||
[[ -n "${lan_ip}" ]] && break
|
||||
done
|
||||
else
|
||||
# Get the names of all attached NICs.
|
||||
all_nics="$(ip addr show | cut -d ' ' -f2 | tr -d :)"
|
||||
all_nics=(${all_nics[@]//lo/}) # Remove lo interface.
|
||||
|
||||
for nic in "${all_nics[@]}"; do
|
||||
# Parse IP address for the NIC.
|
||||
lan_ip="$(ip addr show ${nic} | grep '\<inet\>' | tr -s ' ' | cut -d ' ' -f3)"
|
||||
# Trim the CIDR suffix.
|
||||
lan_ip="${lan_ip%/*}"
|
||||
# Only display the last entry
|
||||
lan_ip="$(echo "$lan_ip" | tail -1)"
|
||||
|
||||
[ -n "$lan_ip" ] && break
|
||||
done
|
||||
fi
|
||||
|
||||
echo "ⓛ ${lan_ip-N/a}"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
# Print the average load.
|
||||
|
||||
run_segment() {
|
||||
uptime | cut -d "," -f 3- | cut -d ":" -f2 | sed -e "s/^[ \t]*//"
|
||||
exit 0
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
# Report macOS notification counts for given app ids (banner notifications only)
|
||||
# Based on http://www.ign.com/boards/threads/a-crumby-way-to-get-an-unread-count-of-imessages-into-applescript.453061379/
|
||||
|
||||
TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_APPIDS_DEFAULT="5"
|
||||
TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_CHAR_DEFAULT="💬"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# App ids to query in notification center, separated by space
|
||||
# To get the app id that is associated with a specific app run:
|
||||
# sqlite3 -list "$(getconf DARWIN_USER_DIR)/com.apple.notificationcenter/db/db" 'select * from app_info'
|
||||
# The first column contains the app ids
|
||||
# "5" is the app id of Messages.app
|
||||
# Only "banner" notifications are supported (see settings in the notification center)
|
||||
export TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_APPIDS="${TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_APPIDS_DEFAULT}"
|
||||
# Notification symbol
|
||||
export TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_CHAR="${TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_CHAR_DEFAULT}"
|
||||
EORC
|
||||
echo "${rccontents}"
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_APPIDS" ]; then
|
||||
export TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_APPIDS="${TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_APPIDS_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_CHAR" ]; then
|
||||
export TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_CHAR="${TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_CHAR_DEFAULT}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
|
||||
local db_location app_ids_array query_condition query_string count
|
||||
db_location="$(getconf DARWIN_USER_DIR)/com.apple.notificationcenter/db/db"
|
||||
app_ids_array=(${TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_APPIDS})
|
||||
query_condition="where app_id=${app_ids_array[0]}"
|
||||
for app_id in "${app_ids_array[@]:1}"; do
|
||||
query_condition="${query_condition} OR app_id=${app_id}"
|
||||
done
|
||||
query_string="select count(*) from notifications ${query_condition}"
|
||||
count=$(sqlite3 -list ${db_location} "${query_string}")
|
||||
|
||||
local exitcode="$?"
|
||||
if [ "$exitcode" -ne 0 ]; then
|
||||
return $exitcode
|
||||
fi
|
||||
|
||||
if [[ -n "$count" && "$count" -gt 0 ]]; then
|
||||
echo "${TMUX_POWERLINE_SEG_MACOS_NOTIFICATION_COUNT_CHAR} ${count}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
# Report mail count for your mailbox type
|
||||
|
||||
TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX_DEFAULT="$HOME/.mail/inbox/new"
|
||||
TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX_DEFAULT="${MAIL}"
|
||||
TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC_DEFAULT="${HOME}/.mailcheckrc"
|
||||
TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER_DEFAULT="gmail.com"
|
||||
TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_INTERVAL_DEFAULT="5"
|
||||
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# Mailbox type to use. Can be any of {apple_mail, gmail, maildir, mbox, mailcheck}
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_MAILBOX_TYPE=""
|
||||
|
||||
## Gmail
|
||||
# Enter your Gmail username here WITH OUT @gmail.com.( OR @domain)
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_USERNAME=""
|
||||
# Google password. Recomenned to use application specific password (https://accounts.google.com/b/0/IssuedAuthSubTokens) Leave this empty to get password from OS X keychain.
|
||||
# For OSX users : MAKE SURE that you add a key to the keychain in the format as follows
|
||||
# Keychain Item name : http://<value-you-fill-in-server-variable-below>
|
||||
# Account name : <username-below>@<server-below>
|
||||
# Password : Your password ( Once again, try to use 2 step-verification and application-specific password)
|
||||
# See http://support.google.com/accounts/bin/answer.py?hl=en&answer=185833 for more info.
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_PASSWORD=""
|
||||
# Domain name that will complete your email. For normal GMail users it probably is "gmail.com but can be "foo.tld" for Google Apps users.
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER="${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER_DEFAULT}"
|
||||
# How often in minutes to check for new mails.
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_INTERVAL="${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_INTERVAL_DEFAULT}"
|
||||
|
||||
## Maildir
|
||||
# Path to the maildir to check.
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX="${TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX_DEFAULT}"
|
||||
|
||||
## mbox
|
||||
# Path to the mbox to check.
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX="${TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX_DEFAULT}"
|
||||
|
||||
## mailcheck
|
||||
# Optional path to mailcheckrc
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC="${TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC_DEFAULT}"
|
||||
EORC
|
||||
echo "${rccontents}"
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER" ]; then
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER="${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_INTERVAL" ]; then
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_INTERVAL="${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_INTERVAL_DEFAULT}"
|
||||
fi
|
||||
|
||||
eval TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX="$TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX"
|
||||
if [ -z "$TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX" ]; then
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX="${TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX_DEFAULT}"
|
||||
fi
|
||||
|
||||
eval TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX="$TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX"
|
||||
if [ -z "${TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX}" ]; then
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX="${TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX_DEFAULT}"
|
||||
fi
|
||||
|
||||
eval TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC="$TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC"
|
||||
if [ -z "${TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC}" ]; then
|
||||
export TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC="${TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC_DEFAULT}"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
|
||||
if [ -z "$TMUX_POWERLINE_SEG_MAILCOUNT_MAILBOX_TYPE" ]; then
|
||||
return 2
|
||||
fi
|
||||
|
||||
local count
|
||||
case "$TMUX_POWERLINE_SEG_MAILCOUNT_MAILBOX_TYPE" in
|
||||
"apple_mail") count=$(__count_apple_mail) ;;
|
||||
"gmail") count=$(__count_gmail) ;;
|
||||
"maildir") count=$(__count_maildir) ;;
|
||||
"mbox") count=$(__count_mbox) ;;
|
||||
"mailcheck") count=$(__count_mailcheck) ;;
|
||||
*)
|
||||
echo "Unknown mailbox type [${TMUX_POWERLINE_SEG_MAILCOUNT_MAILBOX_TYPE}]";
|
||||
return 1
|
||||
esac
|
||||
local exitcode="$?"
|
||||
if [ "$exitcode" -ne 0 ]; then
|
||||
return $exitcode
|
||||
fi
|
||||
|
||||
if [[ -n "$count" && "$count" -gt 0 ]]; then
|
||||
echo "✉ ${count}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
__count_apple_mail() {
|
||||
count=$(${TMUX_POWERLINE_DIR_SEGMENTS}/mailcount_apple_mail.script)
|
||||
echo "$count"
|
||||
}
|
||||
|
||||
__count_gmail() {
|
||||
local tmp_file="${TMUX_POWERLINE_DIR_TEMPORARY}/gmail_count.txt"
|
||||
local tmp_wgetrc="${TMUX_POWERLINE_DIR_TEMPORARY}/tmp_wgetrc.txt"
|
||||
local override_passget="false" # When true a force reloaded will be done.
|
||||
|
||||
# Create the cache file if it doesn't exist.
|
||||
if [ ! -f "$tmp_file" ]; then
|
||||
touch $tmp_file
|
||||
override_passget=true
|
||||
fi
|
||||
|
||||
# Refresh mail count if the tempfile is older than $interval minutes.
|
||||
let interval=60*$TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_INTERVAL
|
||||
if shell_is_osx || shell_is_bsd; then
|
||||
last_update=$(stat -f "%m" ${tmp_file})
|
||||
elif shell_is_linux; then
|
||||
last_update=$(stat -c "%Y" ${tmp_file})
|
||||
fi
|
||||
if [ "$(( $(date +"%s") - ${last_update} ))" -gt "$interval" ] || [ "$override_passget" == true ]; then
|
||||
if [ -z "$TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_PASSWORD" ]; then # Get password from keychain if it isn't already set.
|
||||
if shell_is_osx; then
|
||||
__mac_keychain_get_pass "${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_USERNAME}@${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER}" "$TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER"
|
||||
else
|
||||
echo "Implement your own sexy password fetching mechanism here."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for wget before proceeding.
|
||||
which wget 2>&1 > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "This script requires wget." 1>&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Hide password from command line (visible with e.g. ps(1)).
|
||||
echo -e "user=${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_USERNAME}@${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER}\npassword=${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_PASSWORD}" > "$tmp_wgetrc"
|
||||
mail=$(wget -q -O - https://mail.google.com/a/${TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_SERVER}/feed/atom --config "$tmp_wgetrc" | grep -E -m 1 -o '<fullcount>(.*)</fullcount>' | sed -e 's,.*<fullcount>\([^<]*\)</fullcount>.*,\1,g')
|
||||
rm "$tmp_wgetrc"
|
||||
|
||||
if [ "$mail" != "" ]; then
|
||||
echo $mail > $tmp_file
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
count=$(cat $tmp_file)
|
||||
echo "$count"
|
||||
return 0;
|
||||
}
|
||||
|
||||
__count_maildir() {
|
||||
if [ ! -d "$TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
count=$(ls "$TMUX_POWERLINE_SEG_MAILCOUNT_MAILDIR_INBOX" | wc -l)
|
||||
|
||||
# Fix for mac, otherwise whitespace is left in output
|
||||
if shell_is_osx; then
|
||||
count=$(echo "$count" | sed -e "s/^[ \t]*//")
|
||||
fi
|
||||
|
||||
echo "$count"
|
||||
return 0;
|
||||
}
|
||||
|
||||
__count_mbox() {
|
||||
if [ ! -f "${TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# This matches the From_ line (see man 5 mbox) e.g.
|
||||
# From noreply@github.com Sun Dec 2 03:52:25 2012
|
||||
# See https://github.com/erikw/tmux-powerline/pull/91#issuecomment-10926053 for discussion.
|
||||
count=$(grep -c '^From [^[:space:]]\+ ... ... .. ..:..:.. ....$' ${TMUX_POWERLINE_SEG_MAILCOUNT_MBOX_INBOX})
|
||||
|
||||
echo "$count"
|
||||
return 0;
|
||||
}
|
||||
|
||||
__mac_keychain_get_pass() {
|
||||
result="$(security 2>&1 > /dev/null find-internet-password -ga $1 -s $2)"
|
||||
if [ $? -eq 0 ]; then
|
||||
TMUX_POWERLINE_SEG_MAILCOUNT_GMAIL_PASSWORD=$(echo "$result" | sed -e 's/password: \"\(.*\)\"/\1/g')
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
__count_mailcheck() {
|
||||
count=$(mailcheck -f ${TMUX_POWERLINE_SEG_MAILCOUNT_MAILCHECKRC} | awk '{ if (/new/) { print $3; exit } else { print 0; exit } }')
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "$count"
|
||||
return 0
|
||||
fi
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env osascript
|
||||
# Returns the number of unread mails in the INBOX of Apple Mail.
|
||||
|
||||
tell application "System Events"
|
||||
set process_list to (name of every process)
|
||||
end tell
|
||||
|
||||
if process_list contains "Mail" then
|
||||
tell application "Mail"
|
||||
if unread count of inbox > 0 then
|
||||
set a to unread count of inbox
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# Indicator of pressing TMUX prefix, copy and insert modes.
|
||||
|
||||
prefix_pressed_text="PREFIX PRESSED"
|
||||
insert_mode_text="INSERT"
|
||||
copy_mode_text="COPY"
|
||||
normal_mode_text="NORMAL"
|
||||
separator="✤"
|
||||
|
||||
prefix_mode_fg="colour226"
|
||||
normal_mode_fg="colour16"
|
||||
copy_mode_fg="colour82"
|
||||
bg="colour33"
|
||||
|
||||
run_segment() {
|
||||
prefix_indicator="#[bg=${bg}]#{?client_prefix,#[fg=${prefix_mode_fg}]${prefix_pressed_text},#[fg=${normal_mode_fg}]${normal_mode_text}}"
|
||||
normal_or_copy_indicator="#[bg=${bg}]#{?pane_in_mode,#[fg=${copy_mode_fg}]${copy_mode_text},#[fg=${normal_mode_fg}]${insert_mode_text}}";
|
||||
echo $prefix_indicator "#[fg=${normal_mode_fg}]${separator}" $normal_or_copy_indicator
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,297 +0,0 @@
|
|||
# Print current playing song in your music player of choice.
|
||||
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/text_roll.sh"
|
||||
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN_DEFAULT="40"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_TRIM_METHOD_DEFAULT="trim"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_ROLL_SPEED_DEFAULT="2"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST_DEFAULT="localhost"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT_DEFAULT="6600"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_UPDATE_PERIOD_DEFAULT="30"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_SIMPLE_FORMAT_DEFAULT="%artist% - %title%"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_RHYTHMBOX_FORMAT_DEFAULT="%aa - %tt"
|
||||
TMUX_POWERLINE_SEG_NOW_PLAYING_NOTE_CHAR_DEFAULT="♫"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# Music player to use. Can be any of {audacious, banshee, cmus, itunes, lastfm, mocp, mpd, mpd_simple, pithos, rdio, rhythmbox, spotify, spotify_wine, file}.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MUSIC_PLAYER=""
|
||||
# File to be read in case the song is being read from a file
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_FILE_NAME=""
|
||||
# Maximum output length.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN="${TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN_DEFAULT}"
|
||||
# How to handle too long strings. Can be {trim, roll}.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_TRIM_METHOD="${TMUX_POWERLINE_SEG_NOW_PLAYING_TRIM_METHOD_DEFAULT}"
|
||||
# Charcters per second to roll if rolling trim method is used.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_ROLL_SPEED="${TMUX_POWERLINE_SEG_NOW_PLAYING_ROLL_SPEED_DEFAULT}"
|
||||
|
||||
# Hostname for MPD server in the format "[password@]host"
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST="${TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST_DEFAULT}"
|
||||
# Port the MPD server is running on.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT="${TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT_DEFAULT}"
|
||||
# Song display format for mpd_simple. See mpc(1) for delimiters.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_SIMPLE_FORMAT="${TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_SIMPLE_FORMAT_DEFAULT}"
|
||||
# Song display format for rhythmbox. see "FORMATS" in rhythmbox-client(1).
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_RHYTHMBOX_FORMAT="${TMUX_POWERLINE_SEG_NOW_PLAYING_RHYTHMBOX_FORMAT_DEFAULT}"
|
||||
|
||||
# Username for Last.fm if that music player is used.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_USERNAME=""
|
||||
# How often in seconds to update the data from last.fm.
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_UPDATE_PERIOD_DEFAULT}"
|
||||
# Fancy char to display before now playing track
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_NOTE_CHAR="${TMUX_POWERLINE_SEG_NOW_PLAYING_NOTE_CHAR_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_MUSIC_PLAYER" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local np
|
||||
local app_exit
|
||||
IFS=',' read -ra PLAYERS <<< "$TMUX_POWERLINE_SEG_NOW_PLAYING_MUSIC_PLAYER"
|
||||
for i in "${PLAYERS[@]}"; do
|
||||
case "$i" in
|
||||
"audacious") np=$(__np_audacious) ;;
|
||||
"banshee") np=$(__np_banshee) ;;
|
||||
"cmus") np=$(__np_cmus) ;;
|
||||
"itunes") np=$(__np_itunes) ;;
|
||||
"lastfm") np=$(__np_lastfm) ;;
|
||||
"mocp") np=$(__np_mocp) ;;
|
||||
"mpd") np=$(__np_mpd) ;;
|
||||
"mpd_simple") np=$(__np_mpd_simple) ;;
|
||||
"pithos") np=$(__np_pithos) ;;
|
||||
"rdio") np=$(__np_rdio) ;;
|
||||
"rhythmbox") np=$(__np_rhythmbox) ;;
|
||||
"spotify") np=$(__np_spotify) ;;
|
||||
"file") np=$(__np_file) ;;
|
||||
"spotify_wine") np=$(__np_spotify_native) ;;
|
||||
*)
|
||||
echo "Unknown music player type [${TMUX_POWERLINE_SEG_NOW_PLAYING_MUSIC_PLAYER}]";
|
||||
return 1
|
||||
esac
|
||||
app_exit="$?"
|
||||
[ -n "$np" ] && break
|
||||
done
|
||||
|
||||
local exitcode="$app_exit"
|
||||
if [ "${exitcode}" -ne 0 ]; then
|
||||
return ${exitcode}
|
||||
fi
|
||||
if [ -n "$np" ]; then
|
||||
case "$TMUX_POWERLINE_SEG_NOW_PLAYING_TRIM_METHOD" in
|
||||
"roll")
|
||||
np=$(roll_text "${np}" ${TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN} ${TMUX_POWERLINE_SEG_NOW_PLAYING_ROLL_SPEED_DEFAULT})
|
||||
;;
|
||||
"trim")
|
||||
np=${np:0:TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN}
|
||||
;;
|
||||
esac
|
||||
echo "${TMUX_POWERLINE_SEG_NOW_PLAYING_NOTE_CHAR} ${np}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN="${TMUX_POWERLINE_SEG_NOW_PLAYING_MAX_LEN_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_TRIM_METHOD" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_TRIM_METHOD="${TMUX_POWERLINE_SEG_NOW_PLAYING_TRIM_METHOD_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_ROLL_SPEED" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_ROLL_SPEED="${TMUX_POWERLINE_SEG_NOW_PLAYING_ROLL_SPEED_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST="${TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT="${TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_SIMPLE_FORMAT" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_SIMPLE_FORMAT="${TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_SIMPLE_FORMAT_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_UPDATE_PERIOD" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_UPDATE_PERIOD_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_NOTE_CHAR" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_NOTE_CHAR="${TMUX_POWERLINE_SEG_NOW_PLAYING_NOTE_CHAR_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_NOW_PLAYING_RHYTHMBOX_FORMAT" ]; then
|
||||
export TMUX_POWERLINE_SEG_NOW_PLAYING_RHYTHMBOX_FORMAT="${TMUX_POWERLINE_SEG_NOW_PLAYING_RHYTHMBOX_FORMAT}"
|
||||
fi;
|
||||
}
|
||||
|
||||
__np_mpd() {
|
||||
cd "$TMUX_POWERLINE_DIR_SEGMENTS"
|
||||
|
||||
if [ ! -x "np_mpd" ]; then
|
||||
make clean np_mpd &>/dev/null
|
||||
fi
|
||||
|
||||
if [ ! -x "np_mpd" ]; then
|
||||
return 2
|
||||
fi
|
||||
|
||||
np=$(MPD_HOST="$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST" MPD_PORT="$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT" ./np_mpd)
|
||||
echo "$np"
|
||||
}
|
||||
|
||||
__np_file() {
|
||||
|
||||
np=$(cat $TMUX_POWERLINE_SEG_NOW_PLAYING_FILE_NAME | tr '\n' '|')
|
||||
echo "$np"
|
||||
}
|
||||
|
||||
|
||||
__np_mpd_simple() {
|
||||
np=$(MPD_HOST="$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_HOST" MPD_PORT="$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_PORT" mpc current -f "$TMUX_POWERLINE_SEG_NOW_PLAYING_MPD_SIMPLE_FORMAT" 2>&1)
|
||||
if [ $? -eq 0 ] && [ -n "$np" ]; then
|
||||
mpc | grep "paused" > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
echo "$np"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
__np_audacious() {
|
||||
audacious_pid=$(pidof audacious)
|
||||
if [ -n "$audacious_pid" ]; then
|
||||
if $(audtool playback-playing); then
|
||||
np=$(audtool current-song)
|
||||
echo "$np"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__np_banshee() {
|
||||
banshee_pid=$(pidof banshee)
|
||||
if [ -n "$banshee_pid" ]; then
|
||||
banshee_status=$(banshee --query-current-state 2> /dev/null)
|
||||
if [[ "$banshee_status" == "current-state: playing" ]]; then
|
||||
np=$(banshee --query-artist --query-title | cut -d ":" -f2 | sed -e 's/ *$//g' -e 's/^ *//g'| sed -e ':a;N;$!ba;s/\n/ - /g' )
|
||||
echo "$np"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__np_cmus() {
|
||||
#cmus-remote returns EXIT_FAILURE/EXIT_SUCCESS depending on whether or not cmus is running.
|
||||
if cmus-remote -Q > /dev/null 2>&1; then
|
||||
status=$(cmus-remote -Q | grep "status" | cut -d ' ' -f 2)
|
||||
artist=$(cmus-remote -Q | grep -m 1 "artist" | cut -d ' ' -f 3-)
|
||||
title=$(cmus-remote -Q | grep "title" | cut -d ' ' -f 3-)
|
||||
#The lines below works fine. Just uncomment them and add them
|
||||
# in np below if you want the track number or album name.
|
||||
#tracknumber=$(cmus-remote -Q | grep "tracknumber" | cut -d ' ' -f 3)
|
||||
#album=$(cmus-remote -Q | grep "album" | cut -d ' ' -f 3-)
|
||||
|
||||
np=$(echo "${artist} - ${title}")
|
||||
|
||||
if [ "$status" == "playing" ]; then
|
||||
echo "$np"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__np_itunes() {
|
||||
[ ! shell_is_osx ] && return 1
|
||||
np=$(${TMUX_POWERLINE_DIR_SEGMENTS}/np_itunes.script)
|
||||
echo "$np"
|
||||
}
|
||||
|
||||
__np_lastfm() {
|
||||
local tmp_file="${TMUX_POWERLINE_DIR_TEMPORARY}/np_lastfm.txt"
|
||||
if [ -f "$tmp_file" ]; then
|
||||
if shell_is_osx || shell_is_bsd; then
|
||||
last_update=$(stat -f "%m" ${tmp_file})
|
||||
elif shell_is_linux; then
|
||||
last_update=$(stat -c "%Y" ${tmp_file})
|
||||
fi
|
||||
time_now=$(date +%s)
|
||||
|
||||
up_to_date=$(echo "(${time_now}-${last_update}) < ${TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_UPDATE_PERIOD}" | bc)
|
||||
if [ "$up_to_date" -eq 1 ]; then
|
||||
np=$(cat ${tmp_file})
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$np" ]; then
|
||||
np=$(curl --max-time 2 -s http://ws.audioscrobbler.com/1.0/user/${TMUX_POWERLINE_SEG_NOW_PLAYING_LASTFM_USERNAME}/recenttracks.txt | head -n 1 | sed -e 's/^[0-9]*,//' | sed 's/\xe2\x80\x93/-/')
|
||||
if [ "$?" -eq "0" ] && [ -n "$np" ]; then
|
||||
echo "${np}" > $tmp_file
|
||||
fi
|
||||
fi
|
||||
echo "$np"
|
||||
}
|
||||
|
||||
__np_pithos() {
|
||||
if [ "$(dbus-send --reply-timeout=10 --print-reply --dest=net.kevinmehall.Pithos /net/kevinmehall/Pithos net.kevinmehall.Pithos.IsPlaying 2>/dev/null | grep boolean | cut -d' ' -f5)" == "true" ]; then
|
||||
np=$(${TMUX_POWERLINE_DIR_SEGMENTS}/np_pithos.py)
|
||||
echo "$np"
|
||||
fi
|
||||
}
|
||||
|
||||
__np_mocp() {
|
||||
mocp_pid=$(pidof mocp)
|
||||
if [ -n "$mocp_pid" ]; then
|
||||
np=$(mocp -i | grep ^Title | sed "s/^Title://")
|
||||
mocp_paused=$(mocp -i | grep ^State | sed "s/^State: //")
|
||||
if [ -n "$np" -a "$mocp_paused" != "PAUSE" ]; then
|
||||
echo "$np"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__np_rdio() {
|
||||
[ ! shell_is_osx ] && return 1
|
||||
np=$(osascript ${TMUX_POWERLINE_DIR_SEGMENTS}/np_rdio_mac.script)
|
||||
echo "$np"
|
||||
}
|
||||
|
||||
__np_rhythmbox() {
|
||||
rhythmbox_pid=$(pidof rhythmbox)
|
||||
if [ -n "$rhythmbox_pid" ]; then
|
||||
np=$(rhythmbox-client --no-start --print-playing-format="$TMUX_POWERLINE_SEG_NOW_PLAYING_RHYTHMBOX_FORMAT")
|
||||
rhythmbox_paused=$(xwininfo -root -tree | grep "$np" | sed "s/${np}//;s/ //g" | cut -f2 -d '"')
|
||||
# TODO I cant produce the output "Not playing", using rhythmbox 2.97.
|
||||
#STATUS=$(rhythmbox-client --no-start --print-playing)
|
||||
if [[ "$rhythmbox_paused" != "(Paused)" ]]; then
|
||||
echo "$np"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__np_spotify() {
|
||||
if shell_is_linux; then
|
||||
metadata=$(dbus-send --reply-timeout=42 --print-reply --session --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' string:'Metadata' 2>/dev/null)
|
||||
if [ "$?" -eq 0 ] && [ -n "$metadata" ]; then
|
||||
# TODO how do one express this with dbus-send? It works with qdbus but the problem is that it's probably not as common as dbus-send.
|
||||
state=$(qdbus org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get org.mpris.MediaPlayer2.Player PlaybackStatus)
|
||||
if [[ $state == "Playing" ]]; then
|
||||
artist=$(echo "$metadata" | grep -PA2 "string\s\"xesam:artist\"" | tail -1 | grep -Po "(?<=\").*(?=\")")
|
||||
track=$(echo "$metadata" | grep -PA1 "string\s\"xesam:title\"" | tail -1 | grep -Po "(?<=\").*(?=\")")
|
||||
np=$(echo "${artist} - ${track}")
|
||||
fi
|
||||
fi
|
||||
elif shell_is_osx; then
|
||||
np=$(${TMUX_POWERLINE_DIR_SEGMENTS}/np_spotify_mac.script)
|
||||
fi
|
||||
echo "$np"
|
||||
}
|
||||
|
||||
__np_spotify_wine() {
|
||||
[ ! shell_is_linux ] && return 1
|
||||
spotify_id=$(xwininfo -root -tree | grep '("spotify' | cut -f1 -d'"' | sed 's/ //g')
|
||||
echo $spotify_id
|
||||
if [ -n "$spotify_id" ]; then
|
||||
np=$(xwininfo -id "$spotify_id" | grep "xwininfo.*Spotify -" | grep -Po "(?<=\"Spotify - ).*(?=\"$)")
|
||||
echo "$np"
|
||||
fi
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/env osascript
|
||||
# Returns the current playing song in iTunes for OSX
|
||||
|
||||
tell application "System Events"
|
||||
set process_list to (name of every process)
|
||||
end tell
|
||||
|
||||
if process_list contains "iTunes" then
|
||||
tell application "iTunes"
|
||||
if player state is playing then
|
||||
set track_name to name of current track
|
||||
set artist_name to artist of current track
|
||||
# set album_name to album of current track
|
||||
set trim_length to 40
|
||||
set now_playing to artist_name & " - " & track_name
|
||||
if length of now_playing is less than trim_length then
|
||||
set now_playing_trim to now_playing
|
||||
else
|
||||
set now_playing_trim to characters 1 thru trim_length of now_playing as string
|
||||
end if
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <mpd/client.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DO_DEBUG DEBUG
|
||||
#else
|
||||
#define DO_DEBUG 0
|
||||
#endif
|
||||
#define DEBUG_PRINTF(...) do{ if (DO_DEBUG) { printf(__VA_ARGS__);} } while(0)
|
||||
|
||||
/* Output the current song if MPD is in a playing state. The connection password, host and port is
|
||||
* specified like for mpc with environment variables
|
||||
* MPD_HOST=[password@]host
|
||||
* MPD_PORT=port
|
||||
* If they are empty they defaults to localhost on port 6600.
|
||||
*/
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
char *mpd_host = NULL;
|
||||
char *mpd_password = NULL;
|
||||
unsigned int mpd_port = 0;
|
||||
|
||||
char *mpd_host_m = NULL;
|
||||
char *mpd_password_m = NULL;
|
||||
|
||||
const char *mpd_host_str = getenv("MPD_HOST");
|
||||
if (mpd_host_str == NULL || strlen(mpd_host_str) == 0) {
|
||||
DEBUG_PRINTF("No envvar MPD_HOST set or empty. Using default value (localhost).\n");
|
||||
mpd_host = "localhost";
|
||||
} else {
|
||||
size_t mpd_host_len = strlen(mpd_host_str);
|
||||
char mpd_host_env[mpd_host_len + 1];
|
||||
strncpy(mpd_host_env, mpd_host_str, mpd_host_len);
|
||||
mpd_host_env[mpd_host_len] = '\0';
|
||||
|
||||
char *pch = strtok(mpd_host_env, "@");
|
||||
char *first = NULL;
|
||||
char *second = NULL;
|
||||
if (pch != NULL) {
|
||||
first = pch;
|
||||
}
|
||||
pch = strtok(NULL, "@");
|
||||
if (pch != NULL) {
|
||||
second = pch;
|
||||
}
|
||||
|
||||
if (first != NULL && second != NULL) {
|
||||
DEBUG_PRINTF("%s - %s\n", first, second);
|
||||
size_t first_len = strlen(first);
|
||||
size_t second_len = strlen(second);
|
||||
mpd_password_m = (char *) malloc(first_len + 1);
|
||||
mpd_host_m= (char *) malloc(second_len + 1);
|
||||
if (mpd_password_m == NULL || mpd_host_m == NULL) {
|
||||
DEBUG_PRINTF("Failed alloc password/host.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
strncpy(mpd_password_m, first, first_len);
|
||||
mpd_password_m[first_len] = '\0';
|
||||
strncpy(mpd_host_m, second, second_len);
|
||||
mpd_host_m[second_len] = '\0';
|
||||
} else if (first != NULL) {
|
||||
DEBUG_PRINTF("%s\n", first);
|
||||
size_t first_len = strlen(first);
|
||||
mpd_host_m = (char *) malloc(first_len + 1);
|
||||
if (mpd_host_m == NULL ) {
|
||||
DEBUG_PRINTF("Failed alloc host.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
strncpy(mpd_host_m, first, first_len);
|
||||
mpd_host_m[first_len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (mpd_host_m != NULL) {
|
||||
mpd_host = mpd_host_m;
|
||||
}
|
||||
|
||||
if (mpd_password_m != NULL) {
|
||||
mpd_password = mpd_password_m;
|
||||
}
|
||||
|
||||
const char *mpd_port_env = getenv("MPD_PORT");
|
||||
if (mpd_port_env == NULL || strlen(mpd_port_env) == 0) {
|
||||
DEBUG_PRINTF("No envvar MPD_PORT set or empty. Using default value (6600).\n");
|
||||
mpd_port = 6600;
|
||||
} else {
|
||||
int mpd_port_m = atoi(mpd_port_env);
|
||||
if (mpd_port_m == 0) {
|
||||
DEBUG_PRINTF("Could not convert MPD_PORT to int.\n");
|
||||
return EXIT_FAILURE;
|
||||
} else if (mpd_port_m < 0) {
|
||||
DEBUG_PRINTF("Negative port?!\n");
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
mpd_port = mpd_port_m;
|
||||
DEBUG_PRINTF("Using port %i\n", mpd_port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct mpd_connection *mpd_connection = mpd_connection_new(mpd_host, mpd_port, 1000);
|
||||
if (mpd_connection == NULL) {
|
||||
DEBUG_PRINTF("%s\n", "Could Not connect");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (mpd_password != NULL) {
|
||||
bool authenticated = mpd_run_password(mpd_connection, mpd_password);
|
||||
if (!authenticated) {
|
||||
DEBUG_PRINTF("Failed to authenticate.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
free(mpd_host_m);
|
||||
free(mpd_password_m);
|
||||
|
||||
bool sent_status = mpd_send_status(mpd_connection);
|
||||
if (!sent_status) {
|
||||
DEBUG_PRINTF("Could not send status request.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
struct mpd_status *mpd_status = mpd_recv_status(mpd_connection);
|
||||
if (mpd_status == NULL) {
|
||||
DEBUG_PRINTF("Could not get mpd status.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
enum mpd_state mpd_state = mpd_status_get_state(mpd_status);
|
||||
DEBUG_PRINTF("State: ");
|
||||
if (mpd_state == MPD_STATE_PLAY) {
|
||||
DEBUG_PRINTF("Playing.");
|
||||
} else if (mpd_state == MPD_STATE_PAUSE) {
|
||||
DEBUG_PRINTF("Paused.");
|
||||
} else if (mpd_state == MPD_STATE_UNKNOWN) {
|
||||
DEBUG_PRINTF("Unknown state.");
|
||||
} else if (mpd_state == MPD_STATE_STOP) {
|
||||
DEBUG_PRINTF("Stopped.");
|
||||
}
|
||||
DEBUG_PRINTF("\n");
|
||||
|
||||
if (mpd_state != MPD_STATE_PLAY) {
|
||||
// Nothing to do.
|
||||
mpd_status_free(mpd_status);
|
||||
mpd_connection_free(mpd_connection);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int song_id = mpd_status_get_song_id(mpd_status);
|
||||
DEBUG_PRINTF("songid = %i\n", song_id);
|
||||
|
||||
mpd_status_free(mpd_status);
|
||||
|
||||
struct mpd_song *song = mpd_run_get_queue_song_id(mpd_connection, song_id);
|
||||
if (song == NULL) {
|
||||
DEBUG_PRINTF("Could not get song.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const char *song_artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0);
|
||||
if (song_artist == NULL) {
|
||||
DEBUG_PRINTF("Could not get song artist.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const char *song_title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
|
||||
if (song_title == NULL) {
|
||||
DEBUG_PRINTF("Could not get song title.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("%s - %s\n", song_artist, song_title);
|
||||
|
||||
mpd_song_free(song);
|
||||
mpd_connection_free(mpd_connection);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import dbus
|
||||
|
||||
pithos_bus = dbus.SessionBus()
|
||||
|
||||
pithos = pithos_bus.get_object('net.kevinmehall.Pithos', '/net/kevinmehall/Pithos')
|
||||
props = pithos.get_dbus_method('GetCurrentSong', 'net.kevinmehall.Pithos')
|
||||
|
||||
print props()["artist"] + " - " + props()["title"]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env osascript
|
||||
# Returns the current playing song in Rdio for OS X.
|
||||
|
||||
tell application "System Events"
|
||||
set process_list to (name of every process)
|
||||
end tell
|
||||
|
||||
if process_list contains "Rdio" then
|
||||
tell application "Rdio"
|
||||
if player state is playing then
|
||||
set track_name to name of current track
|
||||
set artist_name to artist of current track
|
||||
set album_name to album of current track
|
||||
set now_playing to artist_name & " - " & track_name
|
||||
# Uncomment if Album name is wanted
|
||||
#set now_playing to now_playing & " - " & album_name
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env osascript
|
||||
# Returns the current playing song in Spotify for OSX
|
||||
|
||||
tell application "System Events"
|
||||
set process_list to (name of every process)
|
||||
end tell
|
||||
|
||||
if process_list contains "Spotify" then
|
||||
tell application "Spotify"
|
||||
if player state is playing or player state is paused then
|
||||
set track_name to name of current track
|
||||
set artist_name to artist of current track
|
||||
#set album_name to album of current track
|
||||
set trim_length to 40
|
||||
if player state is playing then
|
||||
set now_playing to "► " & artist_name & " - " & track_name
|
||||
else
|
||||
set now_playing to "❙❙ " & artist_name & " - " & track_name
|
||||
end if
|
||||
if length of now_playing is less than trim_length then
|
||||
set now_playing_trim to now_playing
|
||||
else
|
||||
set now_playing_trim to characters 1 thru trim_length of now_playing as string
|
||||
end if
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# Print the current working directory (trimmed to max length).
|
||||
# NOTE The trimming code's stolen from the web. Courtesy to who ever wrote it.
|
||||
|
||||
# Source lib to get the function get_tmux_pwd
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/tmux_adapter.sh"
|
||||
|
||||
TMUX_POWERLINE_SEG_PWD_MAX_LEN_DEFAULT="40"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# Maximum length of output.
|
||||
export TMUX_POWERLINE_SEG_PWD_MAX_LEN="${TMUX_POWERLINE_SEG_PWD_MAX_LEN_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_PWD_MAX_LEN" ]; then
|
||||
export TMUX_POWERLINE_SEG_PWD_MAX_LEN="${TMUX_POWERLINE_SEG_PWD_MAX_LEN_DEFAULT}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
# Truncate from the left.
|
||||
tcwd=$(get_tmux_cwd)
|
||||
trunc_symbol="···"
|
||||
dir=${tcwd##*/}
|
||||
local max_len="$TMUX_POWERLINE_SEG_PWD_MAX_LEN"
|
||||
max_len=$(( ( max_len < ${#dir} ) ? ${#dir} : max_len ))
|
||||
ttcwd=${tcwd/#$HOME/\~}
|
||||
pwdoffset=$(( ${#ttcwd} - max_len ))
|
||||
if [ ${pwdoffset} -gt "0" ]; then
|
||||
ttcwd=${ttcwd:$pwdoffset:$max_len}
|
||||
ttcwd=${trunc_symbol}/${ttcwd#*/}
|
||||
fi
|
||||
echo "$ttcwd"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# Print out Memory and CPU using https://github.com/creaktive/rainbarf
|
||||
|
||||
run_segment() {
|
||||
type rainbarf >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo 'rainbarf was not found'
|
||||
return
|
||||
fi
|
||||
|
||||
# Customize via ~/.rainbarf.conf
|
||||
stats=$(rainbarf --rgb --no-battery)
|
||||
if [ -n "$stats" ]; then
|
||||
echo "$stats";
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Prints the current time.
|
||||
|
||||
TMUX_POWERLINE_SEG_TIME_FORMAT_DEFAULT="%H:%M"
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# date(1) format for the time. Americans might want to have "%I:%M %p".
|
||||
export TMUX_POWERLINE_SEG_TIME_FORMAT="${TMUX_POWERLINE_SEG_TIME_FORMAT_DEFAULT}"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_TIME_FORMAT" ]; then
|
||||
export TMUX_POWERLINE_SEG_TIME_FORMAT="${TMUX_POWERLINE_SEG_TIME_FORMAT_DEFAULT}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
date +"$TMUX_POWERLINE_SEG_TIME_FORMAT"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# Print out Memory, cpu and load using https://github.com/thewtex/tmux-mem-cpu-load
|
||||
|
||||
run_segment() {
|
||||
type tmux-mem-cpu-load >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
stats=$(tmux-mem-cpu-load)
|
||||
if [ -n "$stats" ]; then
|
||||
echo "$stats";
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# Prints tmux session info.
|
||||
# Assuems that [ -n "$TMUX"].
|
||||
|
||||
run_segment() {
|
||||
tmux display-message -p '#S:#I.#P'
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
# Prints the uptime.
|
||||
|
||||
run_segment() {
|
||||
uptime | sed 's/.*up \([^,]*\), .*/\1/'
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
# Prints the current time in UTC.
|
||||
|
||||
run_segment() {
|
||||
date -u +"%H:%M"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
# Prints current branch in a VCS directory if it could be detected.
|
||||
|
||||
# Source lib to get the function get_tmux_pwd
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/tmux_adapter.sh"
|
||||
|
||||
branch_symbol=""
|
||||
git_colour="5"
|
||||
svn_colour="220"
|
||||
hg_colour="45"
|
||||
|
||||
|
||||
run_segment() {
|
||||
tmux_path=$(get_tmux_cwd)
|
||||
cd "$tmux_path"
|
||||
branch=""
|
||||
if [ -n "${git_branch=$(__parse_git_branch)}" ]; then
|
||||
branch="$git_branch"
|
||||
elif [ -n "${svn_branch=$(__parse_svn_branch)}" ]; then
|
||||
branch="$svn_branch"
|
||||
elif [ -n "${hg_branch=$(__parse_hg_branch)}" ]; then
|
||||
branch="$hg_branch"
|
||||
fi
|
||||
|
||||
if [ -n "$branch" ]; then
|
||||
echo "${branch}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# Show git banch.
|
||||
__parse_git_branch() {
|
||||
type git >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
#git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ \[\1\]/'
|
||||
|
||||
# Quit if this is not a Git repo.
|
||||
branch=$(git symbolic-ref HEAD 2> /dev/null)
|
||||
if [[ -z $branch ]] ; then
|
||||
# attempt to get short-sha-name
|
||||
branch=":$(git rev-parse --short HEAD 2> /dev/null)"
|
||||
fi
|
||||
if [ "$?" -ne 0 ]; then
|
||||
# this must not be a git repo
|
||||
return
|
||||
fi
|
||||
|
||||
# Clean off unnecessary information.
|
||||
branch=${branch#refs\/heads\/}
|
||||
|
||||
echo -n "#[fg=colour${git_colour}]${branch_symbol} #[fg=colour${TMUX_POWERLINE_CUR_SEGMENT_FG}]${branch}"
|
||||
}
|
||||
|
||||
# Show SVN branch.
|
||||
__parse_svn_branch() {
|
||||
type svn >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local svn_info=$(svn info 2>/dev/null)
|
||||
if [ -z "${svn_info}" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
local svn_root=$(echo "${svn_info}" | sed -ne 's#^Repository Root: ##p')
|
||||
local svn_url=$(echo "${svn_info}" | sed -ne 's#^URL: ##p')
|
||||
|
||||
local branch=$(echo "${svn_url}" | egrep -o '[^/]+$')
|
||||
echo "#[fg=colour${svn_colour}]${branch_symbol} #[fg=colour${TMUX_POWERLINE_CUR_SEGMENT_FG}]${branch}"
|
||||
}
|
||||
|
||||
__parse_hg_branch() {
|
||||
type hg >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
summary=$(hg summary)
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local branch=$(echo "$summary" | grep 'branch:' | cut -d ' ' -f2)
|
||||
echo "#[fg=colour${hg_colour}]${branch_symbol} #[fg=colour${TMUX_POWERLINE_CUR_SEGMENT_FG}]${branch}"
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
# This checks if the current branch is ahead of
|
||||
# or behind the remote branch with which it is tracked
|
||||
|
||||
# Source lib to get the function get_tmux_pwd
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/tmux_adapter.sh"
|
||||
|
||||
flat_symbol="⤚"
|
||||
|
||||
run_segment() {
|
||||
tmux_path=$(get_tmux_cwd)
|
||||
cd "$tmux_path"
|
||||
stats=""
|
||||
if [ -n "${git_stats=$(__parse_git_stats)}" ]; then
|
||||
stats="$git_stats"
|
||||
elif [ -n "${svn_stats=$(__parse_svn_stats)}" ]; then
|
||||
stats="$svn_stats"
|
||||
elif [ -n "${hg_stats=$(__parse_hg_stats)}" ]; then
|
||||
stats="$hg_stats"
|
||||
fi
|
||||
|
||||
if [ -n "$stats" ]; then
|
||||
echo "${stats}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
__parse_git_stats() {
|
||||
type git >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# check if git
|
||||
[[ -z $(git rev-parse --git-dir 2> /dev/null) ]] && return
|
||||
|
||||
symbolic_ref=$(git symbolic-ref -q HEAD)
|
||||
[[ -z "${symbolic_ref}" ]] && return
|
||||
|
||||
tracking_branch=$(git for-each-ref --format='%(upstream:short)' ${symbolic_ref})
|
||||
|
||||
# creates global variables $1 and $2 based on left vs. right tracking
|
||||
set -- $(git rev-list --left-right --count $tracking_branch...HEAD)
|
||||
|
||||
behind=$1
|
||||
ahead=$2
|
||||
|
||||
# print out the information
|
||||
if [[ $behind -gt 0 ]] ; then
|
||||
local ret="↓ $behind"
|
||||
fi
|
||||
if [[ $ahead -gt 0 ]] ; then
|
||||
local ret="${ret}↑ $ahead"
|
||||
fi
|
||||
echo "$ret"
|
||||
}
|
||||
|
||||
__parse_hg_stats() {
|
||||
type hg >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
|
||||
__parse_svn_stats() {
|
||||
type svn >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
# This checks if the current branch is ahead of
|
||||
# or behind the remote branch with which it is tracked
|
||||
|
||||
# Source lib to get the function get_tmux_pwd
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/tmux_adapter.sh"
|
||||
|
||||
mod_symbol="﹢"
|
||||
|
||||
run_segment() {
|
||||
tmux_path=$(get_tmux_cwd)
|
||||
cd "$tmux_path"
|
||||
|
||||
stats=""
|
||||
if [ -n "${git_stats=$(__parse_git_stats)}" ]; then
|
||||
stats="$git_stats"
|
||||
elif [ -n "${svn_stats=$(__parse_svn_stats)}" ]; then
|
||||
stats="$svn_stats"
|
||||
elif [ -n "${hg_stats=$(__parse_hg_stats)}" ]; then
|
||||
stats="$hg_stats"
|
||||
fi
|
||||
if [[ -n "$stats" && $stats -gt 0 ]]; then
|
||||
echo "${mod_symbol}${stats}"
|
||||
fi
|
||||
}
|
||||
|
||||
__parse_git_stats(){
|
||||
type git >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# check if git
|
||||
[[ -z $(git rev-parse --git-dir 2> /dev/null) ]] && return
|
||||
|
||||
# return the number of modified but not staged items
|
||||
modified=$(git ls-files --modified `git rev-parse --show-cdup` | wc -l)
|
||||
echo $modified
|
||||
}
|
||||
__parse_hg_stats(){
|
||||
type hg >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
__parse_svn_stats() {
|
||||
type svn >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local svn_info=$(svn info 2>/dev/null)
|
||||
if [ -z "${svn_info}" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local svn_wcroot=$(echo "${svn_info}" | sed -ne 's#^Working Copy Root Path: ##p')
|
||||
local svn_st=$(cd "${svn_wcroot}"; svn st)
|
||||
local modified=$(echo "${svn_st}" | egrep '^M' | wc -l)
|
||||
local conflicted=$(echo "${svn_st}" | egrep '^!?\s*C' | wc -l)
|
||||
|
||||
#print
|
||||
if [[ $modified -gt 0 ]] ; then
|
||||
local ret="#[fg=colour${TMUX_POWERLINE_CUR_SEGMENT_FG}]±${modified}"
|
||||
fi
|
||||
if [[ $conflicted -gt 0 ]] ; then
|
||||
local ret="#[fg=colour${svn_colour}]ϟ${conflicted} ${ret}"
|
||||
fi
|
||||
echo "${ret}"
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
# This checks if the current branch is ahead of or behind the remote branch with which it is tracked.
|
||||
|
||||
# Source lib to get the function get_tmux_pwd
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/tmux_adapter.sh"
|
||||
|
||||
other_symbol="⋯ "
|
||||
|
||||
run_segment() {
|
||||
tmux_path=$(get_tmux_cwd)
|
||||
cd "$tmux_path"
|
||||
stats=""
|
||||
if [ -n "${git_stats=$(__parse_git_stats)}" ]; then
|
||||
stats="$git_stats"
|
||||
elif [ -n "${svn_stats=$(__parse_svn_stats)}" ]; then
|
||||
stats="$svn_stats"
|
||||
elif [ -n "${hg_stats=$(__parse_hg_stats)}" ]; then
|
||||
stats="$hg_stats"
|
||||
fi
|
||||
if [[ -n "$stats" && $stats -gt 0 ]]; then
|
||||
echo "${other_symbol}${stats}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
__parse_git_stats(){
|
||||
type git >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# check if git
|
||||
[[ -z $(git rev-parse --git-dir 2> /dev/null) ]] && return
|
||||
|
||||
# return the number of untracked items
|
||||
other=$(git ls-files --others --exclude-standard `git rev-parse --show-cdup` | wc -l)
|
||||
echo $other
|
||||
}
|
||||
__parse_hg_stats(){
|
||||
type svn >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
__parse_svn_stats(){
|
||||
type hg >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# This prints the vcs revision in the working directory
|
||||
# currently only used in SVN
|
||||
|
||||
# Source lib to get the function get_tmux_pwd
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/tmux_adapter.sh"
|
||||
|
||||
run_segment() {
|
||||
tmux_path=$(get_tmux_cwd)
|
||||
cd "$tmux_path"
|
||||
|
||||
stats=""
|
||||
if [[ -n "${svn_stats=$(__parse_svn_stats)}" ]]; then
|
||||
stats="$svn_stats"
|
||||
elif [[ -n "${hg_stats=$(__parse_hg_stats)}" ]]; then
|
||||
stats="$hg_stats"
|
||||
fi
|
||||
if [[ -n "$stats" ]]; then
|
||||
echo "${stats}"
|
||||
fi
|
||||
}
|
||||
|
||||
__parse_hg_stats(){
|
||||
type hg >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
__parse_svn_stats(){
|
||||
type svn >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local svn_info=$(svn info 2>/dev/null)
|
||||
if [ -z "${svn_info}" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local svn_ref=$(echo "${svn_info}" | sed -ne 's#^Revision: ##p')
|
||||
|
||||
echo "r${svn_ref}"
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# This checks if the current branch is ahead of
|
||||
# or behind the remote branch with which it is tracked
|
||||
|
||||
# Source lib to get the function get_tmux_pwd
|
||||
source "${TMUX_POWERLINE_DIR_LIB}/tmux_adapter.sh"
|
||||
|
||||
staged_symbol="⊕ "
|
||||
|
||||
run_segment() {
|
||||
tmux_path=$(get_tmux_cwd)
|
||||
cd "$tmux_path"
|
||||
|
||||
stats=""
|
||||
if [ -n "${git_stats=$(__parse_git_stats)}" ]; then
|
||||
stats="$git_stats"
|
||||
elif [ -n "${svn_stats=$(__parse_svn_stats)}" ]; then
|
||||
stats="$svn_stats"
|
||||
elif [ -n "${hg_stats=$(__parse_hg_stats)}" ]; then
|
||||
stats="$hg_stats"
|
||||
fi
|
||||
if [[ -n "$stats" && $stats -gt 0 ]]; then
|
||||
stats=$(echo $stats | sed -e "s/^[ \t]*//")
|
||||
echo "${staged_symbol}${stats}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
__parse_git_stats(){
|
||||
type git >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if git.
|
||||
[[ -z $(git rev-parse --git-dir 2> /dev/null) ]] && return
|
||||
|
||||
# Return the number of staged items.
|
||||
staged=$(git diff --staged --name-status | wc -l)
|
||||
echo "$staged"
|
||||
}
|
||||
|
||||
__parse_hg_stats(){
|
||||
type svn >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
|
||||
__parse_svn_stats(){
|
||||
type hg >/dev/null 2>&1
|
||||
if [ "$?" -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
# not yet implemented
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# Prints the WAN IP address. The result is cached and updated according to $update_period.
|
||||
|
||||
run_segment() {
|
||||
local tmp_file="${TMUX_POWERLINE_DIR_TEMPORARY}/wan_ip.txt"
|
||||
local wan_ip
|
||||
|
||||
if [ -f "$tmp_file" ]; then
|
||||
if shell_is_osx || shell_is_bsd; then
|
||||
stat >/dev/null 2>&1 && is_gnu_stat=false || is_gnu_stat=true
|
||||
if [ "$is_gnu_stat" == "true" ];then
|
||||
last_update=$(stat -c "%Y" ${tmp_file})
|
||||
else
|
||||
last_update=$(stat -f "%m" ${tmp_file})
|
||||
fi
|
||||
elif shell_is_linux || [ -z $is_gnu_stat]; then
|
||||
last_update=$(stat -c "%Y" ${tmp_file})
|
||||
fi
|
||||
|
||||
time_now=$(date +%s)
|
||||
update_period=900
|
||||
up_to_date=$(echo "(${time_now}-${last_update}) < ${update_period}" | bc)
|
||||
|
||||
if [ "$up_to_date" -eq 1 ]; then
|
||||
wan_ip=$(cat ${tmp_file})
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$wan_ip" ]; then
|
||||
wan_ip=$(curl --max-time 2 -s http://whatismyip.akamai.com/)
|
||||
|
||||
if [ "$?" -eq "0" ]; then
|
||||
echo "${wan_ip}" > $tmp_file
|
||||
elif [ -f "${tmp_file}" ]; then
|
||||
wan_ip=$(cat "$tmp_file")
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$wan_ip" ]; then
|
||||
echo "ⓦ ${wan_ip}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
# Prints the current weather in Celsius, Fahrenheits or lord Kelvins. The forecast is cached and updated with a period of $update_period.
|
||||
|
||||
# The update period in seconds.
|
||||
update_period=1
|
||||
|
||||
TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER_DEFAULT="yahoo"
|
||||
TMUX_POWERLINE_SEG_WEATHER_UNIT_DEFAULT="c"
|
||||
TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD_DEFAULT="1"
|
||||
|
||||
# If you want to set your location, set TMUX_POWERLINE_SEG_WEATHER_LOCATION in the tmux-powerlinerc file.
|
||||
# 1. You create tmux-powerlinerc file.
|
||||
# $ ./generate_rc.sh
|
||||
# $ mv ~/.tmux-powerlinerc.default ~/.tmux-powerlinerc
|
||||
# 2. You set TMUX_POWERLINE_SEG_WEATHER_LOCATION.
|
||||
|
||||
if shell_is_bsd && [ -f /user/local/bin/grep ]; then
|
||||
TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT="/usr/local/bin/ggrep"
|
||||
else
|
||||
TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT="grep"
|
||||
fi
|
||||
|
||||
|
||||
generate_segmentrc() {
|
||||
read -d '' rccontents << EORC
|
||||
# The data provider to use. Currently only "yahoo" is supported.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER="${TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER_DEFAULT}"
|
||||
# What unit to use. Can be any of {c,f,k}.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_UNIT="${TMUX_POWERLINE_SEG_WEATHER_UNIT_DEFAULT}"
|
||||
# How often to update the weather in seconds.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD_DEFAULT}"
|
||||
# Name of GNU grep binary if in PATH, or path to it.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_GREP="${TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT}"
|
||||
# Your location. Find a code that works for you:
|
||||
# 1. Go to Yahoo weather http://weather.yahoo.com/
|
||||
# 2. Find the weather for you location
|
||||
# 3. Copy the last numbers in that URL. e.g. "http://weather.yahoo.com/united-states/california/newport-beach-12796587/" has the numbers "12796587"
|
||||
EORC
|
||||
echo "$rccontents"
|
||||
}
|
||||
|
||||
TMUX_POWERLINE_SEG_WEATHER_LOCATION="2365397"
|
||||
|
||||
run_segment() {
|
||||
__process_settings
|
||||
local tmp_file="${TMUX_POWERLINE_DIR_TEMPORARY}/weather_yahoo.txt"
|
||||
local weather
|
||||
case "$TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER" in
|
||||
"yahoo") weather=$(__yahoo_weather) ;;
|
||||
*)
|
||||
echo "Unknown weather provider [${$TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER}]";
|
||||
return 1
|
||||
esac
|
||||
if [ -n "$weather" ]; then
|
||||
echo "$weather"
|
||||
fi
|
||||
}
|
||||
|
||||
__process_settings() {
|
||||
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER" ]; then
|
||||
export TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER="${TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_UNIT" ]; then
|
||||
export TMUX_POWERLINE_SEG_WEATHER_UNIT="${TMUX_POWERLINE_SEG_WEATHER_UNIT_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD" ]; then
|
||||
export TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD="${TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_GREP" ]; then
|
||||
export TMUX_POWERLINE_SEG_WEATHER_GREP="${TMUX_POWERLINE_SEG_WEATHER_GREP_DEFAULT}"
|
||||
fi
|
||||
if [ -z "$TMUX_POWERLINE_SEG_WEATHER_LOCATION" ]; then
|
||||
echo "No weather location specified.";
|
||||
exit 8
|
||||
fi
|
||||
}
|
||||
|
||||
__yahoo_weather() {
|
||||
degree=""
|
||||
if [ -f "$tmp_file" ]; then
|
||||
if shell_is_osx || shell_is_bsd; then
|
||||
last_update=$(stat -f "%m" ${tmp_file})
|
||||
elif shell_is_linux; then
|
||||
last_update=$(stat -c "%Y" ${tmp_file})
|
||||
fi
|
||||
time_now=$(date +%s)
|
||||
|
||||
up_to_date=$(echo "(${time_now}-${last_update}) < ${update_period}" | bc)
|
||||
if [ "$up_to_date" -eq 1 ]; then
|
||||
__read_tmp_file
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$degree" ]; then
|
||||
weather_data=$(curl --max-time 4 -s "https://query.yahooapis.com/v1/public/yql?format=xml&q=SELECT%20*%20FROM%20weather.forecast%20WHERE%20u=%27${TMUX_POWERLINE_SEG_WEATHER_UNIT}%27%20AND%20woeid%20=%20%27${TMUX_POWERLINE_SEG_WEATHER_LOCATION}%27")
|
||||
if [ "$?" -eq "0" ]; then
|
||||
error=$(echo "$weather_data" | grep "problem_cause\|DOCTYPE");
|
||||
if [ -n "$error" ]; then
|
||||
echo "error"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Assume latest grep is in PATH
|
||||
gnugrep="${TMUX_POWERLINE_SEG_WEATHER_GREP}"
|
||||
|
||||
# <yweather:units temperature="F" distance="mi" pressure="in" speed="mph"/>
|
||||
unit=$(echo "$weather_data" | "$gnugrep" -Zo "<yweather:units [^<>]*/>" | sed 's/.*temperature="\([^"]*\)".*/\1/')
|
||||
condition=$(echo "$weather_data" | "$gnugrep" -Zo "<yweather:condition [^<>]*/>")
|
||||
# <yweather:condition text="Clear" code="31" temp="66" date="Mon, 01 Oct 2012 8:00 pm CST" />
|
||||
degree=$(echo "$condition" | sed 's/.*temp="\([^"]*\)".*/\1/')
|
||||
condition=$(echo "$condition" | sed 's/.*text="\([^"]*\)".*/\1/')
|
||||
# Pull the times for sunrise and sunset so we know when to change the day/night indicator
|
||||
# <yweather:astronomy sunrise="6:56 am" sunset="6:21 pm"/>
|
||||
if shell_is_osx || shell_is_bsd; then
|
||||
date_arg='-j -f "%H:%M %p "'
|
||||
else
|
||||
date_arg='-d'
|
||||
fi
|
||||
sunrise=$(date ${date_arg}"$(echo "$weather_data" | "$gnugrep" "yweather:astronomy" | sed 's/^\(.*\)sunset.*/\1/' | sed 's/^.*sunrise="\(.*m\)".*/\1/')" +%H%M)
|
||||
sunset=$(date ${date_arg}"$(echo "$weather_data" | "$gnugrep" "yweather:astronomy" | sed 's/^.*sunset="\(.*m\)".*/\1/')" +%H%M)
|
||||
elif [ -f "${tmp_file}" ]; then
|
||||
__read_tmp_file
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$degree" ]; then
|
||||
if [ "$TMUX_POWERLINE_SEG_WEATHER_UNIT" == "k" ]; then
|
||||
degree=$(echo "${degree} + 273.15" | bc)
|
||||
fi
|
||||
condition_symbol=$(__get_condition_symbol "$condition" "$sunrise" "$sunset")
|
||||
echo "${condition_symbol} ${degree}°$(echo "$TMUX_POWERLINE_SEG_WEATHER_UNIT" | tr '[:lower:]' '[:upper:]')" | tee "${tmp_file}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get symbol for condition. Available conditions: http://developer.yahoo.com/weather/#codes
|
||||
__get_condition_symbol() {
|
||||
local condition=$(echo "$1" | tr '[:upper:]' '[:lower:]')
|
||||
local sunrise="$2"
|
||||
local sunset="$3"
|
||||
case "$condition" in
|
||||
"sunny" | "hot")
|
||||
hourmin=$(date +%H%M)
|
||||
if [ "$hourmin" -ge "$sunset" -o "$hourmin" -le "$sunrise" ]; then
|
||||
#echo "☽"
|
||||
echo "☾"
|
||||
else
|
||||
#echo "☀"
|
||||
echo "☼"
|
||||
fi
|
||||
;;
|
||||
"rain" | "mixed rain and snow" | "mixed rain and sleet" | "freezing drizzle" | "drizzle" | "light drizzle" | "freezing rain" | "showers" | "mixed rain and hail" | "scattered showers" | "isolated thundershowers" | "thundershowers" | "light rain with thunder" | "light rain" | "rain and snow")
|
||||
#echo "☂"
|
||||
echo "☔"
|
||||
;;
|
||||
"snow" | "mixed snow and sleet" | "snow flurries" | "light snow showers" | "blowing snow" | "sleet" | "hail" | "heavy snow" | "scattered snow showers" | "snow showers" | "light snow" | "snow/windy" | "snow grains" | "snow/fog")
|
||||
#echo "☃"
|
||||
echo "❅"
|
||||
;;
|
||||
"cloudy" | "mostly cloudy" | "partly cloudy" | "partly cloudy/windy")
|
||||
echo "☁"
|
||||
;;
|
||||
"tornado" | "tropical storm" | "hurricane" | "severe thunderstorms" | "thunderstorms" | "isolated thunderstorms" | "scattered thunderstorms")
|
||||
#echo "⚡"
|
||||
echo "☈"
|
||||
;;
|
||||
"dust" | "foggy" | "fog" | "haze" | "smoky" | "blustery" | "mist")
|
||||
#echo "♨"
|
||||
#echo "﹌"
|
||||
echo "〰"
|
||||
;;
|
||||
"breezy")
|
||||
#echo "🌬"
|
||||
echo "🍃"
|
||||
;;
|
||||
"windy" | "fair/windy")
|
||||
#echo "⚐"
|
||||
echo "⚑"
|
||||
;;
|
||||
"clear" | "fair" | "cold")
|
||||
hourmin=$(date +%H%M)
|
||||
if [ "$hourmin" -ge "$sunset" -o "$hourmin" -le "$sunrise" ]; then
|
||||
echo "☾"
|
||||
else
|
||||
echo "〇"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "?"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
__read_tmp_file() {
|
||||
if [ ! -f "$tmp_file" ]; then
|
||||
return
|
||||
fi
|
||||
cat "${tmp_file}"
|
||||
exit
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/* xkb_layout
|
||||
* Description:
|
||||
* This program will connect to the X Server and print the id of the currently
|
||||
* active keyboard layout.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DO_DEBUG DEBUG
|
||||
#else
|
||||
#define DO_DEBUG 0
|
||||
#endif
|
||||
#define DEBUG_PRINTF(...) do{ if (DO_DEBUG) { printf(__VA_ARGS__);} } while(0)
|
||||
|
||||
int main() {
|
||||
// Get X display
|
||||
char *displayName = "";
|
||||
int eventCode;
|
||||
int errorReturn;
|
||||
int major = XkbMajorVersion;
|
||||
int minor = XkbMinorVersion;;
|
||||
int reasonReturn;
|
||||
Display *_display = XkbOpenDisplay(displayName, &eventCode, &errorReturn,
|
||||
&major, &minor, &reasonReturn);
|
||||
bool error = false;
|
||||
switch (reasonReturn) {
|
||||
case XkbOD_BadLibraryVersion:
|
||||
DEBUG_PRINTF("Bad XKB library version.\n");
|
||||
error = true;
|
||||
break;
|
||||
case XkbOD_ConnectionRefused:
|
||||
DEBUG_PRINTF("Connection to X server refused.\n");
|
||||
error = true;
|
||||
break;
|
||||
case XkbOD_BadServerVersion:
|
||||
DEBUG_PRINTF("Bad X11 server version.\n");
|
||||
error = true;
|
||||
break;
|
||||
case XkbOD_NonXkbServer:
|
||||
DEBUG_PRINTF("XKB not present.\n");
|
||||
error = true;
|
||||
break;
|
||||
case XkbOD_Success:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Get current state of keyboard.
|
||||
int _deviceId = XkbUseCoreKbd;
|
||||
XkbStateRec xkbState;
|
||||
XkbGetState(_display, _deviceId, &xkbState);
|
||||
// print the groupnumber, may be used with setxkbmap -query to get name
|
||||
// of current layout
|
||||
printf("%d\n", xkbState.group);
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
# Print the currently used keyboard layout
|
||||
# This depends on a specifically developed program which prints the group id of
|
||||
# the currently used layout.
|
||||
# I developed the simple program myself with some guidance as I was unable to
|
||||
# find anything already developed.
|
||||
# Some people might suggest:
|
||||
# $ setxkbmod -query -v | awk -F "+" '{print $2}'
|
||||
# this will only work if you have set up XKB with a single layout which is true
|
||||
# for some.
|
||||
|
||||
# This script will print the correct layout even if layout is set per window.
|
||||
# Exit if platform is not linux as this script is dependant on X11
|
||||
|
||||
run_segment() {
|
||||
if ! shell_is_linux; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
cd "$TMUX_POWERLINE_DIR_SEGMENTS"
|
||||
if [ ! -x "xkb_layout" ]; then
|
||||
make clean xkb_layout &>/dev/null
|
||||
fi
|
||||
|
||||
if [ -x ./xkb_layout ]; then
|
||||
cur_layout_nbr=$(($(./xkb_layout)+1));
|
||||
cur_layout=$(setxkbmap -query | grep layout | sed 's/layout:\s\+//g' | \
|
||||
awk -F ',' '{print $'$(echo "$cur_layout_nbr")'}')
|
||||
echo "⌨ $cur_layout"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
# Default Theme
|
||||
|
||||
if patched_font_in_use; then
|
||||
TMUX_POWERLINE_SEPARATOR_LEFT_BOLD=""
|
||||
TMUX_POWERLINE_SEPARATOR_LEFT_THIN=""
|
||||
TMUX_POWERLINE_SEPARATOR_RIGHT_BOLD=""
|
||||
TMUX_POWERLINE_SEPARATOR_RIGHT_THIN=""
|
||||
else
|
||||
TMUX_POWERLINE_SEPARATOR_LEFT_BOLD="◀"
|
||||
TMUX_POWERLINE_SEPARATOR_LEFT_THIN="❮"
|
||||
TMUX_POWERLINE_SEPARATOR_RIGHT_BOLD="▶"
|
||||
TMUX_POWERLINE_SEPARATOR_RIGHT_THIN="❯"
|
||||
fi
|
||||
|
||||
TMUX_POWERLINE_DEFAULT_BACKGROUND_COLOR=${TMUX_POWERLINE_DEFAULT_BACKGROUND_COLOR:-'235'}
|
||||
TMUX_POWERLINE_DEFAULT_FOREGROUND_COLOR=${TMUX_POWERLINE_DEFAULT_FOREGROUND_COLOR:-'255'}
|
||||
|
||||
TMUX_POWERLINE_DEFAULT_LEFTSIDE_SEPARATOR=${TMUX_POWERLINE_DEFAULT_LEFTSIDE_SEPARATOR:-$TMUX_POWERLINE_SEPARATOR_RIGHT_BOLD}
|
||||
TMUX_POWERLINE_DEFAULT_RIGHTSIDE_SEPARATOR=${TMUX_POWERLINE_DEFAULT_RIGHTSIDE_SEPARATOR:-$TMUX_POWERLINE_SEPARATOR_LEFT_BOLD}
|
||||
|
||||
export TMUX_POWERLINE_SEG_WEATHER_LOCATION="2365397"
|
||||
# The data provider to use. Currently only "yahoo" is supported.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_DATA_PROVIDER="yahoo"
|
||||
# What unit to use. Can be any of {c,f,k}.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_UNIT="c"
|
||||
# How often to update the weather in seconds.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_UPDATE_PERIOD="600"
|
||||
# Name of GNU grep binary if in PATH, or path to it.
|
||||
export TMUX_POWERLINE_SEG_WEATHER_GREP="/usr/local/bin/ggrep"
|
||||
|
||||
|
||||
# Format: segment_name background_color foreground_color [non_default_separator]
|
||||
|
||||
if [ -z $TMUX_POWERLINE_LEFT_STATUS_SEGMENTS ]; then
|
||||
TMUX_POWERLINE_LEFT_STATUS_SEGMENTS=(
|
||||
# "tmux_session_info 148 234" \
|
||||
#"hostname 33 0" \
|
||||
"ifstat 30 255" \
|
||||
# "ifstat_sys 30 255" \
|
||||
# "lan_ip 24 255 ${TMUX_POWERLINE_SEPARATOR_RIGHT_THIN}" \
|
||||
# "wan_ip 24 255" \
|
||||
#"vcs_branch 29 88" \
|
||||
#"vcs_compare 60 255" \
|
||||
#"vcs_staged 64 255" \
|
||||
#"vcs_modified 9 255" \
|
||||
#"vcs_others 245 0" \
|
||||
)
|
||||
fi
|
||||
|
||||
if [ -z $TMUX_POWERLINE_RIGHT_STATUS_SEGMENTS ]; then
|
||||
TMUX_POWERLINE_RIGHT_STATUS_SEGMENTS=(
|
||||
#"earthquake 3 0" \
|
||||
#"pwd 89 211" \
|
||||
#"macos_notification_count 29 255" \
|
||||
"mailcount 9 255" \
|
||||
"now_playing 234 37" \
|
||||
#"cpu 240 136" \
|
||||
#"load 237 167" \
|
||||
#"tmux_mem_cpu_load 234 136" \
|
||||
#"battery 137 127" \
|
||||
"weather 37 255" \
|
||||
"rainbarf 0 ${TMUX_POWERLINE_DEFAULT_FOREGROUND_COLOR}" \
|
||||
#"xkb_layout 125 117" \
|
||||
"date_day 235 136" \
|
||||
"date 235 136 ${TMUX_POWERLINE_SEPARATOR_LEFT_THIN}" \
|
||||
"time 235 136 ${TMUX_POWERLINE_SEPARATOR_LEFT_THIN}" \
|
||||
#"utc_time 235 136 ${TMUX_POWERLINE_SEPARATOR_LEFT_THIN}" \
|
||||
)
|
||||
fi
|
||||
209
tmux/tmux-status/left.sh
Executable file
209
tmux/tmux-status/left.sh
Executable file
|
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
current_session_id="${1:-}"
|
||||
current_session_name="${2:-}"
|
||||
|
||||
detect_current_session_id=$(tmux display-message -p '#{session_id}')
|
||||
detect_current_session_name=$(tmux display-message -p '#{session_name}')
|
||||
|
||||
if [[ -z "$current_session_id" ]]; then
|
||||
current_session_id="$detect_current_session_id"
|
||||
fi
|
||||
|
||||
if [[ -z "$current_session_name" ]]; then
|
||||
current_session_name="$detect_current_session_name"
|
||||
fi
|
||||
|
||||
status_bg=$(tmux show -gqv status-bg)
|
||||
if [[ -z "$status_bg" || "$status_bg" == "default" ]]; then
|
||||
status_bg=black
|
||||
fi
|
||||
|
||||
inactive_bg="#373b41"
|
||||
inactive_fg="#c5c8c6"
|
||||
active_bg="#b294bb"
|
||||
active_fg="#1d1f21"
|
||||
separator=""
|
||||
left_cap="█"
|
||||
max_width=18
|
||||
|
||||
normalize_session_id() {
|
||||
local value="$1"
|
||||
value="${value#\$}"
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
trim_label() {
|
||||
local value="$1"
|
||||
if [[ "$value" =~ ^[0-9]+-(.*)$ ]]; then
|
||||
printf '%s' "${BASH_REMATCH[1]}"
|
||||
else
|
||||
printf '%s' "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
tracker_pending_sessions=""
|
||||
tracker_running_sessions=""
|
||||
TRACKER_CACHE_DIR="$HOME/.config/tmux/cache"
|
||||
TRACKER_CACHE_FILE="$TRACKER_CACHE_DIR/tracker_state_lines"
|
||||
TRACKER_CACHE_TTL=1
|
||||
|
||||
file_mtime() {
|
||||
local path="$1"
|
||||
[[ ! -e "$path" ]] && return 1
|
||||
|
||||
local mtime
|
||||
mtime=$(stat -f %m "$path" 2>/dev/null || true)
|
||||
if [[ -n "$mtime" ]]; then
|
||||
printf '%s\n' "$mtime"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mtime=$(stat -c %Y "$path" 2>/dev/null || true)
|
||||
if [[ -n "$mtime" ]]; then
|
||||
printf '%s\n' "$mtime"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
write_tracker_cache() {
|
||||
mkdir -p "$TRACKER_CACHE_DIR"
|
||||
local tmp_file="$TRACKER_CACHE_FILE.$$"
|
||||
printf '%s\n' "$1" > "$tmp_file"
|
||||
mv "$tmp_file" "$TRACKER_CACHE_FILE"
|
||||
}
|
||||
|
||||
reset_tracker_state() {
|
||||
tracker_pending_sessions=""
|
||||
tracker_running_sessions=""
|
||||
}
|
||||
|
||||
load_tracker_info() {
|
||||
reset_tracker_state
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
|
||||
local tracker_lines=""
|
||||
local now
|
||||
now=$(date +%s 2>/dev/null || true)
|
||||
|
||||
if [[ -f "$TRACKER_CACHE_FILE" && -n "$now" ]]; then
|
||||
local cache_mtime
|
||||
cache_mtime=$(file_mtime "$TRACKER_CACHE_FILE" 2>/dev/null || true)
|
||||
if [[ -n "$cache_mtime" ]]; then
|
||||
local age=$(( now - cache_mtime ))
|
||||
if (( age <= TRACKER_CACHE_TTL )); then
|
||||
tracker_lines=$(cat "$TRACKER_CACHE_FILE" 2>/dev/null || true)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$tracker_lines" ]]; then
|
||||
local tracker_state
|
||||
tracker_state=$(~/.config/agent-tracker/bin/tracker-client state 2>/dev/null || true)
|
||||
if [[ -z "$tracker_state" ]]; then
|
||||
rm -f "$TRACKER_CACHE_FILE" 2>/dev/null || true
|
||||
return
|
||||
fi
|
||||
|
||||
tracker_lines=$(printf '%s\n' "$tracker_state" | jq -r 'select(.kind == "state") | .tasks[] | "\(.session_id)|\(.status)|\(.acknowledged)"' 2>/dev/null || true)
|
||||
if [[ -n "$tracker_lines" ]]; then
|
||||
write_tracker_cache "$tracker_lines"
|
||||
else
|
||||
rm -f "$TRACKER_CACHE_FILE" 2>/dev/null || true
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
while IFS='|' read -r session_id status acknowledged; do
|
||||
[[ -z "$session_id" ]] && continue
|
||||
case "$status" in
|
||||
in_progress)
|
||||
tracker_running_sessions+="$session_id"$'\n'
|
||||
;;
|
||||
completed)
|
||||
if [[ "$acknowledged" == "false" ]]; then
|
||||
tracker_pending_sessions+="$session_id"$'\n'
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done <<< "$tracker_lines"
|
||||
}
|
||||
|
||||
value_in_list() {
|
||||
local needle="$1"
|
||||
local list="$2"
|
||||
if [[ -z "$needle" || -z "$list" ]]; then
|
||||
return 1
|
||||
fi
|
||||
while IFS= read -r line; do
|
||||
[[ "$line" == "$needle" ]] && return 0
|
||||
done <<< "$list"
|
||||
return 1
|
||||
}
|
||||
|
||||
session_has_pending() {
|
||||
value_in_list "$1" "$tracker_pending_sessions"
|
||||
}
|
||||
|
||||
session_has_running() {
|
||||
value_in_list "$1" "$tracker_running_sessions"
|
||||
}
|
||||
|
||||
load_tracker_info
|
||||
|
||||
sessions=$(tmux list-sessions -F '#{session_id}::#{session_name}' 2>/dev/null || true)
|
||||
if [[ -z "$sessions" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
rendered=""
|
||||
prev_bg=""
|
||||
current_session_id_norm=$(normalize_session_id "$current_session_id")
|
||||
current_session_trimmed=$(trim_label "$current_session_name")
|
||||
while IFS= read -r entry; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
session_id="${entry%%::*}"
|
||||
name="${entry#*::}"
|
||||
[[ -z "$session_id" ]] && continue
|
||||
|
||||
session_id_norm=$(normalize_session_id "$session_id")
|
||||
segment_bg="$inactive_bg"
|
||||
segment_fg="$inactive_fg"
|
||||
trimmed_name=$(trim_label "$name")
|
||||
if [[ "$session_id" == "$current_session_id" || "$session_id_norm" == "$current_session_id_norm" || "$trimmed_name" == "$current_session_trimmed" ]]; then
|
||||
segment_bg="$active_bg"
|
||||
segment_fg="$active_fg"
|
||||
fi
|
||||
|
||||
label="$trimmed_name"
|
||||
if (( ${#label} > max_width )); then
|
||||
label="${label:0:max_width-1}…"
|
||||
fi
|
||||
|
||||
indicator=""
|
||||
if session_has_pending "$session_id"; then
|
||||
indicator=" #[fg=#a6e3a1,bg=${segment_bg}]●#[fg=${segment_fg},bg=${segment_bg}]"
|
||||
elif session_has_running "$session_id"; then
|
||||
indicator=" #[fg=#f9e2af,bg=${segment_bg}]●#[fg=${segment_fg},bg=${segment_bg}]"
|
||||
fi
|
||||
|
||||
if [[ -z "$prev_bg" ]]; then
|
||||
rendered+="#[fg=${segment_bg},bg=${status_bg}]${left_cap}"
|
||||
else
|
||||
rendered+="#[fg=${prev_bg},bg=${segment_bg}]${separator}"
|
||||
fi
|
||||
rendered+="#[fg=${segment_fg},bg=${segment_bg}] ${label}${indicator} "
|
||||
prev_bg="$segment_bg"
|
||||
done <<< "$sessions"
|
||||
|
||||
if [[ -n "$prev_bg" ]]; then
|
||||
rendered+="#[fg=${prev_bg},bg=${status_bg}]${separator}"
|
||||
fi
|
||||
|
||||
printf '%s' "$rendered"
|
||||
49
tmux/tmux-status/right.sh
Executable file
49
tmux/tmux-status/right.sh
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
status_bg=$(tmux show -gqv status-bg)
|
||||
if [[ -z "$status_bg" || "$status_bg" == "default" ]]; then
|
||||
status_bg=black
|
||||
fi
|
||||
|
||||
segment_bg="#3b4252"
|
||||
segment_fg="#eceff4"
|
||||
separator=""
|
||||
right_cap="█"
|
||||
hostname=$(hostname -s 2>/dev/null || hostname 2>/dev/null || printf 'host')
|
||||
rainbarf_bg="#2e3440"
|
||||
rainbarf_segment=""
|
||||
rainbarf_toggle="${TMUX_RAINBARF:-1}"
|
||||
|
||||
case "$rainbarf_toggle" in
|
||||
0|false|FALSE|off|OFF|no|NO)
|
||||
rainbarf_toggle="0"
|
||||
;;
|
||||
*)
|
||||
rainbarf_toggle="1"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$rainbarf_toggle" == "1" ]] && command -v rainbarf >/dev/null 2>&1; then
|
||||
rainbarf_output=$(rainbarf --no-battery --no-remaining --no-bolt --tmux --rgb 2>/dev/null || true)
|
||||
rainbarf_output=${rainbarf_output//$'\n'/}
|
||||
if [[ -n "$rainbarf_output" ]]; then
|
||||
rainbarf_segment=$(printf '#[fg=%s,bg=%s]%s#[fg=%s,bg=%s]%s' \
|
||||
"$rainbarf_bg" "$status_bg" "$separator" \
|
||||
"$segment_fg" "$rainbarf_bg" "$rainbarf_output")
|
||||
fi
|
||||
fi
|
||||
|
||||
host_prefix=$(printf '#[fg=%s,bg=%s]%s#[fg=%s,bg=%s] ' \
|
||||
"$segment_bg" "$status_bg" "$separator" \
|
||||
"$segment_fg" "$segment_bg")
|
||||
|
||||
if [[ -n "$rainbarf_segment" ]]; then
|
||||
host_prefix=$(printf '#[fg=%s,bg=%s] ' "$segment_fg" "$segment_bg")
|
||||
fi
|
||||
|
||||
printf '%s%s%s #[fg=%s,bg=%s]%s' \
|
||||
"$rainbarf_segment" \
|
||||
"$host_prefix" \
|
||||
"$hostname" \
|
||||
"$segment_bg" "$status_bg" "$right_cap"
|
||||
|
|
@ -36,4 +36,3 @@ alias t='tmux'
|
|||
alias lo='lsof -p $(fps) +w'
|
||||
alias py="python"
|
||||
alias cl='claude --dangerously-skip-permissions --append-system-prompt "$(cat ~/.config/claude/system-prompt.txt)"'
|
||||
alias co="codex --sandbox danger-full-access -m gpt-5-codex -c model_reasoning_effort=\"high\""
|
||||
|
|
|
|||
109
zsh/functions/co.zsh
Normal file
109
zsh/functions/co.zsh
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
co() {
|
||||
local -a codex_cmd
|
||||
codex_cmd=(codex --sandbox danger-full-access -m gpt-5-codex -c 'model_reasoning_summary_format=experimental' -c 'model_reasoning_effort=medium' --search)
|
||||
local search_dir=$PWD
|
||||
local overlay_file=""
|
||||
while :; do
|
||||
if [ -f "$search_dir/codex-mcp.toml" ]; then
|
||||
overlay_file="$search_dir/codex-mcp.toml"
|
||||
break
|
||||
fi
|
||||
if [ "$search_dir" = "/" ]; then
|
||||
break
|
||||
fi
|
||||
search_dir="$(dirname "$search_dir")"
|
||||
done
|
||||
|
||||
local base_home="${CODEX_HOME:-$HOME/.codex}"
|
||||
local base_config="$base_home/config.toml"
|
||||
if [ ! -f "$base_config" ]; then
|
||||
print -u2 "co: missing base config at $base_config"
|
||||
return 1
|
||||
fi
|
||||
|
||||
setopt local_options null_glob
|
||||
|
||||
local tmp_home
|
||||
tmp_home=$(mktemp -d "${TMPDIR:-/tmp}/codex-home.XXXXXX") || return 1
|
||||
print -u2 "co: using temporary CODEX_HOME at $tmp_home"
|
||||
|
||||
local cleanup_cmd="print -u2 \"co: removing temporary CODEX_HOME $tmp_home\"; rm -rf '$tmp_home'"
|
||||
trap "$cleanup_cmd" EXIT INT TERM
|
||||
|
||||
if ! cp "$base_config" "$tmp_home/config.toml" >/dev/null 2>&1; then
|
||||
trap - EXIT INT TERM
|
||||
eval "$cleanup_cmd"
|
||||
print -u2 "co: failed to copy $base_config"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local base_agents="$base_home/AGENTS.md"
|
||||
if [ -f "$base_agents" ]; then
|
||||
if ! cp "$base_agents" "$tmp_home/AGENTS.md" >/dev/null 2>&1; then
|
||||
trap - EXIT INT TERM
|
||||
eval "$cleanup_cmd"
|
||||
print -u2 "co: failed to copy $base_agents"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -e "$tmp_home/AGENTS.md" ]; then
|
||||
if ! : > "$tmp_home/AGENTS.md"; then
|
||||
trap - EXIT INT TERM
|
||||
eval "$cleanup_cmd"
|
||||
print -u2 "co: failed to create $tmp_home/AGENTS.md"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local entry name
|
||||
for entry in "$base_home"/* "$base_home"/.[!.]* "$base_home"/..?*; do
|
||||
if [ ! -e "$entry" ]; then
|
||||
continue
|
||||
fi
|
||||
name="${entry##*/}"
|
||||
case "$name" in
|
||||
'.'|'..'|'config.toml'|'AGENTS.md')
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
if ! ln -s "$entry" "$tmp_home/$name"; then
|
||||
trap - EXIT INT TERM
|
||||
eval "$cleanup_cmd"
|
||||
print -u2 "co: failed to symlink $entry"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
print -u2 "co: prepared $tmp_home with copies of config.toml and AGENTS.md; symlinked remaining entries"
|
||||
|
||||
local tmux_id
|
||||
if tmux_id=$(tmux display-message -p '#{session_id}::#{window_id}::#{pane_id}' 2>/dev/null); then
|
||||
if ! printf 'The TMUX_ID for this session will be "%s". Pass this id to the tracker mcp\n' "$tmux_id" >> "$tmp_home/AGENTS.md"; then
|
||||
trap - EXIT INT TERM
|
||||
eval "$cleanup_cmd"
|
||||
print -u2 "co: failed to append tmux id to AGENTS.md"
|
||||
return 1
|
||||
fi
|
||||
print -u2 "co: recorded tmux id $tmux_id in AGENTS.md"
|
||||
else
|
||||
print -u2 "co: warning: unable to determine tmux id"
|
||||
fi
|
||||
|
||||
if [ -n "$overlay_file" ]; then
|
||||
if ! printf '\n' >> "$tmp_home/config.toml" || ! cat "$overlay_file" >> "$tmp_home/config.toml"; then
|
||||
trap - EXIT INT TERM
|
||||
eval "$cleanup_cmd"
|
||||
print -u2 "co: failed to append $overlay_file to temporary config"
|
||||
return 1
|
||||
fi
|
||||
print -u2 "co: appended MCP overlay from $overlay_file"
|
||||
fi
|
||||
|
||||
codex_cmd+=("$@")
|
||||
CODEX_HOME="$tmp_home" "${codex_cmd[@]}"
|
||||
local exit_code=$?
|
||||
|
||||
trap - EXIT INT TERM
|
||||
eval "$cleanup_cmd"
|
||||
return $exit_code
|
||||
}
|
||||
|
|
@ -15,3 +15,5 @@ source ~/.zim/modules/fzf-tab/fzf-tab.plugin.zsh
|
|||
source ~/.config/zsh/prompt.zsh
|
||||
|
||||
source ~/.config/zsh/functions/cd_git_root.zsh
|
||||
|
||||
source ~/.config/zsh/functions/co.zsh
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue