Compare commits
	
		
			70 Commits
		
	
	
		
			757112837d
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 78b2afd4fb | |||
| 88fcada19d | |||
| 16e270371e | |||
| cc0174ed24 | |||
| 8763bda3f4 | |||
| 212fa8be4d | |||
| 249e98d060 | |||
| 76964a6a00 | |||
| 6978b3dc47 | |||
| b8865b41dc | |||
| 23fd21dbf5 | |||
| 2fcc42597d | |||
| f3f8995d53 | |||
| 6c9320efae | |||
| c9131bded8 | |||
| e1fe23bcba | |||
| d3febf1f6c | |||
| 0fe9679891 | |||
| 03c835139f | |||
| af42df691b | |||
| 95b4fc1802 | |||
| 3874b2cc9d | |||
| 049e55c9c9 | |||
| a370265e2e | |||
| 535dd86aba | |||
| b0896539c8 | |||
| 28d9c9b659 | |||
| 6f9e1ec589 | |||
| 2adbe3004f | |||
| da9fbaf347 | |||
| 5bca510e83 | |||
| e76c63ccdc | |||
| 288c345a22 | |||
| 151265cc0e | |||
| 91cd6a1810 | |||
| 64b767d0b5 | |||
| 94de300e16 | |||
| 813f8a43b2 | |||
| d219f2b142 | |||
| bbc4a5fecf | |||
| 20de1eccbb | |||
| 7076c40e63 | |||
| 7fafd997ca | |||
| 007c4a5954 | |||
| c39837fa0a | |||
| 956051a8b5 | |||
| 81bdb3d4d8 | |||
| 33354dc21a | |||
| 37682d66c4 | |||
| bd6831233e | |||
| dcf5508576 | |||
| c996dcaef2 | |||
| 0e09e47694 | |||
| 3d4d10f11c | |||
| ae0eb5f001 | |||
| d36a89f61c | |||
| 441809573e | |||
| 671bffdb2f | |||
| a3413934b8 | |||
| 5fb1786b5a | |||
| 8293b7b384 | |||
| db543392d9 | |||
| 405063d20b | |||
| 030a403641 | |||
| 935956c7bf | |||
| d9d57c4b2b | |||
| 155f5a967f | |||
| de3a121b3d | |||
| 7175484edb | |||
| 9a1974a538 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,4 @@ | |||||||
| /server | /server | ||||||
| /cli |  | ||||||
| *.properties | *.properties | ||||||
| .DS_Store | .DS_Store | ||||||
| /vendor | /vendor | ||||||
|   | |||||||
| @@ -1,18 +1,22 @@ | |||||||
| steps: | steps: | ||||||
|   test: |   test: | ||||||
|     image: golang:1.23 |     image: golang:1.25 | ||||||
|     commands: |     commands: | ||||||
|       - go test ./... |       - go test ./... | ||||||
|   build: |   build: | ||||||
|     image: docker |     image: docker | ||||||
|     commands: |     commands: | ||||||
|       - apk add curl |       - apk add curl | ||||||
|       - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD registry.yetaga.in |       - docker login -u docker -p $DOCKER_PASSWORD registry.yetaga.in | ||||||
|       - docker build -t registry.yetaga.in/library:latest . |       - docker build -t registry.yetaga.in/library:latest . | ||||||
|       - docker push registry.yetaga.in/library:latest |       - docker push registry.yetaga.in/library:latest | ||||||
|       - 'curl http://100.113.98.36:4000/api/fetch -H "Authorization: Bearer $COMPOSE_TOKEN"' |       - 'curl http://100.113.98.36:4000/api/fetch -H "Authorization: Bearer $COMPOSE_TOKEN"' | ||||||
|       - 'curl http://100.113.98.36:4000/api/update -H "Authorization: Bearer $COMPOSE_TOKEN"' |       - 'curl http://100.113.98.36:4000/api/update -H "Authorization: Bearer $COMPOSE_TOKEN"' | ||||||
|     secrets: [docker_username, docker_password, compose_token] |     environment: | ||||||
|  |       DOCKER_PASSWORD: | ||||||
|  |         from_secret: docker_password | ||||||
|  |       COMPOSE_TOKEN: | ||||||
|  |         from_secret: compose_token | ||||||
|     when: |     when: | ||||||
|       branch: "master" |       branch: "master" | ||||||
|     volumes: |     volumes: | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| FROM golang:1.23 | FROM golang:1.25 | ||||||
| WORKDIR /src | WORKDIR /src | ||||||
| COPY . ./ | COPY . ./ | ||||||
| RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/serve | RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/serve | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,28 +1,21 @@ | |||||||
| .PHONY: up down run-server run-cli test | .PHONY: up down run test | ||||||
|  |  | ||||||
| GOFILES=$(shell find . -name '*.go' -o -name 'go.*') | GOFILES=$(shell find . -name '*.go' -o -name 'go.*') | ||||||
| STATICFILES=$(shell find . -name '*.js' -o -name '*.css' -o -name '*.html') | STATICFILES=$(shell find . -name '*.js' -o -name '*.css' -o -name '*.html') | ||||||
| SQLFILES=$(shell find . -name '*.sql') |  | ||||||
|  |  | ||||||
| ifneq (,$(wildcard ./local.properties)) | ifneq (,$(wildcard ./local.properties)) | ||||||
| include local.properties | include local.properties | ||||||
| export | export | ||||||
| endif | endif | ||||||
|  |  | ||||||
| build: server cli | build: server | ||||||
|  |  | ||||||
| run-server: build | run: build | ||||||
| 	./server | 	./server | ||||||
|  |  | ||||||
| run-cli: build |  | ||||||
| 	./cli |  | ||||||
|  |  | ||||||
| server: $(GOFILES) $(STATICFILES) | server: $(GOFILES) $(STATICFILES) | ||||||
| 	go build -o server ./cmd/serve | 	go build -o server ./cmd/serve | ||||||
|  |  | ||||||
| cli: $(GOFILES) $(SQLFILES) |  | ||||||
| 	go build -o cli ./cmd/cli |  | ||||||
|  |  | ||||||
| test: | test: | ||||||
| 	go test ./... -cover | 	go test ./... -cover | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,113 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"git.yetaga.in/alazyreader/library/media" |  | ||||||
| 	"github.com/gdamore/tcell/v2" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // error message |  | ||||||
| type EventError struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| 	err error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventError(err error) *EventError { |  | ||||||
| 	e := &EventError{err: err} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // save change to book |  | ||||||
| type EventBookUpdate struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| 	book *media.Book |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventBookUpdate(b *media.Book) *EventBookUpdate { |  | ||||||
| 	e := &EventBookUpdate{book: b} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *EventBookUpdate) Book() *media.Book { |  | ||||||
| 	return e.book |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // open new book in display |  | ||||||
| type EventLoadBook struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| 	ID int |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventLoadBook(id int) *EventLoadBook { |  | ||||||
| 	e := &EventLoadBook{ID: id} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // open new book in display |  | ||||||
| type EventEnterBook struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventEnterBook() *EventEnterBook { |  | ||||||
| 	e := &EventEnterBook{} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // switch back to menu control |  | ||||||
| type EventExitBook struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventExitBook() *EventExitBook { |  | ||||||
| 	e := &EventExitBook{} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // open import window |  | ||||||
| type EventOpenImport struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventOpenImport() *EventOpenImport { |  | ||||||
| 	e := &EventOpenImport{} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // attempt to import given filename.csv |  | ||||||
| type EventAttemptImport struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| 	filename string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventAttemptImport(f string) *EventAttemptImport { |  | ||||||
| 	e := &EventAttemptImport{filename: f} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // close import window |  | ||||||
| type EventCloseImport struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventCloseImport() *EventCloseImport { |  | ||||||
| 	e := &EventCloseImport{} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // quit |  | ||||||
| type EventQuit struct { |  | ||||||
| 	tcell.EventTime |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEventQuit() *EventQuit { |  | ||||||
| 	e := &EventQuit{} |  | ||||||
| 	e.SetEventNow() |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
							
								
								
									
										336
									
								
								cmd/cli/main.go
									
									
									
									
									
								
							
							
						
						
									
										336
									
								
								cmd/cli/main.go
									
									
									
									
									
								
							| @@ -1,336 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
| 	"os" |  | ||||||
| 	"runtime/debug" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"git.yetaga.in/alazyreader/library/config" |  | ||||||
| 	"git.yetaga.in/alazyreader/library/database" |  | ||||||
| 	"git.yetaga.in/alazyreader/library/importer" |  | ||||||
| 	"git.yetaga.in/alazyreader/library/media" |  | ||||||
| 	"git.yetaga.in/alazyreader/library/ui" |  | ||||||
| 	"github.com/gdamore/tcell/v2" |  | ||||||
| 	"github.com/kelseyhightower/envconfig" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // State holds the UI state keys=>value map and manages access to the map with a mutex |  | ||||||
| type State struct { |  | ||||||
| 	m        sync.Mutex |  | ||||||
| 	stateMap map[string]interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // key, present |  | ||||||
| func (s *State) Get(key string) interface{} { |  | ||||||
| 	s.m.Lock() |  | ||||||
| 	defer s.m.Unlock() |  | ||||||
| 	if s.stateMap == nil { |  | ||||||
| 		s.stateMap = make(map[string]interface{}) |  | ||||||
| 	} |  | ||||||
| 	k, ok := s.stateMap[key] |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return k |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // key, value |  | ||||||
| func (s *State) Set(key string, value interface{}) { |  | ||||||
| 	s.m.Lock() |  | ||||||
| 	defer s.m.Unlock() |  | ||||||
| 	if s.stateMap == nil { |  | ||||||
| 		s.stateMap = make(map[string]interface{}) |  | ||||||
| 	} |  | ||||||
| 	s.stateMap[key] = value |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func max(a, b int) int { |  | ||||||
| 	if a > b { |  | ||||||
| 		return a |  | ||||||
| 	} |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UI states |  | ||||||
| const ( |  | ||||||
| 	IN_MENU = iota |  | ||||||
| 	IN_BOOK |  | ||||||
| 	IN_IMPORT |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func main() { |  | ||||||
| 	var c config.Config |  | ||||||
| 	err := envconfig.Process("library", &c) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalln(err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// create state |  | ||||||
| 	state := State{} |  | ||||||
|  |  | ||||||
| 	// set up DB connection |  | ||||||
| 	if c.DBUser == "" || c.DBPass == "" || c.DBHost == "" || c.DBPort == "" || c.DBName == "" { |  | ||||||
| 		if c.DBPass != "" { // obscure password |  | ||||||
| 			c.DBPass = c.DBPass[0:max(3, len(c.DBPass))] + strings.Repeat("*", max(0, len(c.DBPass)-3)) |  | ||||||
| 		} |  | ||||||
| 		log.Fatalf("vars: %+v", c) |  | ||||||
| 	} |  | ||||||
| 	lib, err := database.NewMySQLConnection(c.DBUser, c.DBPass, c.DBHost, c.DBPort, c.DBName) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalln(err) |  | ||||||
| 	} |  | ||||||
| 	err = lib.PrepareDatabase(context.Background()) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalln(err) |  | ||||||
| 	} |  | ||||||
| 	_, _, err = lib.RunMigrations(context.Background()) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalln(err) |  | ||||||
| 	} |  | ||||||
| 	books, err := lib.GetAllBooks(context.Background()) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalln(err) |  | ||||||
| 	} |  | ||||||
| 	state.Set("library", books) |  | ||||||
|  |  | ||||||
| 	screen, err := tcell.NewScreen() |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalln(err) |  | ||||||
| 	} |  | ||||||
| 	err = screen.Init() |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalln(err) |  | ||||||
| 	} |  | ||||||
| 	// cleanup our screen and log if we panic and crash out somewhere |  | ||||||
| 	defer func() { |  | ||||||
| 		if r := recover(); r != nil { |  | ||||||
| 			if screen != nil { |  | ||||||
| 				screen.Fini() |  | ||||||
| 			} |  | ||||||
| 			fmt.Println("fatal panic;", r) |  | ||||||
| 			if c.Debug { |  | ||||||
| 				fmt.Println("stacktrace: \n" + string(debug.Stack())) |  | ||||||
| 			} |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	// book list and options menu (left column) |  | ||||||
| 	l := ui.NewList(Titles(state.Get("library").([]media.Book)), 0) |  | ||||||
| 	menu := ui.NewBox( |  | ||||||
| 		"library", |  | ||||||
| 		[]string{"˄˅ select", "⏎ edit", "(n)ew", "(i)mport", "(q)uit"}, |  | ||||||
| 		ui.Contents{{ |  | ||||||
| 			Offsets:   ui.Offsets{Top: 1, Left: 2, Bottom: -2, Right: -2}, |  | ||||||
| 			Container: l, |  | ||||||
| 		}}, |  | ||||||
| 		ui.StyleActive, |  | ||||||
| 		false, |  | ||||||
| 	) |  | ||||||
| 	activeBookDetails := ui.NewBookDetails(&media.Book{}) |  | ||||||
|  |  | ||||||
| 	// book display (right column) |  | ||||||
| 	activeBook := ui.NewBox( |  | ||||||
| 		"book", |  | ||||||
| 		[]string{"˄˅ select", "⏎ edit", "(esc) close"}, |  | ||||||
| 		ui.Contents{{ |  | ||||||
| 			Offsets:   ui.Offsets{Top: 1, Left: 2, Bottom: 0, Right: 0}, |  | ||||||
| 			Container: activeBookDetails, |  | ||||||
| 		}}, |  | ||||||
| 		ui.StyleInactive, |  | ||||||
| 		false, |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	// parent container |  | ||||||
| 	container := ui.NewContainer( |  | ||||||
| 		ui.Contents{ |  | ||||||
| 			{Container: menu, Offsets: ui.Offsets{Percent: 1}}, |  | ||||||
| 			{Container: activeBook, Offsets: ui.Offsets{Percent: 2}}, |  | ||||||
| 		}, |  | ||||||
| 		ui.LayoutHorizontalPercent, |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	// import pop-up |  | ||||||
| 	wd, _ := os.Getwd() |  | ||||||
| 	fileSelector := ui.NewEditableTextLine(wd) |  | ||||||
| 	fileSelector.ResetCursor(false) |  | ||||||
| 	fileSelector.SetStyle(ui.StyleActive.Underline(true)) |  | ||||||
| 	popup := ui.NewBox( |  | ||||||
| 		"import csv file", |  | ||||||
| 		[]string{"⏎ submit", "(esc)close"}, |  | ||||||
| 		ui.Contents{ |  | ||||||
| 			{Container: fileSelector, Offsets: ui.Offsets{Top: 2, Left: 2}}, |  | ||||||
| 		}, |  | ||||||
| 		ui.StyleActive, |  | ||||||
| 		false, |  | ||||||
| 	) |  | ||||||
| 	popup.SetVisible(false) |  | ||||||
|  |  | ||||||
| 	// error pop-up |  | ||||||
| 	errorMessage := ui.NewEditableTextLine("") |  | ||||||
| 	errorPopup := ui.NewBox( |  | ||||||
| 		"error", |  | ||||||
| 		[]string{"⏎ close"}, |  | ||||||
| 		ui.Contents{ |  | ||||||
| 			{Container: errorMessage, Offsets: ui.Offsets{Top: 1, Left: 1}}, |  | ||||||
| 		}, |  | ||||||
| 		ui.StyleActive.Bold(true).Foreground(tcell.ColorRed), |  | ||||||
| 		false, |  | ||||||
| 	) |  | ||||||
| 	errorPopup.SetVisible(false) |  | ||||||
|  |  | ||||||
| 	// init |  | ||||||
| 	screen.Clear() |  | ||||||
| 	w, h := screen.Size() |  | ||||||
| 	container.SetSize(0, 0, h, w) |  | ||||||
| 	container.Draw(screen) |  | ||||||
| 	screen.Sync() |  | ||||||
|  |  | ||||||
| 	// init UI state |  | ||||||
| 	state.Set("ui_state", IN_MENU) |  | ||||||
| 	screen.PostEvent(NewEventLoadBook(l.SelectedID())) |  | ||||||
|  |  | ||||||
| 	// UI loop |  | ||||||
| 	for { |  | ||||||
| 		e := screen.PollEvent() |  | ||||||
| 		switch v := e.(type) { |  | ||||||
| 		case *tcell.EventError: |  | ||||||
| 			fmt.Fprintf(os.Stderr, "%v", v) |  | ||||||
| 			screen.Beep() |  | ||||||
| 		case *tcell.EventKey: // input handling |  | ||||||
| 			curr := state.Get("ui_state").(int) |  | ||||||
| 			if curr == IN_MENU { |  | ||||||
| 				if v.Key() == tcell.KeyUp && l.Selected() > 0 { |  | ||||||
| 					l.SetSelected(l.Selected() - 1) |  | ||||||
| 					screen.PostEvent(NewEventLoadBook(l.SelectedID())) |  | ||||||
| 				} |  | ||||||
| 				if v.Key() == tcell.KeyDown && l.Selected() < len(l.ListMembers())-1 { |  | ||||||
| 					l.SetSelected(l.Selected() + 1) |  | ||||||
| 					screen.PostEvent(NewEventLoadBook(l.SelectedID())) |  | ||||||
| 				} |  | ||||||
| 				if v.Key() == tcell.KeyEnter { |  | ||||||
| 					screen.PostEvent(NewEventEnterBook()) |  | ||||||
| 				} |  | ||||||
| 				if v.Rune() == 'i' { |  | ||||||
| 					screen.PostEvent(NewEventOpenImport()) |  | ||||||
| 				} |  | ||||||
| 				if v.Rune() == 'q' { |  | ||||||
| 					screen.PostEvent(NewEventQuit()) |  | ||||||
| 				} |  | ||||||
| 			} else if curr == IN_BOOK { |  | ||||||
| 				if v.Key() == tcell.KeyEsc { |  | ||||||
| 					screen.PostEvent(NewEventExitBook()) |  | ||||||
| 				} |  | ||||||
| 			} else if curr == IN_IMPORT { |  | ||||||
| 				if v.Key() == tcell.KeyEsc { |  | ||||||
| 					fileSelector.SetText(wd) |  | ||||||
| 					fileSelector.ResetCursor(false) |  | ||||||
| 					screen.PostEvent(NewEventCloseImport()) |  | ||||||
| 				} |  | ||||||
| 				if v.Key() == tcell.KeyBackspace || v.Key() == tcell.KeyBackspace2 { |  | ||||||
| 					fileSelector.DeleteAtCursor() |  | ||||||
| 				} else if v.Key() == tcell.KeyRight { |  | ||||||
| 					fileSelector.MoveCursor(1) |  | ||||||
|  |  | ||||||
| 				} else if v.Key() == tcell.KeyLeft { |  | ||||||
| 					fileSelector.MoveCursor(-1) |  | ||||||
| 				} else if v.Key() == tcell.KeyEnter { |  | ||||||
| 					screen.PostEvent(NewEventAttemptImport(fileSelector.Text())) |  | ||||||
| 				} else if v.Rune() != 0 { |  | ||||||
| 					fileSelector.InsertAtCursor(v.Rune()) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		case *tcell.EventResize: // screen redraw |  | ||||||
| 			w, h := screen.Size() |  | ||||||
| 			container.SetSize(0, 0, h, w) |  | ||||||
| 		case *EventBookUpdate: |  | ||||||
| 			// TK |  | ||||||
| 		case *EventEnterBook: |  | ||||||
| 			activeBook.SetStyle(ui.StyleActive) |  | ||||||
| 			menu.SetStyle(ui.StyleInactive) |  | ||||||
| 			state.Set("ui_state", IN_BOOK) |  | ||||||
| 		case *EventExitBook: |  | ||||||
| 			state.Set("ui_state", IN_MENU) |  | ||||||
| 			activeBook.SetStyle(ui.StyleInactive) |  | ||||||
| 			menu.SetStyle(ui.StyleActive) |  | ||||||
| 		case *EventLoadBook: |  | ||||||
| 			activeBookDetails.SetBook(GetBookByID(v.ID, books)) |  | ||||||
| 		case *EventOpenImport: |  | ||||||
| 			state.Set("ui_state", IN_IMPORT) |  | ||||||
| 			menu.SetStyle(ui.StyleInactive) |  | ||||||
| 			popup.SetVisible(true) |  | ||||||
| 			popup.SetSize(6, 3, 5, 80) |  | ||||||
| 		case *EventAttemptImport: |  | ||||||
| 			// this will block other events, but it shouldn't take too long... |  | ||||||
| 			f, err := os.Open(v.filename) |  | ||||||
| 			if err != nil { |  | ||||||
| 				screen.PostEvent(NewEventError(err)) |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			books, err := importer.CSVToBooks(f) |  | ||||||
| 			if err != nil { |  | ||||||
| 				screen.PostEvent(NewEventError(err)) |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			for b := range books { |  | ||||||
| 				err = lib.AddBook(context.Background(), &books[b]) |  | ||||||
| 				if err != nil { |  | ||||||
| 					screen.PostEvent(NewEventError(err)) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			screen.PostEvent(NewEventCloseImport()) |  | ||||||
| 			allbooks, err := lib.GetAllBooks(context.Background()) |  | ||||||
| 			if err != nil { |  | ||||||
| 				screen.PostEvent(NewEventError(err)) |  | ||||||
| 			} |  | ||||||
| 			state.Set("library", allbooks) |  | ||||||
| 			state.Set("ui_state", IN_MENU) |  | ||||||
| 		case *EventCloseImport: |  | ||||||
| 			state.Set("ui_state", IN_MENU) |  | ||||||
| 			screen.HideCursor() |  | ||||||
| 			menu.SetStyle(ui.StyleActive) |  | ||||||
| 			popup.SetVisible(false) |  | ||||||
| 		case *EventError: |  | ||||||
| 			errorMessage.SetText(v.err.Error()) |  | ||||||
| 			errorPopup.SetVisible(true) |  | ||||||
| 		case *EventQuit: |  | ||||||
| 			screen.Fini() |  | ||||||
| 			fmt.Printf("Thank you for playing Wing Commander!\n\n") |  | ||||||
| 			return |  | ||||||
| 		case *tcell.EventInterrupt: |  | ||||||
| 		case *tcell.EventMouse: |  | ||||||
| 		case *tcell.EventTime: |  | ||||||
| 		default: |  | ||||||
| 		} |  | ||||||
| 		// repaint |  | ||||||
| 		l.SetMembers(Titles(state.Get("library").([]media.Book))) |  | ||||||
| 		container.Draw(screen) |  | ||||||
| 		popup.Draw(screen) |  | ||||||
| 		errorPopup.Draw(screen) |  | ||||||
| 		screen.Show() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Titles(lb []media.Book) []ui.ListKeyValue { |  | ||||||
| 	r := []ui.ListKeyValue{} |  | ||||||
| 	for i := range lb { |  | ||||||
| 		r = append(r, ui.ListKeyValue{ |  | ||||||
| 			Key:   lb[i].ID, |  | ||||||
| 			Value: lb[i].Title, |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 	return r |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func GetBookByID(id int, lb []media.Book) *media.Book { |  | ||||||
| 	for i := range lb { |  | ||||||
| 		if lb[i].ID == id { |  | ||||||
| 			return &lb[i] |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return &media.Book{} |  | ||||||
| } |  | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| version: "3.8" | version: "3.8" | ||||||
| services: | services: | ||||||
|   mysql: |   mysql: | ||||||
|     image: mysql:9.0 |     image: mysql:9.4 | ||||||
|     ports: |     ports: | ||||||
|       - 3306:3306 |       - 3306:3306 | ||||||
|     environment: |     environment: | ||||||
|   | |||||||
							
								
								
									
										96
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,46 +1,41 @@ | |||||||
| module git.yetaga.in/alazyreader/library | module git.yetaga.in/alazyreader/library | ||||||
|  |  | ||||||
| go 1.23.1 | go 1.25.1 | ||||||
|  |  | ||||||
| toolchain go1.23.2 |  | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	git.yetaga.in/alazyreader/go-openlibrary v0.0.1 | 	git.yetaga.in/alazyreader/go-openlibrary v0.0.1 | ||||||
| 	github.com/gdamore/tcell/v2 v2.7.4 | 	github.com/go-sql-driver/mysql v1.9.3 | ||||||
| 	github.com/go-sql-driver/mysql v1.8.1 |  | ||||||
| 	github.com/irlndts/go-discogs v0.3.6 | 	github.com/irlndts/go-discogs v0.3.6 | ||||||
| 	github.com/kelseyhightower/envconfig v1.4.0 | 	github.com/kelseyhightower/envconfig v1.4.0 | ||||||
| 	golang.org/x/sync v0.8.0 | 	golang.org/x/sync v0.17.0 | ||||||
| 	tailscale.com v1.76.1 | 	tailscale.com v1.88.4 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	filippo.io/edwards25519 v1.1.0 // indirect | 	filippo.io/edwards25519 v1.1.0 // indirect | ||||||
| 	github.com/akutz/memconn v0.1.0 // indirect | 	github.com/akutz/memconn v0.1.0 // indirect | ||||||
| 	github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect | 	github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect | 	github.com/aws/aws-sdk-go-v2 v1.36.0 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect | 	github.com/aws/aws-sdk-go-v2/config v1.29.5 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect | 	github.com/aws/aws-sdk-go-v2/credentials v1.17.58 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect | 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect | 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect | 	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect | 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect | 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect | 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 // indirect | 	github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect | 	github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect | 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect | 	github.com/aws/aws-sdk-go-v2/service/sts v1.33.13 // indirect | ||||||
| 	github.com/aws/smithy-go v1.19.0 // indirect | 	github.com/aws/smithy-go v1.22.2 // indirect | ||||||
| 	github.com/bits-and-blooms/bitset v1.13.0 // indirect |  | ||||||
| 	github.com/coder/websocket v1.8.12 // indirect | 	github.com/coder/websocket v1.8.12 // indirect | ||||||
| 	github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect | 	github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect | ||||||
| 	github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect | 	github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect | ||||||
| 	github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect | 	github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect | ||||||
| 	github.com/fxamacker/cbor/v2 v2.6.0 // indirect | 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect | ||||||
| 	github.com/gaissmai/bart v0.11.1 // indirect | 	github.com/gaissmai/bart v0.18.0 // indirect | ||||||
| 	github.com/gdamore/encoding v1.0.0 // indirect | 	github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced // indirect | ||||||
| 	github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect |  | ||||||
| 	github.com/go-ole/go-ole v1.3.0 // indirect | 	github.com/go-ole/go-ole v1.3.0 // indirect | ||||||
| 	github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect | 	github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect | ||||||
| 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | ||||||
| @@ -48,56 +43,41 @@ require ( | |||||||
| 	github.com/google/go-cmp v0.6.0 // indirect | 	github.com/google/go-cmp v0.6.0 // indirect | ||||||
| 	github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect | 	github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect | ||||||
| 	github.com/google/uuid v1.6.0 // indirect | 	github.com/google/uuid v1.6.0 // indirect | ||||||
| 	github.com/gorilla/csrf v1.7.2 // indirect |  | ||||||
| 	github.com/gorilla/securecookie v1.1.2 // indirect |  | ||||||
| 	github.com/hdevalence/ed25519consensus v0.2.0 // indirect | 	github.com/hdevalence/ed25519consensus v0.2.0 // indirect | ||||||
| 	github.com/illarion/gonotify v1.0.1 // indirect | 	github.com/illarion/gonotify/v3 v3.0.2 // indirect | ||||||
| 	github.com/illarion/gonotify/v2 v2.0.3 // indirect |  | ||||||
| 	github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect |  | ||||||
| 	github.com/jmespath/go-jmespath v0.4.0 // indirect | 	github.com/jmespath/go-jmespath v0.4.0 // indirect | ||||||
| 	github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect |  | ||||||
| 	github.com/jsimonetti/rtnetlink v1.4.0 // indirect | 	github.com/jsimonetti/rtnetlink v1.4.0 // indirect | ||||||
| 	github.com/klauspost/compress v1.17.4 // indirect | 	github.com/klauspost/compress v1.17.11 // indirect | ||||||
| 	github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect |  | ||||||
| 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect |  | ||||||
| 	github.com/mattn/go-runewidth v0.0.15 // indirect |  | ||||||
| 	github.com/mdlayher/genetlink v1.3.2 // indirect | 	github.com/mdlayher/genetlink v1.3.2 // indirect | ||||||
| 	github.com/mdlayher/netlink v1.7.2 // indirect | 	github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect | ||||||
| 	github.com/mdlayher/sdnotify v1.0.0 // indirect | 	github.com/mdlayher/sdnotify v1.0.0 // indirect | ||||||
| 	github.com/mdlayher/socket v0.5.0 // indirect | 	github.com/mdlayher/socket v0.5.0 // indirect | ||||||
| 	github.com/miekg/dns v1.1.58 // indirect | 	github.com/miekg/dns v1.1.58 // indirect | ||||||
| 	github.com/mitchellh/go-ps v1.0.0 // indirect | 	github.com/mitchellh/go-ps v1.0.0 // indirect | ||||||
| 	github.com/pierrec/lz4/v4 v4.1.21 // indirect |  | ||||||
| 	github.com/prometheus-community/pro-bing v0.4.0 // indirect | 	github.com/prometheus-community/pro-bing v0.4.0 // indirect | ||||||
| 	github.com/rivo/uniseg v0.4.4 // indirect |  | ||||||
| 	github.com/safchain/ethtool v0.3.0 // indirect | 	github.com/safchain/ethtool v0.3.0 // indirect | ||||||
| 	github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect | 	github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect | ||||||
| 	github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect | 	github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect | ||||||
| 	github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 // indirect |  | ||||||
| 	github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect | 	github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect | ||||||
| 	github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect | 	github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect | ||||||
| 	github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect | 	github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect | ||||||
| 	github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect | 	github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect | ||||||
| 	github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect | 	github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect | ||||||
| 	github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc // indirect | 	github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da // indirect | ||||||
| 	github.com/tcnksm/go-httpstat v0.2.0 // indirect | 	github.com/vishvananda/netns v0.0.5 // indirect | ||||||
| 	github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect |  | ||||||
| 	github.com/vishvananda/netlink v1.2.1-beta.2 // indirect |  | ||||||
| 	github.com/vishvananda/netns v0.0.4 // indirect |  | ||||||
| 	github.com/x448/float16 v0.8.4 // indirect | 	github.com/x448/float16 v0.8.4 // indirect | ||||||
| 	go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect | 	go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect | ||||||
| 	go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect | 	go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect | ||||||
| 	golang.org/x/crypto v0.25.0 // indirect | 	golang.org/x/crypto v0.38.0 // indirect | ||||||
| 	golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect | 	golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect | ||||||
| 	golang.org/x/mod v0.19.0 // indirect | 	golang.org/x/mod v0.24.0 // indirect | ||||||
| 	golang.org/x/net v0.27.0 // indirect | 	golang.org/x/net v0.40.0 // indirect | ||||||
| 	golang.org/x/sys v0.22.0 // indirect | 	golang.org/x/sys v0.33.0 // indirect | ||||||
| 	golang.org/x/term v0.22.0 // indirect | 	golang.org/x/term v0.32.0 // indirect | ||||||
| 	golang.org/x/text v0.16.0 // indirect | 	golang.org/x/text v0.25.0 // indirect | ||||||
| 	golang.org/x/time v0.5.0 // indirect | 	golang.org/x/time v0.11.0 // indirect | ||||||
| 	golang.org/x/tools v0.23.0 // indirect | 	golang.org/x/tools v0.33.0 // indirect | ||||||
| 	golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect | 	golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect | ||||||
| 	golang.zx2c4.com/wireguard/windows v0.5.3 // indirect | 	golang.zx2c4.com/wireguard/windows v0.5.3 // indirect | ||||||
| 	gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 // indirect | 	gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633 // indirect | ||||||
| 	nhooyr.io/websocket v1.8.10 // indirect |  | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										322
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										322
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,55 +1,57 @@ | |||||||
|  | 9fans.net/go v0.0.8-0.20250307142834-96bdba94b63f h1:1C7nZuxUMNz7eiQALRfiqNOm04+m3edWlRff/BYHf0Q= | ||||||
|  | 9fans.net/go v0.0.8-0.20250307142834-96bdba94b63f/go.mod h1:hHyrZRryGqVdqrknjq5OWDLGCTJ2NeEvtrpR96mjraM= | ||||||
| filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= | ||||||
| filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | ||||||
| filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc= | filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc= | ||||||
| filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA= | filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA= | ||||||
| git.yetaga.in/alazyreader/go-openlibrary v0.0.1 h1:5juCi8d7YyNxXFvHytQNBww5E6GmPetM7nz3kVUqNQY= | git.yetaga.in/alazyreader/go-openlibrary v0.0.1 h1:5juCi8d7YyNxXFvHytQNBww5E6GmPetM7nz3kVUqNQY= | ||||||
| git.yetaga.in/alazyreader/go-openlibrary v0.0.1/go.mod h1:o6zBFJTovdFcpA+As1bRFvk5PDhAs2Lf8iVzUt7dKw8= | git.yetaga.in/alazyreader/go-openlibrary v0.0.1/go.mod h1:o6zBFJTovdFcpA+As1bRFvk5PDhAs2Lf8iVzUt7dKw8= | ||||||
| github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= | github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= | ||||||
| github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= | github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= | ||||||
| github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= | github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= | ||||||
| github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= | github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= | ||||||
| github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= | github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= | ||||||
| github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= | github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= | ||||||
| github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= | ||||||
| github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= | ||||||
| github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= | github.com/aws/aws-sdk-go-v2 v1.36.0 h1:b1wM5CcE65Ujwn565qcwgtOTT1aT4ADOHHgglKjG7fk= | ||||||
| github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= | github.com/aws/aws-sdk-go-v2 v1.36.0/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= | ||||||
| github.com/aws/aws-sdk-go-v2/config v1.26.5 h1:lodGSevz7d+kkFJodfauThRxK9mdJbyutUxGq1NNhvw= | github.com/aws/aws-sdk-go-v2/config v1.29.5 h1:4lS2IB+wwkj5J43Tq/AwvnscBerBJtQQ6YS7puzCI1k= | ||||||
| github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU= | github.com/aws/aws-sdk-go-v2/config v1.29.5/go.mod h1:SNzldMlDVbN6nWxM7XsUiNXPSa1LWlqiXtvh/1PrJGg= | ||||||
| github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= | github.com/aws/aws-sdk-go-v2/credentials v1.17.58 h1:/d7FUpAPU8Lf2KUdjniQvfNdlMID0Sd9pS23FJ3SS9Y= | ||||||
| github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= | github.com/aws/aws-sdk-go-v2/credentials v1.17.58/go.mod h1:aVYW33Ow10CyMQGFgC0ptMRIqJWvJ4nxZb0sUiuQT/A= | ||||||
| github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= | ||||||
| github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= | ||||||
| github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= | ||||||
| github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= | ||||||
| github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= | ||||||
| github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= | ||||||
| github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= | ||||||
| github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= | ||||||
| github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= | ||||||
| github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= | ||||||
| github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= | ||||||
| github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= | ||||||
| github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 h1:a8HvP/+ew3tKwSXqL3BCSjiuicr+XTU2eFYeogV9GJE= | github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 h1:a8HvP/+ew3tKwSXqL3BCSjiuicr+XTU2eFYeogV9GJE= | ||||||
| github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7/go.mod h1:Q7XIWsMo0JcMpI/6TGD6XXcXcV1DbTj6e9BKNntIMIM= | github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7/go.mod h1:Q7XIWsMo0JcMpI/6TGD6XXcXcV1DbTj6e9BKNntIMIM= | ||||||
| github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= | github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= | ||||||
| github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= | github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= | ||||||
| github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= | ||||||
| github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= | ||||||
| github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= | github.com/aws/aws-sdk-go-v2/service/sts v1.33.13 h1:3LXNnmtH3TURctC23hnC0p/39Q5gre3FI7BNOiDcVWc= | ||||||
| github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= | github.com/aws/aws-sdk-go-v2/service/sts v1.33.13/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= | ||||||
| github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= | github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= | ||||||
| github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= | github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= | ||||||
| github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= | github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= | ||||||
| github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= | github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= | ||||||
| github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= |  | ||||||
| github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= |  | ||||||
| github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= | github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= | ||||||
| github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= | github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= | ||||||
| github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= | github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= | ||||||
| github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= | github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= | ||||||
| github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= | github.com/creachadair/taskgroup v0.13.2 h1:3KyqakBuFsm3KkXi/9XIb0QcA8tEzLHLgaoidf0MdVc= | ||||||
| github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= | github.com/creachadair/taskgroup v0.13.2/go.mod h1:i3V1Zx7H8RjwljUEeUWYT30Lmb9poewSb2XI1yTwD0g= | ||||||
|  | github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= | ||||||
|  | github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= | ||||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| @@ -59,30 +61,22 @@ github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yez | |||||||
| github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= | github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= | ||||||
| github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= | github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= | ||||||
| github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= | github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= | ||||||
| github.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI= |  | ||||||
| github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40= |  | ||||||
| github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | ||||||
| github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||||
| github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= | github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= | ||||||
| github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= | github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= | ||||||
| github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= | github.com/gaissmai/bart v0.18.0 h1:jQLBT/RduJu0pv/tLwXE+xKPgtWJejbxuXAR+wLJafo= | ||||||
| github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= | github.com/gaissmai/bart v0.18.0/go.mod h1:JJzMAhNF5Rjo4SF4jWBrANuJfqY+FvsFhW7t1UZJ+XY= | ||||||
| github.com/gaissmai/bart v0.4.1 h1:G1t58voWkNmT47lBDawH5QhtTDsdqRIO+ftq5x4P9Ls= |  | ||||||
| github.com/gaissmai/bart v0.4.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= |  | ||||||
| github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc= |  | ||||||
| github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= |  | ||||||
| github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= |  | ||||||
| github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= |  | ||||||
| github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU= |  | ||||||
| github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= |  | ||||||
| github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= | github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= | ||||||
| github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= | github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= | ||||||
| github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= | github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced h1:Q311OHjMh/u5E2TITc++WlTP5We0xNseRMkHDyvhW7I= | ||||||
| github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= | github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= | ||||||
| github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= | ||||||
| github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= | ||||||
| github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= | github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= | ||||||
| github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= | github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= | ||||||
|  | github.com/go4org/plan9netshell v0.0.0-20250324183649-788daa080737 h1:cf60tHxREO3g1nroKr2osU3JWZsJzkfi7rEg+oAB0Lo= | ||||||
|  | github.com/go4org/plan9netshell v0.0.0-20250324183649-788daa080737/go.mod h1:MIS0jDzbU/vuM9MC4YnBITCv+RYuTRq8dJzmCrFsK9g= | ||||||
| github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= | github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= | ||||||
| github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= | github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= | ||||||
| github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= | ||||||
| @@ -91,22 +85,16 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= | |||||||
| github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= | github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= | ||||||
| github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||||||
| github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||||
| github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | github.com/google/go-tpm v0.9.4 h1:awZRf9FwOeTunQmHoDYSHJps3ie6f1UlhS1fOdPEt1I= | ||||||
| github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | github.com/google/go-tpm v0.9.4/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= | ||||||
| github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= | github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= | ||||||
| github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= | github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= | ||||||
| github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||||||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= |  | ||||||
| github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= |  | ||||||
| github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= |  | ||||||
| github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= |  | ||||||
| github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= | github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= | ||||||
| github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= | github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= | ||||||
| github.com/illarion/gonotify v1.0.1 h1:F1d+0Fgbq/sDWjj/r66ekjDG+IDeecQKUFH4wNwsoio= | github.com/illarion/gonotify/v3 v3.0.2 h1:O7S6vcopHexutmpObkeWsnzMJt/r1hONIEogeVNmJMk= | ||||||
| github.com/illarion/gonotify v1.0.1/go.mod h1:zt5pmDofZpU1f8aqlK0+95eQhoEAn/d4G4B/FjVW4jE= | github.com/illarion/gonotify/v3 v3.0.2/go.mod h1:HWGPdPe817GfvY3w7cx6zkbzNZfi3QjcBm/wgVvEL1U= | ||||||
| github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vyxM+9A= |  | ||||||
| github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE= |  | ||||||
| github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= | github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= | ||||||
| github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= | github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= | ||||||
| github.com/irlndts/go-discogs v0.3.6 h1:3oIJEkLGQ1ffJcoo6wvtawPI4/SyHoRpnu25Y51U4wg= | github.com/irlndts/go-discogs v0.3.6 h1:3oIJEkLGQ1ffJcoo6wvtawPI4/SyHoRpnu25Y51U4wg= | ||||||
| @@ -117,15 +105,12 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y | |||||||
| github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= | ||||||
| github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= | ||||||
| github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= | ||||||
| github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= |  | ||||||
| github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk= |  | ||||||
| github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= |  | ||||||
| github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= | github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= | ||||||
| github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= | github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= | ||||||
| github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= | github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= | ||||||
| github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= | github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= | ||||||
| github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= | github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= | ||||||
| github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= | github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= | ||||||
| github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= | github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= | ||||||
| github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= | github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= | ||||||
| github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= | ||||||
| @@ -134,14 +119,10 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | |||||||
| github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | ||||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||||
| github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= |  | ||||||
| github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= |  | ||||||
| github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= |  | ||||||
| github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= |  | ||||||
| github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= | github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= | ||||||
| github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= | github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= | ||||||
| github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= | github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg= | ||||||
| github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= | github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o= | ||||||
| github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= | github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= | ||||||
| github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= | github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= | ||||||
| github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= | github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= | ||||||
| @@ -150,9 +131,10 @@ github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= | |||||||
| github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= | github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= | ||||||
| github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= | github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= | ||||||
| github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= | github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= | ||||||
|  | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= | ||||||
|  | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= | ||||||
| github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= | ||||||
| github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= | ||||||
| github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= |  | ||||||
| github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= | github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= | ||||||
| github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | ||||||
| github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= | github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= | ||||||
| @@ -162,175 +144,103 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI | |||||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||||
| github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4= | github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4= | ||||||
| github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4= | github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4= | ||||||
| github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= | ||||||
| github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= | ||||||
| github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= | ||||||
| github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= | ||||||
| github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= | ||||||
| github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= | github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= | ||||||
| github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= | github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= | ||||||
| github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= | github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
| github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= |  | ||||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= |  | ||||||
| github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ= | github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ= | ||||||
| github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4= | github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4= | ||||||
| github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= | github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= | ||||||
| github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= | github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= | ||||||
| github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 h1:rXZGgEa+k2vJM8xT0PoSKfVXwFGPQ3z3CJfmnHJkZZw= | github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869 h1:SRL6irQkKGQKKLzvQP/ke/2ZuB7Py5+XuqtOgSj+iMM= | ||||||
| github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ= | github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ= | ||||||
| github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= | github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= | ||||||
| github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= | github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= | ||||||
| github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw= | github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw= | ||||||
| github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= | github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= | ||||||
| github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk= |  | ||||||
| github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= |  | ||||||
| github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= | github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= | ||||||
| github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= | github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= | ||||||
| github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w= | github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA= | ||||||
| github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU= | github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc= | ||||||
| github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g= | github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14= | ||||||
| github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= | github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= | ||||||
| github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M= | github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M= | ||||||
| github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y= | github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y= | ||||||
| github.com/tailscale/wireguard-go v0.0.0-20240429185444-03c5a0ccf754 h1:iazWjqVHE6CbNam7WXRhi33Qad5o7a8LVYgVoILpZdI= | github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da h1:jVRUZPRs9sqyKlYHHzHjAqKN+6e/Vog6NpHYeNPJqOw= | ||||||
| github.com/tailscale/wireguard-go v0.0.0-20240429185444-03c5a0ccf754/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= | github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= | ||||||
| github.com/tailscale/wireguard-go v0.0.0-20240731203015-71393c576b98 h1:RNpJrXfI5u6e+uzyIzvmnXbhmhdRkVf//90sMBH3lso= | github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek= | ||||||
| github.com/tailscale/wireguard-go v0.0.0-20240731203015-71393c576b98/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= | github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg= | ||||||
| github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc h1:cezaQN9pvKVaw56Ma5qr/G646uKIYP0yQf+OyWN/okc= |  | ||||||
| github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= |  | ||||||
| github.com/tailscale/xnet v0.0.0-20240117122442-62b9a7c569f9 h1:81P7rjnikHKTJ75EkjppvbwUfKHDHYk6LJpO5PZy8pA= |  | ||||||
| github.com/tailscale/xnet v0.0.0-20240117122442-62b9a7c569f9/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg= |  | ||||||
| github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= | github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= | ||||||
| github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= | github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= | ||||||
| github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= | github.com/u-root/u-root v0.14.0 h1:Ka4T10EEML7dQ5XDvO9c3MBN8z4nuSnGjcd1jmU2ivg= | ||||||
| github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= | github.com/u-root/u-root v0.14.0/go.mod h1:hAyZorapJe4qzbLWlAkmSVCJGbfoU9Pu4jpJ1WMluqE= | ||||||
| github.com/u-root/u-root v0.12.0 h1:K0AuBFriwr0w/PGS3HawiAw89e3+MU7ks80GpghAsNs= | github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM= | ||||||
| github.com/u-root/u-root v0.12.0/go.mod h1:FYjTOh4IkIZHhjsd17lb8nYW6udgXdJhG1c0r6u0arI= | github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= | ||||||
| github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw= |  | ||||||
| github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= |  | ||||||
| github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= |  | ||||||
| github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= |  | ||||||
| github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= | github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= | ||||||
| github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= | github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= | ||||||
| github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= | github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= | ||||||
| github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= | ||||||
| github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= | ||||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek= | ||||||
| go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= | go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= | ||||||
| go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= |  | ||||||
| go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= | ||||||
| go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= | ||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= | ||||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= | ||||||
| golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= | golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= | ||||||
| golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= | golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= | ||||||
| golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= | golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= | ||||||
| golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= | golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= | ||||||
| golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= | golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= | ||||||
| golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= | golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= | ||||||
| golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a h1:8qmSSA8Gz/1kTrCe0nqR0R3Gb/NDhykzWw2q2mWZydM= | golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= | ||||||
| golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= | golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= | ||||||
| golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= | golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= | ||||||
| golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= | golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= | ||||||
| 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= |  | ||||||
| golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= |  | ||||||
| golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= |  | ||||||
| golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= |  | ||||||
| golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= |  | ||||||
| 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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= |  | ||||||
| golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= |  | ||||||
| golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= |  | ||||||
| golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= |  | ||||||
| golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= |  | ||||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |  | ||||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= | ||||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= | ||||||
| golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= |  | ||||||
| golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= |  | ||||||
| golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= |  | ||||||
| golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= |  | ||||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |  | ||||||
| golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |  | ||||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220622161953-175b2fd9d664/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-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= | ||||||
| golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||||
| golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= | ||||||
| golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= | golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= | ||||||
| golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= | ||||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= | ||||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= | ||||||
| golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= | ||||||
| golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= | golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= | ||||||
| golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= | golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= | ||||||
| golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= |  | ||||||
| golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= |  | ||||||
| golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= |  | ||||||
| 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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= |  | ||||||
| golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= |  | ||||||
| golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= |  | ||||||
| golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= |  | ||||||
| golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= |  | ||||||
| golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= |  | ||||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |  | ||||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |  | ||||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= |  | ||||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= |  | ||||||
| golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= |  | ||||||
| golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= |  | ||||||
| golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= |  | ||||||
| golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= |  | ||||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |  | ||||||
| golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= | ||||||
| golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= | ||||||
| golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= | golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= | ||||||
| golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= | golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= | ||||||
|  | google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= | ||||||
|  | google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633 h1:2gap+Kh/3F47cO6hAu3idFvsJ0ue6TRcEi2IUkv/F8k= | ||||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM= | ||||||
| gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3 h1:/8/t5pz/mgdRXhYOIeqqYhFAQLE4DDGegc0Y4ZjyFJM= | honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= | ||||||
| gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0= | honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= | ||||||
| gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 h1:TU8z2Lh3Bbq77w0t1eG8yRlLcNHzZu3x6mhoH2Mk0c8= |  | ||||||
| gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= |  | ||||||
| honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8= |  | ||||||
| honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= |  | ||||||
| howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= | howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= | ||||||
| howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= | howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= | ||||||
| nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= |  | ||||||
| nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= |  | ||||||
| software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= | software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= | ||||||
| software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= | software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= | ||||||
| tailscale.com v1.68.2 h1:nxy9HTAXPjuTbu/xzF05mS/v9ABMRGGJdPWEScTJxUo= | tailscale.com v1.88.2 h1:S8S+gt/Vx4KDlVjNHk7spcyGihTcJflKMroSnwjp5kQ= | ||||||
| tailscale.com v1.68.2/go.mod h1:uqtoDEA8tw5+S+HLGqQGfpQsqeVtBS/EVVv5mXIaAoQ= | tailscale.com v1.88.2/go.mod h1:LHaTiwRgzebPDLgZ6RQQVzX+1SR5fbNl51fzm7UtMaw= | ||||||
| tailscale.com v1.72.1 h1:hk82jek36ph2S3Tfsh57NVWKEm/pZ9nfUonvlowpfaA= | tailscale.com v1.88.3 h1:OiE6iVqzykhbITxmIKjH8d00cw0LsJFO3TuFd4jQVXU= | ||||||
| tailscale.com v1.72.1/go.mod h1:v7OHtg0KLAnhOVf81Z8WrjNefj238QbFhgkWJQoKxbs= | tailscale.com v1.88.3/go.mod h1:LHaTiwRgzebPDLgZ6RQQVzX+1SR5fbNl51fzm7UtMaw= | ||||||
| tailscale.com v1.74.0 h1:J+vRN9o3D4wCqZBiwvDg9kZpQag2mG4Xz5RXNpmV3KE= | tailscale.com v1.88.4 h1:fXWotRMi9ZARyHRdKQa4ohXj8kqtemvvTzjreWLHVHo= | ||||||
| tailscale.com v1.74.0/go.mod h1:3iACpCONQ4lauDXvwfoGlwNCpfbVxjdc2j6G9EuFOW8= | tailscale.com v1.88.4/go.mod h1:LHaTiwRgzebPDLgZ6RQQVzX+1SR5fbNl51fzm7UtMaw= | ||||||
| tailscale.com v1.74.1 h1:qhhkN+0gFZasczi+0n0eBxwfP/ZaUr+05cWdsOQ3GT0= |  | ||||||
| tailscale.com v1.74.1/go.mod h1:3iACpCONQ4lauDXvwfoGlwNCpfbVxjdc2j6G9EuFOW8= |  | ||||||
| tailscale.com v1.76.0 h1:6fS66odV7LySVzS2ZmJebWETeS26grV8iaKZfWgXaPA= |  | ||||||
| tailscale.com v1.76.0/go.mod h1:myCwmhYBvMCF/5OgBYuIW42zscuEo30bAml7wABVZLk= |  | ||||||
| tailscale.com v1.76.1 h1:Gv0w6LdASTbkihnvNZM2sBVAU3EY0qgeSJ7yZlHxRE8= |  | ||||||
| tailscale.com v1.76.1/go.mod h1:myCwmhYBvMCF/5OgBYuIW42zscuEo30bAml7wABVZLk= |  | ||||||
|   | |||||||
							
								
								
									
										150
									
								
								ui/mock.go
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								ui/mock.go
									
									
									
									
									
								
							| @@ -1,150 +0,0 @@ | |||||||
| package ui |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
|  |  | ||||||
| 	"github.com/gdamore/tcell/v2" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type coord struct { |  | ||||||
| 	x, y int |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type MockScreen struct { |  | ||||||
| 	x, y, h, w int |  | ||||||
| 	content    map[coord]rune |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Init() error { |  | ||||||
| 	m.content = map[coord]rune{} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Fini() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Clear() { |  | ||||||
| 	m.content = map[coord]rune{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Fill(rune, tcell.Style) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) SetCell(x int, y int, style tcell.Style, ch ...rune) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) GetContent(x, y int) (mainc rune, combc []rune, style tcell.Style, width int) { |  | ||||||
| 	return m.content[coord{x, y}], nil, tcell.StyleDefault, 1 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) SetContent(x int, y int, mainc rune, combc []rune, style tcell.Style) { |  | ||||||
| 	m.content[coord{x, y}] = mainc |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Suspend() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Resume() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) SetStyle(style tcell.Style) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) SetCursorStyle(style tcell.CursorStyle) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) ShowCursor(x int, y int) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) HideCursor() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Size() (int, int) { |  | ||||||
| 	return m.h, m.w |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) PollEvent() tcell.Event { |  | ||||||
| 	return tcell.NewEventError(fmt.Errorf("mock error")) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) ChannelEvents(ch chan<- tcell.Event, quit <-chan struct{}) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) HasPendingEvent() bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) PostEvent(ev tcell.Event) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) PostEventWait(ev tcell.Event) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) EnableMouse(...tcell.MouseFlags) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) DisableMouse() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) EnablePaste() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) DisablePaste() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) EnableFocus() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) DisableFocus() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) HasMouse() bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Colors() int { |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Show() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Sync() {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) CharacterSet() string { |  | ||||||
| 	return "UTF-8" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) RegisterRuneFallback(r rune, subst string) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) UnregisterRuneFallback(r rune) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) CanDisplay(r rune, checkFallbacks bool) bool { |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Resize(x, y, h, w int) { |  | ||||||
| 	m.x, m.y, m.h, m.w = x, y, h, w |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) SetSize(h, w int) { |  | ||||||
| 	m.h, m.w = h, w |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) HasKey(tcell.Key) bool { |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Beep() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) LockRegion(x, y, width, height int, lock bool) {} |  | ||||||
|  |  | ||||||
| func (m *MockScreen) Tty() (tcell.Tty, bool) { |  | ||||||
| 	return nil, false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *MockScreen) DumpContents() string { |  | ||||||
| 	var res string |  | ||||||
| 	for i := m.y; i < m.h; i++ { |  | ||||||
| 		str := []rune{} |  | ||||||
| 		for j := m.x; j < m.w; j++ { |  | ||||||
| 			r, ok := m.content[coord{x: j, y: i}] |  | ||||||
| 			if ok { |  | ||||||
| 				str = append(str, r) |  | ||||||
| 			} else { |  | ||||||
| 				str = append(str, ' ') |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		res = res + string(str) + "\n" |  | ||||||
| 	} |  | ||||||
| 	return res |  | ||||||
| } |  | ||||||
							
								
								
									
										616
									
								
								ui/ui.go
									
									
									
									
									
								
							
							
						
						
									
										616
									
								
								ui/ui.go
									
									
									
									
									
								
							| @@ -1,616 +0,0 @@ | |||||||
| package ui |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"git.yetaga.in/alazyreader/library/media" |  | ||||||
| 	"github.com/gdamore/tcell/v2" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Drawable interface { |  | ||||||
| 	Draw(tcell.Screen) |  | ||||||
| 	SetSize(x, y, h, w int) |  | ||||||
| 	SetStyle(tcell.Style) |  | ||||||
| 	SetVisible(bool) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Offsets struct { |  | ||||||
| 	Top     int |  | ||||||
| 	Bottom  int |  | ||||||
| 	Left    int |  | ||||||
| 	Right   int |  | ||||||
| 	Percent int |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Contents []struct { |  | ||||||
| 	Offsets   Offsets |  | ||||||
| 	Container Drawable |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	LayoutUnmanaged = iota |  | ||||||
| 	LayoutHorizontalEven |  | ||||||
| 	LayoutVerticalEven |  | ||||||
| 	LayoutHorizontalPercent |  | ||||||
| 	LayoutVerticalPercent |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	StyleActive   = tcell.Style{}.Foreground(tcell.ColorWhite).Background(tcell.ColorBlack) |  | ||||||
| 	StyleInactive = tcell.Style{}.Foreground(tcell.ColorGray).Background(tcell.ColorBlack) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // A Container has no visible UI of its own, but arranges sub-components on the screen. |  | ||||||
| // layoutMethod manages how subcomponents are organized. If `LayoutUnmanaged`, it just uses the offsets |  | ||||||
| // in contents to paint on the screen. Otherwise, `LayoutHorizontalEven` and `LayoutVerticalEven` will |  | ||||||
| // have it compute even distributions of space for all components either horizontally or vertically, |  | ||||||
| // filling the container. |  | ||||||
| type Container struct { |  | ||||||
| 	x, y         int |  | ||||||
| 	h, w         int |  | ||||||
| 	layoutMethod int |  | ||||||
| 	contents     Contents |  | ||||||
| 	visible      bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewContainer(contents Contents, layoutMethod int) *Container { |  | ||||||
| 	return &Container{ |  | ||||||
| 		layoutMethod: layoutMethod, |  | ||||||
| 		contents:     contents, |  | ||||||
| 		visible:      true, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Container) Draw(s tcell.Screen) { |  | ||||||
| 	if !c.visible { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	for i := range c.contents { |  | ||||||
| 		c.contents[i].Container.Draw(s) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Container) SetSize(x, y, h, w int) { |  | ||||||
| 	c.x, c.y, c.h, c.w = x, y, h, w |  | ||||||
| 	carry := 0 |  | ||||||
| 	if c.layoutMethod == LayoutVerticalEven { |  | ||||||
| 		num := len(c.contents) |  | ||||||
| 		extra := c.h % num |  | ||||||
| 		for r := range c.contents { |  | ||||||
| 			w := c.w |  | ||||||
| 			h := c.h / num |  | ||||||
| 			x := c.x |  | ||||||
| 			y := c.y + (h * r) + carry |  | ||||||
| 			if extra > 0 { // distribute "extra" space to containers as we have some left |  | ||||||
| 				h++ |  | ||||||
| 				extra-- |  | ||||||
| 				carry++ |  | ||||||
| 			} |  | ||||||
| 			c.contents[r].Container.SetSize(x, y, h, w) |  | ||||||
| 		} |  | ||||||
| 	} else if c.layoutMethod == LayoutHorizontalEven { |  | ||||||
| 		num := len(c.contents) |  | ||||||
| 		extra := c.w % num |  | ||||||
| 		for r := range c.contents { |  | ||||||
| 			w := c.w / num |  | ||||||
| 			h := c.h |  | ||||||
| 			x := c.x + (w * r) + carry |  | ||||||
| 			y := c.y |  | ||||||
| 			if extra > 0 { // distribute "extra" space to containers as we have some left |  | ||||||
| 				w++ |  | ||||||
| 				extra-- |  | ||||||
| 				carry++ |  | ||||||
| 			} |  | ||||||
| 			c.contents[r].Container.SetSize(x, y, h, w) |  | ||||||
| 		} |  | ||||||
| 	} else if c.layoutMethod == LayoutHorizontalPercent { |  | ||||||
| 		// first, work out overall distribution |  | ||||||
| 		total := 0 |  | ||||||
| 		for r := range c.contents { |  | ||||||
| 			// `0` or negatives are set as minimum |  | ||||||
| 			if c.contents[r].Offsets.Percent < 1 { |  | ||||||
| 				total += 1 |  | ||||||
| 			} else { |  | ||||||
| 				total += c.contents[r].Offsets.Percent |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		carry := 0 |  | ||||||
| 		// push around containers |  | ||||||
| 		for r := range c.contents { |  | ||||||
| 			ratio := (float64(c.contents[r].Offsets.Percent) / float64(total)) |  | ||||||
| 			w := int(float64(c.w) * ratio) |  | ||||||
| 			h := c.h |  | ||||||
| 			x := c.x + carry |  | ||||||
| 			y := c.y |  | ||||||
| 			carry += w |  | ||||||
| 			// and add any remaining space to the last container |  | ||||||
| 			if r == len(c.contents)-1 { |  | ||||||
| 				w += (c.w - carry) |  | ||||||
| 			} |  | ||||||
| 			c.contents[r].Container.SetSize(x, y, h, w) |  | ||||||
| 		} |  | ||||||
| 	} else if c.layoutMethod == LayoutVerticalPercent { |  | ||||||
| 		// first, work out overall distribution |  | ||||||
| 		total := 0 |  | ||||||
| 		for r := range c.contents { |  | ||||||
| 			// `0` or negatives are set as minimum |  | ||||||
| 			if c.contents[r].Offsets.Percent < 1 { |  | ||||||
| 				total += 1 |  | ||||||
| 			} else { |  | ||||||
| 				total += c.contents[r].Offsets.Percent |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		carry := 0 |  | ||||||
| 		// push around containers |  | ||||||
| 		for r := range c.contents { |  | ||||||
| 			ratio := (float64(c.contents[r].Offsets.Percent) / float64(total)) |  | ||||||
| 			w := c.w |  | ||||||
| 			h := int(float64(c.h) * ratio) |  | ||||||
| 			x := c.x |  | ||||||
| 			y := c.y + carry |  | ||||||
| 			carry += h |  | ||||||
| 			// and add any remaining space to the last container |  | ||||||
| 			if r == len(c.contents)-1 { |  | ||||||
| 				h += (c.h - carry) |  | ||||||
| 			} |  | ||||||
| 			c.contents[r].Container.SetSize(x, y, h, w) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		for r := range c.contents { |  | ||||||
| 			x := c.x + c.contents[r].Offsets.Left |  | ||||||
| 			y := c.y + c.contents[r].Offsets.Top |  | ||||||
| 			h := c.h - c.contents[r].Offsets.Bottom |  | ||||||
| 			w := c.w - c.contents[r].Offsets.Right |  | ||||||
| 			c.contents[r].Container.SetSize(x, y, h, w) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Container) SetStyle(s tcell.Style) { |  | ||||||
| 	// containers have no visible elements to style |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Container) SetVisible(b bool) { |  | ||||||
| 	c.visible = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Container) Contents() Contents { |  | ||||||
| 	return c.contents |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Container) SetContents(con Contents) { |  | ||||||
| 	c.contents = con |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // A Box draws a ASCII box around its contents, with an optional title and footer. |  | ||||||
| type Box struct { |  | ||||||
| 	x, y        int |  | ||||||
| 	h, w        int |  | ||||||
| 	title       Drawable |  | ||||||
| 	menuItems   Drawable |  | ||||||
| 	contents    Contents |  | ||||||
| 	style       tcell.Style |  | ||||||
| 	cascade     bool |  | ||||||
| 	visible     bool |  | ||||||
| 	transparent bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewBox(title string, menuItems []string, contents Contents, initialStyle tcell.Style, cascade bool) *Box { |  | ||||||
| 	return &Box{ |  | ||||||
| 		title:       NewPaddedText(title), |  | ||||||
| 		menuItems:   NewPaddedText(strings.Join(menuItems, " ")), |  | ||||||
| 		contents:    contents, |  | ||||||
| 		style:       initialStyle, |  | ||||||
| 		cascade:     cascade, |  | ||||||
| 		visible:     true, |  | ||||||
| 		transparent: false, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Box) SetSize(x, y, h, w int) { |  | ||||||
| 	b.x, b.y, b.h, b.w = x, y, h, w |  | ||||||
| 	b.title.SetSize(b.x+2, b.y, 0, 0) |  | ||||||
| 	b.menuItems.SetSize(b.x+2, b.y+b.h-1, 0, 0) |  | ||||||
| 	for c := range b.contents { |  | ||||||
| 		x := b.x + b.contents[c].Offsets.Left |  | ||||||
| 		y := b.y + b.contents[c].Offsets.Top |  | ||||||
| 		h := b.h - b.contents[c].Offsets.Bottom |  | ||||||
| 		w := b.w - b.contents[c].Offsets.Right |  | ||||||
| 		b.contents[c].Container.SetSize(x, y, h, w) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Box) Draw(s tcell.Screen) { |  | ||||||
| 	if !b.visible { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	// blank out inner area |  | ||||||
| 	if !b.transparent { |  | ||||||
| 		for m := b.x + 1; m < b.x+b.w-1; m++ { |  | ||||||
| 			for n := b.y + 1; n < b.y+b.h-1; n++ { |  | ||||||
| 				s.SetContent(m, n, ' ', nil, b.style) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// draw outside bars |  | ||||||
| 	for m := b.x + 1; m < b.x+b.w-1; m++ { |  | ||||||
| 		s.SetContent(m, b.y, tcell.RuneHLine, nil, b.style) |  | ||||||
| 		s.SetContent(m, b.y+b.h-1, tcell.RuneHLine, nil, b.style) |  | ||||||
| 	} |  | ||||||
| 	for m := b.y + 1; m < b.y+b.h-1; m++ { |  | ||||||
| 		s.SetContent(b.x, m, tcell.RuneVLine, nil, b.style) |  | ||||||
| 		s.SetContent(b.x+b.w-1, m, tcell.RuneVLine, nil, b.style) |  | ||||||
| 	} |  | ||||||
| 	s.SetContent(b.x, b.y, tcell.RuneULCorner, nil, b.style) |  | ||||||
| 	s.SetContent(b.x+b.w-1, b.y, tcell.RuneURCorner, nil, b.style) |  | ||||||
| 	s.SetContent(b.x, b.y+b.h-1, tcell.RuneLLCorner, nil, b.style) |  | ||||||
| 	s.SetContent(b.x+b.w-1, b.y+b.h-1, tcell.RuneLRCorner, nil, b.style) |  | ||||||
|  |  | ||||||
| 	if b.title != nil { |  | ||||||
| 		b.title.Draw(s) |  | ||||||
| 	} |  | ||||||
| 	if b.menuItems != nil { |  | ||||||
| 		b.menuItems.Draw(s) |  | ||||||
| 	} |  | ||||||
| 	for c := range b.contents { |  | ||||||
| 		b.contents[c].Container.Draw(s) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Box) SetStyle(s tcell.Style) { |  | ||||||
| 	b.style = s |  | ||||||
| 	b.title.SetStyle(s) |  | ||||||
| 	b.menuItems.SetStyle(s) |  | ||||||
| 	if b.cascade { |  | ||||||
| 		for c := range b.contents { |  | ||||||
| 			b.contents[c].Container.SetStyle(s) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Box) SetVisible(v bool) { |  | ||||||
| 	b.visible = v |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Box) SetTransparent(v bool) { |  | ||||||
| 	b.transparent = v |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Box) Contents() Contents { |  | ||||||
| 	return b.contents |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Box) SetContents(c Contents) { |  | ||||||
| 	b.contents = c |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // A List is a scrollable, pageable list with a selector token. |  | ||||||
| type List struct { |  | ||||||
| 	x, y      int |  | ||||||
| 	h, w      int |  | ||||||
| 	selected  int |  | ||||||
| 	listItems []ListKeyValue |  | ||||||
| 	style     tcell.Style |  | ||||||
| 	visible   bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ListKeyValue struct { |  | ||||||
| 	Key   int |  | ||||||
| 	Value string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewList(listItems []ListKeyValue, initialSelected int) *List { |  | ||||||
| 	return &List{ |  | ||||||
| 		listItems: listItems, |  | ||||||
| 		selected:  initialSelected, |  | ||||||
| 		visible:   true, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) SetSize(x, y, h, w int) { |  | ||||||
| 	l.x, l.y, l.h, l.w = x, y, h, w |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) Draw(s tcell.Screen) { |  | ||||||
| 	if !l.visible { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	for i := range l.listItems { |  | ||||||
| 		for j, r := range l.listItems[i].Value { |  | ||||||
| 			s.SetContent(l.x+j, l.y+i, r, nil, l.style) |  | ||||||
| 		} |  | ||||||
| 		if i == l.selected { |  | ||||||
| 			s.SetContent(l.x+len(l.listItems[i].Value)+1, l.y+i, '<', nil, l.style) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) SetVisible(b bool) { |  | ||||||
| 	l.visible = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) SetStyle(s tcell.Style) { |  | ||||||
| 	l.style = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) Selected() int { |  | ||||||
| 	return l.selected |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) SelectedID() int { |  | ||||||
| 	if l.listItems == nil || len(l.listItems) == 0 { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	return l.listItems[l.selected].Key |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) SetSelected(i int) { |  | ||||||
| 	l.selected = i |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) ListMembers() []ListKeyValue { |  | ||||||
| 	return l.listItems |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *List) SetMembers(lkv []ListKeyValue) { |  | ||||||
| 	l.listItems = lkv |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // BookDetails displays an editable list of book details |  | ||||||
| type BookDetails struct { |  | ||||||
| 	x, y    int |  | ||||||
| 	h, w    int |  | ||||||
| 	book    *media.Book |  | ||||||
| 	style   tcell.Style |  | ||||||
| 	visible bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewBookDetails(b *media.Book) *BookDetails { |  | ||||||
| 	return &BookDetails{ |  | ||||||
| 		book:    b, |  | ||||||
| 		visible: true, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *BookDetails) SetBook(b *media.Book) { |  | ||||||
| 	l.book = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *BookDetails) SetSize(x, y, h, w int) { |  | ||||||
| 	l.x, l.y, l.h, l.w = x, y, h, w |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *BookDetails) Draw(s tcell.Screen) { |  | ||||||
| 	if l.book == nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if !l.visible { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	items := []struct { |  | ||||||
| 		label string |  | ||||||
| 		value string |  | ||||||
| 	}{ |  | ||||||
| 		{"Title", l.book.Title}, |  | ||||||
| 		{"Authors", strings.Join(l.book.Authors, ", ")}, |  | ||||||
| 		{"Sort Author", l.book.SortAuthor}, |  | ||||||
| 		{"ISBN-10", l.book.ISBN10}, |  | ||||||
| 		{"ISBN-13", l.book.ISBN13}, |  | ||||||
| 		{"Format", l.book.Format}, |  | ||||||
| 		{"Genre", l.book.Genre}, |  | ||||||
| 		{"Publisher", l.book.Publisher}, |  | ||||||
| 		{"Series", l.book.Series}, |  | ||||||
| 		{"Volume", l.book.Volume}, |  | ||||||
| 		{"Year", l.book.Year}, |  | ||||||
| 		{"Signed", strconv.FormatBool(l.book.Signed)}, |  | ||||||
| 		{"Cover URL", l.book.CoverURL}, |  | ||||||
| 		{"Notes", l.book.Notes}, |  | ||||||
| 		{"Description", l.book.Description}, |  | ||||||
| 	} |  | ||||||
| 	for i := range items { |  | ||||||
| 		if i < l.h-2 { |  | ||||||
| 			kv := NewKeyValue(items[i].label, ": ", items[i].value) |  | ||||||
| 			kv.SetSize(l.x, l.y+i, 0, 0) |  | ||||||
| 			kv.SetStyle(l.style) |  | ||||||
| 			kv.Draw(s) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *BookDetails) SetVisible(b bool) { |  | ||||||
| 	l.visible = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (l *BookDetails) SetStyle(s tcell.Style) { |  | ||||||
| 	l.style = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // PaddedText outputs strings with a space on both sides. |  | ||||||
| // Useful for generating headings, footers, etc. Used by Box. |  | ||||||
| type PaddedText struct { |  | ||||||
| 	x, y    int |  | ||||||
| 	h, w    int |  | ||||||
| 	text    string |  | ||||||
| 	style   tcell.Style |  | ||||||
| 	visible bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewPaddedText(text string) *PaddedText { |  | ||||||
| 	return &PaddedText{text: text, visible: true} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *PaddedText) SetSize(x, y, _, _ int) { |  | ||||||
| 	p.x, p.y, p.h, p.w = x, y, 1, len(p.text)+2 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *PaddedText) SetStyle(s tcell.Style) { |  | ||||||
| 	p.style = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *PaddedText) Draw(s tcell.Screen) { |  | ||||||
| 	if p.text == "" { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if !p.visible { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	t := p.x |  | ||||||
| 	s.SetContent(t, p.y, ' ', nil, p.style) |  | ||||||
| 	t++ |  | ||||||
| 	for _, r := range p.text { |  | ||||||
| 		s.SetContent(t, p.y, r, nil, p.style) |  | ||||||
| 		t++ |  | ||||||
| 	} |  | ||||||
| 	s.SetContent(t, p.y, ' ', nil, p.style) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *PaddedText) SetVisible(b bool) { |  | ||||||
| 	p.visible = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type KeyValue struct { |  | ||||||
| 	x, y      int |  | ||||||
| 	h, w      int |  | ||||||
| 	key       string |  | ||||||
| 	value     string |  | ||||||
| 	separator string |  | ||||||
| 	style     tcell.Style |  | ||||||
| 	visible   bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewKeyValue(key, separator, value string) *KeyValue { |  | ||||||
| 	return &KeyValue{ |  | ||||||
| 		key:       key, |  | ||||||
| 		separator: separator, |  | ||||||
| 		value:     value, |  | ||||||
| 		visible:   true, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *KeyValue) SetSize(x, y, _, _ int) { |  | ||||||
| 	p.x, p.y, p.h, p.w = x, y, 1, len(p.key)+len(p.separator)+len(p.value) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *KeyValue) SetStyle(s tcell.Style) { |  | ||||||
| 	p.style = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *KeyValue) Draw(s tcell.Screen) { |  | ||||||
| 	if !p.visible { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	for j, r := range p.key { |  | ||||||
| 		s.SetContent(p.x+j, p.y, r, nil, p.style) |  | ||||||
| 	} |  | ||||||
| 	for j, r := range p.separator { |  | ||||||
| 		s.SetContent(p.x+len(p.key)+j, p.y, r, nil, p.style) |  | ||||||
| 	} |  | ||||||
| 	for j, r := range p.value { |  | ||||||
| 		s.SetContent(p.x+len(p.key)+len(p.separator)+j, p.y, r, nil, p.style) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *KeyValue) SetVisible(b bool) { |  | ||||||
| 	p.visible = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *KeyValue) GetValue() string { |  | ||||||
| 	return p.value |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type EditableTextLine struct { |  | ||||||
| 	x, y       int |  | ||||||
| 	h, w       int |  | ||||||
| 	text       string |  | ||||||
| 	style      tcell.Style |  | ||||||
| 	visible    bool |  | ||||||
| 	cursorPos  int |  | ||||||
| 	showCursor bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewEditableTextLine(initialText string) *EditableTextLine { |  | ||||||
| 	return &EditableTextLine{ |  | ||||||
| 		text:       initialText, |  | ||||||
| 		visible:    true, |  | ||||||
| 		showCursor: true, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) SetSize(x, y, _, _ int) { |  | ||||||
| 	p.x, p.y, p.h, p.w = x, y, 1, len(p.text) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) SetStyle(s tcell.Style) { |  | ||||||
| 	p.style = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) Draw(s tcell.Screen) { |  | ||||||
| 	if !p.visible { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	for j, r := range p.text { |  | ||||||
| 		s.SetContent(p.x+j, p.y, r, nil, p.style) |  | ||||||
| 	} |  | ||||||
| 	s.ShowCursor(p.x+p.cursorPos, p.y) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) SetVisible(b bool) { |  | ||||||
| 	p.visible = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) SetCursorVisible(b bool) { |  | ||||||
| 	p.showCursor = b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) SetText(t string) { |  | ||||||
| 	p.text = t |  | ||||||
| 	if len(p.text) == 0 { |  | ||||||
| 		p.ResetCursor(true) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	p.ResetCursor(false) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) Text() string { |  | ||||||
| 	return p.text |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) ResetCursor(beginning bool) { |  | ||||||
| 	if beginning { |  | ||||||
| 		p.cursorPos = 0 |  | ||||||
| 	} else { |  | ||||||
| 		p.cursorPos = len(p.text) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) InsertAtCursor(r rune) { |  | ||||||
| 	if len(p.text) == 0 { |  | ||||||
| 		p.text = string(r) |  | ||||||
| 		p.cursorPos = 1 |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	p.text = p.text[0:p.cursorPos] + string(r) + p.text[p.cursorPos:len(p.text)] |  | ||||||
| 	p.cursorPos = p.cursorPos + 1 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) MoveCursor(i int) { |  | ||||||
| 	if p.cursorPos+i < 0 { |  | ||||||
| 		p.cursorPos = 0 |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if p.cursorPos+i > len(p.text) { |  | ||||||
| 		p.cursorPos = len(p.text) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	p.cursorPos = p.cursorPos + i |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *EditableTextLine) DeleteAtCursor() { |  | ||||||
| 	if len(p.text) == 0 { |  | ||||||
| 		p.cursorPos = 0 |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	p.text = p.text[0:p.cursorPos-1] + p.text[p.cursorPos:len(p.text)] |  | ||||||
| 	p.cursorPos = p.cursorPos - 1 |  | ||||||
| } |  | ||||||
							
								
								
									
										241
									
								
								ui/ui_test.go
									
									
									
									
									
								
							
							
						
						
									
										241
									
								
								ui/ui_test.go
									
									
									
									
									
								
							| @@ -1,241 +0,0 @@ | |||||||
| package ui |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"github.com/gdamore/tcell/v2" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestContainerOneBox(t *testing.T) { |  | ||||||
| 	expect := `┌─ box one ────────┐ |  | ||||||
| │                  │ |  | ||||||
| │                  │ |  | ||||||
| │                  │ |  | ||||||
| └──────────────────┘ |  | ||||||
| ` |  | ||||||
| 	m := &MockScreen{} |  | ||||||
| 	one := NewBox("box one", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	container := NewContainer( |  | ||||||
| 		Contents{{Container: one}}, |  | ||||||
| 		LayoutHorizontalEven, |  | ||||||
| 	) |  | ||||||
| 	m.Init() |  | ||||||
| 	m.Resize(0, 0, 5, 20) |  | ||||||
| 	container.SetSize(0, 0, 5, 20) |  | ||||||
| 	container.Draw(m) |  | ||||||
| 	result := m.DumpContents() |  | ||||||
| 	if result != expect { |  | ||||||
| 		fmt.Printf("expected:\n%+v", expect) |  | ||||||
| 		fmt.Printf("actual:\n%+v", result) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestContainerTwoBoxesHStack(t *testing.T) { |  | ||||||
| 	expect := `┌─ one ──┐┌─ two ──┐ |  | ||||||
| │        ││        │ |  | ||||||
| │        ││        │ |  | ||||||
| │        ││        │ |  | ||||||
| └────────┘└────────┘ |  | ||||||
| ` |  | ||||||
| 	m := &MockScreen{} |  | ||||||
| 	one := NewBox("one", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	two := NewBox("two", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	container := NewContainer( |  | ||||||
| 		Contents{{Container: one}, {Container: two}}, |  | ||||||
| 		LayoutHorizontalEven, |  | ||||||
| 	) |  | ||||||
| 	m.Init() |  | ||||||
| 	m.Resize(0, 0, 5, 20) |  | ||||||
| 	container.SetSize(0, 0, 5, 20) |  | ||||||
| 	container.Draw(m) |  | ||||||
| 	result := m.DumpContents() |  | ||||||
| 	if result != expect { |  | ||||||
| 		fmt.Printf("expected:\n%+v", expect) |  | ||||||
| 		fmt.Printf("actual:\n%+v", result) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestContainerThreeBoxesUnevenHStack(t *testing.T) { |  | ||||||
| 	expect := `┌─ one ──┐┌─ two ──┐┌─ three  |  | ||||||
| │        ││        ││       │ |  | ||||||
| │        ││        ││       │ |  | ||||||
| │        ││        ││       │ |  | ||||||
| └────────┘└────────┘└───────┘ |  | ||||||
| ` |  | ||||||
| 	m := &MockScreen{} |  | ||||||
| 	one := NewBox("one", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	two := NewBox("two", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	three := NewBox("three", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	container := NewContainer( |  | ||||||
| 		Contents{{Container: one}, {Container: two}, {Container: three}}, |  | ||||||
| 		LayoutHorizontalEven, |  | ||||||
| 	) |  | ||||||
| 	m.Init() |  | ||||||
| 	m.Resize(0, 0, 5, 29) |  | ||||||
| 	container.SetSize(0, 0, 5, 29) |  | ||||||
| 	container.Draw(m) |  | ||||||
| 	result := m.DumpContents() |  | ||||||
| 	if result != expect { |  | ||||||
| 		fmt.Printf("expected:\n%+v", expect) |  | ||||||
| 		fmt.Printf("actual:\n%+v", result) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestContainerTwoBoxesHPercentStack(t *testing.T) { |  | ||||||
| 	expect := `┌─ one ──────┐┌─ two ┐ |  | ||||||
| │            ││      │ |  | ||||||
| │            ││      │ |  | ||||||
| │            ││      │ |  | ||||||
| └────────────┘└──────┘ |  | ||||||
| ` |  | ||||||
| 	m := &MockScreen{} |  | ||||||
| 	one := NewBox("one", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	two := NewBox("two", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	container := NewContainer( |  | ||||||
| 		Contents{ |  | ||||||
| 			{Container: one, Offsets: Offsets{Percent: 2}}, |  | ||||||
| 			{Container: two, Offsets: Offsets{Percent: 1}}}, |  | ||||||
| 		LayoutHorizontalPercent, |  | ||||||
| 	) |  | ||||||
| 	m.Init() |  | ||||||
| 	m.Resize(0, 0, 5, 22) |  | ||||||
| 	container.SetSize(0, 0, 5, 22) |  | ||||||
| 	container.Draw(m) |  | ||||||
| 	result := m.DumpContents() |  | ||||||
| 	if result != expect { |  | ||||||
| 		fmt.Printf("expected:\n%+v", expect) |  | ||||||
| 		fmt.Printf("actual:\n%+v", result) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestContainerTwoBoxesVStack(t *testing.T) { |  | ||||||
| 	expect := `┌─ one ──┐ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| └────────┘ |  | ||||||
| ┌─ two ──┐ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| └────────┘ |  | ||||||
| ` |  | ||||||
| 	m := &MockScreen{} |  | ||||||
| 	one := NewBox("one", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	two := NewBox("two", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	container := NewContainer( |  | ||||||
| 		Contents{{Container: one}, {Container: two}}, |  | ||||||
| 		LayoutVerticalEven, |  | ||||||
| 	) |  | ||||||
| 	m.Init() |  | ||||||
| 	m.Resize(0, 0, 10, 10) |  | ||||||
| 	container.SetSize(0, 0, 10, 10) |  | ||||||
| 	container.Draw(m) |  | ||||||
| 	result := m.DumpContents() |  | ||||||
| 	if result != expect { |  | ||||||
| 		fmt.Printf("expected:\n%+v", expect) |  | ||||||
| 		fmt.Printf("actual:\n%+v", result) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestContainerTwoBoxesPercentageVStack(t *testing.T) { |  | ||||||
| 	expect := `┌─ one ──┐ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| └────────┘ |  | ||||||
| ┌─ two ──┐ |  | ||||||
| │        │ |  | ||||||
| │        │ |  | ||||||
| └────────┘ |  | ||||||
| ` |  | ||||||
| 	m := &MockScreen{} |  | ||||||
| 	one := NewBox("one", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	two := NewBox("two", nil, Contents{}, tcell.Style{}, false) |  | ||||||
| 	container := NewContainer( |  | ||||||
| 		Contents{ |  | ||||||
| 			{Container: one, Offsets: Offsets{Percent: 2}}, |  | ||||||
| 			{Container: two, Offsets: Offsets{Percent: 1}}}, |  | ||||||
| 		LayoutVerticalPercent, |  | ||||||
| 	) |  | ||||||
| 	m.Init() |  | ||||||
| 	m.Resize(0, 0, 10, 10) |  | ||||||
| 	container.SetSize(0, 0, 10, 10) |  | ||||||
| 	container.Draw(m) |  | ||||||
| 	result := m.DumpContents() |  | ||||||
| 	if result != expect { |  | ||||||
| 		fmt.Printf("expected:\n%+v", expect) |  | ||||||
| 		fmt.Printf("actual:\n%+v", result) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestNewEditableTextLine(t *testing.T) { |  | ||||||
| 	e := NewEditableTextLine("") |  | ||||||
| 	e.InsertAtCursor('a') |  | ||||||
| 	e.InsertAtCursor('b') |  | ||||||
| 	e.InsertAtCursor('c') |  | ||||||
| 	if e.text != "abc" { |  | ||||||
| 		fmt.Printf("expected: 'abc', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.MoveCursor(-1) |  | ||||||
| 	e.InsertAtCursor('d') |  | ||||||
| 	if e.text != "abdc" { |  | ||||||
| 		fmt.Printf("expected: 'abdc', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.MoveCursor(-20) |  | ||||||
| 	e.InsertAtCursor('e') |  | ||||||
| 	if e.text != "eabdc" { |  | ||||||
| 		fmt.Printf("expected: 'eabdc', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.MoveCursor(20) |  | ||||||
| 	e.InsertAtCursor('f') |  | ||||||
| 	if e.text != "eabdcf" { |  | ||||||
| 		fmt.Printf("expected: 'eabdcf', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.MoveCursor(1) |  | ||||||
| 	e.InsertAtCursor('g') |  | ||||||
| 	if e.text != "eabdcfg" { |  | ||||||
| 		fmt.Printf("expected: 'eabdcfg', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.DeleteAtCursor() |  | ||||||
| 	e.DeleteAtCursor() |  | ||||||
| 	e.MoveCursor(-1) |  | ||||||
| 	e.DeleteAtCursor() |  | ||||||
| 	if e.text != "eabc" { |  | ||||||
| 		fmt.Printf("expected: 'eabc', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.ResetCursor(false) |  | ||||||
| 	e.InsertAtCursor('h') |  | ||||||
| 	e.ResetCursor(true) |  | ||||||
| 	e.InsertAtCursor('g') |  | ||||||
| 	if e.text != "geabch" { |  | ||||||
| 		fmt.Printf("expected: 'geabch', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.SetText("the rain in spain") |  | ||||||
| 	e.InsertAtCursor('s') |  | ||||||
| 	if e.text != "the rain in spains" { |  | ||||||
| 		fmt.Printf("expected: 'the rain in spains', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| 	e.SetText("") |  | ||||||
| 	e.InsertAtCursor('s') |  | ||||||
| 	if e.text != "s" { |  | ||||||
| 		fmt.Printf("expected: 's', actual: '%+v'", e.text) |  | ||||||
| 		t.Fail() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user