User interface engine and widget library for Ebiten

Build Status Quality Gate Status Coverage Status GoDoc

Ebiten UI

A user interface engine and widget library for Ebiten

Ebiten UI is an extension to Ebiten that provides an engine to render a complete user interface, with widgets such as buttons, lists, combo boxes, and so on. It uses the retained mode model. All graphics used by Ebiten UI can be fully customized, so you can really make your UI your own.

Documentation on how to use and extend Ebiten UI is available at ebitenui.github.io.

Ebiten UI is currently under development and should not yet be used in production.

Screenshots

License

Ebiten UI is licensed under the MIT license.

Owner
Maik Schreiber
Java and Go hacker, general nerd
Maik Schreiber
Comments
  • _demo: possible to delete characters even when Text Input widget is disabled

    _demo: possible to delete characters even when Text Input widget is disabled

    In the demo application it is currently possible to delete characters (by pressing the [backspace] or [delete] key) typed into the Text Input widget, even when the widget is disabled.

    Steps to reproduce:

    1. Navigate to the Text Input widget in the demo application.
    2. Enter text (e.g. "abcd").
    3. Disable the widget.
    4. Now, use the mouse to position the text carret in the Text Input widget.
    5. Press the [backspace] or the [delete] key and notice that a character is deleted from the disabled widget.

    screenshot_2020-12-31_00:25:04

  • 一次录入多个字 Input multiple words at one time

    一次录入多个字 Input multiple words at one time

    `func (t *TextInput) idleState(newKeyOrCommand bool) textInputState { ....... chars := input.InputChars() if len(chars) > 0 { return t.charInputState(chars[0]), true //多个字一起传入 return t.charInputState(chars), tru } ......
    }

    func (t *TextInput) doInsert(c []rune) { ..... t.cursorPosition += len(c) //一次加多个字符位 }

    func fontStringIndex(s string, f font.Face, x int) int { start := 0 end := len([]rune(s)) //convert to []rune
    line(528) sub := string([]rune(s)[:p]) 不是有可能会出现out of index `

  • Multiple children in anchored container bug/misunderstanding?

    Multiple children in anchored container bug/misunderstanding?

    Hi, I'm not sure if this is my misunderstanding (probably!) or a layout bug.

    I'm trying to put some content in the top left and bottom left of an anchor layout container (red) inside a FlipBook (blue).

    But what I get is...

    Screenshot 2020-10-17 at 21 34 21

    ...or, if I swap the order of the two children of gameplayPage (red) I get...

    Screenshot 2020-10-17 at 21 36 50

    ...but what I'm expecting is that both cyan and yellow boxes be within the red box. Nothing is expected to be in the blue area.

    It feels like the first child of gameplayPage is affecting the layout of the second child somehow?

    I apologise if I've totally misunderstood how layouts or children are supposed to work, or if I've missed some config from the demo code.

    The simplified repro code (based on cmd/scaffold) is...

    package main
    
    import (
    	"image/color"
    	_ "image/png"
    	"io/ioutil"
    	"log"
    
    	"github.com/blizzy78/ebitenui"
    	"github.com/blizzy78/ebitenui/image"
    	"github.com/blizzy78/ebitenui/widget"
    	"github.com/golang/freetype/truetype"
    	"github.com/hajimehoshi/ebiten/v2"
    	"golang.org/x/image/font"
    )
    
    type game struct {
    	ui *ebitenui.UI
    }
    
    func main() {
    	// load button text font
    	face, err := loadFont("fonts/NotoSans-Regular.ttf", 20)
    	if err != nil {
    		log.Println(err)
    		return
    	}
    
    	defer face.Close()
    
    	// construct a new container that serves as the root of the UI hierarchy
    	rootContainer := widget.NewContainer(
    		// the container will use a plain color as its background
    		widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(color.RGBA{0x13, 0x1a, 0x22, 0xff})),
    
    		// the container will use an anchor layout to layout its single child widget
    		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
    	)
    
    	flipBook := widget.NewFlipBook(
    		widget.FlipBookOpts.Padding(widget.Insets{
    			Top:    50,
    			Left:   50,
    			Right:  50,
    			Bottom: 50,
    		}),
    		widget.FlipBookOpts.ContainerOpts(
    			widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(color.RGBA{0x00, 0x00, 0xff, 0xff})),
    			// get the flipbook to fill the page
    			widget.ContainerOpts.WidgetOpts(
    				widget.WidgetOpts.LayoutData(
    					widget.AnchorLayoutData{
    						StretchHorizontal: true,
    						StretchVertical:   true,
    					},
    				),
    			),
    		),
    	)
    
    	playerStatsText := widget.NewText(widget.TextOpts.Text("PLAYER STATS", face, color.Black))
    	playerStatsPanel := widget.NewContainer(
    		widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(color.RGBA{0xff, 0xff, 0x00, 0xff})),
    		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
    		widget.ContainerOpts.WidgetOpts(
    			widget.WidgetOpts.LayoutData(
    				widget.AnchorLayoutData{
    					HorizontalPosition: widget.AnchorLayoutPositionStart,
    					VerticalPosition:   widget.AnchorLayoutPositionStart,
    				},
    			),
    		),
    	)
    
    	nofityText := widget.NewText(widget.TextOpts.Text("NOTIFY", face, color.Black))
    	notifyPanel := widget.NewContainer(
    		widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(color.RGBA{0x00, 0xff, 0xff, 0xff})),
    		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
    		widget.ContainerOpts.WidgetOpts(
    			widget.WidgetOpts.LayoutData(
    				widget.AnchorLayoutData{
    					HorizontalPosition: widget.AnchorLayoutPositionStart,
    					VerticalPosition:   widget.AnchorLayoutPositionEnd,
    				},
    			),
    		),
    	)
    
    	gameplayPage := widget.NewContainer(
    		widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(color.RGBA{0xff, 0x00, 0x00, 0xff})),
    		widget.ContainerOpts.Layout(widget.NewAnchorLayout()),
    		// this is in an implicit AnchorLayout from FlipBook
    		widget.ContainerOpts.WidgetOpts(
    			widget.WidgetOpts.LayoutData(
    				widget.AnchorLayoutData{
    					StretchHorizontal: true,
    					StretchVertical:   true,
    				},
    			),
    		),
    	)
    
    	playerStatsPanel.AddChild(playerStatsText)
    	notifyPanel.AddChild(nofityText)
    
    	// NOTE: ****** swap the below two lines order to see a difference in layout, but neither as expected ******
    	gameplayPage.AddChild(playerStatsPanel)
    	gameplayPage.AddChild(notifyPanel)
    
    	flipBook.SetPage(gameplayPage)
    
    	// add the button as a child of the container
    	rootContainer.AddChild(flipBook)
    
    	// construct the UI
    	ui := ebitenui.UI{
    		Container: rootContainer,
    	}
    
    	// Ebiten setup
    	ebiten.SetWindowSize(400, 400)
    	ebiten.SetWindowTitle("Ebiten UI Scaffold")
    
    	game := game{
    		ui: &ui,
    	}
    
    	// run Ebiten main loop
    	if err := ebiten.RunGame(&game); err != nil {
    		log.Println(err)
    	}
    }
    
    // Update implements Game.
    func (g *game) Layout(outsideWidth int, outsideHeight int) (int, int) {
    	return outsideWidth, outsideHeight
    }
    
    // Update implements Game.
    func (g *game) Update() error {
    	// update the UI
    	g.ui.Update()
    	return nil
    }
    
    // Draw implements Ebiten's Draw method.
    func (g *game) Draw(screen *ebiten.Image) {
    	// draw the UI onto the screen
    	g.ui.Draw(screen)
    }
    
    func loadFont(path string, size float64) (font.Face, error) {
    	fontData, err := ioutil.ReadFile(path)
    	if err != nil {
    		return nil, err
    	}
    
    	ttfFont, err := truetype.Parse(fontData)
    	if err != nil {
    		return nil, err
    	}
    
    	return truetype.NewFace(ttfFont, &truetype.Options{
    		Size:    size,
    		DPI:     72,
    		Hinting: font.HintingFull,
    	}), nil
    }
    
    
  • Creating nine-slice from cropped images don't work as expected

    Creating nine-slice from cropped images don't work as expected

    Hello I've stumbled upon a problem with the rendered button image, if the input image was cropped before creating the nineslice, then it stops being displayed, while everything is fine with the image itself.

    This is spritesheet for button image: image

    This is my crop function:

    func CropSingle(source *ebiten.Image, x, y, w, h int) *ebiten.Image {
    	return source.SubImage(Rect(x, y, w, h)).(*ebiten.Image)
    }
    

    This is loading the atlas and the image for the buttons:

    w, h, corner, center := 64, 64, 12, 40
    sheet := load.Wrap(load.Image(static, "images/ui.png"))
    
    imageA := CropSingle(sheet, 0, 0, w, h)
    imageB := CropSingle(sheet, w, 0, w, h)
    
    buttonImageA := &widget.ButtonImage{
      Idle: image.NewNineSliceSimple(imageA, corner, center),
    }
    buttonImageB := &widget.ButtonImage{
      Idle: image.NewNineSliceSimple(imageB, corner, center),
    }
    

    At the moment I am drawing the original image in the corner and a set of buttons based on it

    func Update() error {
    	container.Update()
    	return nil
    }
    
    func Draw(screen *ebiten.Image) {
    	screen.Fill(colornames.Black)
    	container.Draw(screen)
    	screen.DrawImage(<imageA / imageB>, nil)
    }
    

    When I use imageA as the basis for the buttons, everything is ok:

    image

    But when I use imageB, the buttons stop rendering, although they are done exactly the same, and the image is cut correctly:

    image

  • What'd be the best way to handle Enter key in a TextInput widget?

    What'd be the best way to handle Enter key in a TextInput widget?

    Hi!

    I'm building a chat for my game using ebitenui and I'm not sure if it's possible to handle the Enter event on a TextInput.

    The ChangedEvent doesn't seem to trigger on Enter in any way, only on content updates.

  • How to center middle a Stretch container

    How to center middle a Stretch container

    Hi, I working in a demo "login" view for my game.

    I struggling to understand how I can center the fields into center of a container, this is possible now? Or I need to use a sized panel to center the inside containers and fix the width and height to control the position of the widgets inside a container?

    The code:

    	// Create the container
    	rootContainer := widget.NewContainer(
    		widget.ContainerOpts.Layout(
    			widget.NewGridLayout(
    				widget.GridLayoutOpts.Columns(1),
    				widget.GridLayoutOpts.Stretch([]bool{true}, []bool{false, true, false}),
    				widget.GridLayoutOpts.Padding(widget.Insets{
    					Top:    20,
    					Bottom: 20,
    					Left:   20,
    					Right:  20,
    				}),
    				widget.GridLayoutOpts.Spacing(0, 20)),
    		),
    		widget.ContainerOpts.BackgroundImage(res.Panel.Image),
    	)
    
    	b := widget.NewButton(
    		widget.ButtonOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.RowLayoutData{
    			Stretch: true,
    		})),
    		widget.ButtonOpts.Image(res.Button.Image),
    		widget.ButtonOpts.Text(fmt.Sprintf("Log In"), res.Button.Face, res.Button.Text),
    		widget.ButtonOpts.TextPadding(res.Button.Padding),
    		widget.ButtonOpts.WidgetOpts(
    			widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{
    				HorizontalPosition: widget.AnchorLayoutPositionCenter,
    				VerticalPosition:   widget.AnchorLayoutPositionCenter,
    			}),
    		),
    	)
    	rootContainer.AddChild(b)
    
  • modify the directory structure & update .gitignore and del go.sum

    modify the directory structure & update .gitignore and del go.sum

    The demo cannot run in my local environment. After adjusting the directory structure, it runs normally. Local environment: ide:goland go version 1.16.9

  • Q: Can/should I use multiple ebitenui.UIs to represent each UI screen in my game?

    Q: Can/should I use multiple ebitenui.UIs to represent each UI screen in my game?

    Hi,

    I'm just trying out ebitenui and had a question. I have different UIs on different screens and I'm not sure if each screen should be their own separate ebitenui.UIs or if they should all be structured to be within a single ebitenui.UI?

    Thank you <3

  • _demo: dragging outside of application window results in crash.

    _demo: dragging outside of application window results in crash.

    First off, really cool UI project! The demo looks great!

    I was playing around with the various widgets and encountered a crash when dragging outside of the application window. See screenshot below (right before the crash).

    Stack trace is attached below (at rev 466226fd04c39712cd2faba3413dde9685b93ee4).

    screenshot_2020-12-31_00:19:38

    $ _demo
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x673226]
    
    goroutine 8 [running]:
    main.(*dragContents).Update(0xc00005cc40, 0x0, 0x0, 0x161, 0x15e, 0x0, 0x0)
    	/tmp/ebitenui/_demo/dnd.go:38 +0x26
    github.com/blizzy78/ebitenui/widget.(*DragAndDrop).draggingState.func1(0xc00019b080, 0x753690, 0x0, 0x0)
    	/tmp/ebitenui/widget/dnd.go:180 +0x3c9
    github.com/blizzy78/ebitenui/widget.(*DragAndDrop).Render(0xc000069c70, 0xc00019b080, 0x753690)
    	/tmp/ebitenui/widget/dnd.go:113 +0x3e
    github.com/blizzy78/ebitenui/widget.renderDeferredRenderQueue(0xc00019b080)
    	/tmp/ebitenui/widget/widget.go:359 +0x84
    github.com/blizzy78/ebitenui/widget.RenderWithDeferred(0xc00019b080, 0xc0002461b0, 0x3, 0x3)
    	/tmp/ebitenui/widget/widget.go:347 +0x109
    github.com/blizzy78/ebitenui.(*UI).render(0xc00015dc20, 0xc00019b080)
    	/tmp/ebitenui/ui.go:140 +0x2a6
    github.com/blizzy78/ebitenui.(*UI).Draw(0xc00015dc20, 0xc00019b080)
    	/tmp/ebitenui/ui.go:64 +0x21f
    main.(*game).Draw(0xc000010d00, 0xc00019b080)
    	/tmp/ebitenui/_demo/main.go:376 +0x38
    github.com/hajimehoshi/ebiten/v2.(*imageDumperGameWithDraw).Draw(0xc000a8a420, 0xc00019b080)
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/run.go:120 +0x48
    github.com/hajimehoshi/ebiten/v2.(*uiContext).draw(0xaa0640)
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/uicontext.go:214 +0x6e
    github.com/hajimehoshi/ebiten/v2.(*uiContext).Draw(0xaa0640, 0x0, 0x0)
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/uicontext.go:174 +0xb1
    github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).update(0x9271a0, 0x20001, 0x1)
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/internal/uidriver/glfw/ui.go:871 +0x30d
    github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).loop(0x9271a0, 0x0, 0x0)
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/internal/uidriver/glfw/ui.go:914 +0xcf
    github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).run(0x9271a0, 0x0, 0x0)
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/internal/uidriver/glfw/ui.go:769 +0x2bd
    github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).Run.func1(0x9271a0, 0xc000a6cae0)
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/internal/uidriver/glfw/ui.go:602 +0x72
    created by github.com/hajimehoshi/ebiten/v2/internal/uidriver/glfw.(*UserInterface).Run
    	/home/u/goget/pkg/mod/github.com/hajimehoshi/ebiten/[email protected]/internal/uidriver/glfw/ui.go:594 +0x12f
    
  • TextInput: EnterFunc API

    TextInput: EnterFunc API

    The TextInput seems to be missing one important feature: the ability for user to register a callback when the Enter key is pressed - the user is left the opportunities to react to the Enter event with the currently entered text.

    ebitenui/widget/textinput.go: the EnterFunc support ebitenui/_demo/page.go: example use case for the EnterFunc support

    The changes should be pretty straightforward, please review this PR.

  • [QUESTION] Textinput cannot be

    [QUESTION] Textinput cannot be "clickable" inside Window?

    Hi, I have the following "login" window:

    func setLoginContainerWindow(res *assets.UiResources, ui func() *ebitenui.UI) {
    	var rw ebitenui.RemoveWindowFunc
    
    	c := widget.NewContainer(
    		widget.ContainerOpts.BackgroundImage(res.Panel.Image),
    		widget.ContainerOpts.Layout(widget.NewRowLayout(
    			widget.RowLayoutOpts.Direction(widget.DirectionVertical),
    			widget.RowLayoutOpts.Padding(res.Panel.Padding),
    			widget.RowLayoutOpts.Spacing(15),
    		)),
    	)
    	c.AddChild(widget.NewText(
    		widget.TextOpts.Text("Login", res.Text.BigTitleFace, res.Text.IdleColor),
    	))
    
    	tOpts := []widget.TextInputOpt{
    		widget.TextInputOpts.WidgetOpts(
    			// instruct the container's anchor layout to center the button
    			// both horizontally and vertically
    			widget.WidgetOpts.LayoutData(widget.RowLayoutData{
    				Stretch: true,
    			}),
    		),
    		widget.TextInputOpts.Image(res.TextInput.Image),
    		widget.TextInputOpts.Color(res.TextInput.Color),
    		widget.TextInputOpts.Padding(widget.Insets{
    			Left:   13,
    			Right:  13,
    			Top:    7,
    			Bottom: 7,
    		}),
    		widget.TextInputOpts.Face(res.TextInput.Face),
    		widget.TextInputOpts.CaretOpts(
    			widget.CaretOpts.Size(res.TextInput.Face, 2),
    		),
    	}
    
    	usernameInput := widget.NewTextInput(append(
    		tOpts,
    		widget.TextInputOpts.Placeholder("Username"))...,
    	)
    	c.AddChild(usernameInput)
    	passwordInput := widget.NewTextInput(append(
    		tOpts,
    		widget.TextInputOpts.Secure(true),
    		widget.TextInputOpts.Placeholder("Password"))...,
    	)
    	c.AddChild(passwordInput)
    
    	c1 := widget.NewContainer(
    		widget.ContainerOpts.Layout(widget.NewRowLayout(
    			widget.RowLayoutOpts.Direction(widget.DirectionHorizontal),
    			widget.RowLayoutOpts.Spacing(10),
    		)),
    	)
    	c.AddChild(c1)
    
    	c1.AddChild(widget.NewButton(
    		widget.ButtonOpts.Image(res.Button.Image),
    		widget.ButtonOpts.TextPadding(res.Button.Padding),
    		widget.ButtonOpts.Text("Log in", res.Button.Face, res.Button.Text),
    		widget.ButtonOpts.ClickedHandler(func(args *widget.ButtonClickedEventArgs) {
    			// Do login here
    		}),
    	))
    	c1.AddChild(widget.NewButton(
    		widget.ButtonOpts.Image(res.Button.Image),
    		widget.ButtonOpts.TextPadding(res.Button.Padding),
    		widget.ButtonOpts.Text("Cancel", res.Button.Face, res.Button.Text),
    		widget.ButtonOpts.ClickedHandler(func(args *widget.ButtonClickedEventArgs) {
    			rw()
    		}),
    	))
    
    	w := widget.NewWindow(widget.WindowOpts.Modal(), widget.WindowOpts.Contents(c))
    
    	ww, wh := ebiten.WindowSize()
    	r := image.Rect(0, 0, ww*3/4, wh/3)
    	r = r.Add(image.Point{X: ww / 4 / 2, Y: wh * 2 / 3 / 2})
    	w.SetLocation(r)
    
    	rw = ui().AddWindow(w)
    	//usernameInput.Focus(true)
    }
    

    I notice if I try to click into one of the inputs they not change the status to focused. I have done some wrong or is a common behavior to set focus to true when open an window?

  • UI Scale factor

    UI Scale factor

    It would be great if there was UI scale factor, to enlarge/shrink the entire UI. People can implement it themselves, but it would be nice if the library just took care of it.

  • Changing of List entries not possible after creation?

    Changing of List entries not possible after creation?

    Currently is doe not seem possible to change the entries in a List after creation.

    i.E. a list like this:

    	entries := []interface{}{
    		"Test 1",
    		"Test 2",
    		"Test 3",
    	}
    
    	list := widget.NewList(
    		widget.ListOpts.Entries(entries),
    		widget.ListOpts.EntryLabelFunc(func(e interface{}) string {
    			return e.(string)
    		}),
    
    		[...]
    		[...]
    	)
    

    When the slice is changed end set again for the given list only the underlying model is changed. The widgets (buttons/labels) are not updated/removed/added.


    For example:

    	entries[0] = "Changed 1"
    	widget.ListOpts.Entries(entries)(list)
    

    Result: Visual change: none Bahaviour change: The changed list entry can no longer be selected


    	entries = append(entries, "Teste New")
    	widget.ListOpts.Entries(entries)(list)
    

    Result: Visual change: none Bahaviour change: none


    	entries = []interface{}{}
    	widget.ListOpts.Entries(entries)(list)
    
    

    Result: Visual change: none Bahaviour change: Clicking an entry crashes the program because of index out of bounds.


    Am I missing something/doing something wrong? Or is it not possible to change the model of a list after creation?

    Currently the only way I found to update a List is by recreating the entire list, which is quite costly. I also played around with widget.ScrollContainerOpts.Content to set my own container with contents that I could update. But I had no luck to get this to work at all.

    Thanks for the great library!

  • Touch support

    Touch support

    Just managed to build my game for Android (hooray!), but there doesn't appear to be any touch input support in ebintenui?

    I'm happy to take on this issue, internal/input/input.go looks like the right place to start?

Heroku Terminal User Interface
Heroku Terminal User Interface

_ _ _ | |_| |_ _ _(_) | ' \ _| || | |

Nov 24, 2022
Light weight Terminal User Interface (TUI) to pick material colors written by Go.
Light weight Terminal User Interface (TUI) to pick material colors written by Go.

mcpick Light weight Terminal User Interface (TUI) to pick material colors. You do NOT need to take your hands off the keyboard to pick colors. Getting

Dec 27, 2022
cview - Terminal-based user interface toolkit

cview - Terminal-based user interface toolkit This package is a fork of tview. See FORK.md for more information. Demo ssh cview.rocketnine.space -p 20

Jan 23, 2022
Podman-tui - A Terminal User Interface to interact with the podman (v3.x)
Podman-tui - A Terminal User Interface to interact with the podman (v3.x)

podman-tui podman-tui is a Terminal User Interface to interact with the podman (

Dec 23, 2022
GOSNIFF - A Textual User-Interface Network Sniffer

GOSNIFF - A Textual User-Interface Network Sniffer gosniff-gif.mp4 GOSNIFF is a TUI-based, tcpdump-inspired tool used to provide some graphical insigh

Dec 17, 2022
Terminal user interface for nyaa.si with support of peerflix

nyaa-cli Terminal user interface for nyaa.si with support of peerflix. Peerflix can be enabled with the --peerflix flag. By default the tool will only

Jun 25, 2022
A command-line tool and library for generating regular expressions from user-provided test cases
A command-line tool and library for generating regular expressions from user-provided test cases

Table of Contents What does this tool do? Do I still need to learn to write regexes then? Current features How to install? 4.1 The command-line tool 4

Jan 9, 2023
Go library for detecting and expanding the user's home directory without cgo.

go-homedir This is a Go library for detecting the user's home directory without the use of cgo, so the library can be used in cross-compilation enviro

Jan 5, 2023
A Go library and common interface for running local and remote commands

go-runcmd go-runcmd is a Go library and common interface for running local and remote commands providing the Runner interface which helps to abstract

Nov 25, 2021
Inotify-tools is a C library and a set of command-line programs providing a simple interface to inotify.

inotify-tools This is a package of some commandline utilities relating to inotify. The general purpose of this package is to allow inotify's features

Jan 4, 2023
🚀 goprobe is a promising command line tool for inspecting URLs with modern and user-friendly way.

goprobe Build go build -o ./bin/goprobe Example > goprobe https://github.com/gaitr/goprobe > cat links.txt | goprobe > echo "https://github.com/gaitr/

Oct 24, 2021
ghcv-cli makes it easy to view the user-created issues, pull requests, and repositories in the terminal.
ghcv-cli makes it easy to view the user-created issues, pull requests, and repositories in the terminal.

ghcv-cli ghcv-cli makes it easy to view the user-created issues, pull requests, and repositories in the terminal. About Show a list of pull requests c

Mar 13, 2022
K-Mesh is an experimental Knative distribution which provides a fresh, CLI-focused, holistic user experience of running and managing Knative.

K-Mesh is an experimental Knative distribution which provides a fresh, CLI-focused, holistic user experience of running and managing Knative. N

Feb 14, 2022
The simple and easy-to-use program designed to watch user activity for Cloud Providers.

Cloud Agent The simple and easy-to-use program is designed to watch user activity and possible orphan clusters for Cloud Providers: Gardener GCP (work

Jun 6, 2022
An os/exec like interface for running a command in a container, and being able to easily interact with stdin, stdout, and other adjustments

dockerexec An "os/exec" like interface for running a command in a container, and being able to easily interact with stdin, stdout, and other adjustmen

Jul 14, 2022
vgrep - a user-friendly pager for grep
vgrep - a user-friendly pager for grep

vgrep is a pager for grep, git-grep, ripgrep and similar grep implementations, and allows for opening the indexed file locations in a user-specified e

Jan 3, 2023
A command tool to help user install oh-my-zsh plugins fast in a comfortable way

zshx A command tool to help user install oh-my-zsh plugins fast in a comfortable way. in other way, it is a zsh plugin package manager. How to use the

Feb 11, 2022
Github user stats fetch written in golang
Github user stats fetch written in golang

TACOMA It's like neofetch, but for github users. I saw something similar on reddit and decided to recreate it using only golang. Original inspiration:

Dec 24, 2021
Vaku is a CLI and API for running path- and folder-based operations on the Vault Key/Value secrets engine.
Vaku is a CLI and API for running path- and folder-based operations on the Vault Key/Value secrets engine.

Vaku Vaku is a CLI and API for running path- and folder-based operations on the Vault Key/Value secrets engine. Vaku extends the existing Vault CLI an

Nov 28, 2022