diff --git a/command.go b/command.go index d60f78520..fbb3f1210 100644 --- a/command.go +++ b/command.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "math/rand" "os" "os/exec" "regexp" @@ -49,35 +50,36 @@ type CommandFunc func(c parser.Command, v *VHS) error // CommandFuncs maps command types to their executable functions. var CommandFuncs = map[parser.CommandType]CommandFunc{ - token.BACKSPACE: ExecuteKey(input.Backspace), - token.DELETE: ExecuteKey(input.Delete), - token.INSERT: ExecuteKey(input.Insert), - token.DOWN: ExecuteKey(input.ArrowDown), - token.ENTER: ExecuteKey(input.Enter), - token.LEFT: ExecuteKey(input.ArrowLeft), - token.RIGHT: ExecuteKey(input.ArrowRight), - token.SPACE: ExecuteKey(input.Space), - token.UP: ExecuteKey(input.ArrowUp), - token.TAB: ExecuteKey(input.Tab), - token.ESCAPE: ExecuteKey(input.Escape), - token.PAGEUP: ExecuteKey(input.PageUp), - token.PAGEDOWN: ExecuteKey(input.PageDown), - token.HIDE: ExecuteHide, - token.REQUIRE: ExecuteRequire, - token.SHOW: ExecuteShow, - token.SET: ExecuteSet, - token.OUTPUT: ExecuteOutput, - token.SLEEP: ExecuteSleep, - token.TYPE: ExecuteType, - token.CTRL: ExecuteCtrl, - token.ALT: ExecuteAlt, - token.SHIFT: ExecuteShift, - token.ILLEGAL: ExecuteNoop, - token.SCREENSHOT: ExecuteScreenshot, - token.COPY: ExecuteCopy, - token.PASTE: ExecutePaste, - token.ENV: ExecuteEnv, - token.WAIT: ExecuteWait, + token.BACKSPACE: ExecuteKey(input.Backspace), + token.DELETE: ExecuteKey(input.Delete), + token.INSERT: ExecuteKey(input.Insert), + token.DOWN: ExecuteKey(input.ArrowDown), + token.ENTER: ExecuteKey(input.Enter), + token.LEFT: ExecuteKey(input.ArrowLeft), + token.RIGHT: ExecuteKey(input.ArrowRight), + token.SPACE: ExecuteKey(input.Space), + token.UP: ExecuteKey(input.ArrowUp), + token.TAB: ExecuteKey(input.Tab), + token.ESCAPE: ExecuteKey(input.Escape), + token.PAGEUP: ExecuteKey(input.PageUp), + token.PAGEDOWN: ExecuteKey(input.PageDown), + token.HIDE: ExecuteHide, + token.REQUIRE: ExecuteRequire, + token.SHOW: ExecuteShow, + token.SET: ExecuteSet, + token.OUTPUT: ExecuteOutput, + token.SLEEP: ExecuteSleep, + token.TYPE: ExecuteType, + token.TYPE_VARIABLE: ExecuteTypeVariable, + token.CTRL: ExecuteCtrl, + token.ALT: ExecuteAlt, + token.SHIFT: ExecuteShift, + token.ILLEGAL: ExecuteNoop, + token.SCREENSHOT: ExecuteScreenshot, + token.COPY: ExecuteCopy, + token.PASTE: ExecutePaste, + token.ENV: ExecuteEnv, + token.WAIT: ExecuteWait, } // ExecuteNoop is a no-op command that does nothing. @@ -365,6 +367,47 @@ func ExecuteType(c parser.Command, v *VHS) error { return nil } +// ExecuteTypeVariable types the argument string on the running instance of vhs, in a variable typing speed +func ExecuteTypeVariable(c parser.Command, v *VHS) error { + typingSpeedVariable := v.Options.TypingSpeedVariable + var minTypingSpeed, maxTypingSpeed time.Duration + if c.Options != "" { + var err error + minTypingSpeed, err = time.ParseDuration(c.Options) + if err != nil { + return fmt.Errorf("failed to parse min typing speed: %w", err) + } + maxTypingSpeed, err = time.ParseDuration(c.Options) + if err != nil { + return fmt.Errorf("failed to parse max typing speed: %w", err) + } + } else { + minTypingSpeed = typingSpeedVariable.MinTypingSpeed + maxTypingSpeed = typingSpeedVariable.MaxTypingSpeed + } + + for _, r := range c.Args { + k, ok := keymap[r] + if ok { + err := v.Page.Keyboard.Type(k) + if err != nil { + return fmt.Errorf("failed to type key %c: %w", r, err) + } + } else { + err := v.Page.MustElement("textarea").Input(string(r)) + if err != nil { + return fmt.Errorf("failed to input text: %w", err) + } + + v.Page.MustWaitIdle() + } + sleepDuration := minTypingSpeed + time.Duration(rand.Int63n(int64(maxTypingSpeed-minTypingSpeed))) + time.Sleep(sleepDuration) + } + + return nil +} + // ExecuteOutput applies the output on the vhs videos. func ExecuteOutput(c parser.Command, v *VHS) error { switch c.Options { @@ -420,27 +463,28 @@ func ExecutePaste(_ parser.Command, v *VHS) error { // Settings maps the Set commands to their respective functions. var Settings = map[string]CommandFunc{ - "FontFamily": ExecuteSetFontFamily, - "FontSize": ExecuteSetFontSize, - "Framerate": ExecuteSetFramerate, - "Height": ExecuteSetHeight, - "LetterSpacing": ExecuteSetLetterSpacing, - "LineHeight": ExecuteSetLineHeight, - "PlaybackSpeed": ExecuteSetPlaybackSpeed, - "Padding": ExecuteSetPadding, - "Theme": ExecuteSetTheme, - "TypingSpeed": ExecuteSetTypingSpeed, - "Width": ExecuteSetWidth, - "Shell": ExecuteSetShell, - "LoopOffset": ExecuteLoopOffset, - "MarginFill": ExecuteSetMarginFill, - "Margin": ExecuteSetMargin, - "WindowBar": ExecuteSetWindowBar, - "WindowBarSize": ExecuteSetWindowBarSize, - "BorderRadius": ExecuteSetBorderRadius, - "WaitPattern": ExecuteSetWaitPattern, - "WaitTimeout": ExecuteSetWaitTimeout, - "CursorBlink": ExecuteSetCursorBlink, + "FontFamily": ExecuteSetFontFamily, + "FontSize": ExecuteSetFontSize, + "Framerate": ExecuteSetFramerate, + "Height": ExecuteSetHeight, + "LetterSpacing": ExecuteSetLetterSpacing, + "LineHeight": ExecuteSetLineHeight, + "PlaybackSpeed": ExecuteSetPlaybackSpeed, + "Padding": ExecuteSetPadding, + "Theme": ExecuteSetTheme, + "TypingSpeed": ExecuteSetTypingSpeed, + "TypingSpeedVariable": ExecuteSetTypingSpeedVariable, + "Width": ExecuteSetWidth, + "Shell": ExecuteSetShell, + "LoopOffset": ExecuteLoopOffset, + "MarginFill": ExecuteSetMarginFill, + "Margin": ExecuteSetMargin, + "WindowBar": ExecuteSetWindowBar, + "WindowBarSize": ExecuteSetWindowBarSize, + "BorderRadius": ExecuteSetBorderRadius, + "WaitPattern": ExecuteSetWaitPattern, + "WaitTimeout": ExecuteSetWaitTimeout, + "CursorBlink": ExecuteSetCursorBlink, } // ExecuteSet applies the settings on the running vhs specified by the @@ -590,6 +634,31 @@ func ExecuteSetTypingSpeed(c parser.Command, v *VHS) error { return nil } +// ExecuteSetTypingSpeedVariable applies the default typing speed variable on the vhs. +func ExecuteSetTypingSpeedVariable(c parser.Command, v *VHS) error { + typingSpeedVariable := strings.Split(c.Args, " ") + if len(typingSpeedVariable) != 2 { + return fmt.Errorf("failed to parse typing speed variable args, expected 2 values") + } + minTypingSpeed, err := time.ParseDuration(typingSpeedVariable[0]) + if err != nil { + return fmt.Errorf("failed to parse min typing speed: %w", err) + } + maxTypingSpeed, err := time.ParseDuration(typingSpeedVariable[1]) + if err != nil { + return fmt.Errorf("failed to parse max typing speed: %w", err) + } + + if minTypingSpeed > maxTypingSpeed { + return fmt.Errorf("invalid typing speed variable range: min (%s) should be less than or equal to max (%s)", minTypingSpeed, maxTypingSpeed) + } + + v.Options.TypingSpeedVariable.MinTypingSpeed = minTypingSpeed + v.Options.TypingSpeedVariable.MaxTypingSpeed = maxTypingSpeed + + return nil +} + // ExecuteSetWaitTimeout applies the default wait timeout on the vhs. func ExecuteSetWaitTimeout(c parser.Command, v *VHS) error { waitTimeout, err := time.ParseDuration(c.Args) diff --git a/command_test.go b/command_test.go index eb89fd10e..ce36f569b 100644 --- a/command_test.go +++ b/command_test.go @@ -8,12 +8,12 @@ import ( ) func TestCommand(t *testing.T) { - const numberOfCommands = 29 + const numberOfCommands = 30 if len(parser.CommandTypes) != numberOfCommands { t.Errorf("Expected %d commands, got %d", numberOfCommands, len(parser.CommandTypes)) } - const numberOfCommandFuncs = 29 + const numberOfCommandFuncs = 30 if len(CommandFuncs) != numberOfCommandFuncs { t.Errorf("Expected %d commands, got %d", numberOfCommandFuncs, len(CommandFuncs)) } diff --git a/examples/fixtures/all.tape b/examples/fixtures/all.tape index cfd9e75fc..f1786ad1e 100644 --- a/examples/fixtures/all.tape +++ b/examples/fixtures/all.tape @@ -22,6 +22,7 @@ Set TypingSpeed .1 Set LoopOffset 60.4 Set LoopOffset 20.99% Set CursorBlink false +Set TypingSpeedVariable 0.1 1s # Sleep: Sleep 1 diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index f981d0e94..764a30cc5 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -15,6 +15,7 @@ Set Padding 5 Set CursorBlink false Type "echo 'Hello, world!'" Enter +TypeVariable "echo 'Hello, world!'" Type@.1 "echo 'Hello, world!'" Left 3 Sleep 1 @@ -45,6 +46,8 @@ Wait+Screen@1m /foobar/` {token.TYPE, "Type"}, {token.STRING, "echo 'Hello, world!'"}, {token.ENTER, "Enter"}, + {token.TYPE_VARIABLE, "TypeVariable"}, + {token.STRING, "echo 'Hello, world!'"}, {token.TYPE, "Type"}, {token.AT, "@"}, {token.NUMBER, ".1"}, @@ -164,6 +167,11 @@ func TestLexTapeFile(t *testing.T) { {token.SET, "Set"}, {token.CURSOR_BLINK, "CursorBlink"}, {token.BOOLEAN, "false"}, + {token.SET, "Set"}, + {token.TYPING_SPEED_VARIABLE, "TypingSpeedVariable"}, + {token.NUMBER, "0.1"}, + {token.NUMBER, "1"}, + {token.SECONDS, "s"}, {token.COMMENT, " Sleep:"}, {token.SLEEP, "Sleep"}, {token.NUMBER, "1"}, diff --git a/parser/parser.go b/parser/parser.go index 3f06c3b93..85bc1748e 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -48,6 +48,7 @@ var CommandTypes = []CommandType{ //nolint: deadcode token.SHOW, token.TAB, token.TYPE, + token.TYPE_VARIABLE, token.UP, token.WAIT, token.SOURCE, @@ -155,6 +156,8 @@ func (p *Parser) parseCommand() Command { return p.parseSleep() case token.TYPE: return p.parseType() + case token.TYPE_VARIABLE: + return p.parseTypeVariable() case token.CTRL: return p.parseCtrl() case token.ALT: @@ -462,6 +465,29 @@ func (p *Parser) parseSet() Command { } else if cmd.Options == "TypingSpeed" { cmd.Args += "s" } + case token.TYPING_SPEED_VARIABLE: + firstArg := p.peek.Literal + p.nextToken() + if p.peek.Type == token.MILLISECONDS || p.peek.Type == token.SECONDS { + firstArg += p.peek.Literal + p.nextToken() + } else { + firstArg += "s" + } + + var secondArg string + if p.peek.Type == token.NUMBER { + secondArg = p.peek.Literal + p.nextToken() + if p.peek.Type == token.MILLISECONDS || p.peek.Type == token.SECONDS { + secondArg += p.peek.Literal + p.nextToken() + } else { + secondArg += "s" + } + } + + cmd.Args = firstArg + " " + secondArg case token.WINDOW_BAR: cmd.Args = p.peek.Literal p.nextToken() @@ -590,6 +616,39 @@ func (p *Parser) parseType() Command { return cmd } +// parseTypeVariable parses a type variable command. +// A type variable command takes a string to type. +// +// TypeVariable "string" +func (p *Parser) parseTypeVariable() Command { + cmd := Command{Type: token.TYPE_VARIABLE} + + cmd.Options = p.parseSpeed() + + if p.peek.Type != token.STRING { + p.errors = append(p.errors, NewError(p.peek, p.cur.Literal+" expects string")) + } + + for p.peek.Type == token.STRING { + p.nextToken() + cmd.Args += p.cur.Literal + + // If the next token is a string, add a space between them. + // Since tokens must be separated by a whitespace, this is most likely + // what the user intended. + // + // Although it is possible that there may be multiple spaces / tabs between + // the tokens, however if the user was intending to type multiple spaces + // they would need to use a string literal. + + if p.peek.Type == token.STRING { + cmd.Args += " " + } + } + + return cmd +} + // parseCopy parses a copy command // A copy command takes a string to the clipboard // diff --git a/parser/parser_test.go b/parser/parser_test.go index fd4e577b5..8c9f72a11 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -12,6 +12,9 @@ import ( func TestParser(t *testing.T) { input := ` Set TypingSpeed 100ms +Set TypingSpeedVariable 1s 5s +Set TypingSpeedVariable 0.1 1s +Set TypingSpeedVariable 1s 2 Set WaitTimeout 1m Set WaitPattern /foo/ Type "echo 'Hello, World!'" @@ -37,6 +40,9 @@ Wait@100ms /foobar/` expected := []Command{ {Type: token.SET, Options: "TypingSpeed", Args: "100ms"}, + {Type: token.SET, Options: "TypingSpeedVariable", Args: "1s 5s"}, + {Type: token.SET, Options: "TypingSpeedVariable", Args: "0.1s 1s"}, + {Type: token.SET, Options: "TypingSpeedVariable", Args: "1s 2s"}, {Type: token.SET, Options: "WaitTimeout", Args: "1m"}, {Type: token.SET, Options: "WaitPattern", Args: "foo"}, {Type: token.TYPE, Options: "", Args: "echo 'Hello, World!'"}, @@ -139,6 +145,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: "TypingSpeedVariable", Args: "0.1s 1s"}, {Type: token.SLEEP, Options: "", Args: "1s"}, {Type: token.SLEEP, Options: "", Args: "500ms"}, {Type: token.SLEEP, Options: "", Args: ".5s"}, diff --git a/token/token.go b/token/token.go index 318939661..682fd7803 100644 --- a/token/token.go +++ b/token/token.go @@ -62,108 +62,112 @@ const ( RIGHT = "RIGHT" UP = "UP" - HIDE = "HIDE" - OUTPUT = "OUTPUT" - REQUIRE = "REQUIRE" - SET = "SET" - SHOW = "SHOW" - SOURCE = "SOURCE" - TYPE = "TYPE" - SCREENSHOT = "SCREENSHOT" - COPY = "COPY" - PASTE = "PASTE" - SHELL = "SHELL" - ENV = "ENV" - FONT_FAMILY = "FONT_FAMILY" //nolint:revive - FONT_SIZE = "FONT_SIZE" //nolint:revive - FRAMERATE = "FRAMERATE" - PLAYBACK_SPEED = "PLAYBACK_SPEED" //nolint:revive - HEIGHT = "HEIGHT" - WIDTH = "WIDTH" - LETTER_SPACING = "LETTER_SPACING" //nolint:revive - LINE_HEIGHT = "LINE_HEIGHT" //nolint:revive - TYPING_SPEED = "TYPING_SPEED" //nolint:revive - PADDING = "PADDING" - THEME = "THEME" - LOOP_OFFSET = "LOOP_OFFSET" //nolint:revive - MARGIN_FILL = "MARGIN_FILL" //nolint:revive - MARGIN = "MARGIN" //nolint:revive - WINDOW_BAR = "WINDOW_BAR" //nolint:revive - WINDOW_BAR_SIZE = "WINDOW_BAR_SIZE" //nolint:revive - BORDER_RADIUS = "CORNER_RADIUS" //nolint:revive - WAIT = "WAIT" //nolint:revive - WAIT_TIMEOUT = "WAIT_TIMEOUT" //nolint:revive - WAIT_PATTERN = "WAIT_PATTERN" //nolint:revive - CURSOR_BLINK = "CURSOR_BLINK" //nolint:revive + HIDE = "HIDE" + OUTPUT = "OUTPUT" + REQUIRE = "REQUIRE" + SET = "SET" + SHOW = "SHOW" + SOURCE = "SOURCE" + TYPE = "TYPE" + TYPE_VARIABLE = "TYPE_VARIABLE" + SCREENSHOT = "SCREENSHOT" + COPY = "COPY" + PASTE = "PASTE" + SHELL = "SHELL" + ENV = "ENV" + FONT_FAMILY = "FONT_FAMILY" //nolint:revive + FONT_SIZE = "FONT_SIZE" //nolint:revive + FRAMERATE = "FRAMERATE" + PLAYBACK_SPEED = "PLAYBACK_SPEED" //nolint:revive + HEIGHT = "HEIGHT" + WIDTH = "WIDTH" + LETTER_SPACING = "LETTER_SPACING" //nolint:revive + LINE_HEIGHT = "LINE_HEIGHT" //nolint:revive + TYPING_SPEED = "TYPING_SPEED" //nolint:revive + TYPING_SPEED_VARIABLE = "TYPING_SPEED_VARIABLE" + PADDING = "PADDING" + THEME = "THEME" + LOOP_OFFSET = "LOOP_OFFSET" //nolint:revive + MARGIN_FILL = "MARGIN_FILL" //nolint:revive + MARGIN = "MARGIN" //nolint:revive + WINDOW_BAR = "WINDOW_BAR" //nolint:revive + WINDOW_BAR_SIZE = "WINDOW_BAR_SIZE" //nolint:revive + BORDER_RADIUS = "CORNER_RADIUS" //nolint:revive + WAIT = "WAIT" //nolint:revive + WAIT_TIMEOUT = "WAIT_TIMEOUT" //nolint:revive + WAIT_PATTERN = "WAIT_PATTERN" //nolint:revive + CURSOR_BLINK = "CURSOR_BLINK" //nolint:revive ) // Keywords maps keyword strings to tokens. var Keywords = map[string]Type{ - "em": EM, - "px": PX, - "ms": MILLISECONDS, - "s": SECONDS, - "m": MINUTES, - "Set": SET, - "Sleep": SLEEP, - "Type": TYPE, - "Enter": ENTER, - "Space": SPACE, - "Backspace": BACKSPACE, - "Delete": DELETE, - "Insert": INSERT, - "Ctrl": CTRL, - "Alt": ALT, - "Shift": SHIFT, - "Down": DOWN, - "Left": LEFT, - "Right": RIGHT, - "Up": UP, - "PageUp": PAGEUP, - "PageDown": PAGEDOWN, - "Tab": TAB, - "Escape": ESCAPE, - "End": END, - "Hide": HIDE, - "Require": REQUIRE, - "Show": SHOW, - "Output": OUTPUT, - "Shell": SHELL, - "FontFamily": FONT_FAMILY, - "MarginFill": MARGIN_FILL, - "Margin": MARGIN, - "WindowBar": WINDOW_BAR, - "WindowBarSize": WINDOW_BAR_SIZE, - "BorderRadius": BORDER_RADIUS, - "FontSize": FONT_SIZE, - "Framerate": FRAMERATE, - "Height": HEIGHT, - "LetterSpacing": LETTER_SPACING, - "LineHeight": LINE_HEIGHT, - "PlaybackSpeed": PLAYBACK_SPEED, - "TypingSpeed": TYPING_SPEED, - "Padding": PADDING, - "Theme": THEME, - "Width": WIDTH, - "LoopOffset": LOOP_OFFSET, - "WaitTimeout": WAIT_TIMEOUT, - "WaitPattern": WAIT_PATTERN, - "Wait": WAIT, - "Source": SOURCE, - "CursorBlink": CURSOR_BLINK, - "true": BOOLEAN, - "false": BOOLEAN, - "Screenshot": SCREENSHOT, - "Copy": COPY, - "Paste": PASTE, - "Env": ENV, + "em": EM, + "px": PX, + "ms": MILLISECONDS, + "s": SECONDS, + "m": MINUTES, + "Set": SET, + "Sleep": SLEEP, + "Type": TYPE, + "TypeVariable": TYPE_VARIABLE, + "Enter": ENTER, + "Space": SPACE, + "Backspace": BACKSPACE, + "Delete": DELETE, + "Insert": INSERT, + "Ctrl": CTRL, + "Alt": ALT, + "Shift": SHIFT, + "Down": DOWN, + "Left": LEFT, + "Right": RIGHT, + "Up": UP, + "PageUp": PAGEUP, + "PageDown": PAGEDOWN, + "Tab": TAB, + "Escape": ESCAPE, + "End": END, + "Hide": HIDE, + "Require": REQUIRE, + "Show": SHOW, + "Output": OUTPUT, + "Shell": SHELL, + "FontFamily": FONT_FAMILY, + "MarginFill": MARGIN_FILL, + "Margin": MARGIN, + "WindowBar": WINDOW_BAR, + "WindowBarSize": WINDOW_BAR_SIZE, + "BorderRadius": BORDER_RADIUS, + "FontSize": FONT_SIZE, + "Framerate": FRAMERATE, + "Height": HEIGHT, + "LetterSpacing": LETTER_SPACING, + "LineHeight": LINE_HEIGHT, + "PlaybackSpeed": PLAYBACK_SPEED, + "TypingSpeed": TYPING_SPEED, + "TypingSpeedVariable": TYPING_SPEED_VARIABLE, + "Padding": PADDING, + "Theme": THEME, + "Width": WIDTH, + "LoopOffset": LOOP_OFFSET, + "WaitTimeout": WAIT_TIMEOUT, + "WaitPattern": WAIT_PATTERN, + "Wait": WAIT, + "Source": SOURCE, + "CursorBlink": CURSOR_BLINK, + "true": BOOLEAN, + "false": BOOLEAN, + "Screenshot": SCREENSHOT, + "Copy": COPY, + "Paste": PASTE, + "Env": ENV, } // IsSetting returns whether a token is a setting. 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, + FRAMERATE, TYPING_SPEED, TYPING_SPEED_VARIABLE, THEME, PLAYBACK_SPEED, HEIGHT, WIDTH, PADDING, LOOP_OFFSET, MARGIN_FILL, MARGIN, WINDOW_BAR, WINDOW_BAR_SIZE, BORDER_RADIUS, CURSOR_BLINK, WAIT_TIMEOUT, WAIT_PATTERN: return true diff --git a/vhs.go b/vhs.go index 5141e34c6..265a37939 100644 --- a/vhs.go +++ b/vhs.go @@ -37,21 +37,27 @@ type VHS struct { // Options is the set of options for the setup. type Options struct { - Shell Shell - FontFamily string - FontSize int - LetterSpacing float64 - LineHeight float64 - TypingSpeed time.Duration - Theme Theme - Test TestOptions - Video VideoOptions - LoopOffset float64 - WaitTimeout time.Duration - WaitPattern *regexp.Regexp - CursorBlink bool - Screenshot ScreenshotOptions - Style StyleOptions + Shell Shell + FontFamily string + FontSize int + LetterSpacing float64 + LineHeight float64 + TypingSpeed time.Duration + TypingSpeedVariable TypingSpeedVariableOptions + Theme Theme + Test TestOptions + Video VideoOptions + LoopOffset float64 + WaitTimeout time.Duration + WaitPattern *regexp.Regexp + CursorBlink bool + Screenshot ScreenshotOptions + Style StyleOptions +} + +type TypingSpeedVariableOptions struct { + MinTypingSpeed time.Duration + MaxTypingSpeed time.Duration } const ( @@ -100,13 +106,17 @@ func DefaultVHSOptions() Options { LetterSpacing: defaultLetterSpacing, LineHeight: defaultLineHeight, TypingSpeed: defaultTypingSpeed, - Shell: Shells[defaultShell], - Theme: DefaultTheme, - CursorBlink: defaultCursorBlink, - Video: video, - Screenshot: screenshot, - WaitTimeout: defaultWaitTimeout, - WaitPattern: defaultWaitPattern, + TypingSpeedVariable: TypingSpeedVariableOptions{ + MinTypingSpeed: defaultTypingSpeed, + MaxTypingSpeed: defaultTypingSpeed, + }, + Shell: Shells[defaultShell], + Theme: DefaultTheme, + CursorBlink: defaultCursorBlink, + Video: video, + Screenshot: screenshot, + WaitTimeout: defaultWaitTimeout, + WaitPattern: defaultWaitPattern, } }