mirror of
https://github.com/BigJk/crt.git
synced 2026-02-06 10:47:25 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7710fdc88d | ||
|
|
d976f61673 | ||
|
|
ea37c58ee8 | ||
|
|
a3526f42c5 | ||
|
|
fbecabe083 | ||
|
|
902582a274 | ||
|
|
ac7d8bf4c3 | ||
|
|
ee5e2843e2 | ||
|
|
f2f0473fdf | ||
|
|
5104c9a225 | ||
|
|
b56ac867a2 | ||
|
|
48a7adaf9e | ||
|
|
6151ddad2f | ||
|
|
d9c72af4f2 | ||
|
|
e5f8fb908a | ||
|
|
52a54cbfc8 | ||
|
|
2c21fde141 |
@ -6,7 +6,7 @@
|
||||
|
||||
CRT is a library to provide a simple terminal emulator that can be attached to a ``tea.Program``. It uses ``ebitengine`` to render a terminal. It supports TrueColor, Mouse and Keyboard input. It interprets the CSI escape sequences coming from bubbletea and renders them to the terminal.
|
||||
|
||||
This started as a simple proof of concept for the game I'm writing with the help of bubbletea, called [End Of Eden](github.com/BigJk/end_of_eden). I wanted to give people who have no clue about the terminal a simple option to play the game without interacting with the terminal directly. It's also possible to apply shaders to the terminal to give it a more retro look which is a nice side effect.
|
||||
This started as a simple proof of concept for the game I'm writing with the help of bubbletea, called [End Of Eden](https://github.com/BigJk/end_of_eden). I wanted to give people who have no clue about the terminal a simple option to play the game without interacting with the terminal directly. It's also possible to apply shaders to the terminal to give it a more retro look which is a nice side effect.
|
||||
|
||||
## Usage
|
||||
|
||||
@ -30,7 +30,7 @@ import (
|
||||
|
||||
func main() {
|
||||
// Load fonts for normal, bold and italic text styles.
|
||||
fonts, err := crt.LoadFaces("./fonts/SomeFont-Regular.ttf", "./fonts/SomeFont-Bold.ttf", "./fonts/SomeFont-Italic.ttf", 72.0, 16.0)
|
||||
fonts, err := crt.LoadFaces("./fonts/SomeFont-Regular.ttf", "./fonts/SomeFont-Bold.ttf", "./fonts/SomeFont-Italic.ttf", crt.GetFontDPI(), 16.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -48,6 +48,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
See more examples in the ``/examples`` folder!
|
||||
|
||||
## Limitations
|
||||
|
||||
- ~~Only supports TrueColor at the moment (no 256 color support) so you need to use TrueColor colors in lipgloss (e.g. ``lipgloss.Color("#ff0000")``)~~ **Now supported.**
|
||||
@ -58,4 +60,4 @@ func main() {
|
||||
## Credits
|
||||
|
||||
- Basic CRT Shader ``./shader/crt_basic``: https://quasilyte.dev/blog/post/ebitengine-shaders/
|
||||
- Lottes CRT Shader ``./shader/crt_lotte``: Elias Daler https://github.com/eliasdaler/crten and Timothy Lottes.
|
||||
- Lottes CRT Shader ``./shader/crt_lotte``: Elias Daler https://github.com/eliasdaler/crten and Timothy Lottes.
|
||||
|
||||
@ -13,80 +13,80 @@ type teaKey struct {
|
||||
rune []rune
|
||||
}
|
||||
|
||||
func repeatingKeyPressed(key ebiten.Key) bool {
|
||||
const (
|
||||
delay = 30
|
||||
interval = 3
|
||||
)
|
||||
d := inpututil.KeyPressDuration(key)
|
||||
if d == 1 {
|
||||
return true
|
||||
}
|
||||
if d >= delay && (d-delay)%interval == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var ebitenToTeaKeys = map[ebiten.Key]teaKey{
|
||||
ebiten.KeyEnter: {tea.KeyEnter, []rune{'\n'}},
|
||||
ebiten.KeyTab: {tea.KeyTab, []rune{'\t'}},
|
||||
ebiten.KeySpace: {tea.KeySpace, []rune{' '}},
|
||||
ebiten.KeyBackspace: {tea.KeyBackspace, []rune{}},
|
||||
ebiten.KeyDelete: {tea.KeyDelete, []rune{}},
|
||||
ebiten.KeyHome: {tea.KeyHome, []rune{}},
|
||||
ebiten.KeyEnd: {tea.KeyEnd, []rune{}},
|
||||
ebiten.KeyPageUp: {tea.KeyPgUp, []rune{}},
|
||||
ebiten.KeyArrowUp: {tea.KeyUp, []rune{}},
|
||||
ebiten.KeyArrowDown: {tea.KeyDown, []rune{}},
|
||||
ebiten.KeyArrowLeft: {tea.KeyLeft, []rune{}},
|
||||
ebiten.KeyArrowRight: {tea.KeyRight, []rune{}},
|
||||
ebiten.KeyEscape: {tea.KeyEscape, []rune{}},
|
||||
ebiten.Key1: {tea.KeyRunes, []rune{'1'}},
|
||||
ebiten.Key2: {tea.KeyRunes, []rune{'2'}},
|
||||
ebiten.Key3: {tea.KeyRunes, []rune{'3'}},
|
||||
ebiten.Key4: {tea.KeyRunes, []rune{'4'}},
|
||||
ebiten.Key5: {tea.KeyRunes, []rune{'5'}},
|
||||
ebiten.Key6: {tea.KeyRunes, []rune{'6'}},
|
||||
ebiten.Key7: {tea.KeyRunes, []rune{'7'}},
|
||||
ebiten.Key8: {tea.KeyRunes, []rune{'8'}},
|
||||
ebiten.Key9: {tea.KeyRunes, []rune{'9'}},
|
||||
ebiten.Key0: {tea.KeyRunes, []rune{'0'}},
|
||||
ebiten.KeyA: {tea.KeyRunes, []rune{'a'}},
|
||||
ebiten.KeyB: {tea.KeyRunes, []rune{'b'}},
|
||||
ebiten.KeyC: {tea.KeyRunes, []rune{'c'}},
|
||||
ebiten.KeyD: {tea.KeyRunes, []rune{'d'}},
|
||||
ebiten.KeyE: {tea.KeyRunes, []rune{'e'}},
|
||||
ebiten.KeyF: {tea.KeyRunes, []rune{'f'}},
|
||||
ebiten.KeyG: {tea.KeyRunes, []rune{'g'}},
|
||||
ebiten.KeyH: {tea.KeyRunes, []rune{'h'}},
|
||||
ebiten.KeyI: {tea.KeyRunes, []rune{'i'}},
|
||||
ebiten.KeyJ: {tea.KeyRunes, []rune{'j'}},
|
||||
ebiten.KeyK: {tea.KeyRunes, []rune{'k'}},
|
||||
ebiten.KeyL: {tea.KeyRunes, []rune{'l'}},
|
||||
ebiten.KeyM: {tea.KeyRunes, []rune{'m'}},
|
||||
ebiten.KeyN: {tea.KeyRunes, []rune{'n'}},
|
||||
ebiten.KeyO: {tea.KeyRunes, []rune{'o'}},
|
||||
ebiten.KeyP: {tea.KeyRunes, []rune{'p'}},
|
||||
ebiten.KeyQ: {tea.KeyRunes, []rune{'q'}},
|
||||
ebiten.KeyR: {tea.KeyRunes, []rune{'r'}},
|
||||
ebiten.KeyS: {tea.KeyRunes, []rune{'s'}},
|
||||
ebiten.KeyT: {tea.KeyRunes, []rune{'t'}},
|
||||
ebiten.KeyU: {tea.KeyRunes, []rune{'u'}},
|
||||
ebiten.KeyV: {tea.KeyRunes, []rune{'v'}},
|
||||
ebiten.KeyW: {tea.KeyRunes, []rune{'w'}},
|
||||
ebiten.KeyX: {tea.KeyRunes, []rune{'x'}},
|
||||
ebiten.KeyY: {tea.KeyRunes, []rune{'y'}},
|
||||
ebiten.KeyZ: {tea.KeyRunes, []rune{'z'}},
|
||||
ebiten.KeyComma: {tea.KeyRunes, []rune{','}},
|
||||
ebiten.KeyPeriod: {tea.KeyRunes, []rune{'.'}},
|
||||
ebiten.KeySlash: {tea.KeyRunes, []rune{'/'}},
|
||||
ebiten.KeyBackslash: {tea.KeyRunes, []rune{'\\'}},
|
||||
ebiten.KeySemicolon: {tea.KeyRunes, []rune{';'}},
|
||||
ebiten.KeyApostrophe: {tea.KeyRunes, []rune{'\''}},
|
||||
ebiten.KeyGraveAccent: {tea.KeyRunes, []rune{'`'}},
|
||||
ebiten.KeyEqual: {tea.KeyRunes, []rune{'='}},
|
||||
ebiten.KeyMinus: {tea.KeyRunes, []rune{'-'}},
|
||||
ebiten.KeyLeftBracket: {tea.KeyRunes, []rune{'['}},
|
||||
ebiten.KeyRightBracket: {tea.KeyRunes, []rune{']'}},
|
||||
ebiten.KeyF1: {tea.KeyF1, []rune{}},
|
||||
ebiten.KeyF2: {tea.KeyF2, []rune{}},
|
||||
ebiten.KeyF3: {tea.KeyF3, []rune{}},
|
||||
ebiten.KeyF4: {tea.KeyF4, []rune{}},
|
||||
ebiten.KeyF5: {tea.KeyF5, []rune{}},
|
||||
ebiten.KeyF6: {tea.KeyF6, []rune{}},
|
||||
ebiten.KeyF7: {tea.KeyF7, []rune{}},
|
||||
ebiten.KeyF8: {tea.KeyF8, []rune{}},
|
||||
ebiten.KeyF9: {tea.KeyF9, []rune{}},
|
||||
ebiten.KeyF10: {tea.KeyF10, []rune{}},
|
||||
ebiten.KeyF11: {tea.KeyF11, []rune{}},
|
||||
ebiten.KeyF12: {tea.KeyF12, []rune{}},
|
||||
ebiten.KeyShift: {tea.KeyShiftLeft, []rune{}},
|
||||
ebiten.KeyEnter: {tea.KeyEnter, []rune{'\n'}},
|
||||
ebiten.KeyTab: {tea.KeyTab, []rune{}},
|
||||
ebiten.KeyBackspace: {tea.KeyBackspace, []rune{}},
|
||||
ebiten.KeyDelete: {tea.KeyDelete, []rune{}},
|
||||
ebiten.KeyHome: {tea.KeyHome, []rune{}},
|
||||
ebiten.KeyEnd: {tea.KeyEnd, []rune{}},
|
||||
ebiten.KeyPageUp: {tea.KeyPgUp, []rune{}},
|
||||
ebiten.KeyArrowUp: {tea.KeyUp, []rune{}},
|
||||
ebiten.KeyArrowDown: {tea.KeyDown, []rune{}},
|
||||
ebiten.KeyArrowLeft: {tea.KeyLeft, []rune{}},
|
||||
ebiten.KeyArrowRight: {tea.KeyRight, []rune{}},
|
||||
ebiten.KeyEscape: {tea.KeyEscape, []rune{}},
|
||||
ebiten.KeyF1: {tea.KeyF1, []rune{}},
|
||||
ebiten.KeyF2: {tea.KeyF2, []rune{}},
|
||||
ebiten.KeyF3: {tea.KeyF3, []rune{}},
|
||||
ebiten.KeyF4: {tea.KeyF4, []rune{}},
|
||||
ebiten.KeyF5: {tea.KeyF5, []rune{}},
|
||||
ebiten.KeyF6: {tea.KeyF6, []rune{}},
|
||||
ebiten.KeyF7: {tea.KeyF7, []rune{}},
|
||||
ebiten.KeyF8: {tea.KeyF8, []rune{}},
|
||||
ebiten.KeyF9: {tea.KeyF9, []rune{}},
|
||||
ebiten.KeyF10: {tea.KeyF10, []rune{}},
|
||||
ebiten.KeyF11: {tea.KeyF11, []rune{}},
|
||||
ebiten.KeyF12: {tea.KeyF12, []rune{}},
|
||||
ebiten.KeyShift: {tea.KeyShiftLeft, []rune{}},
|
||||
}
|
||||
|
||||
var ebitenToCtrlKeys = map[ebiten.Key]tea.KeyType{
|
||||
ebiten.KeyA: tea.KeyCtrlA,
|
||||
ebiten.KeyB: tea.KeyCtrlB,
|
||||
ebiten.KeyC: tea.KeyCtrlC,
|
||||
ebiten.KeyD: tea.KeyCtrlD,
|
||||
ebiten.KeyE: tea.KeyCtrlE,
|
||||
ebiten.KeyF: tea.KeyCtrlF,
|
||||
ebiten.KeyG: tea.KeyCtrlG,
|
||||
ebiten.KeyH: tea.KeyCtrlH,
|
||||
ebiten.KeyI: tea.KeyCtrlI,
|
||||
ebiten.KeyJ: tea.KeyCtrlJ,
|
||||
ebiten.KeyK: tea.KeyCtrlK,
|
||||
ebiten.KeyL: tea.KeyCtrlL,
|
||||
ebiten.KeyM: tea.KeyCtrlM,
|
||||
ebiten.KeyN: tea.KeyCtrlN,
|
||||
ebiten.KeyO: tea.KeyCtrlO,
|
||||
ebiten.KeyP: tea.KeyCtrlP,
|
||||
ebiten.KeyQ: tea.KeyCtrlQ,
|
||||
ebiten.KeyR: tea.KeyCtrlR,
|
||||
ebiten.KeyS: tea.KeyCtrlS,
|
||||
ebiten.KeyT: tea.KeyCtrlT,
|
||||
ebiten.KeyU: tea.KeyCtrlU,
|
||||
ebiten.KeyV: tea.KeyCtrlV,
|
||||
ebiten.KeyW: tea.KeyCtrlW,
|
||||
ebiten.KeyX: tea.KeyCtrlX,
|
||||
ebiten.KeyY: tea.KeyCtrlY,
|
||||
ebiten.KeyZ: tea.KeyCtrlZ,
|
||||
ebiten.KeyLeftBracket: tea.KeyCtrlOpenBracket,
|
||||
ebiten.KeyBackslash: tea.KeyCtrlBackslash,
|
||||
ebiten.KeyRightBracket: tea.KeyCtrlCloseBracket,
|
||||
ebiten.KeyApostrophe: tea.KeyCtrlCaret,
|
||||
}
|
||||
|
||||
var ebitenToTeaMouse = map[ebiten.MouseButton]tea.MouseEventType{
|
||||
@ -95,6 +95,16 @@ var ebitenToTeaMouse = map[ebiten.MouseButton]tea.MouseEventType{
|
||||
ebiten.MouseButtonRight: tea.MouseRight,
|
||||
}
|
||||
|
||||
var ebitenToTeaMouseNew = map[ebiten.MouseButton]tea.MouseButton{
|
||||
ebiten.MouseButtonLeft: tea.MouseButtonLeft,
|
||||
ebiten.MouseButtonMiddle: tea.MouseButtonMiddle,
|
||||
ebiten.MouseButtonRight: tea.MouseButtonRight,
|
||||
|
||||
// TODO: is this right?
|
||||
ebiten.MouseButton3: tea.MouseButtonBackward,
|
||||
ebiten.MouseButton4: tea.MouseButtonForward,
|
||||
}
|
||||
|
||||
// Options are used to configure the adapter.
|
||||
type Options func(*Adapter)
|
||||
|
||||
@ -124,11 +134,12 @@ func NewAdapter(prog *tea.Program, options ...Options) *Adapter {
|
||||
|
||||
func (b *Adapter) HandleMouseMotion(motion crt.MouseMotion) {
|
||||
b.prog.Send(tea.MouseMsg{
|
||||
X: motion.X,
|
||||
Y: motion.Y,
|
||||
Alt: false,
|
||||
Ctrl: false,
|
||||
Type: tea.MouseMotion,
|
||||
X: motion.X,
|
||||
Y: motion.Y,
|
||||
Alt: false,
|
||||
Ctrl: false,
|
||||
Type: tea.MouseMotion,
|
||||
Action: tea.MouseActionMotion,
|
||||
})
|
||||
}
|
||||
|
||||
@ -138,13 +149,22 @@ func (b *Adapter) HandleMouseButton(button crt.MouseButton) {
|
||||
return
|
||||
}
|
||||
|
||||
b.prog.Send(tea.MouseMsg{
|
||||
X: button.X,
|
||||
Y: button.Y,
|
||||
Alt: ebiten.IsKeyPressed(ebiten.KeyAlt),
|
||||
Ctrl: ebiten.IsKeyPressed(ebiten.KeyControl),
|
||||
Type: ebitenToTeaMouse[button.Button],
|
||||
})
|
||||
msg := tea.MouseMsg{
|
||||
X: button.X,
|
||||
Y: button.Y,
|
||||
Alt: ebiten.IsKeyPressed(ebiten.KeyAlt),
|
||||
Ctrl: ebiten.IsKeyPressed(ebiten.KeyControl),
|
||||
Type: ebitenToTeaMouse[button.Button],
|
||||
Button: ebitenToTeaMouseNew[button.Button],
|
||||
}
|
||||
|
||||
if button.JustReleased {
|
||||
msg.Action = tea.MouseActionRelease
|
||||
} else if button.JustPressed {
|
||||
msg.Action = tea.MouseActionPress
|
||||
}
|
||||
|
||||
b.prog.Send(msg)
|
||||
}
|
||||
|
||||
func (b *Adapter) HandleMouseWheel(wheel crt.MouseWheel) {
|
||||
@ -168,24 +188,52 @@ func (b *Adapter) HandleMouseWheel(wheel crt.MouseWheel) {
|
||||
}
|
||||
|
||||
func (b *Adapter) HandleKeyPress() {
|
||||
newInputs := ebiten.AppendInputChars([]rune{})
|
||||
for _, v := range newInputs {
|
||||
switch v {
|
||||
case ' ':
|
||||
b.prog.Send(tea.KeyMsg{
|
||||
Type: tea.KeySpace,
|
||||
Runes: []rune{v},
|
||||
Alt: ebiten.IsKeyPressed(ebiten.KeyAlt),
|
||||
})
|
||||
default:
|
||||
b.prog.Send(tea.KeyMsg{
|
||||
Type: tea.KeyRunes,
|
||||
Runes: []rune{v},
|
||||
Alt: ebiten.IsKeyPressed(ebiten.KeyAlt),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var keys []ebiten.Key
|
||||
keys = inpututil.AppendJustReleasedKeys(keys)
|
||||
keys = inpututil.AppendJustPressedKeys(keys)
|
||||
repeatedBackspace := repeatingKeyPressed(ebiten.KeyBackspace)
|
||||
|
||||
if repeatedBackspace {
|
||||
b.prog.Send(tea.KeyMsg{
|
||||
Type: tea.KeyBackspace,
|
||||
Runes: []rune{},
|
||||
Alt: false,
|
||||
})
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
|
||||
switch k {
|
||||
case ebiten.KeyC:
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) {
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) {
|
||||
if tk, ok := ebitenToCtrlKeys[k]; ok {
|
||||
b.prog.Send(tea.KeyMsg{
|
||||
Type: tea.KeyCtrlC,
|
||||
Type: tk,
|
||||
Runes: []rune{},
|
||||
Alt: false,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if repeatedBackspace && k == ebiten.KeyBackspace {
|
||||
continue
|
||||
}
|
||||
|
||||
if val, ok := ebitenToTeaKeys[k]; ok {
|
||||
runes := make([]rune, len(val.rune))
|
||||
copy(runes, val.rune)
|
||||
|
||||
64
crt.go
64
crt.go
@ -60,10 +60,14 @@ type Window struct {
|
||||
bgColors *image.RGBA
|
||||
shader []shader.Shader
|
||||
routine sync.Once
|
||||
shaderByteBuffer []byte
|
||||
shaderBuffer *ebiten.Image
|
||||
lastBuffer *ebiten.Image
|
||||
invalidateBuffer bool
|
||||
}
|
||||
|
||||
type WindowOption func(window *Window)
|
||||
|
||||
// NewGame creates a new terminal game with the given dimensions and font faces.
|
||||
func NewGame(width int, height int, fonts Fonts, tty io.Reader, adapter InputAdapter, defaultBg color.Color) (*Window, error) {
|
||||
if defaultBg == nil {
|
||||
@ -73,12 +77,12 @@ func NewGame(width int, height int, fonts Fonts, tty io.Reader, adapter InputAda
|
||||
bounds, _, _ := fonts.Normal.GlyphBounds([]rune("█")[0])
|
||||
size := bounds.Max.Sub(bounds.Min)
|
||||
|
||||
cellWidth := size.X.Round()
|
||||
cellHeight := size.Y.Round()
|
||||
cellOffsetY := -bounds.Min.Y.Round()
|
||||
cellWidth := size.X.Ceil()
|
||||
cellHeight := size.Y.Ceil()
|
||||
cellOffsetY := -bounds.Min.Y.Ceil()
|
||||
|
||||
cellsWidth := width / cellWidth
|
||||
cellsHeight := height / cellHeight
|
||||
cellsWidth := int(float64(width)*DeviceScale()) / cellWidth
|
||||
cellsHeight := int(float64(height)*DeviceScale()) / cellHeight
|
||||
|
||||
grid := make([][]GridCell, cellsHeight)
|
||||
for y := 0; y < cellsHeight; y++ {
|
||||
@ -105,6 +109,7 @@ func NewGame(width int, height int, fonts Fonts, tty io.Reader, adapter InputAda
|
||||
grid: grid,
|
||||
tty: tty,
|
||||
bgColors: image.NewRGBA(image.Rect(0, 0, cellsWidth*cellWidth, cellsHeight*cellHeight)),
|
||||
lastBuffer: ebiten.NewImage(cellsWidth*cellWidth, cellsHeight*cellHeight),
|
||||
cursorChar: "█",
|
||||
cursorColor: color.RGBA{R: 255, G: 255, B: 255, A: 100},
|
||||
onUpdate: func() {},
|
||||
@ -342,6 +347,15 @@ func (g *Window) handleSGR(sgr any) {
|
||||
colorCache[seq.Id] = col
|
||||
}
|
||||
}
|
||||
case SGRBgColor:
|
||||
if val, ok := colorCache[seq.Id]; ok {
|
||||
g.curBg = val
|
||||
} else {
|
||||
if col, err := colorful.Hex(termenv.ANSI256Color(seq.Id).String()); err == nil {
|
||||
g.curBg = col
|
||||
colorCache[seq.Id] = col
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -541,9 +555,7 @@ func (g *Window) Draw(screen *ebiten.Image) {
|
||||
bufferImage := g.lastBuffer
|
||||
|
||||
// Only draw the buffer if it's invalid
|
||||
if bufferImage == nil || g.invalidateBuffer {
|
||||
bufferImage = ebiten.NewImage(g.cellsWidth*g.cellWidth, g.cellsHeight*g.cellHeight)
|
||||
|
||||
if g.invalidateBuffer {
|
||||
// Draw background
|
||||
bufferImage.WritePixels(g.bgColors.Pix)
|
||||
|
||||
@ -576,13 +588,22 @@ func (g *Window) Draw(screen *ebiten.Image) {
|
||||
|
||||
// Draw shader
|
||||
if g.shader != nil {
|
||||
shaderBuffer := ebiten.NewImageFromImage(bufferImage)
|
||||
if g.shaderBuffer == nil {
|
||||
g.shaderBuffer = ebiten.NewImageFromImage(bufferImage)
|
||||
} else {
|
||||
bounds := g.shaderBuffer.Bounds()
|
||||
if len(g.shaderByteBuffer) < 4*bounds.Dx()*bounds.Dy() {
|
||||
g.shaderByteBuffer = make([]byte, 4*bounds.Dx()*bounds.Dy())
|
||||
}
|
||||
bufferImage.ReadPixels(g.shaderByteBuffer)
|
||||
g.shaderBuffer.WritePixels(g.shaderByteBuffer)
|
||||
}
|
||||
|
||||
for i := range g.shader {
|
||||
_ = g.shader[i].Apply(screen, shaderBuffer)
|
||||
_ = g.shader[i].Apply(screen, g.shaderBuffer)
|
||||
|
||||
if len(g.shader) > 0 {
|
||||
shaderBuffer.DrawImage(screen, nil)
|
||||
g.shaderBuffer.DrawImage(screen, nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -597,14 +618,13 @@ func (g *Window) Draw(screen *ebiten.Image) {
|
||||
}
|
||||
|
||||
func (g *Window) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
return g.cellsWidth * g.cellWidth, g.cellsHeight * g.cellHeight
|
||||
s := DeviceScale()
|
||||
return int(float64(outsideWidth) * s), int(float64(outsideHeight) * s)
|
||||
}
|
||||
|
||||
func (g *Window) Run(title string) error {
|
||||
sw, sh := g.Layout(0, 0)
|
||||
|
||||
ebiten.SetScreenFilterEnabled(false)
|
||||
ebiten.SetWindowSize(sw, sh)
|
||||
ebiten.SetWindowSize(int(float64(g.cellsWidth*g.cellWidth)/DeviceScale()), int(float64(g.cellsHeight*g.cellHeight)/DeviceScale()))
|
||||
ebiten.SetWindowTitle(title)
|
||||
if err := ebiten.RunGame(g); err != nil {
|
||||
return err
|
||||
@ -613,6 +633,20 @@ func (g *Window) Run(title string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Window) RunWithOptions(options ...WindowOption) error {
|
||||
ebiten.SetWindowSize(int(float64(g.cellsWidth*g.cellWidth)/DeviceScale()), int(float64(g.cellsHeight*g.cellHeight)/DeviceScale()))
|
||||
|
||||
for _, opt := range options {
|
||||
opt(g)
|
||||
}
|
||||
|
||||
if err := ebiten.RunGame(g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Window) Kill() {
|
||||
SysKill()
|
||||
}
|
||||
|
||||
26
dpi.go
Normal file
26
dpi.go
Normal file
@ -0,0 +1,26 @@
|
||||
package crt
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// DeviceScale returns the current device scale factor.
|
||||
//
|
||||
// If the environment variable CRT_DEVICE_SCALE is set, it will be used instead.
|
||||
func DeviceScale() float64 {
|
||||
if os.Getenv("CRT_DEVICE_SCALE") != "" {
|
||||
s, err := strconv.ParseFloat(os.Getenv("CRT_DEVICE_SCALE"), 64)
|
||||
if err == nil {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ebiten.DeviceScaleFactor()
|
||||
}
|
||||
|
||||
// GetFontDPI returns the recommended font DPI for the current device.
|
||||
func GetFontDPI() float64 {
|
||||
return 72.0 * DeviceScale()
|
||||
}
|
||||
@ -47,7 +47,7 @@ func main() {
|
||||
enableShader := flag.Bool("shader", false, "Enable shader")
|
||||
flag.Parse()
|
||||
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", 72.0, 9.0)
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", crt.GetFontDPI(), 9.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
113
examples/glamour/main.go
Normal file
113
examples/glamour/main.go
Normal file
@ -0,0 +1,113 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/BigJk/crt"
|
||||
bubbleadapter "github.com/BigJk/crt/bubbletea"
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"image/color"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
Width = 700
|
||||
Height = 900
|
||||
)
|
||||
|
||||
var (
|
||||
helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")).Render
|
||||
readme = ""
|
||||
)
|
||||
|
||||
type example struct {
|
||||
viewport viewport.Model
|
||||
}
|
||||
|
||||
func newExample() *example {
|
||||
vp := viewport.New(20, 20)
|
||||
vp.Style = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("62")).
|
||||
PaddingRight(2)
|
||||
|
||||
return &example{
|
||||
viewport: vp,
|
||||
}
|
||||
}
|
||||
|
||||
func (e example) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e example) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "q", "ctrl+c", "esc":
|
||||
return e, tea.Quit
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
e.viewport, cmd = e.viewport.Update(msg)
|
||||
return e, cmd
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
e.viewport.Width = msg.Width
|
||||
e.viewport.Height = msg.Height - 3
|
||||
|
||||
renderer, err := glamour.NewTermRenderer(
|
||||
glamour.WithAutoStyle(),
|
||||
glamour.WithWordWrap(msg.Width),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
str, err := renderer.Render(readme)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
e.viewport.SetContent(str)
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (e example) View() string {
|
||||
return e.viewport.View() + e.helpView()
|
||||
}
|
||||
|
||||
func (e example) helpView() string {
|
||||
return helpStyle("\n ↑/↓: Navigate • q: Quit\n")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Read the readme from repo
|
||||
f, err := os.ReadFile("./README.md")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
readme = string(f)
|
||||
readme = strings.Replace(readme, "\t", " ", -1)
|
||||
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", crt.GetFontDPI(), 12.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
win, _, err := bubbleadapter.Window(Width, Height, fonts, newExample(), color.RGBA{
|
||||
R: 30,
|
||||
G: 30,
|
||||
B: 30,
|
||||
A: 255,
|
||||
}, tea.WithAltScreen())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := win.Run("Glamour Markdown"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -38,7 +38,7 @@ func (m model) View() string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", 72.0, 16.0)
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", crt.GetFontDPI(), 16.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -187,7 +187,7 @@ func max(a, b int) int {
|
||||
func main() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", 72.0, 16.0)
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", crt.GetFontDPI(), 16.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -188,7 +188,7 @@ func max(a, b int) int {
|
||||
func main() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", 72.0, 16.0)
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", crt.GetFontDPI(), 16.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ func (m model) View() string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", 72.0, 16.0)
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", crt.GetFontDPI(), 16.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
218
examples/split-editor/main.go
Normal file
218
examples/split-editor/main.go
Normal file
@ -0,0 +1,218 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/BigJk/crt"
|
||||
bubbleadapter "github.com/BigJk/crt/bubbletea"
|
||||
"github.com/charmbracelet/bubbles/help"
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/textarea"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
const (
|
||||
Width = 1000
|
||||
Height = 600
|
||||
)
|
||||
|
||||
const (
|
||||
initialInputs = 2
|
||||
maxInputs = 6
|
||||
minInputs = 1
|
||||
helpHeight = 5
|
||||
)
|
||||
|
||||
var (
|
||||
cursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212"))
|
||||
|
||||
cursorLineStyle = lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("57")).
|
||||
Foreground(lipgloss.Color("230"))
|
||||
|
||||
placeholderStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("238"))
|
||||
|
||||
endOfBufferStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("235"))
|
||||
|
||||
focusedPlaceholderStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("99"))
|
||||
|
||||
focusedBorderStyle = lipgloss.NewStyle().
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("238"))
|
||||
|
||||
blurredBorderStyle = lipgloss.NewStyle().
|
||||
Border(lipgloss.HiddenBorder())
|
||||
)
|
||||
|
||||
type keymap = struct {
|
||||
next, prev, add, remove, quit key.Binding
|
||||
}
|
||||
|
||||
func newTextarea() textarea.Model {
|
||||
t := textarea.New()
|
||||
t.Prompt = ""
|
||||
t.Placeholder = "Type something"
|
||||
t.ShowLineNumbers = true
|
||||
t.Cursor.Style = cursorStyle
|
||||
t.FocusedStyle.Placeholder = focusedPlaceholderStyle
|
||||
t.BlurredStyle.Placeholder = placeholderStyle
|
||||
t.FocusedStyle.CursorLine = cursorLineStyle
|
||||
t.FocusedStyle.Base = focusedBorderStyle
|
||||
t.BlurredStyle.Base = blurredBorderStyle
|
||||
t.FocusedStyle.EndOfBuffer = endOfBufferStyle
|
||||
t.BlurredStyle.EndOfBuffer = endOfBufferStyle
|
||||
t.KeyMap.DeleteWordBackward.SetEnabled(false)
|
||||
t.KeyMap.LineNext = key.NewBinding(key.WithKeys("down"))
|
||||
t.KeyMap.LinePrevious = key.NewBinding(key.WithKeys("up"))
|
||||
t.Blur()
|
||||
return t
|
||||
}
|
||||
|
||||
type model struct {
|
||||
width int
|
||||
height int
|
||||
keymap keymap
|
||||
help help.Model
|
||||
inputs []textarea.Model
|
||||
focus int
|
||||
}
|
||||
|
||||
func newModel() model {
|
||||
m := model{
|
||||
inputs: make([]textarea.Model, initialInputs),
|
||||
help: help.New(),
|
||||
keymap: keymap{
|
||||
next: key.NewBinding(
|
||||
key.WithKeys("tab"),
|
||||
key.WithHelp("tab", "next"),
|
||||
),
|
||||
prev: key.NewBinding(
|
||||
key.WithKeys("shift+tab"),
|
||||
key.WithHelp("shift+tab", "prev"),
|
||||
),
|
||||
add: key.NewBinding(
|
||||
key.WithKeys("ctrl+n"),
|
||||
key.WithHelp("ctrl+n", "add an editor"),
|
||||
),
|
||||
remove: key.NewBinding(
|
||||
key.WithKeys("ctrl+w"),
|
||||
key.WithHelp("ctrl+w", "remove an editor"),
|
||||
),
|
||||
quit: key.NewBinding(
|
||||
key.WithKeys("esc", "ctrl+c"),
|
||||
key.WithHelp("esc", "quit"),
|
||||
),
|
||||
},
|
||||
}
|
||||
for i := 0; i < initialInputs; i++ {
|
||||
m.inputs[i] = newTextarea()
|
||||
}
|
||||
m.inputs[m.focus].Focus()
|
||||
m.updateKeybindings()
|
||||
return m
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return textarea.Blink
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, m.keymap.quit):
|
||||
for i := range m.inputs {
|
||||
m.inputs[i].Blur()
|
||||
}
|
||||
return m, tea.Quit
|
||||
case key.Matches(msg, m.keymap.next):
|
||||
m.inputs[m.focus].Blur()
|
||||
m.focus++
|
||||
if m.focus > len(m.inputs)-1 {
|
||||
m.focus = 0
|
||||
}
|
||||
cmd := m.inputs[m.focus].Focus()
|
||||
cmds = append(cmds, cmd)
|
||||
case key.Matches(msg, m.keymap.prev):
|
||||
m.inputs[m.focus].Blur()
|
||||
m.focus--
|
||||
if m.focus < 0 {
|
||||
m.focus = len(m.inputs) - 1
|
||||
}
|
||||
cmd := m.inputs[m.focus].Focus()
|
||||
cmds = append(cmds, cmd)
|
||||
case key.Matches(msg, m.keymap.add):
|
||||
m.inputs = append(m.inputs, newTextarea())
|
||||
case key.Matches(msg, m.keymap.remove):
|
||||
m.inputs = m.inputs[:len(m.inputs)-1]
|
||||
if m.focus > len(m.inputs)-1 {
|
||||
m.focus = len(m.inputs) - 1
|
||||
}
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
m.height = msg.Height
|
||||
m.width = msg.Width
|
||||
}
|
||||
|
||||
m.updateKeybindings()
|
||||
m.sizeInputs()
|
||||
|
||||
// Update all textareas
|
||||
for i := range m.inputs {
|
||||
newModel, cmd := m.inputs[i].Update(msg)
|
||||
m.inputs[i] = newModel
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (m *model) sizeInputs() {
|
||||
for i := range m.inputs {
|
||||
m.inputs[i].SetWidth(m.width / len(m.inputs))
|
||||
m.inputs[i].SetHeight(m.height - helpHeight)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) updateKeybindings() {
|
||||
m.keymap.add.SetEnabled(len(m.inputs) < maxInputs)
|
||||
m.keymap.remove.SetEnabled(len(m.inputs) > minInputs)
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
help := m.help.ShortHelpView([]key.Binding{
|
||||
m.keymap.next,
|
||||
m.keymap.prev,
|
||||
m.keymap.add,
|
||||
m.keymap.remove,
|
||||
m.keymap.quit,
|
||||
})
|
||||
|
||||
var views []string
|
||||
for i := range m.inputs {
|
||||
views = append(views, m.inputs[i].View())
|
||||
}
|
||||
|
||||
return lipgloss.JoinHorizontal(lipgloss.Top, views...) + "\n\n" + help
|
||||
}
|
||||
|
||||
func main() {
|
||||
fonts, err := crt.LoadFaces("./fonts/IosevkaTermNerdFontMono-Regular.ttf", "./fonts/IosevkaTermNerdFontMono-Bold.ttf", "./fonts/IosevkaTermNerdFontMono-Italic.ttf", crt.GetFontDPI(), 12.0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
win, _, err := bubbleadapter.Window(Width, Height, fonts, newModel(), color.Black, tea.WithAltScreen())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := win.Run("Split Editor"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
29
go.mod
29
go.mod
@ -4,37 +4,46 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/charmbracelet/bubbles v0.15.0
|
||||
github.com/charmbracelet/bubbletea v0.24.0
|
||||
github.com/charmbracelet/bubbletea v0.25.0
|
||||
github.com/charmbracelet/glamour v0.6.0
|
||||
github.com/charmbracelet/lipgloss v0.7.1
|
||||
github.com/hajimehoshi/ebiten/v2 v2.5.4
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.3
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6
|
||||
github.com/muesli/termenv v0.15.1
|
||||
github.com/muesli/termenv v0.15.2
|
||||
github.com/stretchr/testify v1.8.2
|
||||
golang.org/x/image v0.7.0
|
||||
golang.org/x/image v0.12.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma v0.10.0 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/ebitengine/purego v0.3.0 // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/ebitengine/purego v0.5.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/jezek/xgb v1.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/yuin/goldmark v1.5.2 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
|
||||
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57 // indirect
|
||||
golang.org/x/net v0.6.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
63
go.sum
63
go.sum
@ -1,14 +1,20 @@
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
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-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI=
|
||||
github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74=
|
||||
github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
||||
github.com/charmbracelet/bubbletea v0.24.0 h1:l8PHrft/GIeikDPCUhQe53AJrDD8xGSn0Agirh8xbe8=
|
||||
github.com/charmbracelet/bubbletea v0.24.0/go.mod h1:rK3g/2+T8vOSEkNHvtq40umJpeVYDn6bLaqbgzhL/hg=
|
||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||
github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
|
||||
github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
|
||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
|
||||
@ -20,13 +26,15 @@ github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:Yyn
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/ebitengine/purego v0.3.0 h1:BDv9pD98k6AuGNQf3IF41dDppGBOe0F4AofvhFtBXF4=
|
||||
github.com/ebitengine/purego v0.3.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/hajimehoshi/bitmapfont/v2 v2.2.3 h1:jmq/TMNj352V062Tr5e3hAoipkoxCbY1JWTzor0zNps=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.5.4 h1:NvUU6LvVc6oc+u+rD9KfHMjruRdpNwbpalVUINNXufU=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.5.4/go.mod h1:mnHSOVysTr/nUZrN1lBTRqhK4NG+T9NR3JsJP2rCppk=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/ebitengine/purego v0.5.0 h1:JrMGKfRIAM4/QVKaesIIT7m/UVjTj5GYhRSQYwfVdpo=
|
||||
github.com/ebitengine/purego v0.5.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.3 h1:xJ5klESxhflZbPUx3GdIPoITzgPgamsyv8aZCVguXGI=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.3/go.mod h1:TZtorL713an00UW4LyvMeKD8uXWnuIuCPtlH11b0pgI=
|
||||
github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk=
|
||||
github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
@ -38,11 +46,14 @@ github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp9
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
|
||||
github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
@ -53,8 +64,10 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
|
||||
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
@ -64,22 +77,28 @@ github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
|
||||
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
|
||||
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||
golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||
golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c h1:Gk61ECugwEHL6IiyyNLXNzmu8XslmRP2dS0xjIYhbb4=
|
||||
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57 h1:Q6NT8ckDYNcwmi/bmxe+XbiDMXqMRW1xFBtJ+bIpie4=
|
||||
golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57/go.mod h1:wEyOn6VvNW7tcf+bW/wBz1sehi2s2BZ4TimyR7qZen4=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
@ -88,11 +107,14 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -102,12 +124,13 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -117,8 +140,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
||||
Loading…
Reference in New Issue
Block a user