Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add WindowTitle setting #507

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,20 @@ Set WindowBar Colorful
<img width="600" alt="Example of setting the margin" src="https://vhs.charm.sh/vhs-4VgviCu38DbaGtbRzhtOUI.gif">
</picture>

#### Set Window Title

Set a title on the window bar of the terminal window with the `Set WindowTitle` command.

```elixir
Set WindowTitle "Live in the Terminal"
```

<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://vhs.charm.sh/vhs-6s07y8kWtPAC5TBLzxhRfS.gif">
<source media="(prefers-color-scheme: light)" srcset="https://vhs.charm.sh/vhs-6s07y8kWtPAC5TBLzxhRfS.gif">
<img width="600" alt="Example of setting the window title" src="https://vhs.charm.sh/vhs-6s07y8kWtPAC5TBLzxhRfS.gif">
</picture>

#### Set Border Radius

Set the border radius (in pixels) of the terminal window with the `Set BorderRadius` command.
Expand Down
7 changes: 7 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ var Settings = map[string]CommandFunc{
"MarginFill": ExecuteSetMarginFill,
"Margin": ExecuteSetMargin,
"WindowBar": ExecuteSetWindowBar,
"WindowTitle": ExecuteSetWindowTitle,
"WindowBarSize": ExecuteSetWindowBarSize,
"BorderRadius": ExecuteSetBorderRadius,
"WaitPattern": ExecuteSetWaitPattern,
Expand Down Expand Up @@ -575,6 +576,7 @@ func ExecuteSetTheme(c parser.Command, v *VHS) error {

v.Options.Video.Style.BackgroundColor = v.Options.Theme.Background
v.Options.Video.Style.WindowBarColor = v.Options.Theme.Background
v.Options.Video.Style.WindowTitleColor = v.Options.Theme.Foreground

return nil
}
Expand Down Expand Up @@ -677,6 +679,11 @@ func ExecuteSetWindowBar(c parser.Command, v *VHS) error {
return nil
}

func ExecuteSetWindowTitle(c parser.Command, v *VHS) error {
v.Options.Video.Style.WindowTitle = c.Args
return nil
}

// ExecuteSetWindowBar sets window bar size
func ExecuteSetWindowBarSize(c parser.Command, v *VHS) error {
windowBarSize, err := strconv.Atoi(c.Args)
Expand Down
101 changes: 101 additions & 0 deletions draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import (
"image/png"
"math"
"os"

"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/font/gofont/goregular"
)

type circle struct {
Expand Down Expand Up @@ -233,6 +238,72 @@ const (
barToDotBorderRatio = 5
)

func drawWindowTitle(
dotsSpace,
borderSpace int,
dotsRight bool,
opts StyleOptions,
windowBar *image.RGBA,
) error {
ttf, err := freetype.ParseFont(goregular.TTF)
if err != nil {
return err
}

title := opts.WindowTitle

titleMinX := dotsSpace
titleMaxX := borderSpace
if dotsRight {
titleMinX, titleMaxX = titleMaxX, titleMinX
}

img := image.NewRGBA(
image.Rectangle{
image.Point{
windowBar.Rect.Min.X + titleMinX,
windowBar.Rect.Min.Y,
},
image.Point{
windowBar.Rect.Max.X - titleMaxX,
windowBar.Rect.Max.Y,
},
},
)

fg, _ := parseHexColor(opts.WindowTitleColor)

c := freetype.NewContext()
c.SetDPI(72)
c.SetFont(ttf)
c.SetFontSize(float64(half(opts.WindowBarSize)))
c.SetClip(img.Bounds())
c.SetDst(windowBar)
c.SetSrc(&image.Uniform{fg})
c.SetHinting(font.HintingNone)

fontFace := truetype.NewFace(ttf, &truetype.Options{})
titleTextWidth := font.MeasureString(fontFace, title).Ceil()
titleBarWidth := img.Rect.Max.X - img.Rect.Min.X

// Center-align title text if it will fit, else left-align (truncated by bounds)
var textStartX int
if titleBarWidth >= titleTextWidth {
textStartX = (img.Rect.Max.X - titleTextWidth - titleMaxX) / 2
} else {
textStartX = titleMinX
}

// Font size is half window bar size, thus Y start pos is 0.75 of window bar size
textStartY := int(c.PointToFixed(float64(opts.WindowBarSize)*0.75) >> 6)

pt := freetype.Pt(textStartX, textStartY)
if _, err := c.DrawString(title, pt); err != nil {
return err
}
return nil
}

func makeColorfulBar(termWidth int, termHeight int, isRight bool, opts StyleOptions, targetpng string) error {
// Radius of dots
dotRad := opts.WindowBarSize / barToDotRatio
Expand Down Expand Up @@ -305,6 +376,21 @@ func makeColorfulBar(termWidth int, termHeight int, isRight bool, opts StyleOpti
draw.Over,
)

if opts.WindowTitle != "" {
titleDotsSpace := dotGap + dotRad*3 + dotSpace*3
titleBorderSpace := dotGap + dotSpace

if err := drawWindowTitle(
titleDotsSpace,
titleBorderSpace,
isRight,
opts,
img,
); err != nil {
fmt.Println(ErrorStyle.Render(fmt.Sprintf("Couldn't draw window title: %s.", err)))
}
}

f, err := os.Create(targetpng)
if err != nil {
fmt.Println(ErrorStyle.Render("Couldn't draw colorful bar: unable to save file."))
Expand Down Expand Up @@ -379,6 +465,21 @@ func makeRingBar(termWidth int, termHeight int, isRight bool, opts StyleOptions,
)
}

if opts.WindowTitle != "" {
titleDotsSpace := ringGap + outerRad*3 + ringSpace*3
titleBorderSpace := ringGap + ringSpace

if err := drawWindowTitle(
titleDotsSpace,
titleBorderSpace,
isRight,
opts,
img,
); err != nil {
fmt.Println(ErrorStyle.Render(fmt.Sprintf("Couldn't draw window title: %s.", err)))
}
}

f, err := os.Create(targetpng)
if err != nil {
fmt.Println(ErrorStyle.Render("Couldn't draw ring bar: unable to save file."))
Expand Down
2 changes: 1 addition & 1 deletion examples/decorations/decorations.tape
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Set WindowBarSize 40
Set BorderRadius 8

Type "I can't believe it's not butter."
Sleep 2s
Sleep 2s
1 change: 1 addition & 0 deletions examples/demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
# Set BorderRadius <number> Set terminal border radius, in pixels.
# Set WindowBar <string> Set window bar type. (one of: Rings, RingsRight, Colorful, ColorfulRight)
# Set WindowBarSize <number> Set window bar size, in pixels. Default is 40.
# Set WindowTitle <string> Set window title. Text color is taken from theme.
# Set TypingSpeed <time> Set the typing speed of the terminal. Default is 50ms.
#
# Sleep:
Expand Down
1 change: 1 addition & 0 deletions examples/fixtures/all.tape
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Set TypingSpeed .1
Set LoopOffset 60.4
Set LoopOffset 20.99%
Set CursorBlink false
Set WindowTitle "Hello, world!"

# Sleep:
Sleep 1
Expand Down
Binary file added examples/settings/set-window-title.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions examples/settings/set-window-title.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Output examples/settings/set-window-title.gif

Set FontSize 25
Set Width 800
Set Height 400
Set Padding 20

Set WindowBar Colorful
Set WindowTitle "Live in the Terminal"
Set WindowBarSize 40

Type "This terminal window has a title."
Sleep 2s
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/charmbracelet/wish v1.4.3
github.com/creack/pty v1.1.23
github.com/go-rod/rod v0.116.2
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/hashicorp/go-version v1.7.0
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-runewidth v0.0.16
Expand All @@ -24,6 +25,7 @@ require (
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a
github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.28.0
golang.org/x/image v0.18.0
golang.org/x/term v0.25.0
)

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
Expand Down Expand Up @@ -136,6 +138,8 @@ golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
Expand Down
7 changes: 7 additions & 0 deletions lexer/lexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
func TestNextToken(t *testing.T) {
input := `
Output examples/out.gif
Set WindowTitle "Hello, world!"
Set FontSize 42
Set Padding 5
Set CursorBlink false
Expand All @@ -34,6 +35,9 @@ Wait+Screen@1m /foobar/`
{token.OUTPUT, "Output"},
{token.STRING, "examples/out.gif"},
{token.SET, "Set"},
{token.WINDOW_TITLE, "WindowTitle"},
{token.STRING, "Hello, world!"},
{token.SET, "Set"},
{token.FONT_SIZE, "FontSize"},
{token.NUMBER, "42"},
{token.SET, "Set"},
Expand Down Expand Up @@ -164,6 +168,9 @@ func TestLexTapeFile(t *testing.T) {
{token.SET, "Set"},
{token.CURSOR_BLINK, "CursorBlink"},
{token.BOOLEAN, "false"},
{token.SET, "Set"},
{token.WINDOW_TITLE, "WindowTitle"},
{token.STRING, "Hello, world!"},
{token.COMMENT, " Sleep:"},
{token.SLEEP, "Sleep"},
{token.NUMBER, "1"},
Expand Down
3 changes: 3 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ func (p *Parser) parseSet() Command {
NewError(p.cur, windowBar+" is not a valid bar style."),
)
}
case token.WINDOW_TITLE:
cmd.Args = p.peek.Literal
p.nextToken()
case token.MARGIN_FILL:
cmd.Args = p.peek.Literal
p.nextToken()
Expand Down
1 change: 1 addition & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func TestParseTapeFile(t *testing.T) {
{Type: token.SET, Options: "LoopOffset", Args: "60.4%"},
{Type: token.SET, Options: "LoopOffset", Args: "20.99%"},
{Type: token.SET, Options: "CursorBlink", Args: "false"},
{Type: token.SET, Options: "WindowTitle", Args: "Hello, world!"},
{Type: token.SLEEP, Options: "", Args: "1s"},
{Type: token.SLEEP, Options: "", Args: "500ms"},
{Type: token.SLEEP, Options: "", Args: ".5s"},
Expand Down
44 changes: 24 additions & 20 deletions style.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,30 +60,34 @@ var (

// StyleOptions represents the ui options for video and screenshots.
type StyleOptions struct {
Width int
Height int
Padding int
BackgroundColor string
MarginFill string
Margin int
WindowBar string
WindowBarSize int
WindowBarColor string
BorderRadius int
Width int
Height int
Padding int
BackgroundColor string
MarginFill string
Margin int
WindowBar string
WindowTitle string
WindowTitleColor string
WindowBarSize int
WindowBarColor string
BorderRadius int
}

// DefaultStyleOptions returns default Style config.
func DefaultStyleOptions() *StyleOptions {
return &StyleOptions{
Width: defaultWidth,
Height: defaultHeight,
Padding: defaultPadding,
MarginFill: DefaultTheme.Background,
Margin: 0,
WindowBar: "",
WindowBarSize: defaultWindowBarSize,
WindowBarColor: DefaultTheme.Background,
BorderRadius: 0,
BackgroundColor: DefaultTheme.Background,
Width: defaultWidth,
Height: defaultHeight,
Padding: defaultPadding,
MarginFill: DefaultTheme.Background,
Margin: 0,
WindowBar: "",
WindowTitle: "",
WindowTitleColor: DefaultTheme.Foreground,
WindowBarSize: defaultWindowBarSize,
WindowBarColor: DefaultTheme.Background,
BorderRadius: 0,
BackgroundColor: DefaultTheme.Background,
}
}
4 changes: 3 additions & 1 deletion token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const (
MARGIN_FILL = "MARGIN_FILL" //nolint:revive
MARGIN = "MARGIN" //nolint:revive
WINDOW_BAR = "WINDOW_BAR" //nolint:revive
WINDOW_TITLE = "WINDOW_TITLE" //nolint:revive
WINDOW_BAR_SIZE = "WINDOW_BAR_SIZE" //nolint:revive
BORDER_RADIUS = "CORNER_RADIUS" //nolint:revive
WAIT = "WAIT" //nolint:revive
Expand Down Expand Up @@ -133,6 +134,7 @@ var Keywords = map[string]Type{
"MarginFill": MARGIN_FILL,
"Margin": MARGIN,
"WindowBar": WINDOW_BAR,
"WindowTitle": WINDOW_TITLE,
"WindowBarSize": WINDOW_BAR_SIZE,
"BorderRadius": BORDER_RADIUS,
"FontSize": FONT_SIZE,
Expand Down Expand Up @@ -164,7 +166,7 @@ func IsSetting(t Type) bool {
switch t {
case SHELL, FONT_FAMILY, FONT_SIZE, LETTER_SPACING, LINE_HEIGHT,
FRAMERATE, TYPING_SPEED, THEME, PLAYBACK_SPEED, HEIGHT, WIDTH,
PADDING, LOOP_OFFSET, MARGIN_FILL, MARGIN, WINDOW_BAR,
PADDING, LOOP_OFFSET, MARGIN_FILL, MARGIN, WINDOW_BAR, WINDOW_TITLE,
WINDOW_BAR_SIZE, BORDER_RADIUS, CURSOR_BLINK, WAIT_TIMEOUT, WAIT_PATTERN:
return true
default:
Expand Down