├── .editorconfig ├── .github └── workflows │ ├── build.yml │ └── lint.yml ├── .gitignore ├── .golangci.yml ├── LICENSE ├── Makefile ├── README.md ├── assets ├── code.png ├── filetree.png ├── help.png ├── image.png ├── markdown.png ├── pdf.png └── statusbar.png ├── code └── code.go ├── examples ├── code │ └── code.go ├── filetree │ └── filetree.go ├── help │ └── help.go ├── image │ ├── bubbletea.png │ └── image.go ├── markdown │ └── markdown.go ├── pdf │ ├── pdf.go │ └── sample-pdf-file.pdf └── statusbar │ └── statusbar.go ├── filesystem └── filesystem.go ├── filetree ├── commands.go ├── init.go ├── keys.go ├── methods.go ├── model.go ├── styles.go ├── update.go └── view.go ├── go.mod ├── go.sum ├── help └── help.go ├── icons ├── directories.go ├── extensions.go ├── filenames.go ├── glyphs.go ├── icons.go └── sub_extensions.go ├── image └── image.go ├── markdown └── markdown.go ├── pdf └── pdf.go ├── statusbar └── statusbar.go └── teacup.go /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | 10 | [{*.go,go.*}] 11 | indent_size = 4 12 | indent_style = tab 13 | 14 | [Makefile] 15 | indent_style = tab -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | strategy: 7 | matrix: 8 | go-version: [~1.22, ^1] 9 | os: [ubuntu-latest, macos-latest, windows-latest] 10 | runs-on: ${{ matrix.os }} 11 | env: 12 | GO111MODULE: "on" 13 | steps: 14 | - name: Install Go 15 | uses: actions/setup-go@v5 16 | with: 17 | go-version: ${{ matrix.go-version }} 18 | 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | 22 | - name: Download Go modules 23 | run: go mod download 24 | 25 | - name: Build 26 | run: go build -v ./... 27 | 28 | - name: Build examples 29 | run: go build -v ./... 30 | working-directory: ./examples 31 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | golangci: 6 | name: lint 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-go@v5 11 | with: 12 | go-version: "1.22" 13 | cache: false 14 | - name: golangci-lint 15 | uses: golangci/golangci-lint-action@v4 16 | with: 17 | version: "latest" 18 | only-new-issues: true 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Binaries for programs and plugins 4 | *.exe 5 | *.exe~ 6 | *.dll 7 | *.so 8 | *.dylib 9 | /bin 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Outputs of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | *.coverage 17 | 18 | # Go vendor folder 19 | vendor/ 20 | 21 | # Mac stuff 22 | .DS_Store 23 | 24 | # Envrc with direnv 25 | .envrc 26 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | tests: false 3 | 4 | issues: 5 | include: 6 | - EXC0001 7 | - EXC0005 8 | - EXC0011 9 | - EXC0012 10 | - EXC0013 11 | 12 | max-issues-per-linter: 0 13 | max-same-issues: 0 14 | 15 | linters: 16 | enable: 17 | - bodyclose 18 | - exportloopref 19 | - goimports 20 | - gosec 21 | - nilerr 22 | - predeclared 23 | - revive 24 | - rowserrcheck 25 | - sqlclosecheck 26 | - tparallel 27 | - unconvert 28 | - unparam 29 | - whitespace 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Tyler Knipfer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: example-filetree 2 | example-filetree: 3 | @go run ./examples/filetree/filetree.go 4 | 5 | .PHONY: example-help 6 | example-help: 7 | @go run ./examples/help/help.go 8 | 9 | .PHONY: example-code 10 | example-code: 11 | @go run ./examples/code/code.go 12 | 13 | .PHONY: example-markdown 14 | example-markdown: 15 | @go run ./examples/markdown/markdown.go 16 | 17 | .PHONY: example-pdf 18 | example-pdf: 19 | @go run ./examples/pdf/pdf.go 20 | 21 | .PHONY: example-statusbar 22 | example-statusbar: 23 | @go run ./examples/statusbar/statusbar.go 24 | 25 | .PHONY: example-image 26 | example-image: 27 | @go run ./examples/image/image.go 28 | 29 | .PHONY: example-csv 30 | example-csv: 31 | @go run ./examples/csv/csv.go 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # teacup 2 | 3 | [![Release](https://img.shields.io/github/release/mistakenelf/teacup.svg?style=flat-square)](https://github.com/mistakenelf/teacup/releases/latest) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/mistakenelf/teacup?style=flat-square)](https://goreportcard.com/report/github.com/mistakenelf/teacup) 6 | [![Godoc](https://godoc.org/github.com/mistakenelf/teacup?status.svg&style=flat-square)](http://godoc.org/github.com/mistakenelf/teacup) 7 | 8 | ## About The Project 9 | 10 | A collection of bubbles and utilities for creating bubbletea applications. 11 | 12 | ### Built With 13 | 14 | - [Go](https://golang.org/) 15 | - [bubbletea](https://github.com/charmbracelet/bubbletea) 16 | - [bubbles](https://github.com/charmbracelet/bubbles) 17 | - [lipgloss](https://github.com/charmbracelet/lipgloss) 18 | 19 | ## Installation 20 | 21 | ### Go 22 | 23 | ``` 24 | go get github.com/mistakenelf/teacup@latest 25 | ``` 26 | 27 | ## Features 28 | 29 | - dirfs - A collection of helper functions for working with the filesystem 30 | - icons - A package to render file icons 31 | - Filetree, Statusbar, Markdown, PDF, Image, Help and Code bubbles 32 | 33 | ## Filetree 34 | 35 | ![filetree](./assets/filetree.png) 36 | 37 | ## Code 38 | 39 | ![code](./assets/code.png) 40 | 41 | ## Help 42 | 43 | ![help](./assets/help.png) 44 | 45 | ## Statusbar 46 | 47 | ![statusbar](./assets/statusbar.png) 48 | 49 | ## Markdown 50 | 51 | ![markdown](./assets/markdown.png) 52 | 53 | ## PDF 54 | 55 | ![PDF](./assets/pdf.png) 56 | 57 | ## Image 58 | 59 | ![image](./assets/image.png) 60 | -------------------------------------------------------------------------------- /assets/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/assets/code.png -------------------------------------------------------------------------------- /assets/filetree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/assets/filetree.png -------------------------------------------------------------------------------- /assets/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/assets/help.png -------------------------------------------------------------------------------- /assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/assets/image.png -------------------------------------------------------------------------------- /assets/markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/assets/markdown.png -------------------------------------------------------------------------------- /assets/pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/assets/pdf.png -------------------------------------------------------------------------------- /assets/statusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/assets/statusbar.png -------------------------------------------------------------------------------- /code/code.go: -------------------------------------------------------------------------------- 1 | // Package code implements a code bubble which renders syntax highlighted 2 | // source code based on a filename. 3 | package code 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "path/filepath" 9 | 10 | "github.com/alecthomas/chroma/quick" 11 | "github.com/charmbracelet/bubbles/viewport" 12 | tea "github.com/charmbracelet/bubbletea" 13 | "github.com/charmbracelet/lipgloss" 14 | "github.com/mistakenelf/teacup/filesystem" 15 | ) 16 | 17 | type syntaxMsg string 18 | type errorMsg error 19 | 20 | // Highlight returns a syntax highlighted string of text. 21 | func Highlight(content, extension, syntaxTheme string) (string, error) { 22 | buf := new(bytes.Buffer) 23 | if err := quick.Highlight(buf, content, extension, "terminal256", syntaxTheme); err != nil { 24 | return "", fmt.Errorf("%w", err) 25 | } 26 | 27 | return buf.String(), nil 28 | } 29 | 30 | // readFileContentCmd reads the content of the file. 31 | func readFileContentCmd(fileName, syntaxTheme string) tea.Cmd { 32 | return func() tea.Msg { 33 | content, err := filesystem.ReadFileContent(fileName) 34 | if err != nil { 35 | return errorMsg(err) 36 | } 37 | 38 | highlightedContent, err := Highlight(content, filepath.Ext(fileName), syntaxTheme) 39 | if err != nil { 40 | return errorMsg(err) 41 | } 42 | 43 | return syntaxMsg(highlightedContent) 44 | } 45 | } 46 | 47 | // Model represents the properties of a code bubble. 48 | type Model struct { 49 | Viewport viewport.Model 50 | Active bool 51 | Filename string 52 | HighlightedContent string 53 | SyntaxTheme string 54 | } 55 | 56 | // New creates a new instance of code. 57 | func New(active bool) Model { 58 | viewPort := viewport.New(0, 0) 59 | 60 | return Model{ 61 | Viewport: viewPort, 62 | Active: active, 63 | SyntaxTheme: "dracula", 64 | } 65 | } 66 | 67 | // Init initializes the code bubble. 68 | func (m Model) Init() tea.Cmd { 69 | return nil 70 | } 71 | 72 | // SetFileName sets current file to highlight. 73 | func (m *Model) SetFileName(filename string) tea.Cmd { 74 | m.Filename = filename 75 | 76 | return readFileContentCmd(filename, m.SyntaxTheme) 77 | } 78 | 79 | // SetIsActive sets if the bubble is currently active. 80 | func (m *Model) SetIsActive(active bool) { 81 | m.Active = active 82 | } 83 | 84 | // SetSyntaxTheme sets the syntax theme of the rendered code. 85 | func (m *Model) SetSyntaxTheme(theme string) { 86 | m.SyntaxTheme = theme 87 | } 88 | 89 | // SetSize sets the size of the bubble. 90 | func (m *Model) SetSize(w, h int) { 91 | m.Viewport.Width = w 92 | m.Viewport.Height = h 93 | } 94 | 95 | // GotoTop jumps to the top of the viewport. 96 | func (m *Model) GotoTop() { 97 | m.Viewport.GotoTop() 98 | } 99 | 100 | // Update handles updating the UI of a code bubble. 101 | func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { 102 | var ( 103 | cmd tea.Cmd 104 | cmds []tea.Cmd 105 | ) 106 | 107 | switch msg := msg.(type) { 108 | case syntaxMsg: 109 | m.Filename = "" 110 | m.HighlightedContent = lipgloss.NewStyle(). 111 | Width(m.Viewport.Width). 112 | Height(m.Viewport.Height). 113 | Render(string(msg)) 114 | 115 | m.Viewport.SetContent(m.HighlightedContent) 116 | 117 | return m, nil 118 | case errorMsg: 119 | m.Filename = "" 120 | m.HighlightedContent = lipgloss.NewStyle(). 121 | Width(m.Viewport.Width). 122 | Height(m.Viewport.Height). 123 | Render("Error: " + msg.Error()) 124 | 125 | m.Viewport.SetContent(m.HighlightedContent) 126 | 127 | return m, nil 128 | } 129 | 130 | if m.Active { 131 | m.Viewport, cmd = m.Viewport.Update(msg) 132 | cmds = append(cmds, cmd) 133 | } 134 | 135 | return m, tea.Batch(cmds...) 136 | } 137 | 138 | // View returns a string representation of the code bubble. 139 | func (m Model) View() string { 140 | return m.Viewport.View() 141 | } 142 | -------------------------------------------------------------------------------- /examples/code/code.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/mistakenelf/teacup/code" 8 | ) 9 | 10 | // model represents the properties of the UI. 11 | type model struct { 12 | code code.Model 13 | } 14 | 15 | // New creates a new instance of the UI. 16 | func New() model { 17 | codeModel := code.New(true) 18 | 19 | return model{ 20 | code: codeModel, 21 | } 22 | } 23 | 24 | // Init intializes the UI. 25 | func (m model) Init() tea.Cmd { 26 | cmd := m.code.SetFileName("code/code.go") 27 | 28 | return cmd 29 | } 30 | 31 | // Update handles all UI interactions. 32 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 33 | var ( 34 | cmd tea.Cmd 35 | cmds []tea.Cmd 36 | ) 37 | 38 | switch msg := msg.(type) { 39 | case tea.WindowSizeMsg: 40 | m.code.SetSize(msg.Width, msg.Height) 41 | 42 | return m, nil 43 | case tea.KeyMsg: 44 | switch msg.String() { 45 | case "ctrl+c", "esc", "q": 46 | cmds = append(cmds, tea.Quit) 47 | } 48 | } 49 | 50 | m.code, cmd = m.code.Update(msg) 51 | cmds = append(cmds, cmd) 52 | 53 | return m, tea.Batch(cmds...) 54 | } 55 | 56 | // View returns a string representation of the UI. 57 | func (m model) View() string { 58 | return m.code.View() 59 | } 60 | 61 | func main() { 62 | b := New() 63 | p := tea.NewProgram(b, tea.WithAltScreen()) 64 | 65 | if _, err := p.Run(); err != nil { 66 | log.Fatal(err) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/filetree/filetree.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/mistakenelf/teacup/filetree" 8 | ) 9 | 10 | // model represents the properties of the UI. 11 | type model struct { 12 | filetree filetree.Model 13 | } 14 | 15 | // New creates a new instance of the UI. 16 | func New() model { 17 | filetree := filetree.New() 18 | 19 | return model{ 20 | filetree: filetree, 21 | } 22 | } 23 | 24 | // Init intializes the UI. 25 | func (m model) Init() tea.Cmd { 26 | return m.filetree.Init() 27 | } 28 | 29 | // Update handles all UI interactions. 30 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 31 | var ( 32 | cmd tea.Cmd 33 | cmds []tea.Cmd 34 | ) 35 | 36 | switch msg := msg.(type) { 37 | case tea.KeyMsg: 38 | switch msg.String() { 39 | case "ctrl+c", "esc", "q": 40 | cmds = append(cmds, tea.Quit) 41 | } 42 | } 43 | 44 | m.filetree, cmd = m.filetree.Update(msg) 45 | cmds = append(cmds, cmd) 46 | 47 | return m, tea.Batch(cmds...) 48 | } 49 | 50 | // View returns a string representation of the UI. 51 | func (m model) View() string { 52 | return m.filetree.View() 53 | } 54 | 55 | func main() { 56 | b := New() 57 | p := tea.NewProgram(&b, tea.WithAltScreen()) 58 | 59 | if _, err := p.Run(); err != nil { 60 | log.Fatal(err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/help/help.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/charmbracelet/lipgloss" 8 | "github.com/mistakenelf/teacup/help" 9 | ) 10 | 11 | // model represents the properties of the UI. 12 | type model struct { 13 | help help.Model 14 | } 15 | 16 | // New create a new instance of the UI. 17 | func New() model { 18 | helpModel := help.New( 19 | true, 20 | "Help", 21 | help.TitleColor{ 22 | Background: lipgloss.AdaptiveColor{Light: "62", Dark: "62"}, 23 | Foreground: lipgloss.AdaptiveColor{Light: "#ffffff", Dark: "#ffffffs"}, 24 | }, 25 | []help.Entry{ 26 | {Key: "ctrl+c", Description: "Exit FM"}, 27 | {Key: "j/up", Description: "Move up"}, 28 | {Key: "k/down", Description: "Move down"}, 29 | {Key: "h/left", Description: "Go back a directory"}, 30 | {Key: "l/right", Description: "Read file or enter directory"}, 31 | {Key: "p", Description: "Preview directory"}, 32 | {Key: "G", Description: "Jump to bottom"}, 33 | {Key: "~", Description: "Go to home directory"}, 34 | {Key: ".", Description: "Toggle hidden files"}, 35 | {Key: "y", Description: "Copy file path to clipboard"}, 36 | {Key: "Z", Description: "Zip currently selected tree item"}, 37 | {Key: "U", Description: "Unzip currently selected tree item"}, 38 | {Key: "n", Description: "Create new file"}, 39 | {Key: "N", Description: "Create new directory"}, 40 | {Key: "ctrl+d", Description: "Delete currently selected tree item"}, 41 | {Key: "M", Description: "Move currently selected tree item"}, 42 | {Key: "enter", Description: "Process command"}, 43 | {Key: "E", Description: "Edit currently selected tree item"}, 44 | {Key: "C", Description: "Copy currently selected tree item"}, 45 | {Key: "esc", Description: "Reset FM to initial state"}, 46 | {Key: "tab", Description: "Toggle between boxes"}, 47 | }, 48 | ) 49 | 50 | return model{ 51 | help: helpModel, 52 | } 53 | } 54 | 55 | // Init initializes the application. 56 | func (m model) Init() tea.Cmd { 57 | return nil 58 | } 59 | 60 | // Update handles all UI interactions. 61 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 62 | var ( 63 | cmd tea.Cmd 64 | cmds []tea.Cmd 65 | ) 66 | 67 | switch msg := msg.(type) { 68 | case tea.WindowSizeMsg: 69 | m.help.SetSize(msg.Width, msg.Height) 70 | case tea.KeyMsg: 71 | switch msg.String() { 72 | case "ctrl+c", "esc", "q": 73 | cmds = append(cmds, tea.Quit) 74 | } 75 | } 76 | 77 | m.help, cmd = m.help.Update(msg) 78 | cmds = append(cmds, cmd) 79 | 80 | return m, tea.Batch(cmds...) 81 | } 82 | 83 | // View renders the UI. 84 | func (m model) View() string { 85 | return m.help.View() 86 | } 87 | 88 | func main() { 89 | b := New() 90 | p := tea.NewProgram(b, tea.WithAltScreen()) 91 | 92 | if _, err := p.Run(); err != nil { 93 | log.Fatal(err) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /examples/image/bubbletea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/examples/image/bubbletea.png -------------------------------------------------------------------------------- /examples/image/image.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/charmbracelet/lipgloss" 8 | "github.com/mistakenelf/teacup/image" 9 | ) 10 | 11 | // model represents the properties of the UI. 12 | type model struct { 13 | image image.Model 14 | } 15 | 16 | // New creates a new instance of the UI. 17 | func New() model { 18 | imageModel := image.New(true, true, lipgloss.AdaptiveColor{Light: "#000000", Dark: "#ffffff"}) 19 | 20 | return model{ 21 | image: imageModel, 22 | } 23 | } 24 | 25 | // Init intializes the UI. 26 | func (b model) Init() tea.Cmd { 27 | return nil 28 | } 29 | 30 | // Update handles all UI interactions. 31 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 32 | var ( 33 | cmd tea.Cmd 34 | cmds []tea.Cmd 35 | ) 36 | 37 | switch msg := msg.(type) { 38 | case tea.WindowSizeMsg: 39 | m.image.SetSize(msg.Width, msg.Height) 40 | cmds = append(cmds, m.image.SetFileName("examples/image/bubbletea.png")) 41 | 42 | return m, tea.Batch(cmds...) 43 | case tea.KeyMsg: 44 | switch msg.String() { 45 | case "ctrl+c", "esc", "q": 46 | cmds = append(cmds, tea.Quit) 47 | } 48 | } 49 | 50 | m.image, cmd = m.image.Update(msg) 51 | cmds = append(cmds, cmd) 52 | 53 | return m, tea.Batch(cmds...) 54 | } 55 | 56 | // View returns a string representation of the UI. 57 | func (m model) View() string { 58 | return m.image.View() 59 | } 60 | 61 | func main() { 62 | b := New() 63 | p := tea.NewProgram(b, tea.WithAltScreen()) 64 | 65 | if _, err := p.Run(); err != nil { 66 | log.Fatal(err) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/markdown/markdown.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/mistakenelf/teacup/markdown" 8 | ) 9 | 10 | // model represents the properties of the UI. 11 | type model struct { 12 | markdown markdown.Model 13 | } 14 | 15 | // New creates a new instance of the UI. 16 | func New() model { 17 | markdownModel := markdown.New(true) 18 | markdownModel.FileName = "README.md" 19 | 20 | return model{ 21 | markdown: markdownModel, 22 | } 23 | } 24 | 25 | // Init intializes the UI. 26 | func (m model) Init() tea.Cmd { 27 | return nil 28 | } 29 | 30 | // Update handles all UI interactions. 31 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 32 | var ( 33 | cmd tea.Cmd 34 | cmds []tea.Cmd 35 | ) 36 | 37 | switch msg := msg.(type) { 38 | case tea.WindowSizeMsg: 39 | cmds = append(cmds, m.markdown.SetSize(msg.Width, msg.Height)) 40 | 41 | return m, tea.Batch(cmds...) 42 | case tea.KeyMsg: 43 | switch msg.String() { 44 | case "ctrl+c", "esc", "q": 45 | cmds = append(cmds, tea.Quit) 46 | } 47 | } 48 | 49 | m.markdown, cmd = m.markdown.Update(msg) 50 | cmds = append(cmds, cmd) 51 | 52 | return m, tea.Batch(cmds...) 53 | } 54 | 55 | // View returns a string representation of the UI. 56 | func (m model) View() string { 57 | return m.markdown.View() 58 | } 59 | 60 | func main() { 61 | b := New() 62 | p := tea.NewProgram(b, tea.WithAltScreen()) 63 | 64 | if _, err := p.Run(); err != nil { 65 | log.Fatal(err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/pdf/pdf.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/mistakenelf/teacup/pdf" 8 | ) 9 | 10 | // model represents the properties of the UI. 11 | type model struct { 12 | pdf pdf.Model 13 | } 14 | 15 | // New creates a new instance of the UI. 16 | func New() model { 17 | pdfModel := pdf.New(true) 18 | 19 | return model{ 20 | pdf: pdfModel, 21 | } 22 | } 23 | 24 | // Init intializes the UI. 25 | func (m model) Init() tea.Cmd { 26 | return nil 27 | } 28 | 29 | // Update handles all UI interactions. 30 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 31 | var ( 32 | cmd tea.Cmd 33 | cmds []tea.Cmd 34 | ) 35 | 36 | switch msg := msg.(type) { 37 | case tea.WindowSizeMsg: 38 | m.pdf.SetSize(msg.Width, msg.Height) 39 | cmds = append(cmds, m.pdf.SetFileName("./examples/pdf/sample-pdf-file.pdf")) 40 | 41 | return m, tea.Batch(cmds...) 42 | case tea.KeyMsg: 43 | switch msg.String() { 44 | case "ctrl+c", "esc", "q": 45 | cmds = append(cmds, tea.Quit) 46 | } 47 | } 48 | 49 | m.pdf, cmd = m.pdf.Update(msg) 50 | cmds = append(cmds, cmd) 51 | 52 | return m, tea.Batch(cmds...) 53 | } 54 | 55 | // View returns a string representation of the UI. 56 | func (m model) View() string { 57 | return m.pdf.View() 58 | } 59 | 60 | func main() { 61 | b := New() 62 | p := tea.NewProgram(b, tea.WithAltScreen()) 63 | 64 | if _, err := p.Run(); err != nil { 65 | log.Fatal(err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/pdf/sample-pdf-file.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mistakenelf/teacup/39d3b2172a19f9e55f2ffa6ece5f1379f3b2cfe0/examples/pdf/sample-pdf-file.pdf -------------------------------------------------------------------------------- /examples/statusbar/statusbar.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/charmbracelet/lipgloss" 8 | "github.com/mistakenelf/teacup/statusbar" 9 | ) 10 | 11 | // model represents the properties of the UI. 12 | type model struct { 13 | statusbar statusbar.Model 14 | height int 15 | } 16 | 17 | // New creates a new instance of the UI. 18 | func New() model { 19 | sb := statusbar.New( 20 | statusbar.ColorConfig{ 21 | Foreground: lipgloss.AdaptiveColor{Dark: "#ffffff", Light: "#ffffff"}, 22 | Background: lipgloss.AdaptiveColor{Light: "#F25D94", Dark: "#F25D94"}, 23 | }, 24 | statusbar.ColorConfig{ 25 | Foreground: lipgloss.AdaptiveColor{Light: "#ffffff", Dark: "#ffffff"}, 26 | Background: lipgloss.AdaptiveColor{Light: "#3c3836", Dark: "#3c3836"}, 27 | }, 28 | statusbar.ColorConfig{ 29 | Foreground: lipgloss.AdaptiveColor{Light: "#ffffff", Dark: "#ffffff"}, 30 | Background: lipgloss.AdaptiveColor{Light: "#A550DF", Dark: "#A550DF"}, 31 | }, 32 | statusbar.ColorConfig{ 33 | Foreground: lipgloss.AdaptiveColor{Light: "#ffffff", Dark: "#ffffff"}, 34 | Background: lipgloss.AdaptiveColor{Light: "#6124DF", Dark: "#6124DF"}, 35 | }, 36 | ) 37 | 38 | return model{ 39 | statusbar: sb, 40 | } 41 | } 42 | 43 | // Init intializes the UI. 44 | func (m model) Init() tea.Cmd { 45 | return nil 46 | } 47 | 48 | // Update handles all UI interactions. 49 | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 50 | var ( 51 | cmds []tea.Cmd 52 | ) 53 | 54 | switch msg := msg.(type) { 55 | case tea.WindowSizeMsg: 56 | m.height = msg.Height 57 | m.statusbar.SetSize(msg.Width) 58 | m.statusbar.SetContent("test.txt", "~/.config/nvim", "1/23", "SB") 59 | 60 | return m, nil 61 | case tea.KeyMsg: 62 | switch msg.String() { 63 | case "ctrl+c", "esc", "q": 64 | cmds = append(cmds, tea.Quit) 65 | } 66 | } 67 | 68 | return m, tea.Batch(cmds...) 69 | } 70 | 71 | // View returns a string representation of the UI. 72 | func (m model) View() string { 73 | return lipgloss.JoinVertical( 74 | lipgloss.Top, 75 | lipgloss.NewStyle().Height(m.height-statusbar.Height).Render("Content"), 76 | m.statusbar.View(), 77 | ) 78 | } 79 | 80 | func main() { 81 | b := New() 82 | p := tea.NewProgram(b, tea.WithAltScreen()) 83 | 84 | if _, err := p.Run(); err != nil { 85 | log.Fatal(err) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /filesystem/filesystem.go: -------------------------------------------------------------------------------- 1 | // Package filesystem is a collection of various different filesystem 2 | // helper functions. 3 | package filesystem 4 | 5 | import ( 6 | "archive/zip" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "io/fs" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | // Directory shortcuts. 18 | const ( 19 | CurrentDirectory = "." 20 | PreviousDirectory = ".." 21 | HomeDirectory = "~" 22 | RootDirectory = "/" 23 | ) 24 | 25 | // Different types of listings. 26 | const ( 27 | DirectoriesListingType = "directories" 28 | FilesListingType = "files" 29 | ) 30 | 31 | // RenameDirectoryItem renames a directory or files given a source and destination. 32 | func RenameDirectoryItem(src, dst string) error { 33 | err := os.Rename(src, dst) 34 | 35 | return errors.Unwrap(err) 36 | } 37 | 38 | // CreateDirectory creates a new directory given a name. 39 | func CreateDirectory(name string) error { 40 | if _, err := os.Stat(name); errors.Is(err, os.ErrNotExist) { 41 | err := os.Mkdir(name, os.ModePerm) 42 | if err != nil { 43 | return errors.Unwrap(err) 44 | } 45 | } 46 | 47 | return nil 48 | } 49 | 50 | // GetDirectoryListing returns a list of files and directories within a given directory. 51 | func GetDirectoryListing(dir string, showHidden bool) ([]fs.DirEntry, error) { 52 | index := 0 53 | 54 | files, err := os.ReadDir(dir) 55 | if err != nil { 56 | return nil, errors.Unwrap(err) 57 | } 58 | 59 | if !showHidden { 60 | for _, file := range files { 61 | // If the file or directory starts with a dot, 62 | // we know its hidden so dont add it to the array 63 | // of files to return. 64 | if !strings.HasPrefix(file.Name(), ".") { 65 | files[index] = file 66 | index++ 67 | } 68 | } 69 | 70 | // Set files to the list that does not include hidden files. 71 | files = files[:index] 72 | } 73 | 74 | return files, nil 75 | } 76 | 77 | // GetDirectoryListingByType returns a directory listing based on type (directories | files). 78 | func GetDirectoryListingByType(dir, listingType string, showHidden bool) ([]fs.DirEntry, error) { 79 | index := 0 80 | 81 | files, err := os.ReadDir(dir) 82 | if err != nil { 83 | return nil, errors.Unwrap(err) 84 | } 85 | 86 | for _, file := range files { 87 | switch { 88 | case file.IsDir() && listingType == DirectoriesListingType && !showHidden: 89 | if !strings.HasPrefix(file.Name(), ".") { 90 | files[index] = file 91 | index++ 92 | } 93 | case file.IsDir() && listingType == DirectoriesListingType && showHidden: 94 | files[index] = file 95 | index++ 96 | case !file.IsDir() && listingType == FilesListingType && !showHidden: 97 | if !strings.HasPrefix(file.Name(), ".") { 98 | files[index] = file 99 | index++ 100 | } 101 | case !file.IsDir() && listingType == FilesListingType && showHidden: 102 | files[index] = file 103 | index++ 104 | } 105 | } 106 | 107 | return files[:index], nil 108 | } 109 | 110 | // DeleteDirectory deletes a directory given a name. 111 | func DeleteDirectory(name string) error { 112 | err := os.RemoveAll(name) 113 | 114 | return errors.Unwrap(err) 115 | } 116 | 117 | // GetHomeDirectory returns the users home directory. 118 | func GetHomeDirectory() (string, error) { 119 | home, err := os.UserHomeDir() 120 | if err != nil { 121 | return "", errors.Unwrap(err) 122 | } 123 | 124 | return home, nil 125 | } 126 | 127 | // GetWorkingDirectory returns the current working directory. 128 | func GetWorkingDirectory() (string, error) { 129 | workingDir, err := os.Getwd() 130 | if err != nil { 131 | return "", errors.Unwrap(err) 132 | } 133 | 134 | return workingDir, nil 135 | } 136 | 137 | // DeleteFile deletes a file given a name. 138 | func DeleteFile(name string) error { 139 | err := os.Remove(name) 140 | 141 | return errors.Unwrap(err) 142 | } 143 | 144 | // MoveDirectoryItem moves a file from one place to another. 145 | func MoveDirectoryItem(src, dst string) error { 146 | err := os.Rename(src, dst) 147 | 148 | return errors.Unwrap(err) 149 | } 150 | 151 | // ReadFileContent returns the contents of a file given a name. 152 | func ReadFileContent(name string) (string, error) { 153 | fileContent, err := os.ReadFile(filepath.Clean(name)) 154 | if err != nil { 155 | return "", errors.Unwrap(err) 156 | } 157 | 158 | return string(fileContent), nil 159 | } 160 | 161 | // CreateFile creates a file given a name. 162 | func CreateFile(name string) error { 163 | f, err := os.Create(filepath.Clean(name)) 164 | if err != nil { 165 | return errors.Unwrap(err) 166 | } 167 | 168 | if err = f.Close(); err != nil { 169 | return errors.Unwrap(err) 170 | } 171 | 172 | return errors.Unwrap(err) 173 | } 174 | 175 | // Zip zips a directory given a name. 176 | func Zip(name string) error { 177 | var splitName []string 178 | var output string 179 | 180 | srcFile, err := os.Open(filepath.Clean(name)) 181 | if err != nil { 182 | return errors.Unwrap(err) 183 | } 184 | 185 | defer func() { 186 | err = srcFile.Close() 187 | }() 188 | 189 | fileExtension := filepath.Ext(name) 190 | splitFileName := strings.Split(name, "/") 191 | fileName := splitFileName[len(splitFileName)-1] 192 | switch { 193 | case strings.HasPrefix(fileName, ".") && fileExtension != "" && fileExtension == fileName: 194 | output = fmt.Sprintf("%s_%d.zip", fileName, time.Now().Unix()) 195 | case strings.HasPrefix(fileName, ".") && fileExtension != "" && fileExtension != fileName: 196 | splitName = strings.Split(fileName, ".") 197 | output = fmt.Sprintf(".%s_%d.zip", splitName[1], time.Now().Unix()) 198 | case fileExtension != "": 199 | splitName = strings.Split(fileName, ".") 200 | output = fmt.Sprintf("%s_%d.zip", splitName[0], time.Now().Unix()) 201 | default: 202 | output = fmt.Sprintf("%s_%d.zip", fileName, time.Now().Unix()) 203 | } 204 | 205 | newfile, err := os.Create(filepath.Clean(output)) 206 | if err != nil { 207 | return errors.Unwrap(err) 208 | } 209 | 210 | defer func() { 211 | err = newfile.Close() 212 | }() 213 | 214 | zipWriter := zip.NewWriter(newfile) 215 | defer func() { 216 | err = zipWriter.Close() 217 | }() 218 | 219 | info, err := os.Stat(name) 220 | if err != nil { 221 | return errors.Unwrap(err) 222 | } 223 | 224 | if info.IsDir() { 225 | err = filepath.Walk(name, func(filePath string, info os.FileInfo, err error) error { 226 | if info.IsDir() { 227 | return nil 228 | } 229 | 230 | if err != nil { 231 | return errors.Unwrap(err) 232 | } 233 | 234 | relPath := strings.TrimPrefix(filePath, name) 235 | zipFile, err := zipWriter.Create(relPath) 236 | if err != nil { 237 | return errors.Unwrap(err) 238 | } 239 | 240 | fsFile, err := os.Open(filepath.Clean(filePath)) 241 | if err != nil { 242 | return errors.Unwrap(err) 243 | } 244 | 245 | _, err = io.Copy(zipFile, fsFile) 246 | if err != nil { 247 | return errors.Unwrap(err) 248 | } 249 | 250 | return nil 251 | }) 252 | } else { 253 | var files []string 254 | 255 | // Walk the directory to get a list of files within it and append it to 256 | // the array of files. 257 | err := filepath.Walk(name, func(path string, f fs.FileInfo, err error) error { 258 | if f.Name() != "." && !f.IsDir() { 259 | files = append(files, path) 260 | } 261 | 262 | return nil 263 | }) 264 | 265 | if err != nil { 266 | return errors.Unwrap(err) 267 | } 268 | 269 | for _, file := range files { 270 | zipfile, err := os.Open(filepath.Clean(file)) 271 | if err != nil { 272 | return errors.Unwrap(err) 273 | } 274 | 275 | defer func() { 276 | err = zipfile.Close() 277 | }() 278 | 279 | info, err := zipfile.Stat() 280 | if err != nil { 281 | return errors.Unwrap(err) 282 | } 283 | 284 | header, err := zip.FileInfoHeader(info) 285 | if err != nil { 286 | return errors.Unwrap(err) 287 | } 288 | 289 | header.Method = zip.Deflate 290 | writer, err := zipWriter.CreateHeader(header) 291 | if err != nil { 292 | return errors.Unwrap(err) 293 | } 294 | 295 | _, err = io.Copy(writer, zipfile) 296 | if err != nil { 297 | return errors.Unwrap(err) 298 | } 299 | } 300 | } 301 | 302 | err = zipWriter.Close() 303 | if err != nil { 304 | return errors.Unwrap(err) 305 | } 306 | 307 | return errors.Unwrap(err) 308 | } 309 | 310 | // Unzip unzips a directory given a name. 311 | func Unzip(name string) error { 312 | var output string 313 | 314 | reader, err := zip.OpenReader(name) 315 | if err != nil { 316 | return errors.Unwrap(err) 317 | } 318 | 319 | defer func() { 320 | err = reader.Close() 321 | }() 322 | 323 | if strings.HasPrefix(name, ".") { 324 | output = strings.Split(name, ".")[1] 325 | } else { 326 | output = strings.Split(name, ".")[0] 327 | } 328 | 329 | for _, file := range reader.File { 330 | archiveFile := file.Name 331 | fpath := filepath.Join(output, archiveFile) 332 | 333 | if !strings.HasPrefix(fpath, filepath.Clean(output)+string(os.PathSeparator)) { 334 | return errors.Unwrap(err) 335 | } 336 | 337 | if file.FileInfo().IsDir() { 338 | err = os.MkdirAll(fpath, os.ModePerm) 339 | if err != nil { 340 | return errors.Unwrap(err) 341 | } 342 | 343 | continue 344 | } 345 | 346 | if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { 347 | return errors.Unwrap(err) 348 | } 349 | 350 | outFile, err := os.OpenFile(filepath.Clean(fpath), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) 351 | if err != nil { 352 | return errors.Unwrap(err) 353 | } 354 | 355 | outputFile, err := file.Open() 356 | if err != nil { 357 | return errors.Unwrap(err) 358 | } 359 | 360 | _, err = io.Copy(outFile, outputFile) 361 | if err != nil { 362 | return errors.Unwrap(err) 363 | } 364 | 365 | err = outFile.Close() 366 | if err != nil { 367 | return errors.Unwrap(err) 368 | } 369 | 370 | err = outputFile.Close() 371 | if err != nil { 372 | return errors.Unwrap(err) 373 | } 374 | } 375 | 376 | return errors.Unwrap(err) 377 | } 378 | 379 | // CopyFile copies a file given a name. 380 | func CopyFile(name string) error { 381 | var splitName []string 382 | var output string 383 | 384 | srcFile, err := os.Open(filepath.Clean(name)) 385 | if err != nil { 386 | return errors.Unwrap(err) 387 | } 388 | 389 | defer func() { 390 | err = srcFile.Close() 391 | }() 392 | 393 | fileExtension := filepath.Ext(name) 394 | splitFileName := strings.Split(name, "/") 395 | fileName := splitFileName[len(splitFileName)-1] 396 | switch { 397 | case strings.HasPrefix(fileName, ".") && fileExtension != "" && fileExtension == fileName: 398 | output = fmt.Sprintf("%s_%d", fileName, time.Now().Unix()) 399 | case strings.HasPrefix(fileName, ".") && fileExtension != "" && fileExtension != fileName: 400 | splitName = strings.Split(fileName, ".") 401 | output = fmt.Sprintf(".%s_%d.%s", splitName[1], time.Now().Unix(), splitName[2]) 402 | case fileExtension != "": 403 | splitName = strings.Split(fileName, ".") 404 | output = fmt.Sprintf("%s_%d.%s", splitName[0], time.Now().Unix(), splitName[1]) 405 | default: 406 | output = fmt.Sprintf("%s_%d", fileName, time.Now().Unix()) 407 | } 408 | 409 | destFile, err := os.Create(filepath.Clean(output)) 410 | if err != nil { 411 | return errors.Unwrap(err) 412 | } 413 | 414 | defer func() { 415 | err = destFile.Close() 416 | }() 417 | 418 | _, err = io.Copy(destFile, srcFile) 419 | if err != nil { 420 | return errors.Unwrap(err) 421 | } 422 | 423 | err = destFile.Sync() 424 | if err != nil { 425 | return errors.Unwrap(err) 426 | } 427 | 428 | return errors.Unwrap(err) 429 | } 430 | 431 | // CopyDirectory copies a directory given a name. 432 | func CopyDirectory(name string) error { 433 | output := fmt.Sprintf("%s_%d", name, time.Now().Unix()) 434 | 435 | err := filepath.Walk(name, func(path string, info os.FileInfo, err error) error { 436 | relPath := strings.Replace(path, name, "", 1) 437 | 438 | if info.IsDir() { 439 | return fmt.Errorf("%w", os.Mkdir(filepath.Join(output, relPath), os.ModePerm)) 440 | } 441 | 442 | var data, err1 = os.ReadFile(filepath.Join(filepath.Clean(name), filepath.Clean(relPath))) 443 | if err1 != nil { 444 | return errors.Unwrap(err) 445 | } 446 | 447 | return fmt.Errorf("%w", os.WriteFile(filepath.Join(output, relPath), data, os.ModePerm)) 448 | }) 449 | 450 | return errors.Unwrap(err) 451 | } 452 | 453 | // GetDirectoryItemSize calculates the size of a directory or file. 454 | func GetDirectoryItemSize(path string) (int64, error) { 455 | curFile, err := os.Stat(path) 456 | if err != nil { 457 | return 0, errors.Unwrap(err) 458 | } 459 | 460 | if !curFile.IsDir() { 461 | return curFile.Size(), nil 462 | } 463 | 464 | var size int64 465 | err = filepath.WalkDir(path, func(path string, entry os.DirEntry, err error) error { 466 | if err != nil { 467 | return errors.Unwrap(err) 468 | } 469 | 470 | fileInfo, err := entry.Info() 471 | if err != nil { 472 | return errors.Unwrap(err) 473 | } 474 | 475 | if !entry.IsDir() { 476 | size += fileInfo.Size() 477 | } 478 | 479 | return errors.Unwrap(err) 480 | }) 481 | 482 | return size, errors.Unwrap(err) 483 | } 484 | 485 | // FindFilesByName returns files found based on a name. 486 | func FindFilesByName(name, dir string) ([]string, []fs.DirEntry, error) { 487 | var paths []string 488 | var entries []fs.DirEntry 489 | 490 | err := filepath.WalkDir(dir, func(path string, entry os.DirEntry, err error) error { 491 | if err != nil { 492 | return filepath.SkipDir 493 | } 494 | 495 | if strings.Contains(entry.Name(), name) { 496 | paths = append(paths, path) 497 | entries = append(entries, entry) 498 | } 499 | 500 | return errors.Unwrap(err) 501 | }) 502 | 503 | return paths, entries, errors.Unwrap(err) 504 | } 505 | 506 | // WriteToFile writes content to a file, overwriting content if it exists. 507 | func WriteToFile(path, content string) error { 508 | file, err := os.OpenFile(filepath.Clean(path), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) 509 | if err != nil { 510 | return errors.Unwrap(err) 511 | } 512 | 513 | workingDir, err := os.Getwd() 514 | if err != nil { 515 | return errors.Unwrap(err) 516 | } 517 | 518 | _, err = file.WriteString(fmt.Sprintf("%s\n", filepath.Join(workingDir, content))) 519 | if err != nil { 520 | err = file.Close() 521 | if err != nil { 522 | return errors.Unwrap(err) 523 | } 524 | 525 | return errors.Unwrap(err) 526 | } 527 | 528 | err = file.Close() 529 | if err != nil { 530 | return errors.Unwrap(err) 531 | } 532 | 533 | return errors.Unwrap(err) 534 | } 535 | -------------------------------------------------------------------------------- /filetree/commands.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | 8 | tea "github.com/charmbracelet/bubbletea" 9 | "github.com/mistakenelf/teacup/filesystem" 10 | ) 11 | 12 | type getDirectoryListingMsg []DirectoryItem 13 | type errorMsg error 14 | 15 | // getDirectoryListingCmd updates the directory listing based on the name of the directory provided. 16 | func getDirectoryListingCmd(directoryName string, showHidden bool) tea.Cmd { 17 | return func() tea.Msg { 18 | var err error 19 | var directoryItems []DirectoryItem 20 | 21 | if directoryName == filesystem.HomeDirectory { 22 | directoryName, err = filesystem.GetHomeDirectory() 23 | if err != nil { 24 | return errorMsg(err) 25 | } 26 | } 27 | 28 | directoryInfo, err := os.Stat(directoryName) 29 | if err != nil { 30 | return errorMsg(err) 31 | } 32 | 33 | if !directoryInfo.IsDir() { 34 | return nil 35 | } 36 | 37 | files, err := filesystem.GetDirectoryListing(directoryName, showHidden) 38 | if err != nil { 39 | return errorMsg(err) 40 | } 41 | 42 | err = os.Chdir(directoryName) 43 | if err != nil { 44 | return errorMsg(err) 45 | } 46 | 47 | workingDirectory, err := filesystem.GetWorkingDirectory() 48 | if err != nil { 49 | return errorMsg(err) 50 | } 51 | 52 | for _, file := range files { 53 | fileInfo, err := file.Info() 54 | if err != nil { 55 | continue 56 | } 57 | 58 | status := fmt.Sprintf("%s %s %s", 59 | fileInfo.ModTime().Format("2006-01-02 15:04:05"), 60 | fileInfo.Mode().String(), 61 | ConvertBytesToSizeString(fileInfo.Size())) 62 | 63 | directoryItems = append(directoryItems, DirectoryItem{ 64 | name: file.Name(), 65 | details: status, 66 | path: filepath.Join(workingDirectory, file.Name()), 67 | extension: filepath.Ext(fileInfo.Name()), 68 | isDirectory: fileInfo.IsDir(), 69 | currentDirectory: workingDirectory, 70 | }) 71 | } 72 | 73 | return getDirectoryListingMsg(directoryItems) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /filetree/init.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | import ( 4 | tea "github.com/charmbracelet/bubbletea" 5 | "github.com/mistakenelf/teacup/filesystem" 6 | ) 7 | 8 | func (m Model) Init() tea.Cmd { 9 | return getDirectoryListingCmd(filesystem.CurrentDirectory, true) 10 | } 11 | -------------------------------------------------------------------------------- /filetree/keys.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | import "github.com/charmbracelet/bubbles/key" 4 | 5 | type KeyMap struct { 6 | Down key.Binding 7 | Up key.Binding 8 | } 9 | 10 | func DefaultKeyMap() KeyMap { 11 | return KeyMap{ 12 | Down: key.NewBinding(key.WithKeys("j", "down", "ctrl+n"), key.WithHelp("j", "down")), 13 | Up: key.NewBinding(key.WithKeys("k", "up", "ctrl+p"), key.WithHelp("k", "up")), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /filetree/methods.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | import "fmt" 4 | 5 | const ( 6 | thousand = 1000 7 | ten = 10 8 | fivePercent = 0.0499 9 | ) 10 | 11 | // ConvertBytesToSizeString converts a byte count to a human readable string. 12 | func ConvertBytesToSizeString(size int64) string { 13 | if size < thousand { 14 | return fmt.Sprintf("%dB", size) 15 | } 16 | 17 | suffix := []string{ 18 | "K", // kilo 19 | "M", // mega 20 | "G", // giga 21 | "T", // tera 22 | "P", // peta 23 | "E", // exa 24 | "Z", // zeta 25 | "Y", // yotta 26 | } 27 | 28 | curr := float64(size) / thousand 29 | for _, s := range suffix { 30 | if curr < ten { 31 | return fmt.Sprintf("%.1f%s", curr-fivePercent, s) 32 | } else if curr < thousand { 33 | return fmt.Sprintf("%d%s", int(curr), s) 34 | } 35 | curr /= thousand 36 | } 37 | 38 | return "" 39 | } 40 | 41 | // SetIsActive sets if the bubble is currently active. 42 | func (m *Model) SetIsActive(active bool) { 43 | m.active = active 44 | } 45 | -------------------------------------------------------------------------------- /filetree/model.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | type DirectoryItem struct { 4 | name string 5 | details string 6 | path string 7 | extension string 8 | isDirectory bool 9 | currentDirectory string 10 | } 11 | 12 | type Model struct { 13 | cursor int 14 | files []DirectoryItem 15 | active bool 16 | keyMap KeyMap 17 | min int 18 | max int 19 | height int 20 | width int 21 | } 22 | 23 | func New() Model { 24 | return Model{ 25 | cursor: 0, 26 | active: true, 27 | keyMap: DefaultKeyMap(), 28 | min: 0, 29 | max: 0, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /filetree/styles.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | import "github.com/charmbracelet/lipgloss" 4 | 5 | var ( 6 | selectedItemStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Bold(true) 7 | ) 8 | -------------------------------------------------------------------------------- /filetree/update.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | import ( 4 | "github.com/charmbracelet/bubbles/key" 5 | tea "github.com/charmbracelet/bubbletea" 6 | ) 7 | 8 | func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { 9 | var ( 10 | cmds []tea.Cmd 11 | ) 12 | 13 | switch msg := msg.(type) { 14 | case tea.WindowSizeMsg: 15 | m.height = msg.Height 16 | m.width = msg.Width 17 | m.max = m.height - 1 18 | case getDirectoryListingMsg: 19 | if msg != nil { 20 | m.files = msg 21 | m.max = max(m.max, m.height-1) 22 | } 23 | case tea.KeyMsg: 24 | switch { 25 | case key.Matches(msg, m.keyMap.Down): 26 | m.cursor++ 27 | if m.cursor >= len(m.files) { 28 | m.cursor = len(m.files) - 1 29 | } 30 | 31 | if m.cursor > m.max { 32 | m.min++ 33 | m.max++ 34 | } 35 | case key.Matches(msg, m.keyMap.Up): 36 | m.cursor-- 37 | if m.cursor < 0 { 38 | m.cursor = 0 39 | } 40 | 41 | if m.cursor < m.min { 42 | m.min-- 43 | m.max-- 44 | } 45 | } 46 | } 47 | 48 | return m, tea.Batch(cmds...) 49 | } 50 | -------------------------------------------------------------------------------- /filetree/view.go: -------------------------------------------------------------------------------- 1 | package filetree 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/charmbracelet/lipgloss" 7 | ) 8 | 9 | func (m Model) View() string { 10 | var fileList strings.Builder 11 | 12 | for i, file := range m.files { 13 | if i < m.min || i > m.max { 14 | continue 15 | } 16 | 17 | if i == m.cursor { 18 | fileList.WriteString(selectedItemStyle.Render(file.name) + "\n") 19 | // fileList.WriteString(selectedItemStyle.Render(file.details) + "\n\n") 20 | } else { 21 | fileList.WriteString(file.name + "\n") 22 | // fileList.WriteString(file.details + "\n\n") 23 | } 24 | } 25 | 26 | for i := lipgloss.Height(fileList.String()); i <= m.height; i++ { 27 | fileList.WriteRune('\n') 28 | } 29 | 30 | return fileList.String() 31 | } 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mistakenelf/teacup 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/alecthomas/chroma v0.10.0 7 | github.com/charmbracelet/bubbles v0.18.0 8 | github.com/charmbracelet/bubbletea v0.25.0 9 | github.com/charmbracelet/glamour v0.6.0 10 | github.com/charmbracelet/lipgloss v0.9.1 11 | github.com/disintegration/imaging v1.6.2 12 | github.com/ledongthuc/pdf v0.0.0-20240201131950-da5b75280b06 13 | github.com/lucasb-eyer/go-colorful v1.2.0 14 | github.com/muesli/reflow v0.3.0 15 | ) 16 | 17 | require ( 18 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 19 | github.com/aymerick/douceur v0.2.0 // indirect 20 | github.com/containerd/console v1.0.4 // indirect 21 | github.com/dlclark/regexp2 v1.11.0 // indirect 22 | github.com/gorilla/css v1.0.1 // indirect 23 | github.com/mattn/go-isatty v0.0.20 // indirect 24 | github.com/mattn/go-localereader v0.0.1 // indirect 25 | github.com/mattn/go-runewidth v0.0.15 // indirect 26 | github.com/microcosm-cc/bluemonday v1.0.26 // indirect 27 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect 28 | github.com/muesli/cancelreader v0.2.2 // indirect 29 | github.com/muesli/termenv v0.15.2 // indirect 30 | github.com/olekukonko/tablewriter v0.0.5 // indirect 31 | github.com/rivo/uniseg v0.4.7 // indirect 32 | github.com/yuin/goldmark v1.7.0 // indirect 33 | github.com/yuin/goldmark-emoji v1.0.2 // indirect 34 | golang.org/x/image v0.15.0 // indirect 35 | golang.org/x/net v0.22.0 // indirect 36 | golang.org/x/sync v0.6.0 // indirect 37 | golang.org/x/sys v0.18.0 // indirect 38 | golang.org/x/term v0.18.0 // indirect 39 | golang.org/x/text v0.14.0 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= 2 | github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= 3 | github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= 4 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 5 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 6 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 7 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 8 | github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= 9 | github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= 10 | github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= 11 | github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= 12 | github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= 13 | github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= 14 | github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= 15 | github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= 16 | github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= 17 | github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= 18 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 20 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 22 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 23 | github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 24 | github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= 25 | github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 26 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 27 | github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= 28 | github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= 29 | github.com/ledongthuc/pdf v0.0.0-20240201131950-da5b75280b06 h1:kacRlPN7EN++tVpGUorNGPn/4DnB7/DfTY82AOn6ccU= 30 | github.com/ledongthuc/pdf v0.0.0-20240201131950-da5b75280b06/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= 31 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 32 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 33 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 34 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 35 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 36 | github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= 37 | github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= 38 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 39 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 40 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 41 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 42 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 43 | github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= 44 | github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= 45 | github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= 46 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= 47 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= 48 | github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= 49 | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= 50 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 51 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 52 | github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= 53 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= 54 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= 55 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 56 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 57 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 58 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 59 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 60 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 61 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 62 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 63 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 64 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 65 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 66 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 67 | github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 68 | github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 69 | github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= 70 | github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 71 | github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= 72 | github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= 73 | github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= 74 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 75 | golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= 76 | golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= 77 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 78 | golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= 79 | golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 80 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 81 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 82 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 83 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 84 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 85 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 86 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 87 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 88 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 89 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 90 | golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= 91 | golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= 92 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 93 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 94 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 95 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 96 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 97 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 98 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 99 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 100 | -------------------------------------------------------------------------------- /help/help.go: -------------------------------------------------------------------------------- 1 | // Package help implements a help bubble which can be used 2 | // to display help information such as keymaps. 3 | package help 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/charmbracelet/bubbles/viewport" 9 | tea "github.com/charmbracelet/bubbletea" 10 | "github.com/charmbracelet/lipgloss" 11 | ) 12 | 13 | const ( 14 | keyWidth = 12 15 | ) 16 | 17 | type TitleColor struct { 18 | Background lipgloss.AdaptiveColor 19 | Foreground lipgloss.AdaptiveColor 20 | } 21 | 22 | // Entry represents a single entry in the help bubble. 23 | type Entry struct { 24 | Key string 25 | Description string 26 | } 27 | 28 | // Model represents the properties of a help bubble. 29 | type Model struct { 30 | Viewport viewport.Model 31 | Entries []Entry 32 | Title string 33 | TitleColor TitleColor 34 | Active bool 35 | } 36 | 37 | // generateHelpScreen generates the help text based on the title and entries. 38 | func generateHelpScreen(title string, titleColor TitleColor, entries []Entry, width, height int) string { 39 | helpScreen := "" 40 | 41 | for _, content := range entries { 42 | keyText := lipgloss.NewStyle(). 43 | Bold(true). 44 | Foreground(lipgloss.AdaptiveColor{Dark: "#ffffff", Light: "#000000"}). 45 | Width(keyWidth). 46 | Render(content.Key) 47 | 48 | descriptionText := lipgloss.NewStyle(). 49 | Foreground(lipgloss.AdaptiveColor{Dark: "#ffffff", Light: "#000000"}). 50 | Render(content.Description) 51 | 52 | row := lipgloss.JoinHorizontal(lipgloss.Top, keyText, descriptionText) 53 | helpScreen += fmt.Sprintf("%s\n", row) 54 | } 55 | 56 | titleText := lipgloss.NewStyle().Bold(true). 57 | Background(titleColor.Background). 58 | Foreground(titleColor.Foreground). 59 | Padding(0, 1). 60 | Italic(true). 61 | Render(title) 62 | 63 | return lipgloss.NewStyle(). 64 | Width(width). 65 | Height(height). 66 | Render(lipgloss.JoinVertical( 67 | lipgloss.Top, 68 | titleText, 69 | helpScreen, 70 | )) 71 | } 72 | 73 | // New creates a new instance of a help bubble. 74 | func New( 75 | active bool, 76 | title string, 77 | titleColor TitleColor, 78 | entries []Entry, 79 | ) Model { 80 | viewPort := viewport.New(0, 0) 81 | viewPort.SetContent(generateHelpScreen(title, titleColor, entries, 0, 0)) 82 | 83 | return Model{ 84 | Viewport: viewPort, 85 | Entries: entries, 86 | Title: title, 87 | Active: active, 88 | TitleColor: titleColor, 89 | } 90 | } 91 | 92 | // SetSize sets the size of the help bubble. 93 | func (m *Model) SetSize(w, h int) { 94 | m.Viewport.Width = w 95 | m.Viewport.Height = h 96 | 97 | m.Viewport.SetContent(generateHelpScreen(m.Title, m.TitleColor, m.Entries, m.Viewport.Width, m.Viewport.Height)) 98 | } 99 | 100 | // SetIsActive sets if the bubble is currently active. 101 | func (m *Model) SetIsActive(active bool) { 102 | m.Active = active 103 | } 104 | 105 | // GotoTop jumps to the top of the viewport. 106 | func (m *Model) GotoTop() { 107 | m.Viewport.GotoTop() 108 | } 109 | 110 | // SetTitleColor sets the color of the title. 111 | func (m *Model) SetTitleColor(color TitleColor) { 112 | m.TitleColor = color 113 | 114 | m.Viewport.SetContent(generateHelpScreen(m.Title, m.TitleColor, m.Entries, m.Viewport.Width, m.Viewport.Height)) 115 | } 116 | 117 | // Update handles UI interactions with the help bubble. 118 | func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { 119 | var ( 120 | cmd tea.Cmd 121 | cmds []tea.Cmd 122 | ) 123 | 124 | if m.Active { 125 | m.Viewport, cmd = m.Viewport.Update(msg) 126 | cmds = append(cmds, cmd) 127 | } 128 | 129 | return m, tea.Batch(cmds...) 130 | } 131 | 132 | // View returns a string representation of the help bubble. 133 | func (m Model) View() string { 134 | return m.Viewport.View() 135 | } 136 | -------------------------------------------------------------------------------- /icons/directories.go: -------------------------------------------------------------------------------- 1 | package icons 2 | 3 | // IconDir is used to get the icon based on the type of directory. 4 | var IconDir = map[string]*IconInfo{ 5 | "config": IconSet["dir-config"], 6 | ".config": IconSet["dir-config"], 7 | "configs": IconSet["dir-config"], 8 | "configuration": IconSet["dir-config"], 9 | "configurations": IconSet["dir-config"], 10 | "settings": IconSet["dir-config"], 11 | ".settings": IconSet["dir-config"], 12 | "META-INF": IconSet["dir-config"], 13 | "controller": IconSet["dir-controller"], 14 | "controllers": IconSet["dir-controller"], 15 | "service": IconSet["dir-controller"], 16 | "services": IconSet["dir-controller"], 17 | "provider": IconSet["dir-controller"], 18 | "providers": IconSet["dir-controller"], 19 | ".git": IconSet["dir-git"], 20 | "githooks": IconSet["dir-git"], 21 | ".githooks": IconSet["dir-git"], 22 | "submodules": IconSet["dir-git"], 23 | ".submodules": IconSet["dir-git"], 24 | ".github": IconSet["dir-github"], 25 | "node_modules": IconSet["dir-npm"], 26 | "include": IconSet["dir-include"], 27 | "includes": IconSet["dir-include"], 28 | "_includes": IconSet["dir-include"], 29 | "import": IconSet["dir-import"], 30 | "imports": IconSet["dir-import"], 31 | "imported": IconSet["dir-import"], 32 | "uploads": IconSet["dir-upload"], 33 | "upload": IconSet["dir-upload"], 34 | "downloads": IconSet["dir-download"], 35 | "download": IconSet["dir-download"], 36 | "auth": IconSet["dir-secure"], 37 | "authentication": IconSet["dir-secure"], 38 | "secure": IconSet["dir-secure"], 39 | "security": IconSet["dir-secure"], 40 | "cert": IconSet["dir-secure"], 41 | "certs": IconSet["dir-secure"], 42 | "certificate": IconSet["dir-secure"], 43 | "certificates": IconSet["dir-secure"], 44 | "ssl": IconSet["dir-secure"], 45 | "images": IconSet["dir-images"], 46 | "image": IconSet["dir-images"], 47 | "pics": IconSet["dir-images"], 48 | "pic": IconSet["dir-images"], 49 | "pictures": IconSet["dir-images"], 50 | "picture": IconSet["dir-images"], 51 | "img": IconSet["dir-images"], 52 | "icons": IconSet["dir-images"], 53 | "icon": IconSet["dir-images"], 54 | "ico": IconSet["dir-images"], 55 | "screenshot": IconSet["dir-images"], 56 | "screenshots": IconSet["dir-images"], 57 | ".env": IconSet["dir-environment"], 58 | ".environment": IconSet["dir-environment"], 59 | "env": IconSet["dir-environment"], 60 | "environment": IconSet["dir-environment"], 61 | "environments": IconSet["dir-environment"], 62 | } 63 | -------------------------------------------------------------------------------- /icons/extensions.go: -------------------------------------------------------------------------------- 1 | package icons 2 | 3 | // IconExt is used to represent an icon with a specific extension. 4 | var IconExt = map[string]*IconInfo{ 5 | "htm": IconSet["html"], 6 | "html": IconSet["html"], 7 | "xhtml": IconSet["html"], 8 | "html_vm": IconSet["html"], 9 | "asp": IconSet["html"], 10 | "jade": IconSet["pug"], 11 | "pug": IconSet["pug"], 12 | "md": IconSet["markdown"], 13 | "markdown": IconSet["markdown"], 14 | "rst": IconSet["markdown"], 15 | "blink": IconSet["blink"], 16 | "css": IconSet["css"], 17 | "scss": IconSet["sass"], 18 | "sass": IconSet["sass"], 19 | "less": IconSet["less"], 20 | "json": IconSet["json"], 21 | "tsbuildinfo": IconSet["json"], 22 | "json5": IconSet["json"], 23 | "jsonl": IconSet["json"], 24 | "ndjson": IconSet["json"], 25 | "jinja": IconSet["jinja"], 26 | "jinja2": IconSet["jinja"], 27 | "j2": IconSet["jinja"], 28 | "jinja-html": IconSet["jinja"], 29 | "sublime-project": IconSet["sublime"], 30 | "sublime-workspace": IconSet["sublime"], 31 | "yaml": IconSet["yaml"], 32 | "yaml-tmlanguage": IconSet["yaml"], 33 | "yml": IconSet["yaml"], 34 | "xml": IconSet["xml"], 35 | "plist": IconSet["xml"], 36 | "xsd": IconSet["xml"], 37 | "dtd": IconSet["xml"], 38 | "xsl": IconSet["xml"], 39 | "xslt": IconSet["xml"], 40 | "resx": IconSet["xml"], 41 | "iml": IconSet["xml"], 42 | "xquery": IconSet["xml"], 43 | "tmLanguage": IconSet["xml"], 44 | "manifest": IconSet["xml"], 45 | "project": IconSet["xml"], 46 | "png": IconSet["image"], 47 | "jpeg": IconSet["image"], 48 | "jpg": IconSet["image"], 49 | "gif": IconSet["image"], 50 | "ico": IconSet["image"], 51 | "tif": IconSet["image"], 52 | "tiff": IconSet["image"], 53 | "psd": IconSet["image"], 54 | "psb": IconSet["image"], 55 | "ami": IconSet["image"], 56 | "apx": IconSet["image"], 57 | "bmp": IconSet["image"], 58 | "bpg": IconSet["image"], 59 | "brk": IconSet["image"], 60 | "cur": IconSet["image"], 61 | "dds": IconSet["image"], 62 | "dng": IconSet["image"], 63 | "exr": IconSet["image"], 64 | "fpx": IconSet["image"], 65 | "gbr": IconSet["image"], 66 | "img": IconSet["image"], 67 | "jbig2": IconSet["image"], 68 | "jb2": IconSet["image"], 69 | "jng": IconSet["image"], 70 | "jxr": IconSet["image"], 71 | "pbm": IconSet["image"], 72 | "pgf": IconSet["image"], 73 | "pic": IconSet["image"], 74 | "raw": IconSet["image"], 75 | "webp": IconSet["image"], 76 | "eps": IconSet["image"], 77 | "afphoto": IconSet["image"], 78 | "ase": IconSet["image"], 79 | "aseprite": IconSet["image"], 80 | "clip": IconSet["image"], 81 | "cpt": IconSet["image"], 82 | "heif": IconSet["image"], 83 | "heic": IconSet["image"], 84 | "kra": IconSet["image"], 85 | "mdp": IconSet["image"], 86 | "ora": IconSet["image"], 87 | "pdn": IconSet["image"], 88 | "reb": IconSet["image"], 89 | "sai": IconSet["image"], 90 | "tga": IconSet["image"], 91 | "xcf": IconSet["image"], 92 | "js": IconSet["javascript"], 93 | "esx": IconSet["javascript"], 94 | "mj": IconSet["javascript"], 95 | "jsx": IconSet["react"], 96 | "tsx": IconSet["react_ts"], 97 | "ini": IconSet["settings"], 98 | "dlc": IconSet["settings"], 99 | "dll": IconSet["settings"], 100 | "config": IconSet["settings"], 101 | "conf": IconSet["settings"], 102 | "properties": IconSet["settings"], 103 | "prop": IconSet["settings"], 104 | "settings": IconSet["settings"], 105 | "option": IconSet["settings"], 106 | "props": IconSet["settings"], 107 | "toml": IconSet["settings"], 108 | "prefs": IconSet["settings"], 109 | "dotsettings": IconSet["settings"], 110 | "cfg": IconSet["settings"], 111 | "ts": IconSet["typescript"], 112 | "marko": IconSet["markojs"], 113 | "pdf": IconSet["pdf"], 114 | "xlsx": IconSet["table"], 115 | "xls": IconSet["table"], 116 | "csv": IconSet["table"], 117 | "tsv": IconSet["table"], 118 | "vscodeignore": IconSet["vscode"], 119 | "vsixmanifest": IconSet["vscode"], 120 | "vsix": IconSet["vscode"], 121 | "code-workplace": IconSet["vscode"], 122 | "csproj": IconSet["visualstudio"], 123 | "ruleset": IconSet["visualstudio"], 124 | "sln": IconSet["visualstudio"], 125 | "suo": IconSet["visualstudio"], 126 | "vb": IconSet["visualstudio"], 127 | "vbs": IconSet["visualstudio"], 128 | "vcxitems": IconSet["visualstudio"], 129 | "vcxproj": IconSet["visualstudio"], 130 | "pdb": IconSet["database"], 131 | "sql": IconSet["mysql"], 132 | "pks": IconSet["database"], 133 | "pkb": IconSet["database"], 134 | "accdb": IconSet["database"], 135 | "mdb": IconSet["database"], 136 | "sqlite": IconSet["sqlite"], 137 | "sqlite3": IconSet["sqlite"], 138 | "pgsql": IconSet["postgresql"], 139 | "postgres": IconSet["postgresql"], 140 | "psql": IconSet["postgresql"], 141 | "cs": IconSet["csharp"], 142 | "csx": IconSet["csharp"], 143 | "qs": IconSet["qsharp"], 144 | "zip": IconSet["zip"], 145 | "tar": IconSet["zip"], 146 | "gz": IconSet["zip"], 147 | "xz": IconSet["zip"], 148 | "br": IconSet["zip"], 149 | "bzip2": IconSet["zip"], 150 | "gzip": IconSet["zip"], 151 | "brotli": IconSet["zip"], 152 | "7z": IconSet["zip"], 153 | "rar": IconSet["zip"], 154 | "tgz": IconSet["zip"], 155 | "vala": IconSet["vala"], 156 | "zig": IconSet["zig"], 157 | "exe": IconSet["exe"], 158 | "msi": IconSet["exe"], 159 | "java": IconSet["java"], 160 | "jar": IconSet["java"], 161 | "jsp": IconSet["java"], 162 | "c": IconSet["c"], 163 | "m": IconSet["c"], 164 | "i": IconSet["c"], 165 | "mi": IconSet["c"], 166 | "h": IconSet["h"], 167 | "cc": IconSet["cpp"], 168 | "cpp": IconSet["cpp"], 169 | "cxx": IconSet["cpp"], 170 | "c++": IconSet["cpp"], 171 | "cp": IconSet["cpp"], 172 | "mm": IconSet["cpp"], 173 | "mii": IconSet["cpp"], 174 | "ii": IconSet["cpp"], 175 | "hh": IconSet["hpp"], 176 | "hpp": IconSet["hpp"], 177 | "hxx": IconSet["hpp"], 178 | "h++": IconSet["hpp"], 179 | "hp": IconSet["hpp"], 180 | "tcc": IconSet["hpp"], 181 | "inl": IconSet["hpp"], 182 | "go": IconSet["go"], 183 | "py": IconSet["python"], 184 | "pyc": IconSet["python-misc"], 185 | "whl": IconSet["python-misc"], 186 | "url": IconSet["url"], 187 | "sh": IconSet["console"], 188 | "ksh": IconSet["console"], 189 | "csh": IconSet["console"], 190 | "tcsh": IconSet["console"], 191 | "zsh": IconSet["console"], 192 | "bash": IconSet["console"], 193 | "bat": IconSet["console"], 194 | "cmd": IconSet["console"], 195 | "awk": IconSet["console"], 196 | "fish": IconSet["console"], 197 | "ps1": IconSet["powershell"], 198 | "psm1": IconSet["powershell"], 199 | "psd1": IconSet["powershell"], 200 | "ps1xml": IconSet["powershell"], 201 | "psc1": IconSet["powershell"], 202 | "pssc": IconSet["powershell"], 203 | "gradle": IconSet["gradle"], 204 | "doc": IconSet["word"], 205 | "docx": IconSet["word"], 206 | "rtf": IconSet["word"], 207 | "cer": IconSet["certificate"], 208 | "cert": IconSet["certificate"], 209 | "crt": IconSet["certificate"], 210 | "pub": IconSet["key"], 211 | "key": IconSet["key"], 212 | "pem": IconSet["key"], 213 | "asc": IconSet["key"], 214 | "gpg": IconSet["key"], 215 | "woff": IconSet["font"], 216 | "woff2": IconSet["font"], 217 | "ttf": IconSet["font"], 218 | "eot": IconSet["font"], 219 | "suit": IconSet["font"], 220 | "otf": IconSet["font"], 221 | "bmap": IconSet["font"], 222 | "fnt": IconSet["font"], 223 | "odttf": IconSet["font"], 224 | "ttc": IconSet["font"], 225 | "font": IconSet["font"], 226 | "fonts": IconSet["font"], 227 | "sui": IconSet["font"], 228 | "ntf": IconSet["font"], 229 | "mrf": IconSet["font"], 230 | "lib": IconSet["lib"], 231 | "bib": IconSet["lib"], 232 | "rb": IconSet["ruby"], 233 | "erb": IconSet["ruby"], 234 | "fs": IconSet["fsharp"], 235 | "fsx": IconSet["fsharp"], 236 | "fsi": IconSet["fsharp"], 237 | "fsproj": IconSet["fsharp"], 238 | "swift": IconSet["swift"], 239 | "ino": IconSet["arduino"], 240 | "dockerignore": IconSet["docker"], 241 | "dockerfile": IconSet["docker"], 242 | "tex": IconSet["tex"], 243 | "sty": IconSet["tex"], 244 | "dtx": IconSet["tex"], 245 | "ltx": IconSet["tex"], 246 | "pptx": IconSet["powerpoint"], 247 | "ppt": IconSet["powerpoint"], 248 | "pptm": IconSet["powerpoint"], 249 | "potx": IconSet["powerpoint"], 250 | "potm": IconSet["powerpoint"], 251 | "ppsx": IconSet["powerpoint"], 252 | "ppsm": IconSet["powerpoint"], 253 | "pps": IconSet["powerpoint"], 254 | "ppam": IconSet["powerpoint"], 255 | "ppa": IconSet["powerpoint"], 256 | "webm": IconSet["video"], 257 | "mkv": IconSet["video"], 258 | "flv": IconSet["video"], 259 | "vob": IconSet["video"], 260 | "ogv": IconSet["video"], 261 | "ogg": IconSet["video"], 262 | "gifv": IconSet["video"], 263 | "avi": IconSet["video"], 264 | "mov": IconSet["video"], 265 | "qt": IconSet["video"], 266 | "wmv": IconSet["video"], 267 | "yuv": IconSet["video"], 268 | "rm": IconSet["video"], 269 | "rmvb": IconSet["video"], 270 | "mp4": IconSet["video"], 271 | "m4v": IconSet["video"], 272 | "mpg": IconSet["video"], 273 | "mp2": IconSet["video"], 274 | "mpeg": IconSet["video"], 275 | "mpe": IconSet["video"], 276 | "mpv": IconSet["video"], 277 | "m2v": IconSet["video"], 278 | "vdi": IconSet["virtual"], 279 | "vbox": IconSet["virtual"], 280 | "vbox-prev": IconSet["virtual"], 281 | "ics": IconSet["email"], 282 | "mp3": IconSet["audio"], 283 | "flac": IconSet["audio"], 284 | "m4a": IconSet["audio"], 285 | "wma": IconSet["audio"], 286 | "aiff": IconSet["audio"], 287 | "coffee": IconSet["coffee"], 288 | "cson": IconSet["coffee"], 289 | "iced": IconSet["coffee"], 290 | "txt": IconSet["document"], 291 | "graphql": IconSet["graphql"], 292 | "gql": IconSet["graphql"], 293 | "rs": IconSet["rust"], 294 | "raml": IconSet["raml"], 295 | "xaml": IconSet["xaml"], 296 | "hs": IconSet["haskell"], 297 | "kt": IconSet["kotlin"], 298 | "kts": IconSet["kotlin"], 299 | "patch": IconSet["git"], 300 | "lua": IconSet["lua"], 301 | "clj": IconSet["clojure"], 302 | "cljs": IconSet["clojure"], 303 | "cljc": IconSet["clojure"], 304 | "groovy": IconSet["groovy"], 305 | "r": IconSet["r"], 306 | "rmd": IconSet["r"], 307 | "dart": IconSet["dart"], 308 | "as": IconSet["actionscript"], 309 | "mxml": IconSet["mxml"], 310 | "ahk": IconSet["autohotkey"], 311 | "swf": IconSet["flash"], 312 | "swc": IconSet["swc"], 313 | "cmake": IconSet["cmake"], 314 | "asm": IconSet["assembly"], 315 | "a51": IconSet["assembly"], 316 | "inc": IconSet["assembly"], 317 | "nasm": IconSet["assembly"], 318 | "s": IconSet["assembly"], 319 | "ms": IconSet["assembly"], 320 | "agc": IconSet["assembly"], 321 | "ags": IconSet["assembly"], 322 | "aea": IconSet["assembly"], 323 | "argus": IconSet["assembly"], 324 | "mitigus": IconSet["assembly"], 325 | "binsource": IconSet["assembly"], 326 | "vue": IconSet["vue"], 327 | "ml": IconSet["ocaml"], 328 | "mli": IconSet["ocaml"], 329 | "cmx": IconSet["ocaml"], 330 | "lock": IconSet["lock"], 331 | "hbs": IconSet["handlebars"], 332 | "mustache": IconSet["handlebars"], 333 | "pm": IconSet["perl"], 334 | "raku": IconSet["perl"], 335 | "hx": IconSet["haxe"], 336 | "pp": IconSet["puppet"], 337 | "ex": IconSet["elixir"], 338 | "exs": IconSet["elixir"], 339 | "eex": IconSet["elixir"], 340 | "leex": IconSet["elixir"], 341 | "erl": IconSet["erlang"], 342 | "twig": IconSet["twig"], 343 | "jl": IconSet["julia"], 344 | "elm": IconSet["elm"], 345 | "pure": IconSet["purescript"], 346 | "purs": IconSet["purescript"], 347 | "tpl": IconSet["smarty"], 348 | "styl": IconSet["stylus"], 349 | "merlin": IconSet["merlin"], 350 | "v": IconSet["verilog"], 351 | "vhd": IconSet["verilog"], 352 | "sv": IconSet["verilog"], 353 | "svh": IconSet["verilog"], 354 | "robot": IconSet["robot"], 355 | "sol": IconSet["solidity"], 356 | "yang": IconSet["yang"], 357 | "mjml": IconSet["mjml"], 358 | "tf": IconSet["terraform"], 359 | "tfvars": IconSet["terraform"], 360 | "tfstate": IconSet["terraform"], 361 | "applescript": IconSet["applescript"], 362 | "ipa": IconSet["applescript"], 363 | "cake": IconSet["cake"], 364 | "nim": IconSet["nim"], 365 | "nimble": IconSet["nim"], 366 | "apib": IconSet["apiblueprint"], 367 | "apiblueprint": IconSet["apiblueprint"], 368 | "pcss": IconSet["postcss"], 369 | "sss": IconSet["postcss"], 370 | "todo": IconSet["todo"], 371 | "nix": IconSet["nix"], 372 | "slim": IconSet["slim"], 373 | "http": IconSet["http"], 374 | "rest": IconSet["http"], 375 | "apk": IconSet["android"], 376 | "env": IconSet["tune"], 377 | "jenkinsfile": IconSet["jenkins"], 378 | "jenkins": IconSet["jenkins"], 379 | "log": IconSet["log"], 380 | "ejs": IconSet["ejs"], 381 | "djt": IconSet["django"], 382 | "pot": IconSet["i18n"], 383 | "po": IconSet["i18n"], 384 | "mo": IconSet["i18n"], 385 | "d": IconSet["d"], 386 | "mdx": IconSet["mdx"], 387 | "gd": IconSet["godot"], 388 | "godot": IconSet["godot-assets"], 389 | "tres": IconSet["godot-assets"], 390 | "tscn": IconSet["godot-assets"], 391 | "azcli": IconSet["azure"], 392 | "vagrantfile": IconSet["vagrant"], 393 | "cshtml": IconSet["razor"], 394 | "vbhtml": IconSet["razor"], 395 | "ad": IconSet["asciidoc"], 396 | "adoc": IconSet["asciidoc"], 397 | "asciidoc": IconSet["asciidoc"], 398 | "edge": IconSet["edge"], 399 | "ss": IconSet["scheme"], 400 | "scm": IconSet["scheme"], 401 | "stl": IconSet["3d"], 402 | "obj": IconSet["3d"], 403 | "ac": IconSet["3d"], 404 | "blend": IconSet["3d"], 405 | "mesh": IconSet["3d"], 406 | "mqo": IconSet["3d"], 407 | "pmd": IconSet["3d"], 408 | "pmx": IconSet["3d"], 409 | "skp": IconSet["3d"], 410 | "vac": IconSet["3d"], 411 | "vdp": IconSet["3d"], 412 | "vox": IconSet["3d"], 413 | "svg": IconSet["svg"], 414 | "vimrc": IconSet["vim"], 415 | "gvimrc": IconSet["vim"], 416 | "exrc": IconSet["vim"], 417 | "moon": IconSet["moonscript"], 418 | "iso": IconSet["disc"], 419 | "f": IconSet["fortran"], 420 | "f77": IconSet["fortran"], 421 | "f90": IconSet["fortran"], 422 | "f95": IconSet["fortran"], 423 | "f03": IconSet["fortran"], 424 | "f08": IconSet["fortran"], 425 | "tcl": IconSet["tcl"], 426 | "liquid": IconSet["liquid"], 427 | "p": IconSet["prolog"], 428 | "pro": IconSet["prolog"], 429 | "coco": IconSet["coconut"], 430 | "sketch": IconSet["sketch"], 431 | "opam": IconSet["opam"], 432 | "dhallb": IconSet["dhall"], 433 | "pwn": IconSet["pawn"], 434 | "amx": IconSet["pawn"], 435 | "dhall": IconSet["dhall"], 436 | "pas": IconSet["pascal"], 437 | "unity": IconSet["shaderlab"], 438 | "nupkg": IconSet["nuget"], 439 | "command": IconSet["command"], 440 | "dsc": IconSet["denizenscript"], 441 | "deb": IconSet["debian"], 442 | "rpm": IconSet["redhat"], 443 | "snap": IconSet["ubuntu"], 444 | "ebuild": IconSet["gentoo"], 445 | "pkg": IconSet["applescript"], 446 | "openbsd": IconSet["freebsd"], 447 | // "ls": IconSet["livescript"], 448 | // "re": IconSet["reason"], 449 | // "rei": IconSet["reason"], 450 | // "cmj": IconSet["bucklescript"], 451 | // "nb": IconSet["mathematica"], 452 | // "wl": IconSet["wolframlanguage"], 453 | // "wls": IconSet["wolframlanguage"], 454 | // "njk": IconSet["nunjucks"], 455 | // "nunjucks": IconSet["nunjucks"], 456 | // "au3": IconSet["autoit"], 457 | // "haml": IconSet["haml"], 458 | // "feature": IconSet["cucumber"], 459 | // "riot": IconSet["riot"], 460 | // "tag": IconSet["riot"], 461 | // "vfl": IconSet["vfl"], 462 | // "kl": IconSet["kl"], 463 | // "cfml": IconSet["coldfusion"], 464 | // "cfc": IconSet["coldfusion"], 465 | // "lucee": IconSet["coldfusion"], 466 | // "cfm": IconSet["coldfusion"], 467 | // "cabal": IconSet["cabal"], 468 | // "rql": IconSet["restql"], 469 | // "restql": IconSet["restql"], 470 | // "kv": IconSet["kivy"], 471 | // "graphcool": IconSet["graphcool"], 472 | // "sbt": IconSet["sbt"], 473 | // "cr": IconSet["crystal"], 474 | // "ecr": IconSet["crystal"], 475 | // "cu": IconSet["cuda"], 476 | // "cuh": IconSet["cuda"], 477 | // "def": IconSet["dotjs"], 478 | // "dot": IconSet["dotjs"], 479 | // "jst": IconSet["dotjs"], 480 | // "pde": IconSet["processing"], 481 | // "wpy": IconSet["wepy"], 482 | // "hcl": IconSet["hcl"], 483 | // "san": IconSet["san"], 484 | // "red": IconSet["red"], 485 | // "fxp": IconSet["foxpro"], 486 | // "prg": IconSet["foxpro"], 487 | // "wat": IconSet["webassembly"], 488 | // "wasm": IconSet["webassembly"], 489 | // "ipynb": IconSet["jupyter"], 490 | // "bal": IconSet["ballerina"], 491 | // "balx": IconSet["ballerina"], 492 | // "rkt": IconSet["racket"], 493 | // "bzl": IconSet["bazel"], 494 | // "bazel": IconSet["bazel"], 495 | // "mint": IconSet["mint"], 496 | // "vm": IconSet["velocity"], 497 | // "fhtml": IconSet["velocity"], 498 | // "vtl": IconSet["velocity"], 499 | // "prisma": IconSet["prisma"], 500 | // "abc": IconSet["abc"], 501 | // "lisp": IconSet["lisp"], 502 | // "lsp": IconSet["lisp"], 503 | // "cl": IconSet["lisp"], 504 | // "fast": IconSet["lisp"], 505 | // "svelte": IconSet["svelte"], 506 | // "prw": IconSet["advpl_prw"], 507 | // "prx": IconSet["advpl_prw"], 508 | // "ptm": IconSet["advpl_ptm"], 509 | // "tlpp": IconSet["advpl_tlpp"], 510 | // "ch": IconSet["advpl_include"], 511 | // "4th": IconSet["forth"], 512 | // "fth": IconSet["forth"], 513 | // "frt": IconSet["forth"], 514 | // "iuml": IconSet["uml"], 515 | // "pu": IconSet["uml"], 516 | // "puml": IconSet["uml"], 517 | // "plantuml": IconSet["uml"], 518 | // "wsd": IconSet["uml"], 519 | // "sml": IconSet["sml"], 520 | // "mlton": IconSet["sml"], 521 | // "mlb": IconSet["sml"], 522 | // "sig": IconSet["sml"], 523 | // "fun": IconSet["sml"], 524 | // "cm": IconSet["sml"], 525 | // "lex": IconSet["sml"], 526 | // "use": IconSet["sml"], 527 | // "grm": IconSet["sml"], 528 | // "imba": IconSet["imba"], 529 | // "drawio": IconSet["drawio"], 530 | // "dio": IconSet["drawio"], 531 | // "sas": IconSet["sas"], 532 | // "sas7bdat": IconSet["sas"], 533 | // "sashdat": IconSet["sas"], 534 | // "astore": IconSet["sas"], 535 | // "ast": IconSet["sas"], 536 | // "sast": IconSet["sas"], 537 | } 538 | -------------------------------------------------------------------------------- /icons/filenames.go: -------------------------------------------------------------------------------- 1 | package icons 2 | 3 | // IconFileName is used to get the icon based on its filename. 4 | var IconFileName = map[string]*IconInfo{ 5 | ".pug-lintrc": IconSet["pug"], 6 | ".pug-lintrc.js": IconSet["pug"], 7 | ".pug-lintrc.json": IconSet["pug"], 8 | ".jscsrc": IconSet["json"], 9 | ".jshintrc": IconSet["json"], 10 | "composer.lock": IconSet["json"], 11 | ".jsbeautifyrc": IconSet["json"], 12 | ".esformatter": IconSet["json"], 13 | "cdp.pid": IconSet["json"], 14 | ".mjmlconfig": IconSet["json"], 15 | ".htaccess": IconSet["xml"], 16 | ".jshintignore": IconSet["settings"], 17 | ".buildignore": IconSet["settings"], 18 | ".mrconfig": IconSet["settings"], 19 | ".yardopts": IconSet["settings"], 20 | "manifest.mf": IconSet["settings"], 21 | ".clang-format": IconSet["settings"], 22 | ".clang-tidy": IconSet["settings"], 23 | "go.mod": IconSet["go-mod"], 24 | "go.sum": IconSet["go-mod"], 25 | "requirements.txt": IconSet["python-misc"], 26 | "pipfile": IconSet["python-misc"], 27 | ".python-version": IconSet["python-misc"], 28 | "manifest.in": IconSet["python-misc"], 29 | "gradle.properties": IconSet["gradle"], 30 | "gradlew": IconSet["gradle"], 31 | "gradle-wrapper.properties": IconSet["gradle"], 32 | "license": IconSet["certificate"], 33 | "license.md": IconSet["certificate"], 34 | "license.txt": IconSet["certificate"], 35 | "licence": IconSet["certificate"], 36 | "licence.md": IconSet["certificate"], 37 | "licence.txt": IconSet["certificate"], 38 | "unlicense": IconSet["certificate"], 39 | "unlicense.md": IconSet["certificate"], 40 | "unlicense.txt": IconSet["certificate"], 41 | ".htpasswd": IconSet["key"], 42 | "gemfile": IconSet["gemfile"], 43 | "dockerfile": IconSet["docker"], 44 | "dockerfile.prod": IconSet["docker"], 45 | "dockerfile.production": IconSet["docker"], 46 | "docker-compose.yml": IconSet["docker"], 47 | "docker-compose.yaml": IconSet["docker"], 48 | "docker-compose.dev.yml": IconSet["docker"], 49 | "docker-compose.local.yml": IconSet["docker"], 50 | "docker-compose.ci.yml": IconSet["docker"], 51 | "docker-compose.override.yml": IconSet["docker"], 52 | "docker-compose.staging.yml": IconSet["docker"], 53 | "docker-compose.prod.yml": IconSet["docker"], 54 | "docker-compose.production.yml": IconSet["docker"], 55 | "docker-compose.test.yml": IconSet["docker"], 56 | ".mailmap": IconSet["email"], 57 | ".graphqlconfig": IconSet["graphql"], 58 | ".gitignore": IconSet["git"], 59 | ".gitconfig": IconSet["git"], 60 | ".gitattributes": IconSet["git"], 61 | ".gitmodules": IconSet["git"], 62 | ".gitkeep": IconSet["git"], 63 | "git-history": IconSet["git"], 64 | ".luacheckrc": IconSet["lua"], 65 | ".Rhistory": IconSet["r"], 66 | "cmakelists.txt": IconSet["cmake"], 67 | "cmakecache.txt": IconSet["cmake"], 68 | "vue.config.js": IconSet["vue-config"], 69 | "vue.config.ts": IconSet["vue-config"], 70 | "nuxt.config.js": IconSet["nuxt"], 71 | "nuxt.config.ts": IconSet["nuxt"], 72 | "security.md": IconSet["lock"], 73 | "security.txt": IconSet["lock"], 74 | "security": IconSet["lock"], 75 | "vercel.json": IconSet["vercel"], 76 | ".vercelignore": IconSet["vercel"], 77 | "now.json": IconSet["vercel"], 78 | ".nowignore": IconSet["vercel"], 79 | "postcss.config.js": IconSet["postcss"], 80 | ".postcssrc.js": IconSet["postcss"], 81 | ".postcssrc": IconSet["postcss"], 82 | ".postcssrc.json": IconSet["postcss"], 83 | ".postcssrc.yml": IconSet["postcss"], 84 | "CNAME": IconSet["http"], 85 | "webpack.js": IconSet["webpack"], 86 | "webpack.ts": IconSet["webpack"], 87 | "webpack.base.js": IconSet["webpack"], 88 | "webpack.base.ts": IconSet["webpack"], 89 | "webpack.config.js": IconSet["webpack"], 90 | "webpack.config.ts": IconSet["webpack"], 91 | "webpack.common.js": IconSet["webpack"], 92 | "webpack.common.ts": IconSet["webpack"], 93 | "webpack.config.common.js": IconSet["webpack"], 94 | "webpack.config.common.ts": IconSet["webpack"], 95 | "webpack.config.common.babel.js": IconSet["webpack"], 96 | "webpack.config.common.babel.ts": IconSet["webpack"], 97 | "webpack.dev.js": IconSet["webpack"], 98 | "webpack.dev.ts": IconSet["webpack"], 99 | "webpack.development.js": IconSet["webpack"], 100 | "webpack.development.ts": IconSet["webpack"], 101 | "webpack.config.dev.js": IconSet["webpack"], 102 | "webpack.config.dev.ts": IconSet["webpack"], 103 | "webpack.config.dev.babel.js": IconSet["webpack"], 104 | "webpack.config.dev.babel.ts": IconSet["webpack"], 105 | "webpack.prod.js": IconSet["webpack"], 106 | "webpack.prod.ts": IconSet["webpack"], 107 | "webpack.production.js": IconSet["webpack"], 108 | "webpack.production.ts": IconSet["webpack"], 109 | "webpack.server.js": IconSet["webpack"], 110 | "webpack.server.ts": IconSet["webpack"], 111 | "webpack.client.js": IconSet["webpack"], 112 | "webpack.client.ts": IconSet["webpack"], 113 | "webpack.config.server.js": IconSet["webpack"], 114 | "webpack.config.server.ts": IconSet["webpack"], 115 | "webpack.config.client.js": IconSet["webpack"], 116 | "webpack.config.client.ts": IconSet["webpack"], 117 | "webpack.config.production.babel.js": IconSet["webpack"], 118 | "webpack.config.production.babel.ts": IconSet["webpack"], 119 | "webpack.config.prod.babel.js": IconSet["webpack"], 120 | "webpack.config.prod.babel.ts": IconSet["webpack"], 121 | "webpack.config.prod.js": IconSet["webpack"], 122 | "webpack.config.prod.ts": IconSet["webpack"], 123 | "webpack.config.production.js": IconSet["webpack"], 124 | "webpack.config.production.ts": IconSet["webpack"], 125 | "webpack.config.staging.js": IconSet["webpack"], 126 | "webpack.config.staging.ts": IconSet["webpack"], 127 | "webpack.config.babel.js": IconSet["webpack"], 128 | "webpack.config.babel.ts": IconSet["webpack"], 129 | "webpack.config.base.babel.js": IconSet["webpack"], 130 | "webpack.config.base.babel.ts": IconSet["webpack"], 131 | "webpack.config.base.js": IconSet["webpack"], 132 | "webpack.config.base.ts": IconSet["webpack"], 133 | "webpack.config.staging.babel.js": IconSet["webpack"], 134 | "webpack.config.staging.babel.ts": IconSet["webpack"], 135 | "webpack.config.coffee": IconSet["webpack"], 136 | "webpack.config.test.js": IconSet["webpack"], 137 | "webpack.config.test.ts": IconSet["webpack"], 138 | "webpack.config.vendor.js": IconSet["webpack"], 139 | "webpack.config.vendor.ts": IconSet["webpack"], 140 | "webpack.config.vendor.production.js": IconSet["webpack"], 141 | "webpack.config.vendor.production.ts": IconSet["webpack"], 142 | "webpack.test.js": IconSet["webpack"], 143 | "webpack.test.ts": IconSet["webpack"], 144 | "webpack.dist.js": IconSet["webpack"], 145 | "webpack.dist.ts": IconSet["webpack"], 146 | "webpackfile.js": IconSet["webpack"], 147 | "webpackfile.ts": IconSet["webpack"], 148 | "ionic.config.json": IconSet["ionic"], 149 | ".io-config.json": IconSet["ionic"], 150 | "gulpfile.js": IconSet["gulp"], 151 | "gulpfile.mjs": IconSet["gulp"], 152 | "gulpfile.ts": IconSet["gulp"], 153 | "gulpfile.babel.js": IconSet["gulp"], 154 | "package.json": IconSet["nodejs"], 155 | "package-lock.json": IconSet["nodejs"], 156 | ".nvmrc": IconSet["nodejs"], 157 | ".esmrc": IconSet["nodejs"], 158 | ".node-version": IconSet["nodejs"], 159 | ".npmignore": IconSet["npm"], 160 | ".npmrc": IconSet["npm"], 161 | ".yarnrc": IconSet["yarn"], 162 | "yarn.lock": IconSet["yarn"], 163 | ".yarnclean": IconSet["yarn"], 164 | ".yarn-integrity": IconSet["yarn"], 165 | "yarn-error.log": IconSet["yarn"], 166 | ".yarnrc.yml": IconSet["yarn"], 167 | ".yarnrc.yaml": IconSet["yarn"], 168 | "androidmanifest.xml": IconSet["android"], 169 | ".env.defaults": IconSet["tune"], 170 | ".env.example": IconSet["tune"], 171 | ".env.sample": IconSet["tune"], 172 | ".env.schema": IconSet["tune"], 173 | ".env.local": IconSet["tune"], 174 | ".env.dev": IconSet["tune"], 175 | ".env.development": IconSet["tune"], 176 | ".env.qa": IconSet["tune"], 177 | ".env.prod": IconSet["tune"], 178 | ".env.production": IconSet["tune"], 179 | ".env.staging": IconSet["tune"], 180 | ".env.preview": IconSet["tune"], 181 | ".env.test": IconSet["tune"], 182 | ".env.testing": IconSet["tune"], 183 | ".env.development.local": IconSet["tune"], 184 | ".env.qa.local": IconSet["tune"], 185 | ".env.production.local": IconSet["tune"], 186 | ".env.staging.local": IconSet["tune"], 187 | ".env.test.local": IconSet["tune"], 188 | ".babelrc": IconSet["babel"], 189 | ".babelrc.js": IconSet["babel"], 190 | ".babelrc.json": IconSet["babel"], 191 | "babel.config.json": IconSet["babel"], 192 | "babel.config.js": IconSet["babel"], 193 | "contributing.md": IconSet["contributing"], 194 | "readme.md": IconSet["readme"], 195 | "readme.txt": IconSet["readme"], 196 | "readme": IconSet["readme"], 197 | "changelog": IconSet["changelog"], 198 | "changelog.md": IconSet["changelog"], 199 | "changelog.txt": IconSet["changelog"], 200 | "changes": IconSet["changelog"], 201 | "changes.md": IconSet["changelog"], 202 | "changes.txt": IconSet["changelog"], 203 | "credits": IconSet["credits"], 204 | "credits.txt": IconSet["credits"], 205 | "credits.md": IconSet["credits"], 206 | "authors": IconSet["authors"], 207 | "authors.md": IconSet["authors"], 208 | "authors.txt": IconSet["authors"], 209 | "favicon.ico": IconSet["favicon"], 210 | "karma.conf.js": IconSet["karma"], 211 | "karma.conf.ts": IconSet["karma"], 212 | "karma.conf.coffee": IconSet["karma"], 213 | "karma.config.js": IconSet["karma"], 214 | "karma.config.ts": IconSet["karma"], 215 | "karma-main.js": IconSet["karma"], 216 | "karma-main.ts": IconSet["karma"], 217 | ".travis.yml": IconSet["travis"], 218 | ".codecov.yml": IconSet["codecov"], 219 | "codecov.yml": IconSet["codecov"], 220 | "protractor.conf.js": IconSet["protractor"], 221 | "protractor.conf.ts": IconSet["protractor"], 222 | "protractor.conf.coffee": IconSet["protractor"], 223 | "protractor.config.js": IconSet["protractor"], 224 | "protractor.config.ts": IconSet["protractor"], 225 | "procfile": IconSet["heroku"], 226 | "procfile.windows": IconSet["heroku"], 227 | ".bowerrc": IconSet["bower"], 228 | "bower.json": IconSet["bower"], 229 | ".eslintrc.js": IconSet["eslint"], 230 | ".eslintrc.cjs": IconSet["eslint"], 231 | ".eslintrc.yaml": IconSet["eslint"], 232 | ".eslintrc.yml": IconSet["eslint"], 233 | ".eslintrc.json": IconSet["eslint"], 234 | ".eslintrc": IconSet["eslint"], 235 | ".eslintignore": IconSet["eslint"], 236 | ".eslintcache": IconSet["eslint"], 237 | "code_of_conduct.md": IconSet["conduct"], 238 | "code_of_conduct.txt": IconSet["conduct"], 239 | "mocha.opts": IconSet["mocha"], 240 | ".mocharc.yml": IconSet["mocha"], 241 | ".mocharc.yaml": IconSet["mocha"], 242 | ".mocharc.js": IconSet["mocha"], 243 | ".mocharc.json": IconSet["mocha"], 244 | ".mocharc.jsonc": IconSet["mocha"], 245 | "jenkinsfile": IconSet["jenkins"], 246 | "firebase.json": IconSet["firebase"], 247 | ".firebaserc": IconSet["firebase"], 248 | "firestore.rules": IconSet["firebase"], 249 | "firestore.indexes.json": IconSet["firebase"], 250 | ".stylelintrc": IconSet["stylelint"], 251 | "stylelint.config.js": IconSet["stylelint"], 252 | ".stylelintrc.json": IconSet["stylelint"], 253 | ".stylelintrc.yaml": IconSet["stylelint"], 254 | ".stylelintrc.yml": IconSet["stylelint"], 255 | ".stylelintrc.js": IconSet["stylelint"], 256 | ".stylelintignore": IconSet["stylelint"], 257 | ".codeclimate.yml": IconSet["code-climate"], 258 | ".prettierrc": IconSet["prettier"], 259 | "prettier.config.js": IconSet["prettier"], 260 | ".prettierrc.js": IconSet["prettier"], 261 | ".prettierrc.json": IconSet["prettier"], 262 | ".prettierrc.yaml": IconSet["prettier"], 263 | ".prettierrc.yml": IconSet["prettier"], 264 | ".prettierignore": IconSet["prettier"], 265 | "gruntfile.js": IconSet["grunt"], 266 | "gruntfile.ts": IconSet["grunt"], 267 | "gruntfile.coffee": IconSet["grunt"], 268 | "gruntfile.babel.js": IconSet["grunt"], 269 | "gruntfile.babel.ts": IconSet["grunt"], 270 | "gruntfile.babel.coffee": IconSet["grunt"], 271 | "jest.config.js": IconSet["jest"], 272 | "jest.config.ts": IconSet["jest"], 273 | "jest.config.cjs": IconSet["jest"], 274 | "jest.config.mjs": IconSet["jest"], 275 | "jest.config.json": IconSet["jest"], 276 | "jest.e2e.config.js": IconSet["jest"], 277 | "jest.e2e.config.ts": IconSet["jest"], 278 | "jest.e2e.config.cjs": IconSet["jest"], 279 | "jest.e2e.config.mjs": IconSet["jest"], 280 | "jest.e2e.config.json": IconSet["jest"], 281 | "jest.setup.js": IconSet["jest"], 282 | "jest.setup.ts": IconSet["jest"], 283 | "jest.json": IconSet["jest"], 284 | ".jestrc": IconSet["jest"], 285 | ".jestrc.js": IconSet["jest"], 286 | ".jestrc.json": IconSet["jest"], 287 | "jest.teardown.js": IconSet["jest"], 288 | "fastfile": IconSet["fastlane"], 289 | "appfile": IconSet["fastlane"], 290 | ".helmignore": IconSet["helm"], 291 | "makefile": IconSet["makefile"], 292 | ".releaserc": IconSet["semantic-release"], 293 | ".releaserc.yaml": IconSet["semantic-release"], 294 | ".releaserc.yml": IconSet["semantic-release"], 295 | ".releaserc.json": IconSet["semantic-release"], 296 | ".releaserc.js": IconSet["semantic-release"], 297 | "release.config.js": IconSet["semantic-release"], 298 | "bitbucket-pipelines.yaml": IconSet["bitbucket"], 299 | "bitbucket-pipelines.yml": IconSet["bitbucket"], 300 | "azure-pipelines.yml": IconSet["azure-pipelines"], 301 | "azure-pipelines.yaml": IconSet["azure-pipelines"], 302 | "vagrantfile": IconSet["vagrant"], 303 | "tailwind.js": IconSet["tailwindcss"], 304 | "tailwind.config.js": IconSet["tailwindcss"], 305 | "codeowners": IconSet["codeowners"], 306 | ".gcloudignore": IconSet["gcp"], 307 | ".huskyrc": IconSet["husky"], 308 | "husky.config.js": IconSet["husky"], 309 | ".huskyrc.json": IconSet["husky"], 310 | ".huskyrc.js": IconSet["husky"], 311 | ".huskyrc.yaml": IconSet["husky"], 312 | ".huskyrc.yml": IconSet["husky"], 313 | ".commitlintrc": IconSet["commitlint"], 314 | ".commitlintrc.js": IconSet["commitlint"], 315 | "commitlint.config.js": IconSet["commitlint"], 316 | ".commitlintrc.json": IconSet["commitlint"], 317 | ".commitlint.yaml": IconSet["commitlint"], 318 | ".commitlint.yml": IconSet["commitlint"], 319 | "dune": IconSet["dune"], 320 | "dune-project": IconSet["dune"], 321 | "roadmap.md": IconSet["roadmap"], 322 | "roadmap.txt": IconSet["roadmap"], 323 | "timeline.md": IconSet["roadmap"], 324 | "timeline.txt": IconSet["roadmap"], 325 | "milestones.md": IconSet["roadmap"], 326 | "milestones.txt": IconSet["roadmap"], 327 | "nuget.config": IconSet["nuget"], 328 | ".nuspec": IconSet["nuget"], 329 | "nuget.exe": IconSet["nuget"], 330 | "stryker.conf.js": IconSet["stryker"], 331 | "stryker.conf.json": IconSet["stryker"], 332 | ".modernizrrc": IconSet["modernizr"], 333 | ".modernizrrc.js": IconSet["modernizr"], 334 | ".modernizrrc.json": IconSet["modernizr"], 335 | "routing.ts": IconSet["routing"], 336 | "routing.tsx": IconSet["routing"], 337 | "routing.js": IconSet["routing"], 338 | "routing.jsx": IconSet["routing"], 339 | "routes.ts": IconSet["routing"], 340 | "routes.tsx": IconSet["routing"], 341 | "routes.js": IconSet["routing"], 342 | "routes.jsx": IconSet["routing"], 343 | // ".vfl": IconSet["vfl"], 344 | // ".kl": IconSet["kl"], 345 | // "project.graphcool": IconSet["graphcool"], 346 | // ".flowconfig": IconSet["flow"], 347 | // ".bithoundrc": IconSet["bithound"], 348 | // ".appveyor.yml": IconSet["appveyor"], 349 | // "appveyor.yml": IconSet["appveyor"], 350 | // "fuse.js": IconSet["fusebox"], 351 | // ".editorconfig": IconSet["editorconfig"], 352 | // ".watchmanconfig": IconSet["watchman"], 353 | // "aurelia.json": IconSet["aurelia"], 354 | // "rollup.config.js": IconSet["rollup"], 355 | // "rollup.config.ts": IconSet["rollup"], 356 | // "rollup-config.js": IconSet["rollup"], 357 | // "rollup-config.ts": IconSet["rollup"], 358 | // "rollup.config.common.js": IconSet["rollup"], 359 | // "rollup.config.common.ts": IconSet["rollup"], 360 | // "rollup.config.base.js": IconSet["rollup"], 361 | // "rollup.config.base.ts": IconSet["rollup"], 362 | // "rollup.config.prod.js": IconSet["rollup"], 363 | // "rollup.config.prod.ts": IconSet["rollup"], 364 | // "rollup.config.dev.js": IconSet["rollup"], 365 | // "rollup.config.dev.ts": IconSet["rollup"], 366 | // "rollup.config.prod.vendor.js": IconSet["rollup"], 367 | // "rollup.config.prod.vendor.ts": IconSet["rollup"], 368 | // ".hhconfig": IconSet["hack"], 369 | // "apollo.config.js": IconSet["apollo"], 370 | // "nodemon.json": IconSet["nodemon"], 371 | // "nodemon-debug.json": IconSet["nodemon"], 372 | // ".hintrc": IconSet["webhint"], 373 | // "browserslist": IconSet["browserlist"], 374 | // ".browserslistrc": IconSet["browserlist"], 375 | // ".snyk": IconSet["snyk"], 376 | // ".drone.yml": IconSet["drone"], 377 | // ".sequelizerc": IconSet["sequelize"], 378 | // "gatsby.config.js": IconSet["gatsby"], 379 | // "gatsby-config.js": IconSet["gatsby"], 380 | // "gatsby-node.js": IconSet["gatsby"], 381 | // "gatsby-browser.js": IconSet["gatsby"], 382 | // "gatsby-ssr.js": IconSet["gatsby"], 383 | // ".wakatime-project": IconSet["wakatime"], 384 | // "circle.yml": IconSet["circleci"], 385 | // ".cfignore": IconSet["cloudfoundry"], 386 | // "wallaby.js": IconSet["wallaby"], 387 | // "wallaby.conf.js": IconSet["wallaby"], 388 | // "stencil.config.js": IconSet["stencil"], 389 | // "stencil.config.ts": IconSet["stencil"], 390 | // ".bazelignore": IconSet["bazel"], 391 | // ".bazelrc": IconSet["bazel"], 392 | // "prisma.yml": IconSet["prisma"], 393 | // ".nycrc": IconSet["istanbul"], 394 | // ".nycrc.json": IconSet["istanbul"], 395 | // "buildkite.yml": IconSet["buildkite"], 396 | // "buildkite.yaml": IconSet["buildkite"], 397 | // "netlify.json": IconSet["netlify"], 398 | // "netlify.yml": IconSet["netlify"], 399 | // "netlify.yaml": IconSet["netlify"], 400 | // "netlify.toml": IconSet["netlify"], 401 | // "nest-cli.json": IconSet["nest"], 402 | // ".nest-cli.json": IconSet["nest"], 403 | // "nestconfig.json": IconSet["nest"], 404 | // ".nestconfig.json": IconSet["nest"], 405 | // ".percy.yml": IconSet["percy"], 406 | // ".gitpod.yml": IconSet["gitpod"], 407 | // "tiltfile": IconSet["tilt"], 408 | // "capacitor.config.json": IconSet["capacitor"], 409 | // ".adonisrc.json": IconSet["adonis"], 410 | // "ace": IconSet["adonis"], 411 | // "meson.build": IconSet["meson"], 412 | // ".buckconfig": IconSet["buck"], 413 | // "nx.json": IconSet["nrwl"], 414 | // ".slugignore": IconSet["slug"], 415 | } 416 | -------------------------------------------------------------------------------- /icons/glyphs.go: -------------------------------------------------------------------------------- 1 | package icons 2 | 3 | import "fmt" 4 | 5 | // IconInfo is a struct that holds information about an icon. 6 | type IconInfo struct { 7 | icon string 8 | color [3]uint8 9 | executable bool 10 | } 11 | 12 | // GetGlyph returns the glyph for the icon. 13 | func (i *IconInfo) GetGlyph() string { 14 | return i.icon 15 | } 16 | 17 | // GetColor returns the color for the icon. 18 | func (i *IconInfo) GetColor(f uint8) string { 19 | switch { 20 | case i.executable: 21 | return "\033[38;2;76;175;080m" 22 | default: 23 | return fmt.Sprintf("\033[38;2;%d;%d;%dm", i.color[0], i.color[1], i.color[2]) 24 | } 25 | } 26 | 27 | // MakeExe is a function that returns a new IconInfo struct with the executable flag set to true. 28 | func (i *IconInfo) MakeExe() { 29 | i.executable = true 30 | } 31 | 32 | // IconSet is a map to represent all the icons. 33 | var IconSet = map[string]*IconInfo{ 34 | "html": {icon: "\uf13b", color: [3]uint8{228, 79, 57}}, // html 35 | "markdown": {icon: "\uf853", color: [3]uint8{66, 165, 245}}, // markdown 36 | "css": {icon: "\uf81b", color: [3]uint8{66, 165, 245}}, // css 37 | "css-map": {icon: "\ue749", color: [3]uint8{66, 165, 245}}, // css-map 38 | "sass": {icon: "\ue603", color: [3]uint8{237, 80, 122}}, // sass 39 | "less": {icon: "\ue60b", color: [3]uint8{2, 119, 189}}, // less 40 | "json": {icon: "\ue60b", color: [3]uint8{251, 193, 60}}, // json 41 | "yaml": {icon: "\ue60b", color: [3]uint8{244, 68, 62}}, // yaml 42 | "xml": {icon: "\uf72d", color: [3]uint8{64, 153, 69}}, // xml 43 | "image": {icon: "\uf71e", color: [3]uint8{48, 166, 154}}, // image 44 | "javascript": {icon: "\ue74e", color: [3]uint8{255, 202, 61}}, // javascript 45 | "javascript-map": {icon: "\ue781", color: [3]uint8{255, 202, 61}}, // javascript-map 46 | "test-jsx": {icon: "\uf595", color: [3]uint8{35, 188, 212}}, // test-jsx 47 | "test-js": {icon: "\uf595", color: [3]uint8{255, 202, 61}}, // test-js 48 | "react": {icon: "\ue7ba", color: [3]uint8{35, 188, 212}}, // react 49 | "react_ts": {icon: "\ue7ba", color: [3]uint8{36, 142, 211}}, // react_ts 50 | "settings": {icon: "\uf013", color: [3]uint8{66, 165, 245}}, // settings 51 | "typescript": {icon: "\ue628", color: [3]uint8{3, 136, 209}}, // typescript 52 | "typescript-def": {icon: "\ufbe4", color: [3]uint8{3, 136, 209}}, // typescript-def 53 | "test-ts": {icon: "\uf595", color: [3]uint8{3, 136, 209}}, // test-ts 54 | "pdf": {icon: "\uf724", color: [3]uint8{244, 68, 62}}, // pdf 55 | "table": {icon: "\uf71a", color: [3]uint8{139, 195, 74}}, // table 56 | "visualstudio": {icon: "\ue70c", color: [3]uint8{173, 99, 188}}, // visualstudio 57 | "database": {icon: "\ue706", color: [3]uint8{255, 202, 61}}, // database 58 | "mysql": {icon: "\ue704", color: [3]uint8{1, 94, 134}}, // mysql 59 | "postgresql": {icon: "\ue76e", color: [3]uint8{49, 99, 140}}, // postgresql 60 | "sqlite": {icon: "\ue7c4", color: [3]uint8{1, 57, 84}}, // sqlite 61 | "csharp": {icon: "\uf81a", color: [3]uint8{2, 119, 189}}, // csharp 62 | "zip": {icon: "\uf410", color: [3]uint8{175, 180, 43}}, // zip 63 | "exe": {icon: "\uf2d0", color: [3]uint8{229, 77, 58}}, // exe 64 | "java": {icon: "\uf675", color: [3]uint8{244, 68, 62}}, // java 65 | "c": {icon: "\ufb70", color: [3]uint8{2, 119, 189}}, // c 66 | "cpp": {icon: "\ufb71", color: [3]uint8{2, 119, 189}}, // cpp 67 | "go": {icon: "\ufcd1", color: [3]uint8{32, 173, 194}}, // go 68 | "go-mod": {icon: "\ufcd1", color: [3]uint8{237, 80, 122}}, // go-mod 69 | "go-test": {icon: "\ufcd1", color: [3]uint8{255, 213, 79}}, // go-test 70 | "python": {icon: "\uf81f", color: [3]uint8{52, 102, 143}}, // python 71 | "python-misc": {icon: "\uf820", color: [3]uint8{130, 61, 28}}, // python-misc 72 | "url": {icon: "\uf836", color: [3]uint8{66, 165, 245}}, // url 73 | "console": {icon: "\uf68c", color: [3]uint8{250, 111, 66}}, // console 74 | "word": {icon: "\uf72b", color: [3]uint8{1, 87, 155}}, // word 75 | "certificate": {icon: "\uf623", color: [3]uint8{249, 89, 63}}, // certificate 76 | "key": {icon: "\uf805", color: [3]uint8{48, 166, 154}}, // key 77 | "font": {icon: "\uf031", color: [3]uint8{244, 68, 62}}, // font 78 | "lib": {icon: "\uf831", color: [3]uint8{139, 195, 74}}, // lib 79 | "ruby": {icon: "\ue739", color: [3]uint8{229, 61, 58}}, // ruby 80 | "gemfile": {icon: "\ue21e", color: [3]uint8{229, 61, 58}}, // gemfile 81 | "fsharp": {icon: "\ue7a7", color: [3]uint8{55, 139, 186}}, // fsharp 82 | "swift": {icon: "\ufbe3", color: [3]uint8{249, 95, 63}}, // swift 83 | "docker": {icon: "\uf308", color: [3]uint8{1, 135, 201}}, // docker 84 | "powerpoint": {icon: "\uf726", color: [3]uint8{209, 71, 51}}, // powerpoint 85 | "video": {icon: "\uf72a", color: [3]uint8{253, 154, 62}}, // video 86 | "virtual": {icon: "\uf822", color: [3]uint8{3, 155, 229}}, // virtual 87 | "email": {icon: "\uf6ed", color: [3]uint8{66, 165, 245}}, // email 88 | "audio": {icon: "\ufb75", color: [3]uint8{239, 83, 80}}, // audio 89 | "coffee": {icon: "\uf675", color: [3]uint8{66, 165, 245}}, // coffee 90 | "document": {icon: "\uf718", color: [3]uint8{66, 165, 245}}, // document 91 | "rust": {icon: "\ue7a8", color: [3]uint8{250, 111, 66}}, // rust 92 | "raml": {icon: "\ue60b", color: [3]uint8{66, 165, 245}}, // raml 93 | "xaml": {icon: "\ufb72", color: [3]uint8{66, 165, 245}}, // xaml 94 | "haskell": {icon: "\ue61f", color: [3]uint8{254, 168, 62}}, // haskell 95 | "git": {icon: "\ue702", color: [3]uint8{229, 77, 58}}, // git 96 | "lua": {icon: "\ue620", color: [3]uint8{66, 165, 245}}, // lua 97 | "clojure": {icon: "\ue76a", color: [3]uint8{100, 221, 23}}, // clojure 98 | "groovy": {icon: "\uf2a6", color: [3]uint8{41, 198, 218}}, // groovy 99 | "r": {icon: "\ufcd2", color: [3]uint8{25, 118, 210}}, // r 100 | "dart": {icon: "\ue798", color: [3]uint8{87, 182, 240}}, // dart 101 | "mxml": {icon: "\uf72d", color: [3]uint8{254, 168, 62}}, // mxml 102 | "assembly": {icon: "\uf471", color: [3]uint8{250, 109, 63}}, // assembly 103 | "vue": {icon: "\ufd42", color: [3]uint8{65, 184, 131}}, // vue 104 | "vue-config": {icon: "\ufd42", color: [3]uint8{58, 121, 110}}, // vue-config 105 | "lock": {icon: "\uf83d", color: [3]uint8{255, 213, 79}}, // lock 106 | "handlebars": {icon: "\ue60f", color: [3]uint8{250, 111, 66}}, // handlebars 107 | "perl": {icon: "\ue769", color: [3]uint8{149, 117, 205}}, // perl 108 | "elixir": {icon: "\ue62d", color: [3]uint8{149, 117, 205}}, // elixir 109 | "erlang": {icon: "\ue7b1", color: [3]uint8{244, 68, 62}}, // erlang 110 | "twig": {icon: "\ue61c", color: [3]uint8{155, 185, 47}}, // twig 111 | "julia": {icon: "\ue624", color: [3]uint8{134, 82, 159}}, // julia 112 | "elm": {icon: "\ue62c", color: [3]uint8{96, 181, 204}}, // elm 113 | "smarty": {icon: "\uf834", color: [3]uint8{255, 207, 60}}, // smarty 114 | "stylus": {icon: "\ue600", color: [3]uint8{192, 202, 51}}, // stylus 115 | "verilog": {icon: "\ufb19", color: [3]uint8{250, 111, 66}}, // verilog 116 | "robot": {icon: "\ufba7", color: [3]uint8{249, 89, 63}}, // robot 117 | "solidity": {icon: "\ufcb9", color: [3]uint8{3, 136, 209}}, // solidity 118 | "yang": {icon: "\ufb7e", color: [3]uint8{66, 165, 245}}, // yang 119 | "vercel": {icon: "\uf47e", color: [3]uint8{207, 216, 220}}, // vercel 120 | "applescript": {icon: "\uf302", color: [3]uint8{120, 144, 156}}, // applescript 121 | "cake": {icon: "\uf5ea", color: [3]uint8{250, 111, 66}}, // cake 122 | "nim": {icon: "\uf6a4", color: [3]uint8{255, 202, 61}}, // nim 123 | "todo": {icon: "\uf058", color: [3]uint8{124, 179, 66}}, // task 124 | "nix": {icon: "\uf313", color: [3]uint8{80, 117, 193}}, // nix 125 | "http": {icon: "\uf484", color: [3]uint8{66, 165, 245}}, // http 126 | "webpack": {icon: "\ufc29", color: [3]uint8{142, 214, 251}}, // webpack 127 | "ionic": {icon: "\ue7a9", color: [3]uint8{79, 143, 247}}, // ionic 128 | "gulp": {icon: "\ue763", color: [3]uint8{229, 61, 58}}, // gulp 129 | "nodejs": {icon: "\uf898", color: [3]uint8{139, 195, 74}}, // nodejs 130 | "npm": {icon: "\ue71e", color: [3]uint8{203, 56, 55}}, // npm 131 | "yarn": {icon: "\uf61a", color: [3]uint8{44, 142, 187}}, // yarn 132 | "android": {icon: "\uf531", color: [3]uint8{139, 195, 74}}, // android 133 | "tune": {icon: "\ufb69", color: [3]uint8{251, 193, 60}}, // tune 134 | "contributing": {icon: "\uf64d", color: [3]uint8{255, 202, 61}}, // contributing 135 | "readme": {icon: "\uf7fb", color: [3]uint8{66, 165, 245}}, // readme 136 | "changelog": {icon: "\ufba6", color: [3]uint8{139, 195, 74}}, // changelog 137 | "credits": {icon: "\uf75f", color: [3]uint8{156, 204, 101}}, // credits 138 | "authors": {icon: "\uf0c0", color: [3]uint8{244, 68, 62}}, // authors 139 | "favicon": {icon: "\ue623", color: [3]uint8{255, 213, 79}}, // favicon 140 | "karma": {icon: "\ue622", color: [3]uint8{60, 190, 174}}, // karma 141 | "travis": {icon: "\ue77e", color: [3]uint8{203, 58, 73}}, // travis 142 | "heroku": {icon: "\ue607", color: [3]uint8{105, 99, 185}}, // heroku 143 | "gitlab": {icon: "\uf296", color: [3]uint8{226, 69, 57}}, // gitlab 144 | "bower": {icon: "\ue61a", color: [3]uint8{239, 88, 60}}, // bower 145 | "conduct": {icon: "\uf64b", color: [3]uint8{205, 220, 57}}, // conduct 146 | "jenkins": {icon: "\ue767", color: [3]uint8{240, 214, 183}}, // jenkins 147 | "code-climate": {icon: "\uf7f4", color: [3]uint8{238, 238, 238}}, // code-climate 148 | "log": {icon: "\uf719", color: [3]uint8{175, 180, 43}}, // log 149 | "ejs": {icon: "\ue618", color: [3]uint8{255, 202, 61}}, // ejs 150 | "grunt": {icon: "\ue611", color: [3]uint8{251, 170, 61}}, // grunt 151 | "django": {icon: "\ue71d", color: [3]uint8{67, 160, 71}}, // django 152 | "makefile": {icon: "\uf728", color: [3]uint8{239, 83, 80}}, // makefile 153 | "bitbucket": {icon: "\uf171", color: [3]uint8{31, 136, 229}}, // bitbucket 154 | "d": {icon: "\ue7af", color: [3]uint8{244, 68, 62}}, // d 155 | "mdx": {icon: "\uf853", color: [3]uint8{255, 202, 61}}, // mdx 156 | "azure-pipelines": {icon: "\uf427", color: [3]uint8{20, 101, 192}}, // azure-pipelines 157 | "azure": {icon: "\ufd03", color: [3]uint8{31, 136, 229}}, // azure 158 | "razor": {icon: "\uf564", color: [3]uint8{66, 165, 245}}, // razor 159 | "asciidoc": {icon: "\uf718", color: [3]uint8{244, 68, 62}}, // asciidoc 160 | "edge": {icon: "\uf564", color: [3]uint8{239, 111, 60}}, // edge 161 | "scheme": {icon: "\ufb26", color: [3]uint8{244, 68, 62}}, // scheme 162 | "3d": {icon: "\ue79b", color: [3]uint8{40, 182, 246}}, // 3d 163 | "svg": {icon: "\ufc1f", color: [3]uint8{255, 181, 62}}, // svg 164 | "vim": {icon: "\ue62b", color: [3]uint8{67, 160, 71}}, // vim 165 | "moonscript": {icon: "\uf186", color: [3]uint8{251, 193, 60}}, // moonscript 166 | "codeowners": {icon: "\uf507", color: [3]uint8{175, 180, 43}}, // codeowners 167 | "disc": {icon: "\ue271", color: [3]uint8{176, 190, 197}}, // disc 168 | "fortran": {icon: "F", color: [3]uint8{250, 111, 66}}, // fortran 169 | "tcl": {icon: "\ufbd1", color: [3]uint8{239, 83, 80}}, // tcl 170 | "liquid": {icon: "\ue275", color: [3]uint8{40, 182, 246}}, // liquid 171 | "prolog": {icon: "\ue7a1", color: [3]uint8{239, 83, 80}}, // prolog 172 | "husky": {icon: "\uf8e8", color: [3]uint8{229, 229, 229}}, // husky 173 | "coconut": {icon: "\uf5d2", color: [3]uint8{141, 110, 99}}, // coconut 174 | "sketch": {icon: "\uf6c7", color: [3]uint8{255, 194, 61}}, // sketch 175 | "pawn": {icon: "\ue261", color: [3]uint8{239, 111, 60}}, // pawn 176 | "commitlint": {icon: "\ufc16", color: [3]uint8{43, 150, 137}}, // commitlint 177 | "dhall": {icon: "\uf448", color: [3]uint8{120, 144, 156}}, // dhall 178 | "dune": {icon: "\uf7f4", color: [3]uint8{244, 127, 61}}, // dune 179 | "shaderlab": {icon: "\ufbad", color: [3]uint8{25, 118, 210}}, // shaderlab 180 | "command": {icon: "\ufb32", color: [3]uint8{175, 188, 194}}, // command 181 | "stryker": {icon: "\uf05b", color: [3]uint8{239, 83, 80}}, // stryker 182 | "modernizr": {icon: "\ue720", color: [3]uint8{234, 72, 99}}, // modernizr 183 | "roadmap": {icon: "\ufb6d", color: [3]uint8{48, 166, 154}}, // roadmap 184 | "debian": {icon: "\uf306", color: [3]uint8{211, 61, 76}}, // debian 185 | "ubuntu": {icon: "\uf31c", color: [3]uint8{214, 73, 53}}, // ubuntu 186 | "arch": {icon: "\uf303", color: [3]uint8{33, 142, 202}}, // arch 187 | "redhat": {icon: "\uf316", color: [3]uint8{231, 61, 58}}, // redhat 188 | "gentoo": {icon: "\uf30d", color: [3]uint8{148, 141, 211}}, // gentoo 189 | "linux": {icon: "\ue712", color: [3]uint8{238, 207, 55}}, // linux 190 | "raspberry-pi": {icon: "\uf315", color: [3]uint8{208, 60, 76}}, // raspberry-pi 191 | "manjaro": {icon: "\uf312", color: [3]uint8{73, 185, 90}}, // manjaro 192 | "opensuse": {icon: "\uf314", color: [3]uint8{111, 180, 36}}, // opensuse 193 | "fedora": {icon: "\uf30a", color: [3]uint8{52, 103, 172}}, // fedora 194 | "freebsd": {icon: "\uf30c", color: [3]uint8{175, 44, 42}}, // freebsd 195 | "centOS": {icon: "\uf304", color: [3]uint8{157, 83, 135}}, // centOS 196 | "alpine": {icon: "\uf300", color: [3]uint8{14, 87, 123}}, // alpine 197 | "mint": {icon: "\uf30f", color: [3]uint8{125, 190, 58}}, // mint 198 | "routing": {icon: "\ufb40", color: [3]uint8{67, 160, 71}}, // routing 199 | "laravel": {icon: "\ue73f", color: [3]uint8{248, 80, 81}}, // laravel 200 | "pug": {icon: "\ue60e", color: [3]uint8{239, 204, 163}}, // pug (Not supported by nerdFont) 201 | "blink": {icon: "\uf72a", color: [3]uint8{249, 169, 60}}, // blink (The Foundry Nuke) (Not supported by nerdFont) 202 | "postcss": {icon: "\uf81b", color: [3]uint8{244, 68, 62}}, // postcss (Not supported by nerdFont) 203 | "jinja": {icon: "\ue000", color: [3]uint8{174, 44, 42}}, // jinja (Not supported by nerdFont) 204 | "sublime": {icon: "\ue7aa", color: [3]uint8{239, 148, 58}}, // sublime (Not supported by nerdFont) 205 | "markojs": {icon: "\uf13b", color: [3]uint8{2, 119, 189}}, // markojs (Not supported by nerdFont) 206 | "vscode": {icon: "\ue70c", color: [3]uint8{33, 150, 243}}, // vscode (Not supported by nerdFont) 207 | "qsharp": {icon: "\uf292", color: [3]uint8{251, 193, 60}}, // qsharp (Not supported by nerdFont) 208 | "vala": {icon: "\uf7ab", color: [3]uint8{149, 117, 205}}, // vala (Not supported by nerdFont) 209 | "zig": {icon: "Z", color: [3]uint8{249, 169, 60}}, // zig (Not supported by nerdFont) 210 | "h": {icon: "h", color: [3]uint8{2, 119, 189}}, // h (Not supported by nerdFont) 211 | "hpp": {icon: "h", color: [3]uint8{2, 119, 189}}, // hpp (Not supported by nerdFont) 212 | "powershell": {icon: "\ufcb5", color: [3]uint8{5, 169, 244}}, // powershell (Not supported by nerdFont) 213 | "gradle": {icon: "\ufcc4", color: [3]uint8{29, 151, 167}}, // gradle (Not supported by nerdFont) 214 | "arduino": {icon: "\ue255", color: [3]uint8{35, 151, 156}}, // arduino (Not supported by nerdFont) 215 | "tex": {icon: "\uf783", color: [3]uint8{66, 165, 245}}, // tex (Not supported by nerdFont) 216 | "graphql": {icon: "\ue284", color: [3]uint8{237, 80, 122}}, // graphql (Not supported by nerdFont) 217 | "kotlin": {icon: "\ue70e", color: [3]uint8{139, 195, 74}}, // kotlin (Not supported by nerdFont) 218 | "actionscript": {icon: "\ufb25", color: [3]uint8{244, 68, 62}}, // actionscript (Not supported by nerdFont) 219 | "autohotkey": {icon: "\uf812", color: [3]uint8{76, 175, 80}}, // autohotkey (Not supported by nerdFont) 220 | "flash": {icon: "\uf740", color: [3]uint8{198, 52, 54}}, // flash (Not supported by nerdFont) 221 | "swc": {icon: "\ufbd3", color: [3]uint8{198, 52, 54}}, // swc (Not supported by nerdFont) 222 | "cmake": {icon: "\uf425", color: [3]uint8{178, 178, 179}}, // cmake (Not supported by nerdFont) 223 | "nuxt": {icon: "\ue2a6", color: [3]uint8{65, 184, 131}}, // nuxt (Not supported by nerdFont) 224 | "ocaml": {icon: "\uf1ce", color: [3]uint8{253, 154, 62}}, // ocaml (Not supported by nerdFont) 225 | "haxe": {icon: "\uf425", color: [3]uint8{246, 137, 61}}, // haxe (Not supported by nerdFont) 226 | "puppet": {icon: "\uf595", color: [3]uint8{251, 193, 60}}, // puppet (Not supported by nerdFont) 227 | "purescript": {icon: "\uf670", color: [3]uint8{66, 165, 245}}, // purescript (Not supported by nerdFont) 228 | "merlin": {icon: "\uf136", color: [3]uint8{66, 165, 245}}, // merlin (Not supported by nerdFont) 229 | "mjml": {icon: "\ue714", color: [3]uint8{249, 89, 63}}, // mjml (Not supported by nerdFont) 230 | "terraform": {icon: "\ue20f", color: [3]uint8{92, 107, 192}}, // terraform (Not supported by nerdFont) 231 | "apiblueprint": {icon: "\uf031", color: [3]uint8{66, 165, 245}}, // apiblueprint (Not supported by nerdFont) 232 | "slim": {icon: "\uf24e", color: [3]uint8{245, 129, 61}}, // slim (Not supported by nerdFont) 233 | "babel": {icon: "\uf5a0", color: [3]uint8{253, 217, 59}}, // babel (Not supported by nerdFont) 234 | "codecov": {icon: "\ue37c", color: [3]uint8{237, 80, 122}}, // codecov (Not supported by nerdFont) 235 | "protractor": {icon: "\uf288", color: [3]uint8{229, 61, 58}}, // protractor (Not supported by nerdFont) 236 | "eslint": {icon: "\ufbf6", color: [3]uint8{121, 134, 203}}, // eslint (Not supported by nerdFont) 237 | "mocha": {icon: "\uf6a9", color: [3]uint8{161, 136, 127}}, // mocha (Not supported by nerdFont) 238 | "firebase": {icon: "\ue787", color: [3]uint8{251, 193, 60}}, // firebase (Not supported by nerdFont) 239 | "stylelint": {icon: "\ufb76", color: [3]uint8{207, 216, 220}}, // stylelint (Not supported by nerdFont) 240 | "prettier": {icon: "\uf8e2", color: [3]uint8{86, 179, 180}}, // prettier (Not supported by nerdFont) 241 | "jest": {icon: "J", color: [3]uint8{244, 85, 62}}, // jest (Not supported by nerdFont) 242 | "storybook": {icon: "\ufd2c", color: [3]uint8{237, 80, 122}}, // storybook (Not supported by nerdFont) 243 | "fastlane": {icon: "\ufbff", color: [3]uint8{149, 119, 232}}, // fastlane (Not supported by nerdFont) 244 | "helm": {icon: "\ufd31", color: [3]uint8{32, 173, 194}}, // helm (Not supported by nerdFont) 245 | "i18n": {icon: "\uf7be", color: [3]uint8{121, 134, 203}}, // i18n (Not supported by nerdFont) 246 | "semantic-release": {icon: "\uf70f", color: [3]uint8{245, 245, 245}}, // semantic-release (Not supported by nerdFont) 247 | "godot": {icon: "\ufba7", color: [3]uint8{79, 195, 247}}, // godot (Not supported by nerdFont) 248 | "godot-assets": {icon: "\ufba7", color: [3]uint8{129, 199, 132}}, // godot-assets (Not supported by nerdFont) 249 | "vagrant": {icon: "\uf27d", color: [3]uint8{20, 101, 192}}, // vagrant (Not supported by nerdFont) 250 | "tailwindcss": {icon: "\ufc8b", color: [3]uint8{77, 182, 172}}, // tailwindcss (Not supported by nerdFont) 251 | "gcp": {icon: "\uf662", color: [3]uint8{70, 136, 250}}, // gcp (Not supported by nerdFont) 252 | "opam": {icon: "\uf1ce", color: [3]uint8{255, 213, 79}}, // opam (Not supported by nerdFont) 253 | "pascal": {icon: "\uf8da", color: [3]uint8{3, 136, 209}}, // pascal (Not supported by nerdFont) 254 | "nuget": {icon: "\ue77f", color: [3]uint8{3, 136, 209}}, // nuget (Not supported by nerdFont) 255 | "denizenscript": {icon: "D", color: [3]uint8{255, 213, 79}}, // denizenscript (Not supported by nerdFont) 256 | // "riot": {icon:"\u", color:[3]uint8{255, 255, 255}}, // riot 257 | // "autoit": {icon:"\u", color:[3]uint8{255, 255, 255}}, // autoit 258 | // "livescript": {icon:"\u", color:[3]uint8{255, 255, 255}}, // livescript 259 | // "reason": {icon:"\u", color:[3]uint8{255, 255, 255}}, // reason 260 | // "bucklescript": {icon:"\u", color:[3]uint8{255, 255, 255}}, // bucklescript 261 | // "mathematica": {icon:"\u", color:[3]uint8{255, 255, 255}}, // mathematica 262 | // "wolframlanguage": {icon:"\u", color:[3]uint8{255, 255, 255}}, // wolframlanguage 263 | // "nunjucks": {icon:"\u", color:[3]uint8{255, 255, 255}}, // nunjucks 264 | // "haml": {icon:"\u", color:[3]uint8{255, 255, 255}}, // haml 265 | // "cucumber": {icon:"\u", color:[3]uint8{255, 255, 255}}, // cucumber 266 | // "vfl": {icon:"\u", color:[3]uint8{255, 255, 255}}, // vfl 267 | // "kl": {icon:"\u", color:[3]uint8{255, 255, 255}}, // kl 268 | // "coldfusion": {icon:"\u", color:[3]uint8{255, 255, 255}}, // coldfusion 269 | // "cabal": {icon:"\u", color:[3]uint8{255, 255, 255}}, // cabal 270 | // "restql": {icon:"\u", color:[3]uint8{255, 255, 255}}, // restql 271 | // "kivy": {icon:"\u", color:[3]uint8{255, 255, 255}}, // kivy 272 | // "graphcool": {icon:"\u", color:[3]uint8{255, 255, 255}}, // graphcool 273 | // "sbt": {icon:"\u", color:[3]uint8{255, 255, 255}}, // sbt 274 | // "flow": {icon:"\u", color:[3]uint8{255, 255, 255}}, // flow 275 | // "bithound": {icon:"\u", color:[3]uint8{255, 255, 255}}, // bithound 276 | // "appveyor": {icon:"\u", color:[3]uint8{255, 255, 255}}, // appveyor 277 | // "fusebox": {icon:"\u", color:[3]uint8{255, 255, 255}}, // fusebox 278 | // "editorconfig": {icon:"\u", color:[3]uint8{255, 255, 255}}, // editorconfig 279 | // "watchman": {icon:"\u", color:[3]uint8{255, 255, 255}}, // watchman 280 | // "aurelia": {icon:"\u", color:[3]uint8{255, 255, 255}}, // aurelia 281 | // "rollup": {icon:"\u", color:[3]uint8{255, 255, 255}}, // rollup 282 | // "hack": {icon:"\u", color:[3]uint8{255, 255, 255}}, // hack 283 | // "apollo": {icon:"\u", color:[3]uint8{255, 255, 255}}, // apollo 284 | // "nodemon": {icon:"\u", color:[3]uint8{255, 255, 255}}, // nodemon 285 | // "webhint": {icon:"\u", color:[3]uint8{255, 255, 255}}, // webhint 286 | // "browserlist": {icon:"\u", color:[3]uint8{255, 255, 255}}, // browserlist 287 | // "crystal": {icon:"\u", color:[3]uint8{255, 255, 255}}, // crystal 288 | // "snyk": {icon:"\u", color:[3]uint8{255, 255, 255}}, // snyk 289 | // "drone": {icon:"\u", color:[3]uint8{255, 255, 255}}, // drone 290 | // "cuda": {icon:"\u", color:[3]uint8{255, 255, 255}}, // cuda 291 | // "dotjs": {icon:"\u", color:[3]uint8{255, 255, 255}}, // dotjs 292 | // "sequelize": {icon:"\u", color:[3]uint8{255, 255, 255}}, // sequelize 293 | // "gatsby": {icon:"\u", color:[3]uint8{255, 255, 255}}, // gatsby 294 | // "wakatime": {icon:"\u", color:[3]uint8{255, 255, 255}}, // wakatime 295 | // "circleci": {icon:"\u", color:[3]uint8{255, 255, 255}}, // circleci 296 | // "cloudfoundry": {icon:"\u", color:[3]uint8{255, 255, 255}}, // cloudfoundry 297 | // "processing": {icon:"\u", color:[3]uint8{255, 255, 255}}, // processing 298 | // "wepy": {icon:"\u", color:[3]uint8{255, 255, 255}}, // wepy 299 | // "hcl": {icon:"\u", color:[3]uint8{255, 255, 255}}, // hcl 300 | // "san": {icon:"\u", color:[3]uint8{255, 255, 255}}, // san 301 | // "wallaby": {icon:"\u", color:[3]uint8{255, 255, 255}}, // wallaby 302 | // "stencil": {icon:"\u", color:[3]uint8{255, 255, 255}}, // stencil 303 | // "red": {icon:"\u", color:[3]uint8{255, 255, 255}}, // red 304 | // "webassembly": {icon:"\u", color:[3]uint8{255, 255, 255}}, // webassembly 305 | // "foxpro": {icon:"\u", color:[3]uint8{255, 255, 255}}, // foxpro 306 | // "jupyter": {icon:"\u", color:[3]uint8{255, 255, 255}}, // jupyter 307 | // "ballerina": {icon:"\u", color:[3]uint8{255, 255, 255}}, // ballerina 308 | // "racket": {icon:"\u", color:[3]uint8{255, 255, 255}}, // racket 309 | // "bazel": {icon:"\u", color:[3]uint8{255, 255, 255}}, // bazel 310 | // "mint": {icon:"\u", color:[3]uint8{255, 255, 255}}, // mint 311 | // "velocity": {icon:"\u", color:[3]uint8{255, 255, 255}}, // velocity 312 | // "prisma": {icon:"\u", color:[3]uint8{255, 255, 255}}, // prisma 313 | // "abc": {icon:"\u", color:[3]uint8{255, 255, 255}}, // abc 314 | // "istanbul": {icon:"\u", color:[3]uint8{255, 255, 255}}, // istanbul 315 | // "lisp": {icon:"\u", color:[3]uint8{255, 255, 255}}, // lisp 316 | // "buildkite": {icon:"\u", color:[3]uint8{255, 255, 255}}, // buildkite 317 | // "netlify": {icon:"\u", color:[3]uint8{255, 255, 255}}, // netlify 318 | // "svelte": {icon:"\u", color:[3]uint8{255, 255, 255}}, // svelte 319 | // "nest": {icon:"\u", color:[3]uint8{255, 255, 255}}, // nest 320 | // "percy": {icon:"\u", color:[3]uint8{255, 255, 255}}, // percy 321 | // "gitpod": {icon:"\u", color:[3]uint8{255, 255, 255}}, // gitpod 322 | // "advpl_prw": {icon:"\u", color:[3]uint8{255, 255, 255}}, // advpl_prw 323 | // "advpl_ptm": {icon:"\u", color:[3]uint8{255, 255, 255}}, // advpl_ptm 324 | // "advpl_tlpp": {icon:"\u", color:[3]uint8{255, 255, 255}}, // advpl_tlpp 325 | // "advpl_include": {icon:"\u", color:[3]uint8{255, 255, 255}}, // advpl_include 326 | // "tilt": {icon:"\u", color:[3]uint8{255, 255, 255}}, // tilt 327 | // "capacitor": {icon:"\u", color:[3]uint8{255, 255, 255}}, // capacitor 328 | // "adonis": {icon:"\u", color:[3]uint8{255, 255, 255}}, // adonis 329 | // "forth": {icon:"\u", color:[3]uint8{255, 255, 255}}, // forth 330 | // "uml": {icon:"\u", color:[3]uint8{255, 255, 255}}, // uml 331 | // "meson": {icon:"\u", color:[3]uint8{255, 255, 255}}, // meson 332 | // "buck": {icon:"\u", color:[3]uint8{255, 255, 255}}, // buck 333 | // "sml": {icon:"\u", color:[3]uint8{255, 255, 255}}, // sml 334 | // "nrwl": {icon:"\u", color:[3]uint8{255, 255, 255}}, // nrwl 335 | // "imba": {icon:"\u", color:[3]uint8{255, 255, 255}}, // imba 336 | // "drawio": {icon:"\u", color:[3]uint8{255, 255, 255}}, // drawio 337 | // "sas": {icon:"\u", color:[3]uint8{255, 255, 255}}, // sas 338 | // "slug": {icon:"\u", color:[3]uint8{255, 255, 255}}, // slug 339 | 340 | "dir-config": {icon: "\ue5fc", color: [3]uint8{32, 173, 194}}, // dir-config 341 | "dir-controller": {icon: "\ue5fc", color: [3]uint8{255, 194, 61}}, // dir-controller 342 | "dir-git": {icon: "\ue5fb", color: [3]uint8{250, 111, 66}}, // dir-git 343 | "dir-github": {icon: "\ue5fd", color: [3]uint8{84, 110, 122}}, // dir-github 344 | "dir-npm": {icon: "\ue5fa", color: [3]uint8{203, 56, 55}}, // dir-npm 345 | "dir-include": {icon: "\uf756", color: [3]uint8{3, 155, 229}}, // dir-include 346 | "dir-import": {icon: "\uf756", color: [3]uint8{175, 180, 43}}, // dir-import 347 | "dir-upload": {icon: "\uf758", color: [3]uint8{250, 111, 66}}, // dir-upload 348 | "dir-download": {icon: "\uf74c", color: [3]uint8{76, 175, 80}}, // dir-download 349 | "dir-secure": {icon: "\uf74f", color: [3]uint8{249, 169, 60}}, // dir-secure 350 | "dir-images": {icon: "\uf74e", color: [3]uint8{43, 150, 137}}, // dir-images 351 | "dir-environment": {icon: "\uf74e", color: [3]uint8{102, 187, 106}}, // dir-environment 352 | } 353 | 354 | // IconDef is a map of default icons if none can be found. 355 | var IconDef = map[string]*IconInfo{ 356 | "dir": {icon: "\uf74a", color: [3]uint8{224, 177, 77}}, 357 | "diropen": {icon: "\ufc6e", color: [3]uint8{224, 177, 77}}, 358 | "hiddendir": {icon: "\uf755", color: [3]uint8{224, 177, 77}}, 359 | "exe": {icon: "\uf713", color: [3]uint8{76, 175, 80}}, 360 | "file": {icon: "\uf723", color: [3]uint8{65, 129, 190}}, 361 | "hiddenfile": {icon: "\ufb12", color: [3]uint8{65, 129, 190}}, 362 | } 363 | -------------------------------------------------------------------------------- /icons/icons.go: -------------------------------------------------------------------------------- 1 | // Package icons provides a set of unicode icons 2 | // based on type and extension. 3 | package icons 4 | 5 | import ( 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // GetIndicator returns the indicator for the given file. 11 | func GetIndicator(modebit os.FileMode) (i string) { 12 | switch { 13 | case modebit&os.ModeDir > 0: 14 | i = "/" 15 | case modebit&os.ModeNamedPipe > 0: 16 | i = "|" 17 | case modebit&os.ModeSymlink > 0: 18 | i = "@" 19 | case modebit&os.ModeSocket > 0: 20 | i = "=" 21 | case modebit&1000000 > 0: 22 | i = "*" 23 | } 24 | 25 | return i 26 | } 27 | 28 | // GetIcon returns the icon based on its name, extension and indicator. 29 | func GetIcon(name, ext, indicator string) (icon, color string) { 30 | var i *IconInfo 31 | var ok bool 32 | const DOT = '.' 33 | 34 | switch indicator { 35 | case "/": 36 | i, ok = IconDir[strings.ToLower(name+ext)] 37 | if ok { 38 | break 39 | } 40 | if len(name) == 0 || DOT == name[0] { 41 | i = IconDef["hiddendir"] 42 | break 43 | } 44 | i = IconDef["dir"] 45 | default: 46 | i, ok = IconFileName[strings.ToLower(name+ext)] 47 | if ok { 48 | break 49 | } 50 | 51 | if ext == ".go" && strings.HasSuffix(name, "_test") { 52 | i = IconSet["go-test"] 53 | 54 | break 55 | } 56 | 57 | t := strings.Split(name, ".") 58 | if len(t) > 1 && t[0] != "" { 59 | i, ok = IconSubExt[strings.ToLower(t[len(t)-1]+ext)] 60 | if ok { 61 | break 62 | } 63 | } 64 | 65 | i, ok = IconExt[strings.ToLower(strings.TrimPrefix(ext, "."))] 66 | if ok { 67 | break 68 | } 69 | 70 | if len(name) == 0 || DOT == name[0] { 71 | i = IconDef["hiddenfile"] 72 | 73 | break 74 | } 75 | i = IconDef["file"] 76 | } 77 | 78 | if indicator == "*" { 79 | if i.GetGlyph() == "\uf723" { 80 | i = IconDef["exe"] 81 | } 82 | 83 | i.MakeExe() 84 | } 85 | 86 | return i.GetGlyph(), i.GetColor(1) 87 | } 88 | -------------------------------------------------------------------------------- /icons/sub_extensions.go: -------------------------------------------------------------------------------- 1 | package icons 2 | 3 | // IconSubExt is a map to get the icon based on its subdirectory extension. 4 | var IconSubExt = map[string]*IconInfo{ 5 | "routing.ts": IconSet["routing"], 6 | "routing.tsx": IconSet["routing"], 7 | "routing.js": IconSet["routing"], 8 | "routing.jsx": IconSet["routing"], 9 | "routes.ts": IconSet["routing"], 10 | "routes.tsx": IconSet["routing"], 11 | "routes.js": IconSet["routing"], 12 | "routes.jsx": IconSet["routing"], 13 | "sln.dotsettings": IconSet["settings"], 14 | "d.ts": IconSet["typescript-def"], 15 | "vcxitems.filters": IconSet["visualstudio"], 16 | "vcxproj.filters": IconSet["visualstudio"], 17 | "js.map": IconSet["javascript-map"], 18 | "mjs.map": IconSet["javascript-map"], 19 | "css.map": IconSet["css-map"], 20 | "spec.ts": IconSet["test-ts"], 21 | "e2e-spec.ts": IconSet["test-ts"], 22 | "test.ts": IconSet["test-ts"], 23 | "ts.snap": IconSet["test-ts"], 24 | "spec.tsx": IconSet["test-jsx"], 25 | "test.tsx": IconSet["test-jsx"], 26 | "tsx.snap": IconSet["test-jsx"], 27 | "spec.jsx": IconSet["test-jsx"], 28 | "test.jsx": IconSet["test-jsx"], 29 | "jsx.snap": IconSet["test-jsx"], 30 | "spec.js": IconSet["test-js"], 31 | "e2e-spec.js": IconSet["test-js"], 32 | "test.js": IconSet["test-js"], 33 | "js.snap": IconSet["test-js"], 34 | "tf.json": IconSet["terraform"], 35 | "blade.php": IconSet["laravel"], 36 | "inky.php": IconSet["laravel"], 37 | "gitlab-ci.yml": IconSet["gitlab"], 38 | "stories.js": IconSet["storybook"], 39 | "stories.jsx": IconSet["storybook"], 40 | "story.js": IconSet["storybook"], 41 | "story.jsx": IconSet["storybook"], 42 | "stories.ts": IconSet["storybook"], 43 | "stories.tsx": IconSet["storybook"], 44 | "story.ts": IconSet["storybook"], 45 | "story.tsx": IconSet["storybook"], 46 | "azure-pipelines.yml": IconSet["azure-pipelines"], 47 | "azure-pipelines.yaml": IconSet["azure-pipelines"], 48 | } 49 | -------------------------------------------------------------------------------- /image/image.go: -------------------------------------------------------------------------------- 1 | // Package image provides an image bubble which can render 2 | // images as strings. 3 | package image 4 | 5 | import ( 6 | "image" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/charmbracelet/bubbles/viewport" 12 | tea "github.com/charmbracelet/bubbletea" 13 | "github.com/charmbracelet/lipgloss" 14 | "github.com/disintegration/imaging" 15 | "github.com/lucasb-eyer/go-colorful" 16 | ) 17 | 18 | type convertImageToStringMsg string 19 | type errorMsg error 20 | 21 | // ToString converts an image to a string representation of an image. 22 | func ToString(width int, img image.Image) string { 23 | img = imaging.Resize(img, width, 0, imaging.Lanczos) 24 | b := img.Bounds() 25 | imageWidth := b.Max.X 26 | h := b.Max.Y 27 | str := strings.Builder{} 28 | 29 | for heightCounter := 0; heightCounter < h; heightCounter += 2 { 30 | for x := imageWidth; x < width; x += 2 { 31 | str.WriteString(" ") 32 | } 33 | 34 | for x := 0; x < imageWidth; x++ { 35 | c1, _ := colorful.MakeColor(img.At(x, heightCounter)) 36 | color1 := lipgloss.Color(c1.Hex()) 37 | c2, _ := colorful.MakeColor(img.At(x, heightCounter+1)) 38 | color2 := lipgloss.Color(c2.Hex()) 39 | str.WriteString(lipgloss.NewStyle().Foreground(color1). 40 | Background(color2).Render("▀")) 41 | } 42 | 43 | str.WriteString("\n") 44 | } 45 | 46 | return str.String() 47 | } 48 | 49 | // convertImageToStringCmd redraws the image based on the width provided. 50 | func convertImageToStringCmd(width int, filename string) tea.Cmd { 51 | return func() tea.Msg { 52 | imageContent, err := os.Open(filepath.Clean(filename)) 53 | if err != nil { 54 | return errorMsg(err) 55 | } 56 | 57 | img, _, err := image.Decode(imageContent) 58 | if err != nil { 59 | return errorMsg(err) 60 | } 61 | 62 | imageString := ToString(width, img) 63 | 64 | return convertImageToStringMsg(imageString) 65 | } 66 | } 67 | 68 | // Model represents the properties of a code bubble. 69 | type Model struct { 70 | Viewport viewport.Model 71 | Active bool 72 | Borderless bool 73 | FileName string 74 | ImageString string 75 | } 76 | 77 | // New creates a new instance of code. 78 | func New(active, borderless bool, borderColor lipgloss.AdaptiveColor) Model { 79 | viewPort := viewport.New(0, 0) 80 | 81 | return Model{ 82 | Viewport: viewPort, 83 | Active: active, 84 | } 85 | } 86 | 87 | // Init initializes the code bubble. 88 | func (m Model) Init() tea.Cmd { 89 | return nil 90 | } 91 | 92 | // SetFileName sets current file to highlight, this 93 | // returns a cmd which will highlight the text. 94 | func (m *Model) SetFileName(filename string) tea.Cmd { 95 | m.FileName = filename 96 | 97 | return convertImageToStringCmd(m.Viewport.Width, filename) 98 | } 99 | 100 | // SetSize sets the size of the bubble. 101 | func (m *Model) SetSize(w, h int) tea.Cmd { 102 | m.Viewport.Width = w 103 | m.Viewport.Height = h 104 | 105 | if m.FileName != "" { 106 | return convertImageToStringCmd(m.Viewport.Width, m.FileName) 107 | } 108 | 109 | return nil 110 | } 111 | 112 | // SetIsActive sets if the bubble is currently active 113 | func (m *Model) SetIsActive(active bool) { 114 | m.Active = active 115 | } 116 | 117 | // GotoTop jumps to the top of the viewport. 118 | func (m *Model) GotoTop() { 119 | m.Viewport.GotoTop() 120 | } 121 | 122 | // SetBorderless sets weather or not to show the border. 123 | func (m *Model) SetBorderless(borderless bool) { 124 | m.Borderless = borderless 125 | } 126 | 127 | // Update handles updating the UI of a code bubble. 128 | func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { 129 | var ( 130 | cmd tea.Cmd 131 | cmds []tea.Cmd 132 | ) 133 | 134 | switch msg := msg.(type) { 135 | case convertImageToStringMsg: 136 | m.ImageString = lipgloss.NewStyle(). 137 | Width(m.Viewport.Width). 138 | Height(m.Viewport.Height). 139 | Render(string(msg)) 140 | 141 | m.Viewport.SetContent(m.ImageString) 142 | 143 | return m, nil 144 | case errorMsg: 145 | m.FileName = "" 146 | m.ImageString = lipgloss.NewStyle(). 147 | Width(m.Viewport.Width). 148 | Height(m.Viewport.Height). 149 | Render("Error: " + msg.Error()) 150 | 151 | m.Viewport.SetContent(m.ImageString) 152 | 153 | return m, nil 154 | } 155 | 156 | if m.Active { 157 | m.Viewport, cmd = m.Viewport.Update(msg) 158 | cmds = append(cmds, cmd) 159 | } 160 | 161 | return m, tea.Batch(cmds...) 162 | } 163 | 164 | // View returns a string representation of the code bubble. 165 | func (m Model) View() string { 166 | return m.Viewport.View() 167 | } 168 | -------------------------------------------------------------------------------- /markdown/markdown.go: -------------------------------------------------------------------------------- 1 | // Package markdown provides an markdown bubble which can render 2 | // markdown in a pretty manor. 3 | package markdown 4 | 5 | import ( 6 | "errors" 7 | 8 | "github.com/charmbracelet/bubbles/viewport" 9 | tea "github.com/charmbracelet/bubbletea" 10 | "github.com/charmbracelet/glamour" 11 | "github.com/charmbracelet/lipgloss" 12 | "github.com/mistakenelf/teacup/filesystem" 13 | ) 14 | 15 | type renderMarkdownMsg string 16 | type errorMsg error 17 | 18 | // Model represents the properties of a code bubble. 19 | type Model struct { 20 | Viewport viewport.Model 21 | Active bool 22 | FileName string 23 | } 24 | 25 | // RenderMarkdown renders the markdown content with glamour. 26 | func RenderMarkdown(width int, content string) (string, error) { 27 | background := "light" 28 | 29 | if lipgloss.HasDarkBackground() { 30 | background = "dark" 31 | } 32 | 33 | r, _ := glamour.NewTermRenderer( 34 | glamour.WithWordWrap(width), 35 | glamour.WithStandardStyle(background), 36 | ) 37 | 38 | out, err := r.Render(content) 39 | if err != nil { 40 | return "", errors.Unwrap(err) 41 | } 42 | 43 | return out, nil 44 | } 45 | 46 | // renderMarkdownCmd renders text as pretty markdown. 47 | func renderMarkdownCmd(width int, filename string) tea.Cmd { 48 | return func() tea.Msg { 49 | content, err := filesystem.ReadFileContent(filename) 50 | if err != nil { 51 | return errorMsg(err) 52 | } 53 | 54 | markdownContent, err := RenderMarkdown(width, content) 55 | if err != nil { 56 | return errorMsg(err) 57 | } 58 | 59 | return renderMarkdownMsg(markdownContent) 60 | } 61 | } 62 | 63 | // New creates a new instance of markdown. 64 | func New(active bool) Model { 65 | viewPort := viewport.New(0, 0) 66 | 67 | return Model{ 68 | Viewport: viewPort, 69 | Active: active, 70 | } 71 | } 72 | 73 | // Init initializes the code bubble. 74 | func (m Model) Init() tea.Cmd { 75 | return nil 76 | } 77 | 78 | // SetFileName sets current file to render, this 79 | // returns a cmd which will render the text. 80 | func (m *Model) SetFileName(filename string) tea.Cmd { 81 | m.FileName = filename 82 | 83 | return renderMarkdownCmd(m.Viewport.Width, filename) 84 | } 85 | 86 | // SetSize sets the size of the bubble. 87 | func (m *Model) SetSize(w, h int) tea.Cmd { 88 | m.Viewport.Width = w 89 | m.Viewport.Height = h 90 | 91 | if m.FileName != "" { 92 | return renderMarkdownCmd(m.Viewport.Width, m.FileName) 93 | } 94 | 95 | return nil 96 | } 97 | 98 | // GotoTop jumps to the top of the viewport. 99 | func (m *Model) GotoTop() { 100 | m.Viewport.GotoTop() 101 | } 102 | 103 | // SetIsActive sets if the bubble is currently active. 104 | func (m *Model) SetIsActive(active bool) { 105 | m.Active = active 106 | } 107 | 108 | // Update handles updating the UI of a code bubble. 109 | func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { 110 | var ( 111 | cmd tea.Cmd 112 | cmds []tea.Cmd 113 | ) 114 | 115 | switch msg := msg.(type) { 116 | case renderMarkdownMsg: 117 | content := lipgloss.NewStyle(). 118 | Width(m.Viewport.Width). 119 | Height(m.Viewport.Height). 120 | Render(string(msg)) 121 | 122 | m.Viewport.SetContent(content) 123 | 124 | return m, nil 125 | case errorMsg: 126 | m.FileName = "" 127 | m.Viewport.SetContent(msg.Error()) 128 | 129 | return m, nil 130 | } 131 | 132 | if m.Active { 133 | m.Viewport, cmd = m.Viewport.Update(msg) 134 | cmds = append(cmds, cmd) 135 | } 136 | 137 | return m, tea.Batch(cmds...) 138 | } 139 | 140 | // View returns a string representation of the markdown bubble. 141 | func (m Model) View() string { 142 | return m.Viewport.View() 143 | } 144 | -------------------------------------------------------------------------------- /pdf/pdf.go: -------------------------------------------------------------------------------- 1 | // Package pdf provides an pdf bubble which can render 2 | // pdf files as strings. 3 | package pdf 4 | 5 | import ( 6 | "bytes" 7 | "errors" 8 | 9 | "github.com/charmbracelet/bubbles/viewport" 10 | tea "github.com/charmbracelet/bubbletea" 11 | "github.com/charmbracelet/lipgloss" 12 | "github.com/ledongthuc/pdf" 13 | ) 14 | 15 | type renderPDFMsg string 16 | type errorMsg error 17 | 18 | // Model represents the properties of a pdf bubble. 19 | type Model struct { 20 | Viewport viewport.Model 21 | Active bool 22 | FileName string 23 | } 24 | 25 | // readPdf reads a PDF file given a name. 26 | func readPdf(name string) (string, error) { 27 | file, reader, err := pdf.Open(name) 28 | if err != nil { 29 | return "", errors.Unwrap(err) 30 | } 31 | 32 | defer func() { 33 | if e := file.Close(); e != nil { 34 | err = e 35 | } 36 | }() 37 | 38 | buf := new(bytes.Buffer) 39 | buffer, err := reader.GetPlainText() 40 | 41 | if err != nil { 42 | return "", errors.Unwrap(err) 43 | } 44 | 45 | _, err = buf.ReadFrom(buffer) 46 | if err != nil { 47 | return "", errors.Unwrap(err) 48 | } 49 | 50 | return buf.String(), nil 51 | } 52 | 53 | // renderPDFCmd reads the content of a PDF and returns its content as a string. 54 | func renderPDFCmd(filename string) tea.Cmd { 55 | return func() tea.Msg { 56 | pdfContent, err := readPdf(filename) 57 | if err != nil { 58 | return errorMsg(err) 59 | } 60 | 61 | return renderPDFMsg(pdfContent) 62 | } 63 | } 64 | 65 | // New creates a new instance of a PDF. 66 | func New(active bool) Model { 67 | viewPort := viewport.New(0, 0) 68 | 69 | return Model{ 70 | Viewport: viewPort, 71 | Active: active, 72 | } 73 | } 74 | 75 | // Init initializes the PDF bubble. 76 | func (m Model) Init() tea.Cmd { 77 | return nil 78 | } 79 | 80 | // SetFileName sets current file to render, this 81 | // returns a cmd which will render the pdf. 82 | func (m *Model) SetFileName(filename string) tea.Cmd { 83 | m.FileName = filename 84 | 85 | return renderPDFCmd(filename) 86 | } 87 | 88 | // SetSize sets the size of the bubble. 89 | func (m *Model) SetSize(w, h int) { 90 | m.Viewport.Width = w 91 | m.Viewport.Height = h 92 | } 93 | 94 | // SetIsActive sets if the bubble is currently active. 95 | func (m *Model) SetIsActive(active bool) { 96 | m.Active = active 97 | } 98 | 99 | // GotoTop jumps to the top of the viewport. 100 | func (m *Model) GotoTop() { 101 | m.Viewport.GotoTop() 102 | } 103 | 104 | // Update handles updating the UI of a code bubble. 105 | func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { 106 | var ( 107 | cmd tea.Cmd 108 | cmds []tea.Cmd 109 | ) 110 | 111 | switch msg := msg.(type) { 112 | case renderPDFMsg: 113 | pdfContent := lipgloss.NewStyle(). 114 | Width(m.Viewport.Width). 115 | Height(m.Viewport.Height). 116 | Render(string(msg)) 117 | 118 | m.Viewport.SetContent(pdfContent) 119 | 120 | return m, nil 121 | case errorMsg: 122 | m.FileName = "" 123 | m.Viewport.SetContent(msg.Error()) 124 | 125 | return m, nil 126 | } 127 | 128 | if m.Active { 129 | m.Viewport, cmd = m.Viewport.Update(msg) 130 | cmds = append(cmds, cmd) 131 | } 132 | 133 | return m, tea.Batch(cmds...) 134 | } 135 | 136 | // View returns a string representation of the markdown bubble. 137 | func (m Model) View() string { 138 | return m.Viewport.View() 139 | } 140 | -------------------------------------------------------------------------------- /statusbar/statusbar.go: -------------------------------------------------------------------------------- 1 | // Package statusbar provides an statusbar bubble which can render 2 | // four different status sections 3 | package statusbar 4 | 5 | import ( 6 | tea "github.com/charmbracelet/bubbletea" 7 | "github.com/charmbracelet/lipgloss" 8 | "github.com/muesli/reflow/truncate" 9 | ) 10 | 11 | // Height represents the height of the statusbar. 12 | const Height = 1 13 | 14 | // ColorConfig 15 | type ColorConfig struct { 16 | Foreground lipgloss.AdaptiveColor 17 | Background lipgloss.AdaptiveColor 18 | } 19 | 20 | // Model represents the properties of the statusbar. 21 | type Model struct { 22 | Width int 23 | Height int 24 | FirstColumn string 25 | SecondColumn string 26 | ThirdColumn string 27 | FourthColumn string 28 | FirstColumnColors ColorConfig 29 | SecondColumnColors ColorConfig 30 | ThirdColumnColors ColorConfig 31 | FourthColumnColors ColorConfig 32 | } 33 | 34 | // New creates a new instance of the statusbar. 35 | func New(firstColumnColors, secondColumnColors, thirdColumnColors, fourthColumnColors ColorConfig) Model { 36 | return Model{ 37 | FirstColumnColors: firstColumnColors, 38 | SecondColumnColors: secondColumnColors, 39 | ThirdColumnColors: thirdColumnColors, 40 | FourthColumnColors: fourthColumnColors, 41 | } 42 | } 43 | 44 | // SetSize sets the width of the statusbar. 45 | func (m *Model) SetSize(width int) { 46 | m.Width = width 47 | } 48 | 49 | // Update updates the size of the statusbar. 50 | func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { 51 | switch msg := msg.(type) { 52 | case tea.WindowSizeMsg: 53 | m.SetSize(msg.Width) 54 | } 55 | 56 | return m, nil 57 | } 58 | 59 | // SetContent sets the content of the statusbar. 60 | func (m *Model) SetContent(firstColumn, secondColumn, thirdColumn, fourthColumn string) { 61 | m.FirstColumn = firstColumn 62 | m.SecondColumn = secondColumn 63 | m.ThirdColumn = thirdColumn 64 | m.FourthColumn = fourthColumn 65 | } 66 | 67 | // SetColors sets the colors of the 4 columns. 68 | func (m *Model) SetColors(firstColumnColors, secondColumnColors, thirdColumnColors, fourthColumnColors ColorConfig) { 69 | m.FirstColumnColors = firstColumnColors 70 | m.SecondColumnColors = secondColumnColors 71 | m.ThirdColumnColors = thirdColumnColors 72 | m.FourthColumnColors = fourthColumnColors 73 | } 74 | 75 | // View returns a string representation of a statusbar. 76 | func (m Model) View() string { 77 | width := lipgloss.Width 78 | 79 | firstColumn := lipgloss.NewStyle(). 80 | Foreground(m.FirstColumnColors.Foreground). 81 | Background(m.FirstColumnColors.Background). 82 | Padding(0, 1). 83 | Height(Height). 84 | Render(truncate.StringWithTail(m.FirstColumn, 30, "...")) 85 | 86 | thirdColumn := lipgloss.NewStyle(). 87 | Foreground(m.ThirdColumnColors.Foreground). 88 | Background(m.ThirdColumnColors.Background). 89 | Align(lipgloss.Right). 90 | Padding(0, 1). 91 | Height(Height). 92 | Render(m.ThirdColumn) 93 | 94 | fourthColumn := lipgloss.NewStyle(). 95 | Foreground(m.FourthColumnColors.Foreground). 96 | Background(m.FourthColumnColors.Background). 97 | Padding(0, 1). 98 | Height(Height). 99 | Render(m.FourthColumn) 100 | 101 | secondColumn := lipgloss.NewStyle(). 102 | Foreground(m.SecondColumnColors.Foreground). 103 | Background(m.SecondColumnColors.Background). 104 | Padding(0, 1). 105 | Height(Height). 106 | Width(m.Width - width(firstColumn) - width(thirdColumn) - width(fourthColumn)). 107 | Render(truncate.StringWithTail( 108 | m.SecondColumn, 109 | uint(m.Width-width(firstColumn)-width(thirdColumn)-width(fourthColumn)-3), 110 | "..."), 111 | ) 112 | 113 | return lipgloss.JoinHorizontal(lipgloss.Top, 114 | firstColumn, 115 | secondColumn, 116 | thirdColumn, 117 | fourthColumn, 118 | ) 119 | } 120 | -------------------------------------------------------------------------------- /teacup.go: -------------------------------------------------------------------------------- 1 | package teacup 2 | --------------------------------------------------------------------------------