4 Commits

Author SHA1 Message Date
Hadi 2e61ffa125 Add ilovetui instructions
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-26 20:01:41 +02:00
Hadi 9ac6040629 Migrate components to ilovetui
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-26 16:38:09 +02:00
Hadi c7523e69e7 add vim/neovim integration instructions
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-26 16:15:07 +02:00
Hadi aa458c142f use ilovetui colors & styles 2026-05-26 16:12:10 +02:00
41 changed files with 333 additions and 810 deletions
+12
View File
@@ -25,6 +25,7 @@
- [Configuration](#configuration)
- [CLI Flags](#cli-flags)
- [Plugin System](#plugin-system)
- [Vim / Neovim Integration](#vim--neovim-integration)
- [Deployment](#deployment)
- [Tech Stack](#tech-stack)
@@ -115,6 +116,8 @@ On startup, you choose:
Spilltea is fully configured via a YAML file at `~/.config/spilltea/config.yaml`.
Check the default configuration with all the options [here](./internal/config/default_config.yaml)
Colors and styles can be customized using [ilovetui](https://github.com/anotherhadi/ilovetui), which applies theme changes across all compatible TUI applications at once.
## CLI Flags
```
@@ -137,6 +140,15 @@ Usage: spilltea [flags]
Spilltea supports plugins written in **Lua**. Plugins are loaded from `~/.config/spilltea/plugins/` by default and do not require recompilation or access to the source code.
For a full reference and examples, see the [plugin documentation](./docs/plugins.md) or [plugin examples](./plugins/).
## Vim / Neovim Integration
Spilltea pairs naturally with Neovim. If you live in your editor, these plugins let you inspect and transform captured traffic without switching context:
- **[sttr.nvim](https://github.com/anotherhadi/sttr.nvim)**: Run string transformations (base64, URL encode/decode, hex, hashing, and more) on any selected text directly from Neovim. Handy for decoding values spotted in the proxy history.
- **[jwt-tui.nvim](https://github.com/anotherhadi/jwt-tui.nvim)**: Decode and inspect JWT tokens in a floating TUI window. Great for quickly reading tokens intercepted through the proxy.
Both plugins are built to feel native in a terminal workflow, just like Spilltea.
## Deployment
spilltea runs **locally** on the machine used for pentesting or CTF. There is no separate server component.
+3 -2
View File
@@ -44,7 +44,8 @@ func main() {
)
flag.CommandLine.SetOutput(os.Stdout)
flag.Usage = func() {
fmt.Println("Usage: spilltea [flags]\n")
fmt.Println("Usage: spilltea [flags]")
fmt.Println("")
flag.PrintDefaults()
}
flag.Parse()
@@ -121,7 +122,7 @@ func main() {
config.Global.App.UpstreamProxy = *flagUpstreamProxy
}
style.Init(config.Global)
style.Init()
icons.Init(config.Global)
keys.Init(config.Global)
+2
View File
@@ -13,6 +13,8 @@ On startup, you choose:
Spilltea is fully configured via a YAML file at `~/.config/spilltea/config.yaml`.
Check the default configuration with all the options [here](./internal/config/default_config.yaml)
Colors and styles can be customized using [ilovetui](https://github.com/anotherhadi/ilovetui), which applies theme changes across all compatible TUI applications at once.
## CLI Flags
<!-- exec: echo '```' && go run ./cmd/spilltea -h && echo '```' -->
+3 -2
View File
@@ -7,7 +7,10 @@ require (
charm.land/bubbletea/v2 v2.0.6
charm.land/glamour/v2 v2.0.0
charm.land/lipgloss/v2 v2.0.3
github.com/andybalholm/brotli v1.0.4
github.com/anotherhadi/ilovetui v0.1.6
github.com/charmbracelet/x/ansi v0.11.7
github.com/klauspost/compress v1.17.8
github.com/lqqyt2423/go-mitmproxy v1.8.11
github.com/sirupsen/logrus v1.9.4
github.com/spf13/pflag v1.0.10
@@ -20,7 +23,6 @@ require (
require (
github.com/alecthomas/chroma/v2 v2.24.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
@@ -39,7 +41,6 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
+2
View File
@@ -16,6 +16,8 @@ github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/anotherhadi/ilovetui v0.1.6 h1:NKg+T1DpV08Q4r+iowFrXF+0bTd6Y2f4OFpFwhsfsyY=
github.com/anotherhadi/ilovetui v0.1.6/go.mod h1:HVai6u5NGKSMOpmioYpwrN0lSxQjc7HtISUc5hTwvOw=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
-20
View File
@@ -1,20 +0,0 @@
package config
type Colors struct {
Base00 string `mapstructure:"base00"`
Base01 string `mapstructure:"base01"`
Base02 string `mapstructure:"base02"`
Base03 string `mapstructure:"base03"`
Base04 string `mapstructure:"base04"`
Base05 string `mapstructure:"base05"`
Base06 string `mapstructure:"base06"`
Base07 string `mapstructure:"base07"`
Base08 string `mapstructure:"base08"`
Base09 string `mapstructure:"base09"`
Base0A string `mapstructure:"base0a"`
Base0B string `mapstructure:"base0b"`
Base0C string `mapstructure:"base0c"`
Base0D string `mapstructure:"base0d"`
Base0E string `mapstructure:"base0e"`
Base0F string `mapstructure:"base0f"`
}
-1
View File
@@ -31,7 +31,6 @@ type Config struct {
} `mapstructure:"app"`
TUI struct {
Colors Colors `mapstructure:"colors"`
UseNerdfontIcons bool `mapstructure:"use_nerdfont_icons"`
DefaultSidebarState string `mapstructure:"default_sidebar_state"`
PrettyPrintBody bool `mapstructure:"pretty_print_body"`
-17
View File
@@ -27,23 +27,6 @@ tui:
use_nerdfont_icons: false
default_sidebar_state: "expanded" # hidden, collapsed, expanded
pretty_print_body: true # auto-indent JSON and HTML response bodies
colors:
base00: "110F12" # Default Background
base01: "1C1920" # Lighter Background (status bars, line numbers)
base02: "1D1A26" # Selection Background
base03: "514D63" # Comments, Invisibles, faint text
base04: "8E8AA0" # Dark Foreground (status bars)
base05: "C2BED6" # Default Foreground, Caret, Delimiters
base06: "D8D5EA" # Light Foreground (rarely used)
base07: "EAE7F7" # Light Background (rarely used)
base08: "E07080" # Red: errors, diff deleted
base09: "D49070" # Orange: integers, constants, warnings
base0a: "C4B060" # Yellow: classes, search highlight
base0b: "80B880" # Green: strings, diff inserted, success
base0c: "70B8C0" # Cyan: support, regex, escape chars
base0d: "9E97F8" # Blue/Accent: functions, headings, primary
base0e: "C090E8" # Purple: keywords, storage
base0f: "D080A0" # Pink: deprecated, embedded language tags
keybindings:
global:
-43
View File
@@ -1,43 +0,0 @@
package style
import (
"strings"
"charm.land/lipgloss/v2"
)
// PanelContentH returns the usable inner content height for a panel rendered by
// RenderWithTitle. It subtracts the two border lines (top + bottom) from the
// total panel height.
func PanelContentH(totalH int) int {
h := totalH - 2
if h < 0 {
return 0
}
return h
}
// RenderWithTitle renders a lipgloss bordered box with a title embedded in the
// top border, matching the border's own foreground color. height is the total
// desired output height (including both border lines).
func RenderWithTitle(border lipgloss.Style, title, content string, width, height int) string {
boxH := height - 1
if contentH := boxH - 1; contentH > 0 {
lines := strings.Split(content, "\n")
if len(lines) > contentH {
content = strings.Join(lines[:contentH], "\n")
}
}
box := border.BorderTop(false).Width(width).Height(boxH).Render(content)
boxWidth := lipgloss.Width(strings.SplitN(box, "\n", 2)[0])
label := " " + title + " "
fillW := boxWidth - lipgloss.Width(label) - 2
if fillW < 0 {
fillW = 0
}
bc := lipgloss.NewStyle().Foreground(border.GetBorderTopForeground())
topLine := bc.Render("╭ ") + bc.Render(title) + bc.Render(" "+strings.Repeat("─", fillW)+"╮")
return lipgloss.JoinVertical(lipgloss.Left, topLine, box)
}
+10 -80
View File
@@ -1,83 +1,23 @@
package style
import (
"strings"
"charm.land/bubbles/v2/paginator"
"charm.land/bubbles/v2/textarea"
"charm.land/bubbles/v2/viewport"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
)
func NewViewport() viewport.Model {
vp := viewport.New()
vp.MouseWheelEnabled = false
return vp
}
func ViewportView(vp *viewport.Model) string {
v := vp.View()
if vp.AtBottom() {
return v
}
lines := strings.Split(v, "\n")
if len(lines) == 0 {
return v
}
arrow := lipgloss.NewStyle().Foreground(S.Subtle).Render("↓")
arrowW := lipgloss.Width(arrow)
inner := vp.Width() - 2*arrowW
if inner < 0 {
inner = 0
}
lines[len(lines)-1] = arrow + strings.Repeat(" ", inner) + arrow
return strings.Join(lines, "\n")
}
func NewPaginator() paginator.Model {
p := paginator.New()
p.Type = paginator.Dots
p.ActiveDot = S.PagerDotActive
p.InactiveDot = S.PagerDotInactive
return p
}
func NewTextarea(showLineNumbers bool) textarea.Model {
ta := textarea.New()
ta.Prompt = ""
ta.ShowLineNumbers = showLineNumbers
ta.CharLimit = 0
ta.EndOfBufferCharacter = '~'
ts := ta.Styles()
ts.Focused.Base = lipgloss.NewStyle()
ts.Blurred.Base = lipgloss.NewStyle()
ts.Focused.Text = lipgloss.NewStyle().Foreground(S.Text)
ts.Focused.CursorLine = lipgloss.NewStyle().Background(S.Selection).Foreground(S.Text)
ts.Focused.CursorLineNumber = lipgloss.NewStyle().Background(S.Selection).Foreground(S.Primary).Bold(true)
ts.Focused.LineNumber = lipgloss.NewStyle().Foreground(S.Subtle)
ts.Focused.Placeholder = lipgloss.NewStyle().Foreground(S.Subtle)
ts.Focused.EndOfBuffer = lipgloss.NewStyle().Foreground(S.SubtleBg)
ts.Blurred.Text = lipgloss.NewStyle().Foreground(S.MutedFg)
ts.Blurred.LineNumber = lipgloss.NewStyle().Foreground(S.SubtleBg)
ts.Blurred.Placeholder = lipgloss.NewStyle().Foreground(S.Subtle)
ts.Blurred.EndOfBuffer = lipgloss.NewStyle().Foreground(S.SubtleBg)
ta.SetStyles(ts)
return ta
}
func SeverityStyle(sev string) lipgloss.Style {
base := lipgloss.NewStyle().Bold(true)
switch sev {
case "critical":
return base.Foreground(S.Error)
return base.Foreground(ilovetui.S.Error)
case "high":
return base.Foreground(S.Warning)
return base.Foreground(ilovetui.S.Warning)
case "medium":
return base.Foreground(S.Primary)
return base.Foreground(ilovetui.S.Primary)
case "low":
return base.Foreground(S.Success)
return base.Foreground(ilovetui.S.Success)
default:
return base.Foreground(S.Subtle)
return base.Foreground(ilovetui.S.Subtle)
}
}
@@ -85,22 +25,12 @@ func StatusStyle(code, width int) lipgloss.Style {
base := lipgloss.NewStyle().Bold(true).Width(width)
switch {
case code >= 500:
return base.Foreground(S.Error)
return base.Foreground(ilovetui.S.Error)
case code >= 400:
return base.Foreground(S.Warning)
return base.Foreground(ilovetui.S.Warning)
case code >= 300:
return base.Foreground(S.Primary)
return base.Foreground(ilovetui.S.Primary)
default:
return base.Foreground(S.Success)
return base.Foreground(ilovetui.S.Success)
}
}
// SplitH splits totalHeight into top and bottom sections, accounting for the
// status bar height.
func SplitH(totalHeight int, statusBar string, ratio float64) (top, bottom int) {
statusH := strings.Count(statusBar, "\n") + 1
available := totalHeight - statusH
top = int(float64(available) * ratio)
bottom = available - top
return
}
-236
View File
@@ -1,236 +0,0 @@
package style
import (
"github.com/anotherhadi/spilltea/internal/config"
"charm.land/glamour/v2/ansi"
)
func GlamourStyleConfig(cfg *config.Config) ansi.StyleConfig {
c := cfg.TUI.Colors
str := func(s string) *string { return &s }
hex := func(base string) *string { return str("#" + base) }
boolPtr := func(b bool) *bool { return &b }
uintPtr := func(u uint) *uint { return &u }
return ansi.StyleConfig{
Document: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
BlockPrefix: "\n",
BlockSuffix: "\n",
Color: hex(c.Base05),
},
Margin: uintPtr(2),
},
BlockQuote: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Color: hex(c.Base03),
Italic: boolPtr(true),
},
Indent: uintPtr(1),
IndentToken: str("│ "),
},
List: ansi.StyleList{
LevelIndent: 2,
},
Heading: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
BlockSuffix: "\n",
Color: hex(c.Base0D),
Bold: boolPtr(true),
},
},
H1: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Prefix: " ",
Suffix: " ",
Color: hex(c.Base07),
BackgroundColor: hex(c.Base0D),
Bold: boolPtr(true),
},
},
H2: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Prefix: "## ",
Color: hex(c.Base0D),
Bold: boolPtr(true),
},
},
H3: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Prefix: "### ",
Color: hex(c.Base0C),
},
},
H4: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Prefix: "#### ",
Color: hex(c.Base0B),
},
},
H5: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Prefix: "##### ",
Color: hex(c.Base09),
},
},
H6: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Prefix: "###### ",
Color: hex(c.Base08),
Bold: boolPtr(false),
},
},
Strikethrough: ansi.StylePrimitive{
CrossedOut: boolPtr(true),
},
Emph: ansi.StylePrimitive{
Italic: boolPtr(true),
},
Strong: ansi.StylePrimitive{
Bold: boolPtr(true),
},
HorizontalRule: ansi.StylePrimitive{
Color: hex(c.Base03),
Format: "\n--------\n",
},
Item: ansi.StylePrimitive{
BlockPrefix: "• ",
},
Enumeration: ansi.StylePrimitive{
BlockPrefix: ". ",
},
Task: ansi.StyleTask{
Ticked: "[✓] ",
Unticked: "[ ] ",
},
Link: ansi.StylePrimitive{
Color: hex(c.Base0C),
Underline: boolPtr(true),
},
LinkText: ansi.StylePrimitive{
Color: hex(c.Base0D),
Bold: boolPtr(true),
},
Image: ansi.StylePrimitive{
Color: hex(c.Base0C),
Underline: boolPtr(true),
},
ImageText: ansi.StylePrimitive{
Color: hex(c.Base04),
Format: "Image: {{.text}} ->",
},
Code: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Prefix: " ",
Suffix: " ",
Color: hex(c.Base0B),
BackgroundColor: hex(c.Base01),
},
},
CodeBlock: ansi.StyleCodeBlock{
StyleBlock: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Color: hex(c.Base04),
},
Margin: uintPtr(2),
},
Chroma: &ansi.Chroma{
Text: ansi.StylePrimitive{
Color: hex(c.Base05),
},
Error: ansi.StylePrimitive{
Color: hex(c.Base07),
BackgroundColor: hex(c.Base08),
},
Comment: ansi.StylePrimitive{
Color: hex(c.Base03),
Italic: boolPtr(true),
},
CommentPreproc: ansi.StylePrimitive{
Color: hex(c.Base09),
},
Keyword: ansi.StylePrimitive{
Color: hex(c.Base0E),
},
KeywordReserved: ansi.StylePrimitive{
Color: hex(c.Base0E),
},
KeywordNamespace: ansi.StylePrimitive{
Color: hex(c.Base0D),
},
KeywordType: ansi.StylePrimitive{
Color: hex(c.Base0A),
},
Operator: ansi.StylePrimitive{
Color: hex(c.Base05),
},
Punctuation: ansi.StylePrimitive{
Color: hex(c.Base05),
},
Name: ansi.StylePrimitive{
Color: hex(c.Base05),
},
NameBuiltin: ansi.StylePrimitive{
Color: hex(c.Base0D),
},
NameTag: ansi.StylePrimitive{
Color: hex(c.Base08),
},
NameAttribute: ansi.StylePrimitive{
Color: hex(c.Base0A),
},
NameClass: ansi.StylePrimitive{
Color: hex(c.Base0A),
Bold: boolPtr(true),
Underline: boolPtr(true),
},
NameConstant: ansi.StylePrimitive{
Color: hex(c.Base09),
},
NameDecorator: ansi.StylePrimitive{
Color: hex(c.Base0C),
},
NameFunction: ansi.StylePrimitive{
Color: hex(c.Base0D),
},
LiteralNumber: ansi.StylePrimitive{
Color: hex(c.Base09),
},
LiteralString: ansi.StylePrimitive{
Color: hex(c.Base0B),
},
LiteralStringEscape: ansi.StylePrimitive{
Color: hex(c.Base0C),
},
GenericDeleted: ansi.StylePrimitive{
Color: hex(c.Base08),
},
GenericEmph: ansi.StylePrimitive{
Italic: boolPtr(true),
},
GenericInserted: ansi.StylePrimitive{
Color: hex(c.Base0B),
},
GenericStrong: ansi.StylePrimitive{
Bold: boolPtr(true),
},
GenericSubheading: ansi.StylePrimitive{
Color: hex(c.Base04),
},
Background: ansi.StylePrimitive{
BackgroundColor: hex(c.Base01),
},
},
},
Table: ansi.StyleTable{
StyleBlock: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{},
},
},
DefinitionDescription: ansi.StylePrimitive{
BlockPrefix: "\n> ",
},
}
}
+26 -25
View File
@@ -3,12 +3,13 @@ package style
import (
"bytes"
"encoding/json"
"image/color"
"strings"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/config"
"golang.org/x/net/html"
"image/color"
)
func Paint(c color.Color, s string) string {
@@ -75,8 +76,8 @@ func highlightHeaders(raw string) string {
} else if trimmed == "" {
out.WriteString(line)
} else if idx := strings.Index(trimmed, ": "); idx != -1 {
out.WriteString(Paint(S.Subtle, trimmed[:idx+2]))
out.WriteString(Paint(S.Text, trimmed[idx+2:]))
out.WriteString(Paint(ilovetui.S.Subtle, trimmed[:idx+2]))
out.WriteString(Paint(ilovetui.S.Text, trimmed[idx+2:]))
} else {
out.WriteString(line)
}
@@ -95,16 +96,16 @@ func highlightStatusLine(line string) string {
switch parts[0] {
case "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "CONNECT", "TRACE":
result := S.Method(parts[0]).Width(0).Render(parts[0]) + " "
result += Paint(S.Primary, parts[1])
result += Paint(ilovetui.S.Primary, parts[1])
if len(parts) == 3 {
result += " " + Paint(S.Subtle, parts[2])
result += " " + Paint(ilovetui.S.Subtle, parts[2])
}
return result
}
result := Paint(S.Subtle, parts[0]) + " "
result += Paint(S.Warning, parts[1])
result := Paint(ilovetui.S.Subtle, parts[0]) + " "
result += Paint(ilovetui.S.Warning, parts[1])
if len(parts) == 3 {
result += " " + Paint(S.MutedFg, parts[2])
result += " " + Paint(ilovetui.S.Muted, parts[2])
}
return result
}
@@ -134,9 +135,9 @@ func highlightJSON(s string) string {
k++
}
if k < n && s[k] == ':' {
out.WriteString(Paint(S.Primary, str))
out.WriteString(Paint(ilovetui.S.Primary, str))
} else {
out.WriteString(Paint(S.Success, str))
out.WriteString(Paint(ilovetui.S.Success, str))
}
i = j
case (ch >= '0' && ch <= '9') || (ch == '-' && i+1 < n && s[i+1] >= '0' && s[i+1] <= '9'):
@@ -147,19 +148,19 @@ func highlightJSON(s string) string {
for j < n && ((s[j] >= '0' && s[j] <= '9') || s[j] == '.' || s[j] == 'e' || s[j] == 'E' || s[j] == '+' || s[j] == '-') {
j++
}
out.WriteString(Paint(S.Warning, s[i:j]))
out.WriteString(Paint(ilovetui.S.Warning, s[i:j]))
i = j
case i+4 <= n && s[i:i+4] == "true":
out.WriteString(Paint(S.Error, "true"))
out.WriteString(Paint(ilovetui.S.Error, "true"))
i += 4
case i+5 <= n && s[i:i+5] == "false":
out.WriteString(Paint(S.Error, "false"))
out.WriteString(Paint(ilovetui.S.Error, "false"))
i += 5
case i+4 <= n && s[i:i+4] == "null":
out.WriteString(Paint(S.Error, "null"))
out.WriteString(Paint(ilovetui.S.Error, "null"))
i += 4
case ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == ':' || ch == ',':
out.WriteString(Paint(S.Subtle, string(ch)))
out.WriteString(Paint(ilovetui.S.Subtle, string(ch)))
i++
default:
out.WriteByte(ch)
@@ -265,11 +266,11 @@ func highlightHTML(s string) string {
if i+4 <= n && s[i:i+4] == "<!--" {
end := strings.Index(s[i:], "-->")
if end == -1 {
out.WriteString(Paint(S.Subtle, s[i:]))
out.WriteString(Paint(ilovetui.S.Subtle, s[i:]))
break
}
end = i + end + 3
out.WriteString(Paint(S.Subtle, s[i:end]))
out.WriteString(Paint(ilovetui.S.Subtle, s[i:end]))
i = end
continue
}
@@ -278,10 +279,10 @@ func highlightHTML(s string) string {
i++
continue
}
out.WriteString(Paint(S.Subtle, "<"))
out.WriteString(Paint(ilovetui.S.Subtle, "<"))
i++
if i < n && (s[i] == '/' || s[i] == '!') {
out.WriteString(Paint(S.Subtle, string(s[i])))
out.WriteString(Paint(ilovetui.S.Subtle, string(s[i])))
i++
}
j := i
@@ -289,7 +290,7 @@ func highlightHTML(s string) string {
j++
}
if j > i {
out.WriteString(Paint(S.Primary, s[i:j]))
out.WriteString(Paint(ilovetui.S.Primary, s[i:j]))
i = j
}
for i < n && s[i] != '>' {
@@ -299,10 +300,10 @@ func highlightHTML(s string) string {
out.WriteByte(ch)
i++
case ch == '/':
out.WriteString(Paint(S.Subtle, "/"))
out.WriteString(Paint(ilovetui.S.Subtle, "/"))
i++
case ch == '=':
out.WriteString(Paint(S.Subtle, "="))
out.WriteString(Paint(ilovetui.S.Subtle, "="))
i++
case ch == '"' || ch == '\'':
q := ch
@@ -313,19 +314,19 @@ func highlightHTML(s string) string {
if j < n {
j++
}
out.WriteString(Paint(S.Success, s[i:j]))
out.WriteString(Paint(ilovetui.S.Success, s[i:j]))
i = j
default:
j = i
for j < n && s[j] != '=' && s[j] != ' ' && s[j] != '>' && s[j] != '/' && s[j] != '\t' && s[j] != '\n' {
j++
}
out.WriteString(Paint(S.Warning, s[i:j]))
out.WriteString(Paint(ilovetui.S.Warning, s[i:j]))
i = j
}
}
if i < n && s[i] == '>' {
out.WriteString(Paint(S.Subtle, ">"))
out.WriteString(Paint(ilovetui.S.Subtle, ">"))
i++
}
}
+8 -78
View File
@@ -1,34 +1,13 @@
package style
import (
"image/color"
"charm.land/bubbles/v2/help"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/config"
ilovetui "github.com/anotherhadi/ilovetui"
)
type Styles struct {
Primary color.Color
Success color.Color
Error color.Color
Warning color.Color
SubtleBg color.Color
Selection color.Color
Text color.Color
MutedFg color.Color
Subtle color.Color
Bold lipgloss.Style
Faint lipgloss.Style
Panel lipgloss.Style
PanelFocused lipgloss.Style
PanelEditing lipgloss.Style
PagerDotActive string
PagerDotInactive string
methodGet lipgloss.Style
methodPost lipgloss.Style
methodPutPatch lipgloss.Style
@@ -38,70 +17,21 @@ type Styles struct {
var S *Styles
func Init(cfg *config.Config) {
c := cfg.TUI.Colors
subtleBg := lipgloss.Color("#" + c.Base01) // Lighter Background (status bars)
selection := lipgloss.Color("#" + c.Base02) // Selection Background
subtle := lipgloss.Color("#" + c.Base03) // Faint text, borders
mutedFg := lipgloss.Color("#" + c.Base04) // Muted foreground
text := lipgloss.Color("#" + c.Base05) // Default Foreground
errCol := lipgloss.Color("#" + c.Base08) // Red: errors
warning := lipgloss.Color("#" + c.Base09) // Orange: warnings
success := lipgloss.Color("#" + c.Base0B) // Green: success
primary := lipgloss.Color("#" + c.Base0D) // Accent: primary
purple := lipgloss.Color("#" + c.Base0E) // Purple: editing
func Init() {
methodBase := lipgloss.NewStyle().Bold(true).Width(7)
S = &Styles{
Primary: primary,
Success: success,
Error: errCol,
Warning: warning,
SubtleBg: subtleBg,
Selection: selection,
MutedFg: mutedFg,
Text: text,
Subtle: subtle,
Bold: lipgloss.NewStyle().Bold(true),
Faint: lipgloss.NewStyle().Foreground(subtle).Faint(true),
Panel: lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(subtle),
PanelFocused: lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(primary),
PanelEditing: lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(purple),
BorderForeground(ilovetui.S.Base0E),
PagerDotActive: lipgloss.NewStyle().Foreground(primary).SetString("•").String(),
PagerDotInactive: lipgloss.NewStyle().Foreground(subtle).SetString("•").String(),
methodGet: methodBase.Foreground(success),
methodPost: methodBase.Foreground(warning),
methodPutPatch: methodBase.Foreground(primary),
methodDelete: methodBase.Foreground(errCol),
methodDefault: methodBase.Foreground(text),
methodGet: methodBase.Foreground(ilovetui.S.Success),
methodPost: methodBase.Foreground(ilovetui.S.Warning),
methodPutPatch: methodBase.Foreground(ilovetui.S.Primary),
methodDelete: methodBase.Foreground(ilovetui.S.Error),
methodDefault: methodBase.Foreground(ilovetui.S.Text),
}
}
func NewHelp() help.Model {
h := help.New()
h.Styles.ShortKey = lipgloss.NewStyle().Foreground(S.Primary)
h.Styles.ShortDesc = lipgloss.NewStyle().Foreground(S.MutedFg)
h.Styles.ShortSeparator = lipgloss.NewStyle().Foreground(S.Subtle)
h.Styles.FullKey = lipgloss.NewStyle().Foreground(S.Primary)
h.Styles.FullDesc = lipgloss.NewStyle().Foreground(S.MutedFg)
h.Styles.FullSeparator = lipgloss.NewStyle().Foreground(S.Subtle)
h.Styles.Ellipsis = lipgloss.NewStyle().Foreground(S.Subtle)
return h
}
func (s *Styles) Method(method string) lipgloss.Style {
switch method {
case "GET":
+10 -11
View File
@@ -5,7 +5,7 @@ import (
"strings"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
)
type sidebarState string
@@ -42,7 +42,6 @@ func (m *Model) renderSidebar() string {
if m.sidebarState == sidebarHidden {
return ""
}
s := style.S
// content width inside bordered panel
inner := m.getSidebarWidth() - 2
@@ -50,18 +49,18 @@ func (m *Model) renderSidebar() string {
if m.sidebarState == sidebarCollapsed {
titleText = "SPLT"
}
title := lipgloss.NewStyle().Width(inner).Bold(true).Foreground(s.Primary).Padding(0, 1).Render(titleText)
title := lipgloss.NewStyle().Width(inner).Bold(true).Foreground(ilovetui.S.Primary).Padding(0, 1).Render(titleText)
divider := strings.Repeat("─", inner)
badgeSelected := lipgloss.NewStyle().Foreground(s.Primary).Bold(true)
badgeNormal := lipgloss.NewStyle().Foreground(s.Subtle)
textSelected := lipgloss.NewStyle().Foreground(s.Primary)
textNormal := lipgloss.NewStyle().Foreground(s.Text)
badgeSelected := lipgloss.NewStyle().Foreground(ilovetui.S.Primary).Bold(true)
badgeNormal := lipgloss.NewStyle().Foreground(ilovetui.S.Subtle)
textSelected := lipgloss.NewStyle().Foreground(ilovetui.S.Primary)
textNormal := lipgloss.NewStyle().Foreground(ilovetui.S.Text)
lineStyle := lipgloss.NewStyle().Width(inner).Padding(0, 1)
var items strings.Builder
badgeUnread := lipgloss.NewStyle().Foreground(s.Warning).Bold(true)
badgeUnread := lipgloss.NewStyle().Foreground(ilovetui.S.Warning).Bold(true)
for i, entry := range sidebarEntries {
selected := entry.id == m.page
@@ -95,13 +94,13 @@ func (m *Model) renderSidebar() string {
}
parts := []string{
title,
lipgloss.NewStyle().Width(inner).Foreground(s.Subtle).Padding(0, 1).Render(name),
lipgloss.NewStyle().Width(inner).Foreground(ilovetui.S.Subtle).Padding(0, 1).Render(name),
}
parts = append(parts,
lipgloss.NewStyle().Foreground(s.Subtle).Render(divider),
lipgloss.NewStyle().Foreground(ilovetui.S.Subtle).Render(divider),
items.String(),
)
body := lipgloss.JoinVertical(lipgloss.Left, parts...)
return s.Panel.Width(m.getSidebarWidth()).Height(m.height).Render(body)
return ilovetui.S.Panel.Width(m.getSidebarWidth()).Height(m.height).Render(body)
}
+2 -2
View File
@@ -3,7 +3,7 @@ package app
import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
)
func (m Model) View() tea.View {
@@ -52,5 +52,5 @@ func (m *Model) renderActivePage() string {
return e.render(m)
}
}
return style.S.Faint.Render("Work in progress")
return ilovetui.S.Faint.Render("Work in progress")
}
+8 -10
View File
@@ -6,7 +6,7 @@ import (
"charm.land/bubbles/v2/list"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
)
const (
@@ -47,20 +47,18 @@ type Model struct {
}
func New() Model {
s := style.S
delegate := list.NewDefaultDelegate()
delegate.SetSpacing(0)
delegate.Styles.NormalTitle = lipgloss.NewStyle().Foreground(s.Text).PaddingLeft(2)
delegate.Styles.NormalDesc = lipgloss.NewStyle().Foreground(s.Subtle).PaddingLeft(2)
delegate.Styles.NormalTitle = lipgloss.NewStyle().Foreground(ilovetui.S.Text).PaddingLeft(2)
delegate.Styles.NormalDesc = lipgloss.NewStyle().Foreground(ilovetui.S.Subtle).PaddingLeft(2)
delegate.Styles.SelectedTitle = lipgloss.NewStyle().
Border(lipgloss.NormalBorder(), false, false, false, true).
BorderForeground(s.Primary).
Foreground(s.Primary).Bold(true).PaddingLeft(1)
BorderForeground(ilovetui.S.Primary).
Foreground(ilovetui.S.Primary).Bold(true).PaddingLeft(1)
delegate.Styles.SelectedDesc = lipgloss.NewStyle().
Border(lipgloss.NormalBorder(), false, false, false, true).
BorderForeground(s.Primary).
Foreground(s.MutedFg).PaddingLeft(1)
BorderForeground(ilovetui.S.Primary).
Foreground(ilovetui.S.Muted).PaddingLeft(1)
l := list.New(allItems, delegate, popupW, 8)
l.SetShowTitle(false)
@@ -125,7 +123,7 @@ func (m Model) popupHeight() int {
}
func (m Model) listHeight() int {
return style.PanelContentH(m.popupHeight()) - 1
return ilovetui.ContentHeight(m.popupHeight()) - 1
}
func (m Model) extract(id string) string {
+4 -6
View File
@@ -2,14 +2,12 @@ package copy
import (
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
copyasUI "github.com/anotherhadi/spilltea/internal/ui/components/copyas"
)
func (m *Model) View(background string) string {
s := style.S
hint := lipgloss.NewStyle().Foreground(s.Subtle).
hint := lipgloss.NewStyle().Foreground(ilovetui.S.Subtle).
Render(" enter: copy • /: filter • esc: cancel")
inner := lipgloss.JoinVertical(lipgloss.Left,
@@ -19,10 +17,10 @@ func (m *Model) View(background string) string {
border := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(s.Primary)
BorderForeground(ilovetui.S.Primary)
popupH := m.popupHeight()
popup := style.RenderWithTitle(border, "Copy", inner, m.popupInnerWidth()+2, popupH)
popup := ilovetui.RenderWithTitle(border, "Copy", inner, m.popupInnerWidth()+2, popupH)
return copyasUI.OverlayCenter(background, popup, m.width, m.height)
}
+8 -10
View File
@@ -4,7 +4,7 @@ import (
"charm.land/bubbles/v2/list"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
)
const (
@@ -48,20 +48,18 @@ type Model struct {
}
func New() Model {
s := style.S
delegate := list.NewDefaultDelegate()
delegate.SetSpacing(0)
delegate.Styles.NormalTitle = lipgloss.NewStyle().Foreground(s.Text).PaddingLeft(2)
delegate.Styles.NormalDesc = lipgloss.NewStyle().Foreground(s.Subtle).PaddingLeft(2)
delegate.Styles.NormalTitle = lipgloss.NewStyle().Foreground(ilovetui.S.Text).PaddingLeft(2)
delegate.Styles.NormalDesc = lipgloss.NewStyle().Foreground(ilovetui.S.Subtle).PaddingLeft(2)
delegate.Styles.SelectedTitle = lipgloss.NewStyle().
Border(lipgloss.NormalBorder(), false, false, false, true).
BorderForeground(s.Primary).
Foreground(s.Primary).Bold(true).PaddingLeft(1)
BorderForeground(ilovetui.S.Primary).
Foreground(ilovetui.S.Primary).Bold(true).PaddingLeft(1)
delegate.Styles.SelectedDesc = lipgloss.NewStyle().
Border(lipgloss.NormalBorder(), false, false, false, true).
BorderForeground(s.Primary).
Foreground(s.MutedFg).PaddingLeft(1)
BorderForeground(ilovetui.S.Primary).
Foreground(ilovetui.S.Muted).PaddingLeft(1)
l := list.New(allFormats, delegate, popupW, 8)
l.SetShowTitle(false)
@@ -116,5 +114,5 @@ func (m Model) popupHeight() int {
// listHeight = panel content area - hint line (1)
func (m Model) listHeight() int {
return style.PanelContentH(m.popupHeight()) - 1
return ilovetui.ContentHeight(m.popupHeight()) - 1
}
+5 -9
View File
@@ -4,14 +4,12 @@ import (
"strings"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/charmbracelet/x/ansi"
)
func (m *Model) View(background string) string {
s := style.S
hint := lipgloss.NewStyle().Foreground(s.Subtle).
hint := lipgloss.NewStyle().Foreground(ilovetui.S.Subtle).
Render(" enter: copy • /: filter • esc: cancel")
inner := lipgloss.JoinVertical(lipgloss.Left,
@@ -21,17 +19,15 @@ func (m *Model) View(background string) string {
border := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(s.Primary)
BorderForeground(ilovetui.S.Primary)
popupH := m.popupHeight()
popup := style.RenderWithTitle(border, "Copy as", inner, m.popupInnerWidth()+2, popupH)
popup := ilovetui.RenderWithTitle(border, "Copy as", inner, m.popupInnerWidth()+2, popupH)
return OverlayCenter(background, popup, m.width, m.height)
}
func OverlayCenter(bg, popup string, w, h int) string {
s := style.S
stripped := ansi.Strip(bg)
rawLines := strings.Split(stripped, "\n")
bgRunes := make([][]rune, h)
@@ -67,7 +63,7 @@ func OverlayCenter(bg, popup string, w, h int) string {
startX = 0
}
dim := lipgloss.NewStyle().Foreground(s.Subtle).Faint(true)
dim := lipgloss.NewStyle().Foreground(ilovetui.S.Subtle).Faint(true)
result := make([]string, h)
for y := 0; y < h; y++ {
@@ -7,7 +7,7 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/charmbracelet/x/ansi"
)
@@ -76,7 +76,6 @@ func (m Model) View(background string) string {
return background
}
s := style.S
const popupW = 34
var popups []string
@@ -89,17 +88,17 @@ func (m Model) View(background string) string {
var accent color.Color
switch n.kind {
case KindSuccess:
accent = s.Success
accent = ilovetui.S.Success
case KindWarning:
accent = s.Warning
accent = ilovetui.S.Warning
case KindError:
accent = s.Error
accent = ilovetui.S.Error
default:
accent = s.Primary
accent = ilovetui.S.Primary
}
titleStr := lipgloss.NewStyle().Foreground(accent).Bold(true).Render(n.title)
bodyStr := lipgloss.NewStyle().Foreground(s.Text).Width(popupW).Render(n.body)
bodyStr := lipgloss.NewStyle().Foreground(ilovetui.S.Text).Width(popupW).Render(n.body)
inner := lipgloss.JoinVertical(lipgloss.Left, titleStr, bodyStr)
box := lipgloss.NewStyle().
+10 -12
View File
@@ -8,6 +8,7 @@ import (
"charm.land/bubbles/v2/viewport"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
@@ -85,10 +86,9 @@ func wordDiff(leftLine, rightLine string) (leftRendered, rightRendered string) {
segs[lo], segs[hi] = segs[hi], segs[lo]
}
s := style.S
boldErr := lipgloss.NewStyle().Foreground(s.Error).Bold(true)
boldOk := lipgloss.NewStyle().Foreground(s.Success).Bold(true)
dim := lipgloss.NewStyle().Foreground(s.Subtle)
boldErr := lipgloss.NewStyle().Foreground(ilovetui.S.Error).Bold(true)
boldOk := lipgloss.NewStyle().Foreground(ilovetui.S.Success).Bold(true)
dim := lipgloss.NewStyle().Foreground(ilovetui.S.Subtle)
var lb, rb strings.Builder
for _, seg := range segs {
@@ -212,9 +212,9 @@ type Model struct {
func New() Model {
return Model{
leftViewport: style.NewViewport(),
rightViewport: style.NewViewport(),
help: style.NewHelp(),
leftViewport: ilovetui.NewViewport(),
rightViewport: ilovetui.NewViewport(),
help: ilovetui.NewHelp(),
}
}
@@ -255,7 +255,7 @@ func (m *Model) recalcSizes() {
rightInner = 0
}
viewportH := style.PanelContentH(panelH)
viewportH := ilovetui.ContentHeight(panelH)
m.leftViewport.SetWidth(leftInner)
m.leftViewport.SetHeight(viewportH)
@@ -296,13 +296,11 @@ func hlLines(raw string) []string {
}
func (m *Model) refreshViewports() {
s := style.S
if m.left.raw == "" {
placeholder := lipgloss.Place(
m.leftViewport.Width(), m.leftViewport.Height(),
lipgloss.Center, lipgloss.Center,
s.Faint.Render(util.CenterLines("<(^_^)>", "send two entries here to compare")),
ilovetui.S.Faint.Render(util.CenterLines("<(^_^)>", "send two entries here to compare")),
)
m.leftViewport.SetContent(placeholder)
m.rightViewport.SetContent("")
@@ -314,7 +312,7 @@ func (m *Model) refreshViewports() {
placeholder := lipgloss.Place(
m.rightViewport.Width(), m.rightViewport.Height(),
lipgloss.Center, lipgloss.Center,
s.Faint.Render(util.CenterLines("(・3・)", "waiting for second entry…")),
ilovetui.S.Faint.Render(util.CenterLines("(・3・)", "waiting for second entry…")),
)
m.rightViewport.SetContent(placeholder)
return
+11 -14
View File
@@ -5,6 +5,7 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/charmbracelet/x/ansi"
@@ -26,8 +27,6 @@ func (m Model) View() tea.View {
}
func (m *Model) renderPanels(panelH int) string {
s := style.S
leftW := m.width / 2
rightW := m.width - leftW
@@ -46,20 +45,20 @@ func (m *Model) renderPanels(panelH int) string {
rightTitle = ansi.Truncate(rightTitle, maxW, "…")
}
leftBorder := s.Panel
rightBorder := s.Panel
leftBorder := ilovetui.S.Panel
rightBorder := ilovetui.S.Panel
switch m.focus {
case bothSlots:
leftBorder = s.PanelFocused
rightBorder = s.PanelFocused
leftBorder = ilovetui.S.PanelFocused
rightBorder = ilovetui.S.PanelFocused
case leftSlot:
leftBorder = s.PanelFocused
leftBorder = ilovetui.S.PanelFocused
case rightSlot:
rightBorder = s.PanelFocused
rightBorder = ilovetui.S.PanelFocused
}
left := style.RenderWithTitle(leftBorder, leftTitle, style.ViewportView(&m.leftViewport), leftW, panelH)
right := style.RenderWithTitle(rightBorder, rightTitle, style.ViewportView(&m.rightViewport), rightW, panelH)
left := ilovetui.RenderWithTitle(leftBorder, leftTitle, ilovetui.ViewportView(&m.leftViewport), leftW, panelH)
right := ilovetui.RenderWithTitle(rightBorder, rightTitle, ilovetui.ViewportView(&m.rightViewport), rightW, panelH)
return lipgloss.JoinHorizontal(lipgloss.Top, left, right)
}
@@ -69,12 +68,11 @@ func (m *Model) renderStatusBar() string {
}
func renderLeftLines(lines []diffLine) string {
s := style.S
var sb strings.Builder
for _, l := range lines {
switch l.kind {
case lineRemoved:
sb.WriteString(style.Paint(s.Error, "- ") + l.text + "\n")
sb.WriteString(style.Paint(ilovetui.S.Error, "- ") + l.text + "\n")
case lineAdded:
sb.WriteString("\n")
default:
@@ -85,12 +83,11 @@ func renderLeftLines(lines []diffLine) string {
}
func renderRightLines(lines []diffLine) string {
s := style.S
var sb strings.Builder
for _, l := range lines {
switch l.kind {
case lineAdded:
sb.WriteString(style.Paint(s.Success, "+ ") + l.text + "\n")
sb.WriteString(style.Paint(ilovetui.S.Success, "+ ") + l.text + "\n")
case lineRemoved:
sb.WriteString("\n")
default:
+5 -5
View File
@@ -14,8 +14,8 @@ import (
"charm.land/bubbles/v2/viewport"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
)
func readDoc(name string) string {
@@ -58,12 +58,12 @@ func New() Model {
ti := textinput.New()
ti.Prompt = "/"
s := ti.Styles()
s.Focused.Prompt = lipgloss.NewStyle().Foreground(style.S.Primary)
s.Focused.Prompt = lipgloss.NewStyle().Foreground(ilovetui.S.Primary)
ti.SetStyles(s)
return Model{
viewport: viewport.New(),
help: style.NewHelp(),
help: ilovetui.NewHelp(),
searchInput: ti,
}
}
@@ -182,8 +182,8 @@ func injectHighlightsInLine(ansiLine string, intervals [][]int, currentIdx int)
return ansiLine
}
normalOpen, normalClose := lipglossAnsiCodes(lipgloss.NewStyle().Background(style.S.SubtleBg))
currentOpen, currentClose := lipglossAnsiCodes(lipgloss.NewStyle().Background(style.S.Primary).Foreground(style.S.Text))
normalOpen, normalClose := lipglossAnsiCodes(lipgloss.NewStyle().Background(ilovetui.S.SubtleBg))
currentOpen, currentClose := lipglossAnsiCodes(lipgloss.NewStyle().Background(ilovetui.S.Primary).Foreground(ilovetui.S.Text))
type injection struct {
visPos int
+4 -4
View File
@@ -9,15 +9,15 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/glamour/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/config"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/charmbracelet/x/ansi"
)
func windowStyle() lipgloss.Style {
return lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(style.S.Subtle).
BorderForeground(ilovetui.S.Subtle).
Padding(0, 0)
}
@@ -31,7 +31,7 @@ func (e Model) View() tea.View {
countText = fmt.Sprintf("%d/%d", e.matchIndex+1, len(e.matches))
}
count := lipgloss.NewStyle().Padding(0, 1).
Foreground(style.S.MutedFg).
Foreground(ilovetui.S.Muted).
Render(countText)
statusBar = lipgloss.JoinHorizontal(lipgloss.Top, statusBar, count)
}
@@ -61,7 +61,7 @@ func (m *Model) renderMarkdown() {
width := m.viewport.Width() - 2
renderer, _ := glamour.NewTermRenderer(
glamour.WithStyles(style.GlamourStyleConfig(cfg)),
glamour.WithStyles(ilovetui.GlamourStyleConfig()),
glamour.WithWordWrap(width),
)
+10 -11
View File
@@ -11,10 +11,9 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/glamour/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/config"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/db"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
)
@@ -39,10 +38,10 @@ type Model struct {
func New() Model {
return Model{
listViewport: style.NewViewport(),
bodyViewport: style.NewViewport(),
pager: style.NewPaginator(),
help: style.NewHelp(),
listViewport: ilovetui.NewViewport(),
bodyViewport: ilovetui.NewViewport(),
pager: ilovetui.NewPaginator(),
help: ilovetui.NewHelp(),
}
}
@@ -76,9 +75,9 @@ func (m *Model) recalcSizes() {
m.help.SetWidth(m.width - 2)
inner := m.width - 2
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
listVH := style.PanelContentH(listH) - 1 // -1 for the pager dots row
listVH := ilovetui.ContentHeight(listH) - 1 // -1 for the pager dots row
if listVH < 0 {
listVH = 0
}
@@ -89,7 +88,7 @@ func (m *Model) recalcSizes() {
m.pager.PerPage = 1
}
bodyVH := style.PanelContentH(bodyH)
bodyVH := ilovetui.ContentHeight(bodyH)
m.bodyViewport.SetWidth(inner)
m.bodyViewport.SetHeight(bodyVH)
@@ -148,7 +147,7 @@ func (m *Model) refreshBodyScroll(reset bool) {
func (m *Model) renderMarkdownCached(src string, width int) string {
if src == "" {
return style.S.Faint.Render(util.CenterLines("(ㆆ _ ㆆ)", "no description"))
return ilovetui.S.Faint.Render(util.CenterLines("(ㆆ _ ㆆ)", "no description"))
}
tmpl, err := template.New("").Parse(src)
if err != nil {
@@ -164,7 +163,7 @@ func (m *Model) renderMarkdownCached(src string, width int) string {
// Rebuild renderer if width changed or not yet built.
if m.renderer == nil || m.rendererWidth != width {
r, err := glamour.NewTermRenderer(
glamour.WithStyles(style.GlamourStyleConfig(config.Global)),
glamour.WithStyles(ilovetui.GlamourStyleConfig()),
glamour.WithWordWrap(width),
)
if err == nil {
+14 -16
View File
@@ -6,6 +6,7 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
@@ -16,7 +17,7 @@ func (m Model) View() tea.View {
return tea.NewView("Loading...")
}
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
content := lipgloss.JoinVertical(lipgloss.Left,
m.renderListPanel(m.width, listH),
@@ -27,34 +28,31 @@ func (m Model) View() tea.View {
}
func (m *Model) renderListPanel(w, h int) string {
s := style.S
var dots string
if len(m.findings) > 0 {
dots = s.Faint.Render(m.pager.View())
dots = ilovetui.S.Faint.Render(m.pager.View())
}
inner := lipgloss.JoinVertical(lipgloss.Left,
m.listViewport.View(),
lipgloss.PlaceHorizontal(m.listViewport.Width(), lipgloss.Center, dots),
)
return style.RenderWithTitle(s.PanelFocused, icons.I.Findings+"Findings", inner, w, h)
return ilovetui.RenderWithTitle(ilovetui.S.PanelFocused, icons.I.Findings+"Findings", inner, w, h)
}
func (m *Model) renderBodyPanel(h int) string {
s := style.S
title := "Description"
if len(m.findings) > 0 {
title = m.findings[m.cursor].Title
}
return style.RenderWithTitle(s.Panel, title, style.ViewportView(&m.bodyViewport), m.width, h)
return ilovetui.RenderWithTitle(ilovetui.S.Panel, title, ilovetui.ViewportView(&m.bodyViewport), m.width, h)
}
func (m *Model) renderList() string {
s := style.S
if len(m.findings) == 0 {
return lipgloss.Place(
m.listViewport.Width(), m.listViewport.Height(),
lipgloss.Center, lipgloss.Center,
s.Faint.Render(util.CenterLines("(҂◡_◡) ᕤ", "no findings")),
ilovetui.S.Faint.Render(util.CenterLines("(҂◡_◡) ᕤ", "no findings")),
)
}
@@ -76,18 +74,18 @@ func (m *Model) renderList() string {
titleW = 0
}
pluginStr := s.Faint.Width(8).Render(util.Truncate(f.PluginName, 8))
pluginStr := ilovetui.S.Faint.Width(8).Render(util.Truncate(f.PluginName, 8))
var line string
if selected {
bg := lipgloss.NewStyle().Background(s.Selection)
bg := lipgloss.NewStyle().Background(ilovetui.S.Selection)
line = lipgloss.JoinHorizontal(lipgloss.Top,
bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"),
sevStyle.Background(s.Selection).Width(8).Render(f.Severity),
bg.Bold(true).Foreground(ilovetui.S.Primary).Width(2).Render(">"),
sevStyle.Background(ilovetui.S.Selection).Width(8).Render(f.Severity),
bg.Width(1).Render(""),
bg.Foreground(s.Subtle).Width(8).Render(util.Truncate(f.PluginName, 8)),
bg.Foreground(ilovetui.S.Subtle).Width(8).Render(util.Truncate(f.PluginName, 8)),
bg.Width(1).Render(""),
bg.Foreground(s.Subtle).Width(10).Render(ts),
bg.Foreground(ilovetui.S.Subtle).Width(10).Render(ts),
bg.Width(1).Render(""),
bg.Bold(true).Width(titleW).Render(f.Title),
)
@@ -98,9 +96,9 @@ func (m *Model) renderList() string {
" ",
pluginStr,
" ",
s.Faint.Width(10).Render(ts),
ilovetui.S.Faint.Width(10).Render(ts),
" ",
s.Bold.Render(f.Title),
ilovetui.S.Bold.Render(f.Title),
)
}
sb.WriteString(fmt.Sprintf("%s\n", line))
+8 -8
View File
@@ -7,9 +7,9 @@ import (
"charm.land/bubbles/v2/textinput"
"charm.land/bubbles/v2/viewport"
tea "charm.land/bubbletea/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/db"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
)
@@ -44,10 +44,10 @@ func New() Model {
ti := textinput.New()
ti.Prompt = ""
return Model{
listViewport: style.NewViewport(),
bodyViewport: style.NewViewport(),
pager: style.NewPaginator(),
help: style.NewHelp(),
listViewport: ilovetui.NewViewport(),
bodyViewport: ilovetui.NewViewport(),
pager: ilovetui.NewPaginator(),
help: ilovetui.NewHelp(),
searchInput: ti,
}
}
@@ -124,14 +124,14 @@ func (m *Model) recalcSizes() {
// 2 (padding) + 2 (prefix char + space)
m.searchInput.SetWidth(m.width - 4)
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
inner := m.width - 2
if inner < 0 {
inner = 0
}
listVH := style.PanelContentH(listH) - 1 // -1 for the pager dots row
listVH := ilovetui.ContentHeight(listH) - 1 // -1 for the pager dots row
if listVH < 0 {
listVH = 0
}
@@ -142,7 +142,7 @@ func (m *Model) recalcSizes() {
m.pager.PerPage = 1
}
bodyVH := style.PanelContentH(bodyH)
bodyVH := ilovetui.ContentHeight(bodyH)
m.bodyViewport.SetWidth(inner)
m.bodyViewport.SetHeight(bodyVH)
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"charm.land/bubbles/v2/key"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/db"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
@@ -345,7 +346,7 @@ func (m *Model) refreshBody() {
}
if raw == "" {
w, h := m.bodyViewport.Width(), m.bodyViewport.Height()
m.bodyViewport.SetContent(lipgloss.Place(w, h, lipgloss.Center, lipgloss.Center, style.S.Faint.Render(util.CenterLines("(˘・_・˘)", "no response stored"))))
m.bodyViewport.SetContent(lipgloss.Place(w, h, lipgloss.Center, lipgloss.Center, ilovetui.S.Faint.Render(util.CenterLines("(˘・_・˘)", "no response stored"))))
return
}
m.bodyViewport.SetContent(style.HighlightHTTP(raw))
+23 -26
View File
@@ -6,6 +6,7 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
@@ -17,7 +18,7 @@ func (m Model) View() tea.View {
return tea.NewView("Loading...")
}
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
content := lipgloss.JoinVertical(lipgloss.Left,
m.renderListPanel(m.width, listH),
@@ -28,60 +29,56 @@ func (m Model) View() tea.View {
}
func (m *Model) renderListPanel(w, h int) string {
s := style.S
var dots string
if len(m.entries) > 0 {
dots = s.Faint.Render(m.pager.View())
dots = ilovetui.S.Faint.Render(m.pager.View())
}
inner := lipgloss.JoinVertical(lipgloss.Left,
m.listViewport.View(),
lipgloss.PlaceHorizontal(m.listViewport.Width(), lipgloss.Center, dots),
)
return style.RenderWithTitle(s.PanelFocused, icons.I.History+"History", inner, w, h)
return ilovetui.RenderWithTitle(ilovetui.S.PanelFocused, icons.I.History+"History", inner, w, h)
}
func (m *Model) renderBodyPanel(h int) string {
s := style.S
title := icons.I.Request + "Request"
if m.focusedPanel == panelResponse {
title = icons.I.Response + "Response"
}
return style.RenderWithTitle(s.Panel, title, style.ViewportView(&m.bodyViewport), m.width, h)
return ilovetui.RenderWithTitle(ilovetui.S.Panel, title, ilovetui.ViewportView(&m.bodyViewport), m.width, h)
}
func (m *Model) renderStatusBar() string {
s := style.S
pad := lipgloss.NewStyle().Padding(0, 1)
escKey := keys.Keys.Global.Escape.Help().Key
switch m.searchKind {
case searchKindFulltext:
filterKey := keys.Keys.History.Filter.Help().Key
if m.searchAccepted {
accent := lipgloss.NewStyle().Foreground(s.Primary)
filterLine := pad.Render(accent.Render(filterKey) + " " + s.Bold.Render(m.searchInput.Value()) + s.Faint.Render(" "+escKey+" to clear"))
accent := lipgloss.NewStyle().Foreground(ilovetui.S.Primary)
filterLine := pad.Render(accent.Render(filterKey) + " " + ilovetui.S.Bold.Render(m.searchInput.Value()) + ilovetui.S.Faint.Render(" "+escKey+" to clear"))
return lipgloss.JoinVertical(lipgloss.Left, filterLine, pad.Render(m.help.View(historyKeyMap{width: m.width})))
}
return pad.Render(s.Faint.Render(filterKey) + " " + m.searchInput.View())
return pad.Render(ilovetui.S.Faint.Render(filterKey) + " " + m.searchInput.View())
case searchKindSQL:
sqlKey := keys.Keys.History.SqlQuery.Help().Key
if m.searchAccepted {
accent := lipgloss.NewStyle().Foreground(s.Primary)
filterLine := pad.Render(accent.Render(sqlKey) + " " + s.Bold.Render(m.searchInput.Value()) + s.Faint.Render(" "+escKey+" to clear"))
accent := lipgloss.NewStyle().Foreground(ilovetui.S.Primary)
filterLine := pad.Render(accent.Render(sqlKey) + " " + ilovetui.S.Bold.Render(m.searchInput.Value()) + ilovetui.S.Faint.Render(" "+escKey+" to clear"))
return lipgloss.JoinVertical(lipgloss.Left, filterLine, pad.Render(m.help.View(historyKeyMap{width: m.width})))
}
return pad.Render(s.Faint.Render(sqlKey) + " " + m.searchInput.View())
return pad.Render(ilovetui.S.Faint.Render(sqlKey) + " " + m.searchInput.View())
default:
return pad.Render(m.help.View(historyKeyMap{width: m.width}))
}
}
func (m *Model) renderList() string {
s := style.S
if m.searchErr != "" {
return lipgloss.Place(
m.listViewport.Width(), m.listViewport.Height(),
lipgloss.Center, lipgloss.Center,
lipgloss.NewStyle().Foreground(s.Error).Render(m.searchErr),
lipgloss.NewStyle().Foreground(ilovetui.S.Error).Render(m.searchErr),
)
}
if len(m.entries) == 0 {
@@ -92,7 +89,7 @@ func (m *Model) renderList() string {
return lipgloss.Place(
m.listViewport.Width(), m.listViewport.Height(),
lipgloss.Center, lipgloss.Center,
s.Faint.Render(msg),
ilovetui.S.Faint.Render(msg),
)
}
@@ -103,7 +100,7 @@ func (m *Model) renderList() string {
globalIdx := start + i
selected := globalIdx == m.cursor
selBg := s.Selection
selBg := ilovetui.S.Selection
w := m.listViewport.Width()
statusStr := fmt.Sprintf("%3d", e.StatusCode)
@@ -115,7 +112,7 @@ func (m *Model) renderList() string {
ts := e.Timestamp.Format("15:04:05")
statusSt := style.StatusStyle(e.StatusCode, 3)
flagSt := lipgloss.NewStyle().Foreground(s.Primary)
flagSt := lipgloss.NewStyle().Foreground(ilovetui.S.Primary)
var line string
if selected {
@@ -128,13 +125,13 @@ func (m *Model) renderList() string {
}
}
line = lipgloss.JoinHorizontal(lipgloss.Top,
bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"),
bg.Foreground(s.Primary).Width(2).Render(flagStr),
s.Method(e.Method).Background(selBg).Render(e.Method),
bg.Bold(true).Foreground(ilovetui.S.Primary).Width(2).Render(">"),
bg.Foreground(ilovetui.S.Primary).Width(2).Render(flagStr),
style.S.Method(e.Method).Background(selBg).Render(e.Method),
bg.Width(1).Render(""),
statusSt.Background(selBg).Render(statusStr),
bg.Width(1).Render(""),
bg.Foreground(s.Subtle).Width(10).Render(ts),
bg.Foreground(ilovetui.S.Subtle).Width(10).Render(ts),
bg.Width(1).Render(""),
bg.Bold(true).Width(hostPathW).Render(e.Host+e.Path),
)
@@ -149,14 +146,14 @@ func (m *Model) renderList() string {
line = lipgloss.JoinHorizontal(lipgloss.Top,
" ",
flagSt.Width(2).Render(flagStr),
s.Method(e.Method).Render(e.Method),
style.S.Method(e.Method).Render(e.Method),
" ",
statusSt.Render(statusStr),
" ",
s.Faint.Width(10).Render(ts),
ilovetui.S.Faint.Width(10).Render(ts),
" ",
s.Bold.Render(e.Host),
s.Faint.Render(e.Path),
ilovetui.S.Bold.Render(e.Host),
ilovetui.S.Faint.Render(e.Path),
)
}
sb.WriteString(line + "\n")
+10 -12
View File
@@ -14,10 +14,10 @@ import (
"charm.land/bubbles/v2/textinput"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/db"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/ui/components/teapot"
)
@@ -88,19 +88,18 @@ type homeDelegate struct {
}
func newHomeDelegate() homeDelegate {
s := style.S
leftBorder := lipgloss.Border{Left: "│"}
return homeDelegate{
normalTitle: lipgloss.NewStyle().Foreground(s.Text).PaddingLeft(4),
normalDesc: lipgloss.NewStyle().Foreground(s.Subtle).Faint(true).PaddingLeft(4),
normalTitle: lipgloss.NewStyle().Foreground(ilovetui.S.Text).PaddingLeft(4),
normalDesc: lipgloss.NewStyle().Foreground(ilovetui.S.Subtle).Faint(true).PaddingLeft(4),
selectedTitle: lipgloss.NewStyle().
Border(leftBorder, false, false, false, true).
BorderForeground(s.Primary).
Foreground(s.Primary).Bold(true).PaddingLeft(3),
BorderForeground(ilovetui.S.Primary).
Foreground(ilovetui.S.Primary).Bold(true).PaddingLeft(3),
selectedDesc: lipgloss.NewStyle().
Border(leftBorder, false, false, false, true).
BorderForeground(s.Primary).
Foreground(s.MutedFg).PaddingLeft(3),
BorderForeground(ilovetui.S.Primary).
Foreground(ilovetui.S.Muted).PaddingLeft(3),
filterMatch: lipgloss.NewStyle().Underline(true),
}
}
@@ -304,14 +303,13 @@ func buildItems(projects []Project) []list.Item {
}
func (m Model) renderHelpLine() string {
s := style.S
k := keys.Keys.Home
fs := m.list.FilterState()
kStyle := lipgloss.NewStyle().Foreground(s.MutedFg).Inline(true)
dStyle := s.Faint.Inline(true)
kStyle := lipgloss.NewStyle().Foreground(ilovetui.S.Muted).Inline(true)
dStyle := ilovetui.S.Faint.Inline(true)
sep := s.Faint.Inline(true).Render(" • ")
sep := ilovetui.S.Faint.Inline(true).Render(" • ")
item := func(keyStr, desc string) string {
return kStyle.Render(keyStr) + " " + dStyle.Render(desc)
}
+7 -9
View File
@@ -5,29 +5,28 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/style"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/ui/components/teapot"
)
const inputPanelMaxW = 44
func (m Model) View() tea.View {
s := style.S
iw := m.innerW()
var sb strings.Builder
sb.WriteString("\n")
if m.height > teapotMinH {
frames := teapot.TeapotFrames()
frame := lipgloss.NewStyle().Foreground(s.Primary).Render(frames[m.teapotFrame])
frame := lipgloss.NewStyle().Foreground(ilovetui.S.Primary).Render(frames[m.teapotFrame])
sb.WriteString(center(iw, frame))
sb.WriteString("\n\n")
} else {
sb.WriteString("\n")
}
sb.WriteString(center(iw, lipgloss.NewStyle().Bold(true).Foreground(s.Primary).Render("SPILLTEA")))
sb.WriteString(center(iw, lipgloss.NewStyle().Bold(true).Foreground(ilovetui.S.Primary).Render("SPILLTEA")))
sb.WriteString("\n")
sb.WriteString(center(iw, s.Faint.Render("choose a project to get started")))
sb.WriteString(center(iw, ilovetui.S.Faint.Render("choose a project to get started")))
sb.WriteString("\n\n")
if m.mode == modeNaming {
@@ -50,7 +49,6 @@ func (m Model) View() tea.View {
}
func (m Model) renderNamingPanel() string {
s := style.S
iw := m.innerW()
panelW := inputPanelMaxW
@@ -63,15 +61,15 @@ func (m Model) renderNamingPanel() string {
innerW := inputPanelInnerW(iw)
inputLine := lipgloss.NewStyle().Width(innerW).Render(m.nameInput.View())
label := lipgloss.NewStyle().Foreground(s.MutedFg).Render("Project name")
label := lipgloss.NewStyle().Foreground(ilovetui.S.Muted).Render("Project name")
panel := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(s.Primary).
BorderForeground(ilovetui.S.Primary).
Padding(1, 2).
Width(panelW).
Render(label + "\n" + inputLine)
hint := s.Faint.Render("[enter] confirm [esc] cancel")
hint := ilovetui.S.Faint.Render("[enter] confirm [esc] cancel")
var sb strings.Builder
sb.WriteString(center(iw, panel))
+4 -3
View File
@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/intercept"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
@@ -157,20 +158,20 @@ func (m *Model) listHalfWidths() (leftW, rightW int) {
func (m *Model) recalcSizes() {
m.help.SetWidth(m.width - 2)
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
bodyInner := m.width - 2
if bodyInner < 0 {
bodyInner = 0
}
bodyVH := style.PanelContentH(bodyH)
bodyVH := ilovetui.ContentHeight(bodyH)
m.textarea.SetWidth(bodyInner)
m.textarea.SetHeight(bodyVH)
m.bodyViewport.SetWidth(bodyInner)
m.bodyViewport.SetHeight(bodyVH)
listVH := style.PanelContentH(listH) - 1 // -1 for the pager dots row
listVH := ilovetui.ContentHeight(listH) - 1 // -1 for the pager dots row
if listVH < 0 {
listVH = 0
}
-4
View File
@@ -1,15 +1,11 @@
package intercept
import (
"charm.land/bubbles/v2/help"
"charm.land/bubbles/v2/key"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
)
func newHelp() help.Model { return style.NewHelp() }
type interceptKeyMap struct{ width int }
func iconBinding(b key.Binding, icon string) key.Binding {
+8 -8
View File
@@ -6,9 +6,9 @@ import (
"charm.land/bubbles/v2/textarea"
"charm.land/bubbles/v2/viewport"
tea "charm.land/bubbletea/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/config"
"github.com/anotherhadi/spilltea/internal/intercept"
"github.com/anotherhadi/spilltea/internal/style"
)
type panel int
@@ -48,14 +48,14 @@ type Model struct {
func New(broker *intercept.Broker) Model {
cfg := config.Global
ta := style.NewTextarea(false)
ta := ilovetui.NewTextarea(false)
ta.Blur()
lv := style.NewViewport()
rv := style.NewViewport()
bv := style.NewViewport()
p := style.NewPaginator()
rp := style.NewPaginator()
lv := ilovetui.NewViewport()
rv := ilovetui.NewViewport()
bv := ilovetui.NewViewport()
p := ilovetui.NewPaginator()
rp := ilovetui.NewPaginator()
broker.SetCaptureResponse(cfg.Intercept.DefaultCaptureResponse)
@@ -69,7 +69,7 @@ func New(broker *intercept.Broker) Model {
textarea: ta,
pager: p,
responsePager: rp,
help: newHelp(),
help: ilovetui.NewHelp(),
pendingEdits: make(map[*intercept.PendingRequest]string),
pendingResponseEdits: make(map[*intercept.PendingResponse]string),
}
+29 -36
View File
@@ -6,6 +6,7 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
@@ -16,7 +17,7 @@ func (m Model) View() tea.View {
return tea.NewView("Loading...")
}
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
var listRow string
if m.captureResponse {
@@ -38,17 +39,15 @@ func (m Model) View() tea.View {
}
func (m *Model) renderListPanel(w, h int) string {
s := style.S
focused := !m.editing && (!m.captureResponse || m.focusedPanel == panelRequests)
border := s.Panel
border := ilovetui.S.Panel
if focused {
border = s.PanelFocused
border = ilovetui.S.PanelFocused
}
var dots string
if len(m.queue) > 0 {
dots = s.Faint.Render(m.pager.View())
dots = ilovetui.S.Faint.Render(m.pager.View())
}
inner := lipgloss.JoinVertical(lipgloss.Left,
m.listViewport.View(),
@@ -57,46 +56,42 @@ func (m *Model) renderListPanel(w, h int) string {
title := icons.I.Request + "Requests"
if !m.interceptEnabled {
title += " " + lipgloss.NewStyle().Foreground(style.S.Error).Render("[intercept off]")
title += " " + lipgloss.NewStyle().Foreground(ilovetui.S.Error).Render("[intercept off]")
}
return style.RenderWithTitle(border, title, inner, w, h)
return ilovetui.RenderWithTitle(border, title, inner, w, h)
}
func (m *Model) renderResponseListPanel(w, h int) string {
s := style.S
focused := !m.editing && m.focusedPanel == panelResponses
border := s.Panel
border := ilovetui.S.Panel
if focused {
border = s.PanelFocused
border = ilovetui.S.PanelFocused
}
dots := s.Faint.Render(m.responsePager.View())
dots := ilovetui.S.Faint.Render(m.responsePager.View())
inner := lipgloss.JoinVertical(lipgloss.Left,
m.responseViewport.View(),
lipgloss.PlaceHorizontal(m.responseViewport.Width(), lipgloss.Center, dots),
)
return style.RenderWithTitle(border, icons.I.Response+"Responses", inner, w, h)
return ilovetui.RenderWithTitle(border, icons.I.Response+"Responses", inner, w, h)
}
func (m *Model) renderBodyPanel(h int) string {
s := style.S
var body string
if m.editing {
body = m.textarea.View()
} else {
body = style.ViewportView(&m.bodyViewport)
body = ilovetui.ViewportView(&m.bodyViewport)
}
border := s.Panel
border := ilovetui.S.Panel
if m.editing {
border = s.PanelFocused
border = ilovetui.S.PanelFocused
}
title := icons.I.Detail + "Details"
return style.RenderWithTitle(border, title, body, m.width, h)
return ilovetui.RenderWithTitle(border, title, body, m.width, h)
}
func (m *Model) renderStatusBar() string {
@@ -105,10 +100,9 @@ func (m *Model) renderStatusBar() string {
func (m *Model) renderList() string {
if len(m.queue) == 0 {
return lipgloss.Place(m.listViewport.Width(), m.listViewport.Height(), lipgloss.Center, lipgloss.Center, style.S.Faint.Render(util.CenterLines("(。◕‿‿◕。)", "waiting for a request")))
return lipgloss.Place(m.listViewport.Width(), m.listViewport.Height(), lipgloss.Center, lipgloss.Center, ilovetui.S.Faint.Render(util.CenterLines("(。◕‿‿◕。)", "waiting for a request")))
}
s := style.S
start, end := util.PageBounds(m.pager, len(m.queue))
var sb strings.Builder
@@ -121,7 +115,7 @@ func (m *Model) renderList() string {
}
selected := globalIdx == m.cursor
selBg := s.Selection
selBg := ilovetui.S.Selection
w := m.listViewport.Width()
const fixedW = 2 + 7 + 2
@@ -134,18 +128,18 @@ func (m *Model) renderList() string {
if selected {
bg := lipgloss.NewStyle().Background(selBg)
line = lipgloss.JoinHorizontal(lipgloss.Top,
bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"),
s.Method(r.Method).Background(selBg).Render(r.Method),
bg.Bold(true).Foreground(ilovetui.S.Primary).Width(2).Render(">"),
style.S.Method(r.Method).Background(selBg).Render(r.Method),
bg.Width(2).Render(""),
bg.Bold(true).Width(hostPathW).Render(r.URL.Host+path),
)
} else {
line = lipgloss.JoinHorizontal(lipgloss.Top,
" ",
s.Method(r.Method).Render(r.Method),
s.Faint.Render(" "),
s.Bold.Render(r.URL.Host),
s.Faint.Render(path),
style.S.Method(r.Method).Render(r.Method),
ilovetui.S.Faint.Render(" "),
ilovetui.S.Bold.Render(r.URL.Host),
ilovetui.S.Faint.Render(path),
)
}
sb.WriteString(line + "\n")
@@ -155,10 +149,9 @@ func (m *Model) renderList() string {
func (m *Model) renderResponseList() string {
if len(m.responseQueue) == 0 {
return lipgloss.Place(m.responseViewport.Width(), m.responseViewport.Height(), lipgloss.Center, lipgloss.Center, style.S.Faint.Render(util.CenterLines("(҂◡_◡)", "no response yet")))
return lipgloss.Place(m.responseViewport.Width(), m.responseViewport.Height(), lipgloss.Center, lipgloss.Center, ilovetui.S.Faint.Render(util.CenterLines("(҂◡_◡)", "no response yet")))
}
s := style.S
start, end := util.PageBounds(m.responsePager, len(m.responseQueue))
var sb strings.Builder
@@ -177,7 +170,7 @@ func (m *Model) renderResponseList() string {
statusStr := fmt.Sprintf("%d", code)
selected := globalIdx == m.responseCursor
selBg := s.Selection
selBg := ilovetui.S.Selection
statusSt := style.StatusStyle(code, 7)
@@ -192,7 +185,7 @@ func (m *Model) renderResponseList() string {
if selected {
bg := lipgloss.NewStyle().Background(selBg)
line = lipgloss.JoinHorizontal(lipgloss.Top,
bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"),
bg.Bold(true).Foreground(ilovetui.S.Primary).Width(2).Render(">"),
statusSt.Background(selBg).Render(statusStr),
bg.Width(2).Render(""),
bg.Bold(true).Width(hostPathW).Render(f.Request.URL.Host+path),
@@ -201,9 +194,9 @@ func (m *Model) renderResponseList() string {
line = lipgloss.JoinHorizontal(lipgloss.Top,
" ",
statusSt.Render(statusStr),
s.Faint.Render(" "),
s.Bold.Render(f.Request.URL.Host),
s.Faint.Render(path),
ilovetui.S.Faint.Render(" "),
ilovetui.S.Bold.Render(f.Request.URL.Host),
ilovetui.S.Faint.Render(path),
)
}
sb.WriteString(line + "\n")
+9 -9
View File
@@ -10,9 +10,9 @@ import (
"charm.land/bubbles/v2/textinput"
"charm.land/bubbles/v2/viewport"
tea "charm.land/bubbletea/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/plugins"
"github.com/anotherhadi/spilltea/internal/style"
)
type Model struct {
@@ -36,7 +36,7 @@ type Model struct {
}
func New(mgr *plugins.Manager) Model {
ta := style.NewTextarea(true)
ta := ilovetui.NewTextarea(true)
ta.Placeholder = "plugin configuration..."
ta.Blur()
@@ -45,12 +45,12 @@ func New(mgr *plugins.Manager) Model {
return Model{
manager: mgr,
listViewport: style.NewViewport(),
detailViewport: style.NewViewport(),
listViewport: ilovetui.NewViewport(),
detailViewport: ilovetui.NewViewport(),
textarea: ta,
filterInput: fi,
pager: style.NewPaginator(),
help: style.NewHelp(),
pager: ilovetui.NewPaginator(),
help: ilovetui.NewHelp(),
}
}
@@ -70,14 +70,14 @@ func (m *Model) recalcSizes() {
}
m.help.SetWidth(m.width - 2)
listH, detailH := style.SplitH(m.height, m.renderStatusBar(), 0.4)
listH, detailH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.4)
inner := m.width - 2
if inner < 0 {
inner = 0
}
listVH := style.PanelContentH(listH) - 1 // -1 for the pager dots row
listVH := ilovetui.ContentHeight(listH) - 1 // -1 for the pager dots row
if listVH < 0 {
listVH = 0
}
@@ -90,7 +90,7 @@ func (m *Model) recalcSizes() {
m.filterInput.SetWidth(inner - 2)
detailContentH := style.PanelContentH(detailH)
detailContentH := ilovetui.ContentHeight(detailH)
const headerH = 2
const configFixedH = 2 // blank line + label line
textareaH := max(3, detailContentH/3)
+29 -34
View File
@@ -7,19 +7,18 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/glamour/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/spilltea/internal/config"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
)
func (m Model) View() tea.View {
if m.width == 0 || m.manager == nil {
return tea.NewView(lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, style.S.Faint.Render(util.CenterLines("(._.)~*.'", "no plugins loaded"))))
return tea.NewView(lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, ilovetui.S.Faint.Render(util.CenterLines("(._.)~*.'", "no plugins loaded"))))
}
listH, detailH := style.SplitH(m.height, m.renderStatusBar(), 0.4)
listH, detailH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.4)
content := lipgloss.JoinVertical(lipgloss.Left,
m.renderListPanel(m.width, listH),
@@ -30,36 +29,34 @@ func (m Model) View() tea.View {
}
func (m *Model) renderListPanel(w, h int) string {
s := style.S
panelStyle := s.PanelFocused
panelStyle := ilovetui.S.PanelFocused
if m.editing {
panelStyle = s.Panel
panelStyle = ilovetui.S.Panel
}
var dots string
if len(m.filtered) > 0 {
dots = s.Faint.Render(m.pager.View())
dots = ilovetui.S.Faint.Render(m.pager.View())
}
inner := lipgloss.JoinVertical(lipgloss.Left,
m.listViewport.View(),
lipgloss.PlaceHorizontal(m.listViewport.Width(), lipgloss.Center, dots),
)
return style.RenderWithTitle(panelStyle, icons.I.Plugin+"Plugins", inner, w, h)
return ilovetui.RenderWithTitle(panelStyle, icons.I.Plugin+"Plugins", inner, w, h)
}
func (m *Model) renderDetailPanel(h int) string {
s := style.S
panelStyle := s.Panel
panelStyle := ilovetui.S.Panel
if m.editing {
panelStyle = s.PanelFocused
panelStyle = ilovetui.S.PanelFocused
}
info, ok := m.selected()
if !ok {
return style.RenderWithTitle(panelStyle, icons.I.Detail+"Detail", "", m.width, h)
return ilovetui.RenderWithTitle(panelStyle, icons.I.Detail+"Detail", "", m.width, h)
}
statusSt := lipgloss.NewStyle().Foreground(s.Error)
statusSt := lipgloss.NewStyle().Foreground(ilovetui.S.Error)
if info.Enabled {
statusSt = lipgloss.NewStyle().Foreground(s.Success)
statusSt = lipgloss.NewStyle().Foreground(ilovetui.S.Success)
}
status := "disabled"
if info.Enabled {
@@ -69,26 +66,26 @@ func (m *Model) renderDetailPanel(h int) string {
pad := lipgloss.NewStyle().Padding(0, 1)
header := pad.Render(
s.Bold.Render(info.Name) + " " + statusSt.Render(status) + "\n" +
s.Faint.Render(filepath.Base(info.FilePath)),
ilovetui.S.Bold.Render(info.Name) + " " + statusSt.Render(status) + "\n" +
ilovetui.S.Faint.Render(filepath.Base(info.FilePath)),
)
parts := []string{header, style.ViewportView(&m.detailViewport)}
parts := []string{header, ilovetui.ViewportView(&m.detailViewport)}
if m.hasConfig() {
var configLabel string
if m.editing {
escKey := keys.Keys.Global.Escape.Help().Key
configLabel = pad.Render(s.Faint.Render("editing config (" + escKey + " to save):"))
configLabel = pad.Render(ilovetui.S.Faint.Render("editing config (" + escKey + " to save):"))
} else {
editKey := keys.Keys.Plugins.EditConfig.Help().Key
configLabel = pad.Render(s.Faint.Render("config (" + editKey + " to edit):"))
configLabel = pad.Render(ilovetui.S.Faint.Render("config (" + editKey + " to edit):"))
}
parts = append(parts, "", configLabel, pad.Render(m.textarea.View()))
}
inner := lipgloss.JoinVertical(lipgloss.Left, parts...)
return style.RenderWithTitle(panelStyle, icons.I.Detail+"Detail", inner, m.width, h)
return ilovetui.RenderWithTitle(panelStyle, icons.I.Detail+"Detail", inner, m.width, h)
}
func renderPluginDescription(desc string, width int) string {
@@ -100,7 +97,7 @@ func renderPluginDescription(desc string, width int) string {
desc = strings.Join(lines, "\n")
r, err := glamour.NewTermRenderer(
glamour.WithStyles(style.GlamourStyleConfig(config.Global)),
glamour.WithStyles(ilovetui.GlamourStyleConfig()),
glamour.WithWordWrap(width),
)
if err != nil {
@@ -114,23 +111,21 @@ func renderPluginDescription(desc string, width int) string {
}
func (m *Model) renderStatusBar() string {
s := style.S
pad := lipgloss.NewStyle().Padding(0, 1)
filterKey := keys.Keys.Plugins.Filter.Help().Key
if m.filtering {
return pad.Render(s.Faint.Render(filterKey) + " " + m.filterInput.View())
return pad.Render(ilovetui.S.Faint.Render(filterKey) + " " + m.filterInput.View())
}
if m.filter != "" {
escKey := keys.Keys.Global.Escape.Help().Key
accent := lipgloss.NewStyle().Foreground(s.Primary)
filterLine := pad.Render(accent.Render(filterKey) + " " + s.Bold.Render(m.filter) + s.Faint.Render(" "+escKey+" to clear"))
accent := lipgloss.NewStyle().Foreground(ilovetui.S.Primary)
filterLine := pad.Render(accent.Render(filterKey) + " " + ilovetui.S.Bold.Render(m.filter) + ilovetui.S.Faint.Render(" "+escKey+" to clear"))
return lipgloss.JoinVertical(lipgloss.Left, filterLine, pad.Render(m.help.View(pluginsKeyMap{editing: m.editing, hasConfig: m.hasConfig(), width: m.width})))
}
return pad.Render(m.help.View(pluginsKeyMap{editing: m.editing, hasConfig: m.hasConfig(), width: m.width}))
}
func (m *Model) renderList() string {
s := style.S
if len(m.filtered) == 0 {
msg := util.CenterLines("(ง •̀_•́)ง", "no plugins", "", "spilltea --add-default-plugins")
if m.filter != "" {
@@ -139,7 +134,7 @@ func (m *Model) renderList() string {
return lipgloss.Place(
m.listViewport.Width(), m.listViewport.Height(),
lipgloss.Center, lipgloss.Center,
s.Faint.Render(msg),
ilovetui.S.Faint.Render(msg),
)
}
@@ -150,10 +145,10 @@ func (m *Model) renderList() string {
globalIdx := start + i
selected := globalIdx == m.cursor
enabledSt := lipgloss.NewStyle().Foreground(s.Error)
enabledSt := lipgloss.NewStyle().Foreground(ilovetui.S.Error)
enabledStr := "off"
if p.Enabled {
enabledSt = lipgloss.NewStyle().Foreground(s.Success)
enabledSt = lipgloss.NewStyle().Foreground(ilovetui.S.Success)
enabledStr = "on "
}
@@ -166,10 +161,10 @@ func (m *Model) renderList() string {
var line string
if selected {
bg := lipgloss.NewStyle().Background(s.Selection)
bg := lipgloss.NewStyle().Background(ilovetui.S.Selection)
line = lipgloss.JoinHorizontal(lipgloss.Top,
bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"),
enabledSt.Background(s.Selection).Width(3).Render(enabledStr),
bg.Bold(true).Foreground(ilovetui.S.Primary).Width(2).Render(">"),
enabledSt.Background(ilovetui.S.Selection).Width(3).Render(enabledStr),
bg.Width(1).Render(""),
bg.Bold(true).Width(nameW).Render(p.Name),
)
@@ -178,7 +173,7 @@ func (m *Model) renderList() string {
" ",
enabledSt.Width(3).Render(enabledStr),
" ",
s.Bold.Render(p.Name),
ilovetui.S.Bold.Render(p.Name),
)
}
sb.WriteString(line + "\n")
+10 -10
View File
@@ -9,9 +9,9 @@ import (
"charm.land/bubbles/v2/textarea"
"charm.land/bubbles/v2/viewport"
tea "charm.land/bubbletea/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/db"
"github.com/anotherhadi/spilltea/internal/keys"
"github.com/anotherhadi/spilltea/internal/style"
)
type SendToReplayMsg struct {
@@ -61,15 +61,15 @@ type Model struct {
}
func New() Model {
ta := style.NewTextarea(false)
ta := ilovetui.NewTextarea(false)
ta.Blur()
return Model{
listViewport: style.NewViewport(),
requestViewport: style.NewViewport(),
responseViewport: style.NewViewport(),
listViewport: ilovetui.NewViewport(),
requestViewport: ilovetui.NewViewport(),
responseViewport: ilovetui.NewViewport(),
textarea: ta,
pager: style.NewPaginator(),
help: style.NewHelp(),
pager: ilovetui.NewPaginator(),
help: ilovetui.NewHelp(),
}
}
@@ -149,13 +149,13 @@ func (m *Model) SetSize(w, h int) {
func (m *Model) recalcSizes() {
m.help.SetWidth(m.width - 2)
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
listInner := m.width - 2
if listInner < 0 {
listInner = 0
}
listVH := style.PanelContentH(listH) - 1 // -1 for the pager dots row
listVH := ilovetui.ContentHeight(listH) - 1 // -1 for the pager dots row
if listVH < 0 {
listVH = 0
}
@@ -175,7 +175,7 @@ func (m *Model) recalcSizes() {
if rightInner < 0 {
rightInner = 0
}
bodyVH := style.PanelContentH(bodyH)
bodyVH := ilovetui.ContentHeight(bodyH)
m.requestViewport.SetWidth(leftInner)
m.requestViewport.SetHeight(bodyVH)
+3 -2
View File
@@ -16,6 +16,7 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/andybalholm/brotli"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/config"
"github.com/anotherhadi/spilltea/internal/db"
"github.com/anotherhadi/spilltea/internal/keys"
@@ -350,11 +351,11 @@ func (m *Model) refreshBody() {
m.requestViewport.SetXOffset(0)
if e.Sending {
m.responseViewport.SetContent(lipgloss.Place(m.responseViewport.Width(), m.responseViewport.Height(), lipgloss.Center, lipgloss.Center, style.S.Faint.Render(util.CenterLines("(ノ◕ヮ◕)ノ*:・゚", "sending..."))))
m.responseViewport.SetContent(lipgloss.Place(m.responseViewport.Width(), m.responseViewport.Height(), lipgloss.Center, lipgloss.Center, ilovetui.S.Faint.Render(util.CenterLines("(ノ◕ヮ◕)ノ*:・゚", "sending..."))))
} else if e.ResponseRaw != "" {
m.responseViewport.SetContent(style.HighlightHTTP(e.ResponseRaw))
} else {
m.responseViewport.SetContent(lipgloss.Place(m.responseViewport.Width(), m.responseViewport.Height(), lipgloss.Center, lipgloss.Center, style.S.Faint.Render(util.CenterLines("( •_•)>⌐■", "press send to fire"))))
m.responseViewport.SetContent(lipgloss.Place(m.responseViewport.Width(), m.responseViewport.Height(), lipgloss.Center, lipgloss.Center, ilovetui.S.Faint.Render(util.CenterLines("( •_•)>⌐■", "press send to fire"))))
}
m.responseViewport.SetYOffset(0)
m.responseViewport.SetXOffset(0)
+24 -27
View File
@@ -6,6 +6,7 @@ import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
ilovetui "github.com/anotherhadi/ilovetui"
"github.com/anotherhadi/spilltea/internal/icons"
"github.com/anotherhadi/spilltea/internal/style"
"github.com/anotherhadi/spilltea/internal/util"
@@ -16,7 +17,7 @@ func (m Model) View() tea.View {
return tea.NewView("Loading...")
}
listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)
listH, bodyH := ilovetui.SplitH(m.height, m.renderStatusBar(), 0.35)
leftW, rightW := m.bodyHalfWidths()
bodyRow := lipgloss.JoinHorizontal(lipgloss.Top,
@@ -33,45 +34,42 @@ func (m Model) View() tea.View {
}
func (m *Model) renderListPanel(w, h int) string {
s := style.S
panelStyle := s.Panel
panelStyle := ilovetui.S.Panel
if !m.editing && m.focusedPanel == panelList {
panelStyle = s.PanelFocused
panelStyle = ilovetui.S.PanelFocused
}
var dots string
if len(m.entries) > 0 {
dots = s.Faint.Render(m.pager.View())
dots = ilovetui.S.Faint.Render(m.pager.View())
}
inner := lipgloss.JoinVertical(lipgloss.Left,
m.listViewport.View(),
lipgloss.PlaceHorizontal(m.listViewport.Width(), lipgloss.Center, dots),
)
return style.RenderWithTitle(panelStyle, icons.I.Replay+"Replay", inner, w, h)
return ilovetui.RenderWithTitle(panelStyle, icons.I.Replay+"Replay", inner, w, h)
}
func (m *Model) renderRequestPanel(w, h int) string {
s := style.S
var body string
border := s.Panel
border := ilovetui.S.Panel
if m.editing {
body = m.textarea.View()
border = s.PanelFocused
border = ilovetui.S.PanelFocused
} else {
body = style.ViewportView(&m.requestViewport)
body = ilovetui.ViewportView(&m.requestViewport)
if m.focusedPanel == panelRequest {
border = s.PanelFocused
border = ilovetui.S.PanelFocused
}
}
return style.RenderWithTitle(border, icons.I.Request+"Request", body, w, h)
return ilovetui.RenderWithTitle(border, icons.I.Request+"Request", body, w, h)
}
func (m *Model) renderResponsePanel(w, h int) string {
s := style.S
border := s.Panel
border := ilovetui.S.Panel
if !m.editing && m.focusedPanel == panelResponse {
border = s.PanelFocused
border = ilovetui.S.PanelFocused
}
return style.RenderWithTitle(border, icons.I.Response+"Response", style.ViewportView(&m.responseViewport), w, h)
return ilovetui.RenderWithTitle(border, icons.I.Response+"Response", ilovetui.ViewportView(&m.responseViewport), w, h)
}
func (m *Model) renderStatusBar() string {
@@ -83,18 +81,17 @@ func (m *Model) renderList() string {
return lipgloss.Place(
m.listViewport.Width(), m.listViewport.Height(),
lipgloss.Center, lipgloss.Center,
style.S.Faint.Render(util.CenterLines("(╥﹏╥)", "send a request from History or Intercept")),
ilovetui.S.Faint.Render(util.CenterLines("(╥﹏╥)", "send a request from History or Intercept")),
)
}
s := style.S
start, end := util.PageBounds(m.pager, len(m.entries))
var sb strings.Builder
for i, e := range m.entries[start:end] {
globalIdx := start + i
selected := globalIdx == m.cursor
selBg := s.Selection
selBg := ilovetui.S.Selection
w := m.listViewport.Width()
const fixedW = 2 + 7 + 1 + 3 + 1
@@ -109,8 +106,8 @@ func (m *Model) renderList() string {
if selected {
bg := lipgloss.NewStyle().Background(selBg)
line = lipgloss.JoinHorizontal(lipgloss.Top,
bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"),
s.Method(e.Method).Background(selBg).Render(e.Method),
bg.Bold(true).Foreground(ilovetui.S.Primary).Width(2).Render(">"),
style.S.Method(e.Method).Background(selBg).Render(e.Method),
bg.Width(1).Render(""),
statusSt.Background(selBg).Render(statusStr),
bg.Width(1).Render(""),
@@ -119,12 +116,12 @@ func (m *Model) renderList() string {
} else {
line = lipgloss.JoinHorizontal(lipgloss.Top,
" ",
s.Method(e.Method).Render(e.Method),
style.S.Method(e.Method).Render(e.Method),
" ",
statusSt.Render(statusStr),
" ",
s.Bold.Render(e.Host),
s.Faint.Render(e.Path),
ilovetui.S.Bold.Render(e.Host),
ilovetui.S.Faint.Render(e.Path),
)
}
sb.WriteString(line + "\n")
@@ -136,11 +133,11 @@ func entryStatus(e Entry) (string, lipgloss.Style) {
base := lipgloss.NewStyle().Bold(true).Width(3)
switch {
case e.Sending:
return "···", base.Foreground(style.S.Subtle)
return "···", base.Foreground(ilovetui.S.Subtle)
case e.Err != nil:
return "ERR", base.Foreground(style.S.Error)
return "ERR", base.Foreground(ilovetui.S.Error)
case e.StatusCode == 0:
return "---", base.Foreground(style.S.Subtle)
return "---", base.Foreground(ilovetui.S.Subtle)
}
return fmt.Sprintf("%3d", e.StatusCode), style.StatusStyle(e.StatusCode, 3)
}
+4
View File
@@ -25,6 +25,10 @@ schema = 3
version = 'v1.0.4'
hash = 'sha256-gAnPRdGP4yna4hiRIEDyBtDOVJqd7RU27wlPu96Rdf8='
[mod.'github.com/anotherhadi/ilovetui']
version = 'v0.1.6'
hash = 'sha256-7E+7UFks5vM3XWCvX2joFmHGcW7qqnoox6ZPFglaLO4='
[mod.'github.com/atotto/clipboard']
version = 'v0.1.4'
hash = 'sha256-ZZ7U5X0gWOu8zcjZcWbcpzGOGdycwq0TjTFh/eZHjXk='