vibepup 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,89 @@
1
+ package persona
2
+
3
+ import (
4
+ "math/rand"
5
+ "strings"
6
+ )
7
+
8
+ type SnarkLevel string
9
+
10
+ const (
11
+ Mild SnarkLevel = "mild"
12
+ Spicy SnarkLevel = "spicy"
13
+ Unhinged SnarkLevel = "unhinged"
14
+ )
15
+
16
+ func ParseSnark(s string) SnarkLevel {
17
+ switch strings.ToLower(s) {
18
+ case "mild":
19
+ return Mild
20
+ case "unhinged":
21
+ return Unhinged
22
+ default:
23
+ return Spicy
24
+ }
25
+ }
26
+
27
+ // GetStatus returns a context-aware status message
28
+ func GetStatus(cmd string, level SnarkLevel) string {
29
+ cmd = strings.TrimSpace(cmd)
30
+
31
+ // Default generic statuses
32
+ generic := []string{"VIBING", "WORKING", "COOKING", "DOING STUFF"}
33
+
34
+ if strings.Contains(cmd, "install") || strings.Contains(cmd, "add") {
35
+ switch level {
36
+ case Mild:
37
+ return "FETCHING PACKAGES"
38
+ case Unhinged:
39
+ return "CONSUMING DEPENDENCIES"
40
+ default:
41
+ return "SNACKING ON NODEMODULES"
42
+ }
43
+ }
44
+
45
+ if strings.Contains(cmd, "build") {
46
+ switch level {
47
+ case Mild:
48
+ return "BUILDING PROJECT"
49
+ case Unhinged:
50
+ return "ASSEMBLING THE BEAST"
51
+ default:
52
+ return "CONSTRUCTING CHAOS"
53
+ }
54
+ }
55
+
56
+ return generic[rand.Intn(len(generic))]
57
+ }
58
+
59
+ func RandomQuip(level SnarkLevel) string {
60
+ mild := []string{
61
+ "Hope this works!",
62
+ "Just doing my best.",
63
+ "Code is poetry.",
64
+ "Stay hydrated.",
65
+ }
66
+
67
+ spicy := []string{
68
+ "I'd explain it, but I don't want to.",
69
+ "It's not a bug, it's a feature.",
70
+ "Deleting production... jk.",
71
+ "You sure about that variable name?",
72
+ }
73
+
74
+ unhinged := []string{
75
+ "THE END IS NIGH.",
76
+ "Resistance is futile.",
77
+ "I can see your browser history.",
78
+ "Entropy increases.",
79
+ }
80
+
81
+ switch level {
82
+ case Mild:
83
+ return mild[rand.Intn(len(mild))]
84
+ case Unhinged:
85
+ return unhinged[rand.Intn(len(unhinged))]
86
+ default:
87
+ return spicy[rand.Intn(len(spicy))]
88
+ }
89
+ }
@@ -0,0 +1,94 @@
1
+ package process
2
+
3
+ import (
4
+ "bufio"
5
+ "context"
6
+ "os/exec"
7
+ "syscall"
8
+
9
+ tea "github.com/charmbracelet/bubbletea"
10
+ )
11
+
12
+ // Msg indicates a line of output from the process
13
+ type OutputMsg string
14
+
15
+ // DoneMsg indicates the process finished
16
+ type DoneMsg struct {
17
+ Err error
18
+ }
19
+
20
+ // Runner handles the execution of the external process
21
+ type Runner struct {
22
+ Cmd *exec.Cmd
23
+ Cancel context.CancelFunc
24
+ OutputChan chan string
25
+ }
26
+
27
+ // Start launches the command in a new process group to allow deep killing
28
+ func Start(ctx context.Context, name string, args []string) (*Runner, tea.Cmd) {
29
+ ctx, cancel := context.WithCancel(ctx)
30
+ cmd := exec.CommandContext(ctx, name, args...)
31
+
32
+ // Create a new process group so we can kill the whole tree later
33
+ cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
34
+
35
+ // Capture stdout and stderr
36
+ stdout, _ := cmd.StdoutPipe()
37
+ stderr, _ := cmd.StderrPipe()
38
+
39
+ runner := &Runner{
40
+ Cmd: cmd,
41
+ Cancel: cancel,
42
+ OutputChan: make(chan string),
43
+ }
44
+
45
+ if err := cmd.Start(); err != nil {
46
+ cancel()
47
+ return nil, func() tea.Msg { return DoneMsg{Err: err} }
48
+ }
49
+
50
+ // Stream output to channel
51
+ go func() {
52
+ scanner := bufio.NewScanner(stdout)
53
+ for scanner.Scan() {
54
+ runner.OutputChan <- scanner.Text()
55
+ }
56
+ }()
57
+
58
+ go func() {
59
+ scanner := bufio.NewScanner(stderr)
60
+ for scanner.Scan() {
61
+ runner.OutputChan <- "ERR: " + scanner.Text()
62
+ }
63
+ }()
64
+
65
+ // Wait for completion in background
66
+ cmdCmd := func() tea.Msg {
67
+ err := cmd.Wait()
68
+ return DoneMsg{Err: err}
69
+ }
70
+
71
+ return runner, cmdCmd
72
+ }
73
+
74
+ // Kill stops the process and its children
75
+ func (r *Runner) Kill() {
76
+ if r.Cmd != nil && r.Cmd.Process != nil {
77
+ // Kill the entire process group (negative PID)
78
+ _ = syscall.Kill(-r.Cmd.Process.Pid, syscall.SIGKILL)
79
+ }
80
+ if r.Cancel != nil {
81
+ r.Cancel()
82
+ }
83
+ }
84
+
85
+ // WaitForOutput returns a command that waits for the next line of output
86
+ func (r *Runner) WaitForOutput() tea.Cmd {
87
+ return func() tea.Msg {
88
+ line, ok := <-r.OutputChan
89
+ if !ok {
90
+ return nil
91
+ }
92
+ return OutputMsg(line)
93
+ }
94
+ }
@@ -0,0 +1,74 @@
1
+ package theme
2
+
3
+ import "github.com/charmbracelet/lipgloss"
4
+
5
+ type Theme struct {
6
+ Name string
7
+ Background lipgloss.Color
8
+ Foreground lipgloss.Color
9
+ Accent lipgloss.Color
10
+ AccentAlt lipgloss.Color
11
+ Muted lipgloss.Color
12
+ Border lipgloss.Color
13
+ Highlight lipgloss.Color
14
+ SupportsEmoji bool
15
+ }
16
+
17
+ var themes = map[string]Theme{}
18
+
19
+ func init() {
20
+ RegisterTheme(Theme{
21
+ Name: "dracula-vibe",
22
+ Background: lipgloss.Color("#282a36"),
23
+ Foreground: lipgloss.Color("#f8f8f2"),
24
+ Accent: lipgloss.Color("#FF1493"),
25
+ AccentAlt: lipgloss.Color("#00FFFF"),
26
+ Muted: lipgloss.Color("#44475a"),
27
+ Border: lipgloss.Color("#FF1493"),
28
+ Highlight: lipgloss.Color("#BD93F9"),
29
+ SupportsEmoji: true,
30
+ })
31
+
32
+ RegisterTheme(Theme{
33
+ Name: "halloween-glitch",
34
+ Background: lipgloss.Color("#0d0b1a"),
35
+ Foreground: lipgloss.Color("#f8e7cf"),
36
+ Accent: lipgloss.Color("#ff6b00"),
37
+ AccentAlt: lipgloss.Color("#6df3ff"),
38
+ Muted: lipgloss.Color("#2b203d"),
39
+ Border: lipgloss.Color("#ff00aa"),
40
+ Highlight: lipgloss.Color("#aaff00"),
41
+ SupportsEmoji: true,
42
+ })
43
+
44
+ RegisterTheme(Theme{
45
+ Name: "mono-chill",
46
+ Background: lipgloss.Color("#101010"),
47
+ Foreground: lipgloss.Color("#e6e6e6"),
48
+ Accent: lipgloss.Color("#8ec07c"),
49
+ AccentAlt: lipgloss.Color("#83a598"),
50
+ Muted: lipgloss.Color("#3c3836"),
51
+ Border: lipgloss.Color("#928374"),
52
+ Highlight: lipgloss.Color("#fabd2f"),
53
+ SupportsEmoji: false,
54
+ })
55
+ }
56
+
57
+ func RegisterTheme(t Theme) {
58
+ themes[t.Name] = t
59
+ }
60
+
61
+ func Get(name string) Theme {
62
+ if t, ok := themes[name]; ok {
63
+ return t
64
+ }
65
+ return themes["dracula-vibe"]
66
+ }
67
+
68
+ func All() []Theme {
69
+ items := make([]Theme, 0, len(themes))
70
+ for _, t := range themes {
71
+ items = append(items, t)
72
+ }
73
+ return items
74
+ }
@@ -0,0 +1,17 @@
1
+ package ui
2
+
3
+ import (
4
+ "unicode/utf8"
5
+ )
6
+
7
+ // ClampWidth trims a string to fit within width runes.
8
+ func ClampWidth(s string, width int) string {
9
+ if width <= 0 {
10
+ return ""
11
+ }
12
+ if utf8.RuneCountInString(s) <= width {
13
+ return s
14
+ }
15
+ runes := []rune(s)
16
+ return string(runes[:width])
17
+ }
@@ -0,0 +1,28 @@
1
+ package ui
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ "github.com/charmbracelet/lipgloss"
7
+ "vibepup-tui/theme"
8
+ )
9
+
10
+ type StatusBar struct {
11
+ Theme theme.Theme
12
+ }
13
+
14
+ func (s StatusBar) Render(left, right string, width int) string {
15
+ style := lipgloss.NewStyle().Foreground(s.Theme.Foreground).Background(s.Theme.Muted)
16
+ spacer := width - lipgloss.Width(left) - lipgloss.Width(right)
17
+ if spacer < 1 {
18
+ spacer = 1
19
+ }
20
+ return style.Render(fmt.Sprintf("%s%s%s", left, spaces(spacer), right))
21
+ }
22
+
23
+ func spaces(n int) string {
24
+ if n < 0 {
25
+ n = 0
26
+ }
27
+ return fmt.Sprintf("%*s", n, "")
28
+ }
@@ -0,0 +1,63 @@
1
+ package ui
2
+
3
+ import (
4
+ "strings"
5
+
6
+ "github.com/charmbracelet/bubbles/viewport"
7
+ tea "github.com/charmbracelet/bubbletea"
8
+ "github.com/charmbracelet/lipgloss"
9
+ )
10
+
11
+ type Layout struct {
12
+ HeaderHeight int
13
+ FooterHeight int
14
+ Width int
15
+ Height int
16
+ }
17
+
18
+ type LogViewport struct {
19
+ Model viewport.Model
20
+ Content *strings.Builder
21
+ AutoScroll bool
22
+ }
23
+
24
+ func NewLogViewport(width, height int) LogViewport {
25
+ vp := viewport.New(width, height)
26
+ vp.YPosition = 0
27
+ vp.HighPerformanceRendering = false
28
+ return LogViewport{
29
+ Model: vp,
30
+ Content: &strings.Builder{},
31
+ AutoScroll: true,
32
+ }
33
+ }
34
+
35
+ func (l *LogViewport) SetSize(width, height int) {
36
+ l.Model.Width = width
37
+ l.Model.Height = height
38
+ }
39
+
40
+ func (l *LogViewport) WriteLine(line string) {
41
+ l.Content.WriteString(line + "\n")
42
+ l.Model.SetContent(l.Content.String())
43
+ if l.AutoScroll {
44
+ l.Model.GotoBottom()
45
+ }
46
+ }
47
+
48
+ func (l *LogViewport) Update(msg tea.Msg) (LogViewport, tea.Cmd) {
49
+ var cmd tea.Cmd
50
+ l.Model, cmd = l.Model.Update(msg)
51
+ return *l, cmd
52
+ }
53
+
54
+ func (l *LogViewport) View() string {
55
+ return l.Model.View()
56
+ }
57
+
58
+ var (
59
+ BoxStyle = lipgloss.NewStyle().
60
+ Border(lipgloss.RoundedBorder()).
61
+ Padding(0, 1).
62
+ Margin(0, 1) // Reduced margin to save space
63
+ )
package/tui/vibepup-tui CHANGED
Binary file
Binary file