pax_global_header00006660000000000000000000000064147243675500014527gustar00rootroot0000000000000052 comment=285b8a238f5388b075e23ff7ddc5dd09744e4699 jqp-0.7.0/000077500000000000000000000000001472436755000123255ustar00rootroot00000000000000jqp-0.7.0/.github/000077500000000000000000000000001472436755000136655ustar00rootroot00000000000000jqp-0.7.0/.github/dependabot.yml000066400000000000000000000013041472436755000165130ustar00rootroot00000000000000version: 2 updates: # Maintain golang dependencies defined in go.mod # These would open PR, these PR would be tested with the CI # They will have to be merged manually by a maintainer - package-ecosystem: "gomod" directory: "/" open-pull-requests-limit: 10 # avoid spam, if no one reacts schedule: interval: "daily" time: "11:00" # Maintain dependencies for GitHub Actions # These would open PR, these PR would be tested with the CI # They will have to be merged manually by a maintainer - package-ecosystem: "github-actions" directory: "/" open-pull-requests-limit: 10 # avoid spam, if no one reacts schedule: interval: "daily" time: "11:00" jqp-0.7.0/.github/workflows/000077500000000000000000000000001472436755000157225ustar00rootroot00000000000000jqp-0.7.0/.github/workflows/ci.yml000066400000000000000000000012241472436755000170370ustar00rootroot00000000000000name: CI on: push: branches: [ main ] tags: [ v* ] pull_request: types: [opened, synchronize, reopened] jobs: test: strategy: matrix: go-version: ["1.22.x"] platform: [ubuntu-latest] runs-on: ${{ matrix.platform }} steps: - name: Install Go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout uses: actions/checkout@v4 - name: Run tests with race detector run: go test -count=1 -race ./... - name: Run golangci-lint uses: golangci/golangci-lint-action@v6 with: version: latest jqp-0.7.0/.github/workflows/release.yml000066400000000000000000000014751472436755000200740ustar00rootroot00000000000000name: goreleaser on: push: # run only against tags tags: - '*' permissions: contents: write # packages: write # issues: write jobs: goreleaser: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Fetch all tags run: git fetch --force --tags - name: Set up Go uses: actions/setup-go@v5 with: go-version: 1.22 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v5 with: distribution: goreleaser version: latest args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} jqp-0.7.0/.github/workflows/spellchecker.yml000066400000000000000000000004051472436755000211100ustar00rootroot00000000000000name: spell checking on: [pull_request] jobs: typos: name: Spell Check with Typos runs-on: ubuntu-latest steps: - name: Checkout Actions Repository uses: actions/checkout@v4 - name: typos-action uses: crate-ci/typos@v1.22.1 jqp-0.7.0/.gitignore000066400000000000000000000000241472436755000143110ustar00rootroot00000000000000jqp dist/ debug.log jqp-0.7.0/.golangci.yaml000066400000000000000000000153031472436755000150540ustar00rootroot00000000000000--- # Options for analysis running. run: # Timeout for analysis, e.g. 30s, 5m. # Default: 1m timeout: 5m # Include test files or not. # Default: true tests: true issues: # Maximum issues count per one linter. # Set to 0 to disable. # Default: 50 max-issues-per-linter: 0 # Maximum count of issues with the same text. # Set to 0 to disable. # Default: 3 max-same-issues: 0 linters: enable: # check when errors are compared without errors.Is - errorlint # check imports order and makes it always deterministic. - gci # linter to detect errors invalid key values count - loggercheck # Very Basic spell error checker - misspell # Forbid some imports - depguard # simple security check - gosec # Copyloopvar is a linter detects places where loop variables are copied. # this hack was needed before golang 1.22 - copyloopvar # Fast, configurable, extensible, flexible, and beautiful linter for Go. # Drop-in replacement of golint. - revive # Finds sending http request without context.Context - noctx # make sure to use t.Helper() when needed - thelper # make sure that error are checked after a rows.Next() - rowserrcheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. - sqlclosecheck # ensure that lint exceptions have explanations. Consider the case below: - nolintlint # detect duplicated words in code - dupword # detect the possibility to use variables/constants from the Go standard library. - usestdlibvars # mirror suggests rewrites to avoid unnecessary []byte/string conversion - mirror # testify checks good usage of github.com/stretchr/testify. - testifylint # Check whether the function uses a non-inherited context. - contextcheck # We already identified we don't want these ones # - gochecknoinit # - goerr113 # errorlint is better # - testpackage linters-settings: # configure the golang imports we don't want depguard: rules: # Name of a rule. main: # Packages that are not allowed where the value is a suggestion. deny: - pkg: "github.com/pkg/errors" desc: Should be replaced by standard lib errors package - pkg: "golang.org/x/net/context" desc: Should be replaced by standard lib context package loggercheck: # invalid key values count require-string-key: true # Require printf-like format specifier (%s, %d for example) not present. # Default: false no-printf-like: true nolintlint: # Disable to ensure that all nolint directives actually have an effect. # Default: false allow-unused: true # Enable to require an explanation of nonzero length # after each nolint directive. # Default: false require-explanation: true # Enable to require nolint directives to mention the specific # linter being suppressed. # Default: false require-specific: true # define the import orders gci: sections: # Standard section: captures all standard packages. - standard # Default section: catchall that is not standard or custom - default # linters that related to local tool, so they should be separated - localmodule staticcheck: # SAxxxx checks in https://staticcheck.io/docs/configuration/options/#checks checks: ["all"] revive: enable-all-rules: true rules: # we must provide configuration for linter that requires them # enable-all-rules is OK, but many revive linters expect configuration # and cannot work without them # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cognitive-complexity - name: cognitive-complexity severity: warning arguments: [7] # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument - name: context-as-argument arguments: - allowTypesBefore: "*testing.T" # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cyclomatic - name: cyclomatic arguments: [5] # default 3 # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported - name: exported arguments: # enables checking public methods of private types - "checkPrivateReceivers" # make error messages clearer - "sayRepetitiveInsteadOfStutters" # this linter completes errcheck linter, it will report method called without handling the error # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error - name: unhandled-error arguments: # here are the exceptions we don't want to be reported - "fmt.Print.*" - "fmt.Fprint.*" - "bytes.Buffer.Write" - "bytes.Buffer.WriteByte" - "bytes.Buffer.WriteString" - "strings.Builder.WriteString" - "strings.Builder.WriteRune" # boolean parameters that create a control coupling could be useful # but this one is way too noisy # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter - name: flag-parameter disabled: true # depguard linter is easier to configure and more powerful # than revive.imports-blocklist # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#imports-blocklist - name: imports-blocklist disabled: true # it's not really a problem for us in term of readability # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#nested-structs - name: nested-structs disabled: true # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#add-constant # too noisy - name: add-constant disabled: true # too many false positive on jqp code - name: modifies-value-receiver disabled: true # disable everything we don't want - name: line-length-limit disabled: true - name: argument-limit disabled: true - name: banned-characters disabled: true - name: max-public-structs disabled: true - name: function-result-limit disabled: true - name: function-length disabled: true - name: file-header disabled: true - name: empty-lines disabled: true misspell: locale: "US" # Fix the colour => color, and co output: # Make issues output unique by line. # Default: true # Note: unique in this case means that you can have at most 1 issue per line of code. # one issue with a given line and we want to see them all at once. uniq-by-line: false jqp-0.7.0/.goreleaser.yaml000066400000000000000000000017731472436755000154270ustar00rootroot00000000000000project_name: "jqp" before: hooks: # You may remove this if you don't use go modules. - go mod tidy builds: - env: - CGO_ENABLED=0 goos: - linux - windows - darwin archives: - id: default name_template: >- {{ .ProjectName }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} checksum: name_template: 'checksums.txt' snapshot: name_template: "{{ incpatch .Version }}-next" changelog: sort: asc filters: exclude: - '^docs:' - '^test:' brews: - homepage: https://github.com/noahgorstein/jqp description: "a TUI playground to experiment and play with jq" folder: Formula repository: owner: noahgorstein name: homebrew-tap branch: main token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" commit_author: name: goreleaserbot email: bot@goreleaser.com license: "MIT" install: | bin.install "jqp" jqp-0.7.0/CONTRIBUTING.md000066400000000000000000000004421472436755000145560ustar00rootroot00000000000000# Contributing Pull requests are welcome for any changes. Consider opening an issue for larger changes to get feedback on the idea. For commit messages, please use conventional commits[^1] to make it easier to generate release notes. [^1]: https://www.conventionalcommits.org/en/v1.0.0 jqp-0.7.0/LICENSE000066400000000000000000000020701472436755000133310ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2022 Noah Gorstein Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jqp-0.7.0/README.md000066400000000000000000000155311472436755000136110ustar00rootroot00000000000000# jqp a TUI playground for exploring jq. ![jqp](https://user-images.githubusercontent.com/23270779/191256434-05aeda9d-9ee2-4b5e-a23f-6548dac08fdb.gif) This application utilizes [itchyny's](https://github.com/itchyny) implementation of `jq` written in Go, [`gojq`](https://github.com/itchyny/gojq). ## Installation ### homebrew ```bash brew install noahgorstein/tap/jqp ``` ### macports ```bash sudo port install jqp ``` ### Arch Linux Available through the Arch User Repository as [jqp-bin](https://aur.archlinux.org/packages/jqp-bin). ```bash yay -S jqp-bin ``` ### GitHub releases Download the relevant asset for your operating system from the latest GitHub release. Unpack it, then move the binary to somewhere accessible in your `PATH`, e.g. `mv ./jqp /usr/local/bin`. ### Build from source Clone this repository, build from source with `cd jqp && go build`, then move the binary to somewhere accessible in your `PATH`, e.g. `mv ./jqp /usr/local/bin`. ## Usage ``` ➜ jqp --help jqp is a terminal user interface (TUI) for exploring the jq command line utility. You can use it to run jq queries interactively. If no query is provided, the interface will prompt you for one. The command accepts an optional query argument which will be executed against the input JSON or newline-delimited JSON (NDJSON). You can provide the input JSON or NDJSON either through a file or via standard input (stdin). Usage: jqp [query] [flags] Flags: --config string path to config file (default is $HOME/.jqp.yaml) -f, --file string path to the input JSON file -h, --help help for jqp -t, --theme string jqp theme -v, --version version for jqp ``` `jqp` also supports input from STDIN. STDIN takes precedence over the command-line flag. Additionally, you can pass an optional query argument to jqp that it will execute upon loading. ``` ➜ curl "https://api.github.com/repos/jqlang/jq/issues" | jqp '.[] | {"title": .title, "url": .url}' ``` > [!NOTE] > Valid JSON or NDJSON [(newline-delimited JSON)](https://jsonlines.org/) can be provided as input to `jqp`. ## Keybindings | **Keybinding** | **Action** | |:---------------|:-----------| | `tab` | cycle through sections | | `shift-tab` | cycle through sections in reverse | | `ctrl-y` | copy query to system clipboard[^1] | | `ctrl-s` | save output to file (copy to clipboard if file not specified) | | `ctrl-c` | quit program / kill long-running query | ### Query Mode | **Keybinding** | **Action** | |:---------------|:-----------| | `enter` | execute query | | `↑`/`↓` | cycle through query history | | `ctrl-a` | go to beginning of line | | `ctrl-e` | go to end of line | | `←`/`ctrl-b` | move cursor one character to left | | `→`/`ctrl-f`| move cursor one character to right | | `ctrl-k` | delete text after cursor line | | `ctrl-u` | delete text before cursor | | `ctrl-w` | delete word to left | | `ctrl-d` | delete character under cursor | ### Input Preview and Output Mode | **Keybinding** | **Action** | |:---------------|:-----------| | `↑/k` | up | | `↓/j` | down | | `ctrl-u` | page up | | `ctrl-d` | page down | ## Configuration `jqp` can be configured with a configuration file. By default, `jqp` will search your home directory for a YAML file named `.jqp.yaml`. A path to a YAML configuration file can also be provided to the `--config` command-line flag. ```bash ➜ jqp --config ~/my_jqp_config.yaml < data.json ``` If a configuration option is present in both the configuration file and the command-line, the command-line option takes precedence. For example, if a theme is specified in the configuration file and via `-t/--theme flag`, the command-line flag will take precedence. ### Available Configuration Options ```yaml theme: name: "nord" # controls the color scheme chromaStyleOverrides: # override parts of the chroma style kc: "#009900 underline" # keys use the chroma short names ``` ## Themes Themes can be specified on the command-line via the `-t/--theme ` flag. You can also set a theme in your [configuration file](#configuration). ```yaml theme: name: "monokai" ``` Screen Shot 2022-10-02 at 5 31 40 PM ### Chroma Style Overrides Overrides to the chroma styles used for a theme can be configured in your [configuration file](#configuration). For the list of short keys, see [`chroma.StandardTypes`](https://github.com/alecthomas/chroma/blob/d38b87110b078027006bc34aa27a065fa22295a1/types.go#L210-L308). To see which token to use for a value, see the [JSON lexer](https://github.com/alecthomas/chroma/blob/master/lexers/embedded/json.xml) (look for `` tags). To see the color and what's used in the style you're using, look for your style in the chroma [styles directory](https://github.com/alecthomas/chroma/tree/master/styles). ```yaml theme: name: "monokai" # name is required to know which theme to override chromaStyleOverrides: kc: "#009900 underline" ``` You can change non-syntax colors using the `styleOverrides` key: ```yaml theme: styleOverrides: primary: "#c4b28a" secondary: "#8992a7" error: "#c4746e" inactive: "#a6a69c" success: "#87a987" ``` Themes are broken up into [light](#light-themes) and [dark](#dark-themes) themes. Light themes work best in terminals with a light background and dark themes work best in a terminal with a dark background. If no theme is specified or a non-existent theme is provided, the default theme is used, which was created to work with both terminals with a light and dark background. ### Light Themes - `abap` - `algol` - `arduino` - `autumn` - `borland` - `catppuccin-latte` - `colorful` - `emacs` - `friendly` - `github` - `gruvbox-light` - `hrdark` - `igor` - `lovelace` - `manni` - `monokai-light` - `murphy` - `onesenterprise` - `paraiso-light` - `pastie` - `perldoc` - `pygments` - `solarized-light` - `tango` - `trac` - `visual_studio` - `vulcan` - `xcode` ### Dark Themes - `average` - `base16snazzy` - `catppuccin-frappe` - `catppuccin-macchiato` - `catppuccin-mocha` - `doom-one` - `doom-one2` - `dracula` - `fruity` - `github-dark` - `gruvbox` - `monokai` - `native` - `paraiso-dark` - `rrt` - `solarized-dark` - `solarized-dark256` - `swapoff` - `vim` - `witchhazel` - `xcode-dark` ## Built with: - [Bubbletea](https://github.com/charmbracelet/bubbletea) - [Bubbles](https://github.com/charmbracelet/bubbles) - [Lipgloss](https://github.com/charmbracelet/lipgloss) - [gojq](https://github.com/itchyny/gojq) - [chroma](https://github.com/alecthomas/chroma) ## Credits - [jqq](https://github.com/jcsalterego/jqq) for inspiration -------- [^1]: `jqp` uses [https://github.com/atotto/clipboard](https://github.com/atotto/clipboard) for clipboard functionality. Things should work as expected with OSX and Windows. Linux, Unix require `xclip` or `xsel` to be installed. jqp-0.7.0/_typos.toml000066400000000000000000000000601472436755000145330ustar00rootroot00000000000000[default] extend-ignore-identifiers-re = ["nd"] jqp-0.7.0/cmd/000077500000000000000000000000001472436755000130705ustar00rootroot00000000000000jqp-0.7.0/cmd/root.go000066400000000000000000000116101472436755000144010ustar00rootroot00000000000000package cmd import ( "errors" "fmt" "os" "strings" "github.com/alecthomas/chroma/v2" "github.com/charmbracelet/bubbletea" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/noahgorstein/jqp/tui/bubbles/jqplayground" "github.com/noahgorstein/jqp/tui/theme" ) var rootCmd = &cobra.Command{ Version: "0.7.0", Use: "jqp [query]", Short: "jqp is a TUI to explore jq", Long: `jqp is a terminal user interface (TUI) for exploring the jq command line utility. You can use it to run jq queries interactively. If no query is provided, the interface will prompt you for one. The command accepts an optional query argument which will be executed against the input JSON or newline-delimited JSON (NDJSON). You can provide the input JSON or NDJSON either through a file or via standard input (stdin).`, Args: cobra.MaximumNArgs(1), SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { query := "" if len(args) == 1 { query = strings.TrimSpace(args[0]) } configTheme := viper.GetString(configKeysName.themeName) if !cmd.Flags().Changed(flagsName.theme) { flags.theme = configTheme } themeOverrides := viper.GetStringMapString(configKeysName.themeOverrides) styleOverrides := viper.GetStringMapString(configKeysName.styleOverrides) jqtheme, defaultTheme := theme.GetTheme(flags.theme, styleOverrides) // If not using the default theme, // and if theme specified is the same as in the config, // which happens if the theme flag was used, // apply chroma style overrides. if !defaultTheme && configTheme == flags.theme && len(themeOverrides) > 0 { // Reverse chroma.StandardTypes to be keyed by short string chromaTypes := make(map[string]chroma.TokenType) for tokenType, short := range chroma.StandardTypes { chromaTypes[short] = tokenType } builder := jqtheme.ChromaStyle.Builder() for k, v := range themeOverrides { builder.Add(chromaTypes[k], v) } style, err := builder.Build() if err == nil { jqtheme.ChromaStyle = style } } if isInputFromPipe() { stdin, err := streamToBytes(os.Stdin) if err != nil { return err } bubble, err := jqplayground.New(stdin, "STDIN", query, jqtheme) if err != nil { return err } p := tea.NewProgram(bubble, tea.WithAltScreen()) m, err := p.Run() if err != nil { return err } if m, ok := m.(jqplayground.Bubble); ok && m.ExitMessage != "" { return errors.New(m.ExitMessage) } return nil } // get the file file, e := getFile() if e != nil { return e } defer file.Close() // read the file data, err := os.ReadFile(flags.filepath) if err != nil { return err } // get file info so we can get the filename fi, err := os.Stat(flags.filepath) if err != nil { return err } bubble, err := jqplayground.New(data, fi.Name(), query, jqtheme) if err != nil { return err } p := tea.NewProgram(bubble, tea.WithAltScreen()) m, err := p.Run() if err != nil { return err } if m, ok := m.(jqplayground.Bubble); ok && m.ExitMessage != "" { return errors.New(m.ExitMessage) } return nil }, } func initConfig() { if cfgFile != "" { // Use config file from the flag. viper.SetConfigFile(cfgFile) if err := viper.ReadInConfig(); err != nil { fmt.Fprintf(os.Stderr, "Config file %s was unable to be read: %v\n", viper.ConfigFileUsed(), err) } return } // Find home directory. home, err := os.UserHomeDir() cobra.CheckErr(err) // Search config in home directory viper.AddConfigPath(home) // register the config file viper.SetConfigName(".jqp") // only read from yaml files viper.SetConfigType("yaml") // Try to read the default config file if err := viper.ReadInConfig(); err != nil { // Check if the error is due to the file not existing var errFileNotFound viper.ConfigFileNotFoundError if !errors.As(err, &errFileNotFound) { // For errors other than file not found, print the error message fmt.Fprintf(os.Stderr, "Default config file %s was unable to be read: %v\n", viper.ConfigFileUsed(), err) } } } var flags struct { filepath, theme string } var flagsName = struct { file, fileShort, theme, themeShort string }{ file: "file", fileShort: "f", theme: "theme", themeShort: "t", } var configKeysName = struct { themeName string themeOverrides string styleOverrides string }{ themeName: "theme.name", themeOverrides: "theme.chromaStyleOverrides", styleOverrides: "theme.styleOverrides", } var cfgFile string func Execute() error { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "path to config file (default is $HOME/.jqp.yaml)") rootCmd.Flags().StringVarP( &flags.filepath, flagsName.file, flagsName.fileShort, "", "path to the input JSON file") rootCmd.Flags().StringVarP( &flags.theme, flagsName.theme, flagsName.themeShort, "", "jqp theme") return rootCmd.Execute() } jqp-0.7.0/cmd/utils.go000066400000000000000000000015001472436755000145530ustar00rootroot00000000000000package cmd import ( "bytes" "errors" "fmt" "io" "os" ) func streamToBytes(stream io.Reader) ([]byte, error) { buf := new(bytes.Buffer) _, err := buf.ReadFrom(stream) if err != nil { return nil, err } return buf.Bytes(), nil } func isInputFromPipe() bool { fi, _ := os.Stdin.Stat() return (fi.Mode() & os.ModeCharDevice) == 0 } func getFile() (*os.File, error) { if flags.filepath == "" { return nil, errors.New("please provide an input file") } if !fileExists(flags.filepath) { return nil, errors.New("the file provided does not exist") } file, e := os.Open(flags.filepath) if e != nil { return nil, fmt.Errorf("Unable to open file: %w", e) } return file, nil } func fileExists(filepath string) bool { info, e := os.Stat(filepath) if os.IsNotExist(e) { return false } return !info.IsDir() } jqp-0.7.0/go.mod000066400000000000000000000043411472436755000134350ustar00rootroot00000000000000module github.com/noahgorstein/jqp go 1.22 require ( github.com/alecthomas/chroma/v2 v2.14.0 github.com/atotto/clipboard v0.1.4 github.com/charmbracelet/bubbles v0.18.0 github.com/charmbracelet/bubbletea v0.26.4 github.com/charmbracelet/lipgloss v0.11.0 github.com/itchyny/gojq v0.12.16 github.com/muesli/termenv v0.15.2 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.19.0 ) require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/x/ansi v0.1.2 // indirect github.com/charmbracelet/x/input v0.1.0 // indirect github.com/charmbracelet/x/term v0.1.1 // indirect github.com/charmbracelet/x/windows v0.1.0 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.6 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) jqp-0.7.0/go.sum000066400000000000000000000274061472436755000134710ustar00rootroot00000000000000github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= github.com/charmbracelet/bubbletea v0.26.4 h1:2gDkkzLZaTjMl/dQBpNVtnvcCxsh/FCkimep7FC9c40= github.com/charmbracelet/bubbletea v0.26.4/go.mod h1:P+r+RRA5qtI1DOHNFn0otoNwB4rn+zNAzSj/EXz6xU0= github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4= github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/itchyny/gojq v0.12.16 h1:yLfgLxhIr/6sJNVmYfQjTIv0jGctu6/DgDoivmxTr7g= github.com/itchyny/gojq v0.12.16/go.mod h1:6abHbdC2uB9ogMS38XsErnfqJ94UlngIJGlRAIj4jTM= github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q= github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= jqp-0.7.0/main.go000066400000000000000000000003001472436755000135710ustar00rootroot00000000000000package main import ( "os" "github.com/noahgorstein/jqp/cmd" ) func main() { err := cmd.Execute() if err != nil { // error is discarded as cobra already reported it os.Exit(1) } } jqp-0.7.0/schema.json000066400000000000000000000777421472436755000145010ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "override": { "type": "string", "examples": [ "#000000", "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF", "#FFFFFF", "bold", "underline", "italic", "bold #000000", "bold #FF0000", "bold #00FF00", "bold #0000FF", "bold #FFFF00", "bold #FF00FF", "bold #00FFFF", "bold #FFFFFF", "italic #000000", "italic #FF0000", "italic #00FF00", "italic #0000FF", "italic #FFFF00", "italic #FF00FF", "italic #00FFFF", "italic #FFFFFF", "underline #000000", "underline #FF0000", "underline #00FF00", "underline #0000FF", "underline #FFFF00", "underline #FF00FF", "underline #00FFFF", "underline #FFFFFF" ] } }, "title": "jqp settings", "description": "jqp settings\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#configuration", "type": "object", "properties": { "theme": { "title": "theme", "description": "A theme\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#themes", "type": "object", "properties": { "name": { "title": "name", "description": "A theme name\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#themes", "type": "string", "minLength": 1, "enum": [ "abap", "algol", "arduino", "autumn", "borland", "catppuccin-latte", "colorful", "emacs", "friendly", "github", "gruvbox-light", "hrdark", "igor", "lovelace", "manni", "monokai-light", "murphy", "onesenterprise", "paradaiso-light", "pastie", "perldoc", "pygments", "solarized-light", "tango", "trac", "visual_studio", "vulcan", "xcode", "average", "base16snazzy", "catppuccin-frappe", "catppuccin-macchiato", "catppuccin-mocha", "doom-one", "doom-one2", "dracula", "fruity", "github-dark", "gruvbox", "monokai", "native", "paradaiso-dark", "rrt", "solarized-dark", "solarized-dark256", "swapoff", "vim", "witchhazel", "xcode-dark" ] }, "chromaStyleOverrides": { "title": "chroma style overrides", "description": "Chroma style overrides\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "type": "object", "properties": { "bg": { "title": "bg", "description": "A property which corresponds to Background\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "chroma": { "title": "chroma", "description": "A property which corresponds to PreWrapper\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "line": { "title": "line", "description": "A property which corresponds to Line\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ln": { "title": "ln", "description": "A property which corresponds to LineNumbers\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "lnt": { "title": "lnt", "description": "A property which corresponds to LineNumbersTable\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "hl": { "title": "hl", "description": "A property which corresponds to LineHighlight\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "lntable": { "title": "lntable", "description": "A property which corresponds to LineTable\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "lntd": { "title": "lntd", "description": "A property which corresponds to LineTableTD\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "cl": { "title": "cl", "description": "A property which corresponds to CodeLine\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "w": { "title": "w", "description": "A property which corresponds to Whitespace\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "err": { "title": "err", "description": "A property which corresponds to Error\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "x": { "title": "x", "description": "A property which corresponds to Other\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "k": { "title": "k", "description": "A property which corresponds to Keyword\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "kc": { "title": "kc", "description": "A property which corresponds to KeywordConstant\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "kd": { "title": "kd", "description": "A property which corresponds to KeywordDeclaration\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "kn": { "title": "kn", "description": "A property which corresponds to KeywordNamespace\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "kp": { "title": "kp", "description": "A property which corresponds to KeywordPseudo\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "kr": { "title": "kr", "description": "A property which corresponds to KeywordReserved\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "kt": { "title": "kt", "description": "A property which corresponds to KeywordType\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "n": { "title": "n", "description": "A property which corresponds to Name\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "na": { "title": "na", "description": "A property which corresponds to NameAttribute\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nb": { "title": "nb", "description": "A property which corresponds to NameBuiltin\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "bp": { "title": "bp", "description": "A property which corresponds to NameBuiltinPseudo\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nc": { "title": "nc", "description": "A property which corresponds to NameClass\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "no": { "title": "no", "description": "A property which corresponds to NameConstant\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nd": { "title": "nd", "description": "A property which corresponds to NameDecorator\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ni": { "title": "ni", "description": "A property which corresponds to NameEntity\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ne": { "title": "ne", "description": "A property which corresponds to NameException\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nf": { "title": "nf", "description": "A property which corresponds to NameFunction\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "fm": { "title": "fm", "description": "A property which corresponds to NameFunctionMagic\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "py": { "title": "py", "description": "A property which corresponds to NameProperty\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nl": { "title": "nl", "description": "A property which corresponds to NameLabel\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nn": { "title": "nn", "description": "A property which corresponds to NameNamespace\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nx": { "title": "nx", "description": "A property which corresponds to NameOther\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nt": { "title": "nt", "description": "A property which corresponds to NameTag\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "nv": { "title": "nv", "description": "A property which corresponds to NameVariable\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "vc": { "title": "vc", "description": "A property which corresponds to NameVariableClass\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "vg": { "title": "vg", "description": "A property which corresponds to NameVariableGlobal\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "vi": { "title": "vi", "description": "A property which corresponds to NameVariableInstance\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "vm": { "title": "vm", "description": "A property which corresponds to NameVariableMagic\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "l": { "title": "l", "description": "A property which corresponds to Literal\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ld": { "title": "ld", "description": "A property which corresponds to LiteralDate\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "s": { "title": "s", "description": "A property which corresponds to String\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "sa": { "title": "sa", "description": "A property which corresponds to StringAffix\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "sb": { "title": "sb", "description": "A property which corresponds to StringBacktick\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "sc": { "title": "sc", "description": "A property which corresponds to StringChar\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "dl": { "title": "dl", "description": "A property which corresponds to StringDelimiter\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "sd": { "title": "sd", "description": "A property which corresponds to StringDoc\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "s2": { "title": "s2", "description": "A property which corresponds to StringDouble\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "se": { "title": "se", "description": "A property which corresponds to StringEscape\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "sh": { "title": "sh", "description": "A property which corresponds to StringHeredoc\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "si": { "title": "si", "description": "A property which corresponds to StringInterpol\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "sx": { "title": "sx", "description": "A property which corresponds to StringOther\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "sr": { "title": "sr", "description": "A property which corresponds to StringRegex\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "s1": { "title": "s1", "description": "A property which corresponds to StringSingle\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ss": { "title": "ss", "description": "A property which corresponds to StringSymbol\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "m": { "title": "m", "description": "A property which corresponds to Number\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "mb": { "title": "mb", "description": "A property which corresponds to NumberBin\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "mf": { "title": "mf", "description": "A property which corresponds to NumberFloat\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "mh": { "title": "mh", "description": "A property which corresponds to NumberHex\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "mi": { "title": "mi", "description": "A property which corresponds to NumberInteger\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "il": { "title": "il", "description": "A property which corresponds to NumberIntegerLong\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "mo": { "title": "mo", "description": "A property which corresponds to NumberOct\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "o": { "title": "o", "description": "A property which corresponds to Operator\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ow": { "title": "ow", "description": "A property which corresponds to OperatorWord\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "p": { "title": "p", "description": "A property which corresponds to Punctuation\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "c": { "title": "c", "description": "A property which corresponds to Comment\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ch": { "title": "ch", "description": "A property which corresponds to CommentHashbang\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "cm": { "title": "cm", "description": "A property which corresponds to CommentMultiline\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "cp": { "title": "cp", "description": "A property which corresponds to CommentPreproc\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "cpf": { "title": "cpf", "description": "A property which corresponds to CommentPreprocFile\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "c1": { "title": "c1", "description": "A property which corresponds to CommentSingle\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "cs": { "title": "cs", "description": "A property which corresponds to CommentSpecial\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "g": { "title": "g", "description": "A property which corresponds to Generic\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gd": { "title": "gd", "description": "A property which corresponds to GenericDeleted\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "ge": { "title": "ge", "description": "A property which corresponds to GenericEmph\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gr": { "title": "gr", "description": "A property which corresponds to GenericError\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gh": { "title": "gh", "description": "A property which corresponds to GenericHeading\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gi": { "title": "gi", "description": "A property which corresponds to GenericInserted\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "go": { "title": "go", "description": "A property which corresponds to GenericOutput\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gp": { "title": "gp", "description": "A property which corresponds to GenericPrompt\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gs": { "title": "gs", "description": "A property which corresponds to GenericStrong\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gu": { "title": "gu", "description": "A property which corresponds to GenericSubheading\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gt": { "title": "gt", "description": "A property which corresponds to GenericTraceback\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" }, "gl": { "title": "gl", "description": "A property which corresponds to GenericUnderline\nhttps://github.com/noahgorstein/jqp?tab=readme-ov-file#chroma-style-overrides", "$ref": "#/definitions/override" } }, "minProperties": 1, "additionalProperties": false } }, "minProperties": 1, "additionalProperties": false } }, "minProperties": 1, "additionalProperties": false } jqp-0.7.0/tui/000077500000000000000000000000001472436755000131265ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/000077500000000000000000000000001472436755000145445ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/fileselector/000077500000000000000000000000001472436755000172245ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/fileselector/fileselector.go000066400000000000000000000024411472436755000222340ustar00rootroot00000000000000package fileselector import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/noahgorstein/jqp/tui/theme" ) type Bubble struct { Styles Styles textinput textinput.Model } func New(jqtheme theme.Theme) Bubble { s := DefaultStyles() ti := textinput.New() ti.Focus() ti.PromptStyle = s.promptStyle.Foreground(jqtheme.Secondary) s.inputLabelStyle.Foreground(jqtheme.Primary) return Bubble{ Styles: s, textinput: ti, } } func (b Bubble) GetInput() string { return b.textinput.Value() } func (b *Bubble) SetInput(input string) { b.textinput.SetValue(input) } func (Bubble) Init() tea.Cmd { return nil } func (b Bubble) View() string { return b.Styles.containerStyle.Render( lipgloss.JoinVertical( lipgloss.Left, b.Styles.inputLabelStyle.Render("Save output to file (leave empty to copy to clipboard): "), b.textinput.View())) } func (b Bubble) Update(msg tea.Msg) (Bubble, tea.Cmd) { var ( cmd tea.Cmd cmds []tea.Cmd ) b.textinput, cmd = b.textinput.Update(msg) cmds = append(cmds, cmd) return b, tea.Batch(cmds...) } func (b Bubble) SetSize(width int) { b.Styles.containerStyle = b.Styles.containerStyle.Width(width - b.Styles.containerStyle.GetHorizontalFrameSize()) } jqp-0.7.0/tui/bubbles/fileselector/styles.go000066400000000000000000000006361472436755000211030ustar00rootroot00000000000000package fileselector import ( "github.com/charmbracelet/lipgloss" ) type Styles struct { containerStyle lipgloss.Style inputLabelStyle lipgloss.Style promptStyle lipgloss.Style } func DefaultStyles() (s Styles) { s.containerStyle = lipgloss.NewStyle().Align(lipgloss.Left).PaddingLeft(1) s.inputLabelStyle = lipgloss.NewStyle().Bold(true) s.promptStyle = lipgloss.NewStyle().Bold(true) return s } jqp-0.7.0/tui/bubbles/help/000077500000000000000000000000001472436755000154745ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/help/help.go000066400000000000000000000032701472436755000167550ustar00rootroot00000000000000package help import ( "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbletea" "github.com/noahgorstein/jqp/tui/bubbles/state" "github.com/noahgorstein/jqp/tui/theme" ) type Bubble struct { state state.State help help.Model keys keyMap Styles Styles } func New(jqtheme theme.Theme) Bubble { styles := DefaultStyles() model := help.New() model.Styles.ShortKey = styles.helpKeyStyle.Foreground(jqtheme.Primary) model.Styles.ShortDesc = styles.helpTextStyle.Foreground(jqtheme.Secondary) model.Styles.ShortSeparator = styles.helpSeparatorStyle.Foreground(jqtheme.Inactive) return Bubble{ state: state.Query, Styles: styles, help: model, keys: keys, } } func (b Bubble) collectHelpBindings() []key.Binding { k := b.keys bindings := []key.Binding{} switch b.state { case state.Query: bindings = append(bindings, k.submit, k.section, k.copyQuery, k.save) case state.Running: bindings = append(bindings, k.abort) case state.Input, state.Output: bindings = append(bindings, k.section, k.navigate, k.page, k.copyQuery, k.save) case state.Save: bindings = append(bindings, k.back) } return bindings } func (b *Bubble) SetWidth(width int) { b.Styles.helpbarStyle = b.Styles.helpbarStyle.Width(width - 1) } func (Bubble) Init() tea.Cmd { return nil } func (b Bubble) View() string { return b.Styles.helpbarStyle.Render(b.help.ShortHelpView(b.collectHelpBindings())) } func (b *Bubble) SetState(mode state.State) { b.state = mode } func (b Bubble) Update(msg tea.Msg) (Bubble, tea.Cmd) { var cmd tea.Cmd if msg, ok := msg.(tea.WindowSizeMsg); ok { b.SetWidth(msg.Width) } return b, tea.Batch(cmd) } jqp-0.7.0/tui/bubbles/help/keys.go000066400000000000000000000020021472436755000167700ustar00rootroot00000000000000package help import "github.com/charmbracelet/bubbles/key" type keyMap struct { section key.Binding back key.Binding submit key.Binding abort key.Binding navigate key.Binding page key.Binding save key.Binding copyQuery key.Binding } var keys = keyMap{ section: key.NewBinding( key.WithKeys("tab"), key.WithHelp("tab", "section")), back: key.NewBinding( key.WithKeys("esc"), key.WithHelp("esc", "back")), submit: key.NewBinding( key.WithKeys("enter"), key.WithHelp("enter", "submit query")), abort: key.NewBinding( key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "abort running query")), navigate: key.NewBinding( key.WithKeys("↑↓"), key.WithHelp("↑↓", "scroll")), page: key.NewBinding( key.WithKeys("ctrl+u/ctrl+d"), key.WithHelp("ctrl+u/ctrl+d", "page up/down")), save: key.NewBinding( key.WithKeys("ctrl+s"), key.WithHelp("ctrl+s", "save output")), copyQuery: key.NewBinding( key.WithKeys("ctrl+y"), key.WithHelp("ctrl+y", "copy query")), } jqp-0.7.0/tui/bubbles/help/styles.go000066400000000000000000000007501472436755000173500ustar00rootroot00000000000000package help import ( "github.com/charmbracelet/lipgloss" ) type Styles struct { helpbarStyle lipgloss.Style helpKeyStyle lipgloss.Style helpTextStyle lipgloss.Style helpSeparatorStyle lipgloss.Style } func DefaultStyles() (s Styles) { s.helpbarStyle = lipgloss.NewStyle().MarginLeft(1).MarginBottom(1) s.helpKeyStyle = lipgloss.NewStyle().Bold(true) s.helpSeparatorStyle = lipgloss.NewStyle().Bold(true) s.helpTextStyle = lipgloss.NewStyle() return s } jqp-0.7.0/tui/bubbles/inputdata/000077500000000000000000000000001472436755000165355ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/inputdata/inputdata.go000066400000000000000000000103251472436755000210560ustar00rootroot00000000000000package inputdata import ( "bytes" "fmt" "strings" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/noahgorstein/jqp/tui/theme" "github.com/noahgorstein/jqp/tui/utils" ) type Bubble struct { styles Styles viewport viewport.Model height int width int inputJSON []byte highlightedJSON *bytes.Buffer filename string theme theme.Theme setInitialContentSub chan setPrettifiedContentMsg } func New(inputJSON []byte, filename string, jqtheme theme.Theme) (Bubble, error) { styles := DefaultStyles() styles.containerStyle = styles.containerStyle.BorderForeground(jqtheme.Inactive) styles.infoStyle = styles.infoStyle.BorderForeground(jqtheme.Inactive) v := viewport.New(0, 0) v.SetContent("Loading...") b := Bubble{ styles: styles, viewport: v, inputJSON: inputJSON, filename: filename, theme: jqtheme, setInitialContentSub: make(chan setPrettifiedContentMsg), } return b, nil } func (b *Bubble) SetBorderColor(color lipgloss.TerminalColor) { b.styles.containerStyle = b.styles.containerStyle.BorderForeground(color) b.styles.infoStyle = b.styles.infoStyle.BorderForeground(color) } func (b Bubble) GetInputJSON() []byte { return b.inputJSON } func (b Bubble) GetHighlightedInputJSON() []byte { return b.highlightedJSON.Bytes() } func (b *Bubble) SetSize(width, height int) { b.width = width b.height = height b.styles.containerStyle = b.styles.containerStyle. Width(width - b.styles.containerStyle.GetHorizontalFrameSize()/2). Height(height - b.styles.containerStyle.GetVerticalFrameSize()) b.viewport.Width = width - b.styles.containerStyle.GetHorizontalFrameSize() b.viewport.Height = height - b.styles.containerStyle.GetVerticalFrameSize() - 3 } func max(a, b int) int { if a > b { return a } return b } func (b Bubble) View() string { scrollPercent := fmt.Sprintf("%3.f%%", b.viewport.ScrollPercent()*100) info := b.styles.infoStyle.Render(fmt.Sprintf("%s | %s", lipgloss.NewStyle().Italic(true).Render(b.filename), scrollPercent)) line := strings.Repeat(" ", max(0, b.viewport.Width-lipgloss.Width(info))) footer := lipgloss.JoinHorizontal(lipgloss.Center, line, info) content := lipgloss.JoinVertical(lipgloss.Left, b.viewport.View(), footer) return b.styles.containerStyle.Render(content) } func (b *Bubble) SetContent(content string) { formattedContent := lipgloss.NewStyle().Width(b.viewport.Width - 3).Render(content) b.viewport.SetContent(formattedContent) } // ReadyMsg signals that the inputdata Bubble has loaded the user's data // into the viewport type ReadyMsg struct{} // setPrettifiedContentMsg contains the input data prettified type setPrettifiedContentMsg struct { Content *bytes.Buffer } // prettifyContentCmd sends the initial prettified content to the provided channel. // // Prettifying the input data can be an expensive operation particularly for large inputs, so it is performed here and // sent through the channel to ensure the prettified data is available without blocking other operations. func (b Bubble) prettifyContentCmd(sub chan setPrettifiedContentMsg, isJSONLines bool) tea.Cmd { return func() tea.Msg { prettifiedData, _ := utils.Prettify(b.inputJSON, b.theme.ChromaStyle, isJSONLines) sub <- setPrettifiedContentMsg{Content: prettifiedData} return nil } } // A command that waits for a setPrettifiedContentMsg on a channel. func waitForPrettifiedContent(sub chan setPrettifiedContentMsg) tea.Cmd { return func() tea.Msg { return setPrettifiedContentMsg(<-sub) } } func (b Bubble) Init(isJSONLines bool) tea.Cmd { return tea.Batch( b.prettifyContentCmd(b.setInitialContentSub, isJSONLines), waitForPrettifiedContent(b.setInitialContentSub)) } func (b Bubble) Update(msg tea.Msg) (Bubble, tea.Cmd) { var ( cmd tea.Cmd cmds []tea.Cmd ) if msg, ok := msg.(setPrettifiedContentMsg); ok { b.highlightedJSON = msg.Content b.SetContent(msg.Content.String()) return b, func() tea.Msg { return ReadyMsg{} } } b.viewport, cmd = b.viewport.Update(msg) cmds = append(cmds, cmd) return b, tea.Batch(cmds...) } jqp-0.7.0/tui/bubbles/inputdata/styles.go000066400000000000000000000005721472436755000204130ustar00rootroot00000000000000package inputdata import ( "github.com/charmbracelet/lipgloss" ) type Styles struct { infoStyle lipgloss.Style containerStyle lipgloss.Style } func DefaultStyles() (s Styles) { s.infoStyle = lipgloss.NewStyle().Bold(true).Border(lipgloss.RoundedBorder()).Padding(0, 1) s.containerStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).Padding(1) return s } jqp-0.7.0/tui/bubbles/jqplayground/000077500000000000000000000000001472436755000172635ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/jqplayground/commands.go000066400000000000000000000065111472436755000214160ustar00rootroot00000000000000package jqplayground import ( "context" "encoding/json" "fmt" "os" "strings" "github.com/atotto/clipboard" tea "github.com/charmbracelet/bubbletea" "github.com/itchyny/gojq" "github.com/noahgorstein/jqp/tui/utils" ) type errorMsg struct { error error } type queryResultMsg struct { rawResults string highlightedResults string } type writeToFileMsg struct{} type copyQueryToClipboardMsg struct{} type copyResultsToClipboardMsg struct{} // processQueryResults iterates through the results of a gojq query on the provided JSON object // and appends the formatted results to the provided string builder. func processQueryResults(ctx context.Context, results *strings.Builder, query *gojq.Query, obj any) error { iter := query.RunWithContext(ctx, obj) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { return err } if r, err := gojq.Marshal(v); err == nil { results.WriteString(fmt.Sprintf("%s\n", string(r))) } } return nil } func processJSONWithQuery(ctx context.Context, results *strings.Builder, query *gojq.Query, data []byte) error { var obj any if err := json.Unmarshal(data, &obj); err != nil { return err } err := processQueryResults(ctx, results, query, obj) if err != nil { return err } return nil } func processJSONLinesWithQuery(ctx context.Context, results *strings.Builder, query *gojq.Query, data []byte) error { const maxBufferSize = 100 * 1024 * 1024 // 100MB max buffer size processLine := func(line []byte) error { return processJSONWithQuery(ctx, results, query, line) } return utils.ScanLinesWithDynamicBufferSize(data, maxBufferSize, processLine) } func (b *Bubble) executeQueryOnInput(ctx context.Context) (string, error) { var results strings.Builder query, err := gojq.Parse(b.queryinput.GetInputValue()) if err != nil { return "", err } processor := processJSONWithQuery if b.isJSONLines { processor = processJSONLinesWithQuery } if err := processor(ctx, &results, query, b.inputdata.GetInputJSON()); err != nil { return "", err } return results.String(), nil } func (b *Bubble) executeQueryCommand(ctx context.Context) tea.Cmd { return func() tea.Msg { results, err := b.executeQueryOnInput(ctx) if err != nil { return errorMsg{error: err} } highlightedOutput, err := utils.Prettify([]byte(results), b.theme.ChromaStyle, true) if err != nil { return errorMsg{error: err} } return queryResultMsg{ rawResults: results, highlightedResults: highlightedOutput.String(), } } } func (b Bubble) saveOutput() tea.Cmd { if b.fileselector.GetInput() == "" { return b.copyOutputToClipboard() } return b.writeOutputToFile() } func (b Bubble) copyOutputToClipboard() tea.Cmd { return func() tea.Msg { err := clipboard.WriteAll(b.results) if err != nil { return errorMsg{ error: err, } } return copyResultsToClipboardMsg{} } } func (b Bubble) writeOutputToFile() tea.Cmd { return func() tea.Msg { err := os.WriteFile(b.fileselector.GetInput(), []byte(b.results), 0o600) if err != nil { return errorMsg{ error: err, } } return writeToFileMsg{} } } func (b Bubble) copyQueryToClipboard() tea.Cmd { return func() tea.Msg { err := clipboard.WriteAll(b.queryinput.GetInputValue()) if err != nil { return errorMsg{ error: err, } } return copyQueryToClipboardMsg{} } } jqp-0.7.0/tui/bubbles/jqplayground/init.go000066400000000000000000000023521472436755000205570ustar00rootroot00000000000000package jqplayground import ( tea "github.com/charmbracelet/bubbletea" "github.com/noahgorstein/jqp/tui/utils" ) // invalidInputMsg signals that the user's data is not valid JSON or NDJSON type invalidInputMsg struct{} type setupMsg struct { isJSONLines bool } // initialQueryMsg represents a message containing an initial query string to execute when // the app is loaded. type initialQueryMsg struct { query string } func setupCmd(isJSONLines bool) tea.Cmd { return func() tea.Msg { return setupMsg{isJSONLines: isJSONLines} } } // initialQueryCmd creates a command that returns an initialQueryMsg with the provided query string. func initialQueryCmd(query string) tea.Cmd { return func() tea.Msg { return initialQueryMsg{query: query} } } func (b Bubble) Init() tea.Cmd { var cmds []tea.Cmd // validate input data _, isJSONLines, err := utils.IsValidInput(b.inputdata.GetInputJSON()) if err != nil { return func() tea.Msg { return invalidInputMsg{} } } // initialize rest of app cmds = append(cmds, b.queryinput.Init(), b.inputdata.Init(isJSONLines), setupCmd(isJSONLines)) if b.queryinput.GetInputValue() != "" { cmds = append(cmds, initialQueryCmd(b.queryinput.GetInputValue())) } return tea.Sequence(cmds...) } jqp-0.7.0/tui/bubbles/jqplayground/model.go000066400000000000000000000034241472436755000207150ustar00rootroot00000000000000package jqplayground import ( "os" "time" "github.com/noahgorstein/jqp/tui/bubbles/fileselector" "github.com/noahgorstein/jqp/tui/bubbles/help" "github.com/noahgorstein/jqp/tui/bubbles/inputdata" "github.com/noahgorstein/jqp/tui/bubbles/output" "github.com/noahgorstein/jqp/tui/bubbles/queryinput" "github.com/noahgorstein/jqp/tui/bubbles/state" "github.com/noahgorstein/jqp/tui/bubbles/statusbar" "github.com/noahgorstein/jqp/tui/theme" ) func (b Bubble) GetState() state.State { return b.state } type Bubble struct { width int height int workingDirectory string state state.State queryinput queryinput.Bubble inputdata inputdata.Bubble output output.Bubble help help.Bubble statusbar statusbar.Bubble fileselector fileselector.Bubble results string cancel func() theme theme.Theme ExitMessage string isJSONLines bool } func New(inputJSON []byte, filename string, query string, jqtheme theme.Theme) (Bubble, error) { workingDirectory, err := os.Getwd() if err != nil { return Bubble{}, err } sb := statusbar.New(jqtheme) sb.StatusMessageLifetime = time.Second * 10 fs := fileselector.New(jqtheme) fs.SetInput(workingDirectory) inputData, err := inputdata.New(inputJSON, filename, jqtheme) if err != nil { return Bubble{}, err } queryInput := queryinput.New(jqtheme) if query != "" { queryInput.SetQuery(query) } b := Bubble{ workingDirectory: workingDirectory, state: state.Loading, queryinput: queryInput, inputdata: inputData, output: output.New(jqtheme), help: help.New(jqtheme), statusbar: sb, fileselector: fs, theme: jqtheme, } return b, nil } jqp-0.7.0/tui/bubbles/jqplayground/update.go000066400000000000000000000161551472436755000211040ustar00rootroot00000000000000package jqplayground import ( "context" "fmt" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/noahgorstein/jqp/tui/bubbles/inputdata" "github.com/noahgorstein/jqp/tui/bubbles/state" ) func (b Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd prevState := b.state b.handleMessage(msg, &cmds) b.updateState(prevState, &cmds) b.updateComponents(msg, &cmds) return b, tea.Batch(cmds...) } func totalHeight(bubbles ...interface{ View() string }) int { var height int for _, bubble := range bubbles { height += lipgloss.Height(bubble.View()) } return height } func (b *Bubble) resizeBubbles() { b.queryinput.SetWidth(b.width) b.statusbar.SetSize(b.width) b.help.SetWidth(b.width) height := b.height if b.state == state.Save { b.fileselector.SetSize(b.width) height -= totalHeight(b.help, b.queryinput, b.statusbar, b.fileselector) } else { height -= totalHeight(b.help, b.queryinput, b.statusbar) } b.inputdata.SetSize(b.width/2, height) b.output.SetSize(b.width/2, height) } //nolint:revive // don't see a more elegant way to reduce complexity here since types can't be keys in a map func (b *Bubble) handleMessage(msg tea.Msg, cmds *[]tea.Cmd) { switch msg := msg.(type) { case setupMsg: b.isJSONLines = msg.isJSONLines case tea.WindowSizeMsg: b.handleWindowSizeMsg(msg) case tea.KeyMsg: b.handleKeyMsg(msg, cmds) case initialQueryMsg: b.executeQuery(cmds) case queryResultMsg: b.handleQueryResultMsg(msg, cmds) case writeToFileMsg: b.handleWriteToFileMsg(msg, cmds) case copyResultsToClipboardMsg: b.handleCopyResultsToClipboardMsg(cmds) case copyQueryToClipboardMsg: b.handleCopyQueryToClipboardMsg(cmds) case errorMsg: b.handleErrorMsg(msg, cmds) case invalidInputMsg: b.handleInvalidInput(cmds) case inputdata.ReadyMsg: b.state = state.Query } } func (b *Bubble) handleQueryResultMsg(msg queryResultMsg, cmds *[]tea.Cmd) { b.state = state.Query b.output.ScrollToTop() b.output.SetContent(msg.highlightedResults) b.results = msg.rawResults *cmds = append(*cmds, b.statusbar.NewStatusMessage("Successfully executed query.", true)) } func (b *Bubble) handleWriteToFileMsg(_ writeToFileMsg, cmds *[]tea.Cmd) { b.state = state.Query *cmds = append(*cmds, b.statusbar.NewStatusMessage(fmt.Sprintf("Successfully wrote results to file: %s", b.fileselector.GetInput()), true)) b.fileselector.SetInput(b.workingDirectory) } func (b *Bubble) handleCopyResultsToClipboardMsg(cmds *[]tea.Cmd) { b.state = state.Query *cmds = append(*cmds, b.statusbar.NewStatusMessage("Successfully copied results to system clipboard.", true)) } func (b *Bubble) handleCopyQueryToClipboardMsg(cmds *[]tea.Cmd) { *cmds = append(*cmds, b.statusbar.NewStatusMessage("Successfully copied query to system clipboard.", true)) } func (b *Bubble) handleErrorMsg(msg errorMsg, cmds *[]tea.Cmd) { if b.state == state.Running { b.state = state.Query } *cmds = append(*cmds, b.statusbar.NewStatusMessage(msg.error.Error(), false)) } func (b *Bubble) handleWindowSizeMsg(msg tea.WindowSizeMsg) { b.width = msg.Width b.height = msg.Height b.resizeBubbles() } func (b *Bubble) handleKeyMsg(msg tea.KeyMsg, cmds *[]tea.Cmd) { keyHandlers := map[tea.KeyType]func(){ tea.KeyCtrlC: func() { b.handleCtrlC(cmds) }, tea.KeyTab: b.handleTab, tea.KeyShiftTab: b.handleShiftTab, tea.KeyEsc: b.handleEsc, tea.KeyEnter: func() { b.handleEnter(cmds) }, tea.KeyCtrlS: b.handleCtrlS, tea.KeyCtrlY: func() { b.handleCtrlY(cmds) }, } if handler, ok := keyHandlers[msg.Type]; ok { handler() } } func (b *Bubble) handleInvalidInput(cmds *[]tea.Cmd) { b.ExitMessage = "Data is not valid JSON or NDJSON" *cmds = append(*cmds, tea.Quit) } func (b *Bubble) handleCtrlC(cmds *[]tea.Cmd) { if b.state != state.Running { *cmds = append(*cmds, tea.Quit) } if b.cancel != nil { b.cancel() b.cancel = nil } b.state = state.Query } func (b *Bubble) handleTab() { if b.state != state.Save { switch b.state { case state.Query: b.state = state.Input case state.Input: b.state = state.Output case state.Output: b.state = state.Query } } } func (b *Bubble) handleShiftTab() { if b.state != state.Save { switch b.state { case state.Query: b.state = state.Output case state.Input: b.state = state.Query case state.Output: b.state = state.Input } } } func (b *Bubble) handleEsc() { if b.state == state.Save { b.state = state.Query } } func (b *Bubble) executeQuery(cmds *[]tea.Cmd) { b.queryinput.RotateHistory() b.state = state.Running var ctx context.Context ctx, b.cancel = context.WithCancel(context.Background()) *cmds = append(*cmds, b.executeQueryCommand(ctx)) } func (b *Bubble) handleEnter(cmds *[]tea.Cmd) { if b.state == state.Save { *cmds = append(*cmds, b.saveOutput()) } if b.state == state.Query { b.executeQuery(cmds) } } func (b *Bubble) handleCtrlS() { b.state = state.Save } func (b *Bubble) handleCtrlY(cmds *[]tea.Cmd) { if b.state != state.Save { *cmds = append(*cmds, b.copyQueryToClipboard()) } } func (b *Bubble) updateState(prevState state.State, cmds *[]tea.Cmd) { if b.state != prevState { b.updateActiveComponent(cmds) b.help.SetState(b.state) // Help menu may overflow when we switch sections // so we need resize when active section changed. // We also need to resize when file selector (dis)appears. b.resizeBubbles() } } func (b *Bubble) updateActiveComponent(cmds *[]tea.Cmd) { switch b.state { case state.Query: b.setComponentBorderColors(b.theme.Primary, b.theme.Inactive, b.theme.Inactive) *cmds = append(*cmds, textinput.Blink) case state.Input: b.setComponentBorderColors(b.theme.Inactive, b.theme.Primary, b.theme.Inactive) case state.Output: b.setComponentBorderColors(b.theme.Inactive, b.theme.Inactive, b.theme.Primary) case state.Save: b.setComponentBorderColors(b.theme.Inactive, b.theme.Inactive, b.theme.Inactive) } } func (b *Bubble) setComponentBorderColors(query, input, output lipgloss.Color) { b.queryinput.SetBorderColor(query) b.inputdata.SetBorderColor(input) b.output.SetBorderColor(output) } func (b *Bubble) updateComponents(msg tea.Msg, cmds *[]tea.Cmd) { var cmd tea.Cmd dispatch := map[state.State]func(msg tea.Msg, cmds *[]tea.Cmd){ state.Query: func(msg tea.Msg, cmds *[]tea.Cmd) { b.queryinput, cmd = b.queryinput.Update(msg) *cmds = append(*cmds, cmd) }, state.Input: func(msg tea.Msg, cmds *[]tea.Cmd) { b.inputdata, cmd = b.inputdata.Update(msg) *cmds = append(*cmds, cmd) }, state.Output: func(msg tea.Msg, cmds *[]tea.Cmd) { b.output, cmd = b.output.Update(msg) *cmds = append(*cmds, cmd) }, state.Save: func(msg tea.Msg, cmds *[]tea.Cmd) { b.fileselector, cmd = b.fileselector.Update(msg) *cmds = append(*cmds, cmd) }, state.Loading: func(msg tea.Msg, cmds *[]tea.Cmd) { b.inputdata, cmd = b.inputdata.Update(msg) *cmds = append(*cmds, cmd) }, } if updateFunc, ok := dispatch[b.state]; ok { updateFunc(msg, cmds) } b.statusbar, cmd = b.statusbar.Update(msg) *cmds = append(*cmds, cmd) b.help, cmd = b.help.Update(msg) *cmds = append(*cmds, cmd) } jqp-0.7.0/tui/bubbles/jqplayground/view.go000066400000000000000000000013141472436755000205630ustar00rootroot00000000000000package jqplayground import ( "github.com/charmbracelet/lipgloss" "github.com/noahgorstein/jqp/tui/bubbles/state" ) func (b Bubble) View() string { inputoutput := []string{b.inputdata.View()} if b.width%2 != 0 { inputoutput = append(inputoutput, " ") } inputoutput = append(inputoutput, b.output.View()) if b.state == state.Save { return lipgloss.JoinVertical( lipgloss.Left, b.queryinput.View(), lipgloss.JoinHorizontal(lipgloss.Top, inputoutput...), b.fileselector.View(), b.statusbar.View(), b.help.View()) } return lipgloss.JoinVertical( lipgloss.Left, b.queryinput.View(), lipgloss.JoinHorizontal(lipgloss.Top, inputoutput...), b.statusbar.View(), b.help.View()) } jqp-0.7.0/tui/bubbles/output/000077500000000000000000000000001472436755000161045ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/output/output.go000066400000000000000000000045701472436755000200010ustar00rootroot00000000000000package output import ( "fmt" "strings" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/noahgorstein/jqp/tui/theme" ) type Bubble struct { Ready bool Styles Styles viewport viewport.Model content string height int width int } func New(jqtheme theme.Theme) Bubble { styles := DefaultStyles() styles.containerStyle = styles.containerStyle.BorderForeground(jqtheme.Inactive) styles.infoStyle = styles.infoStyle.BorderForeground(jqtheme.Inactive) v := viewport.New(1, 1) b := Bubble{ Styles: styles, viewport: v, content: "", } return b } func (b *Bubble) SetBorderColor(color lipgloss.TerminalColor) { b.Styles.containerStyle = b.Styles.containerStyle.BorderForeground(color) b.Styles.infoStyle = b.Styles.infoStyle.BorderForeground(color) } func (b *Bubble) SetSize(width, height int) { b.width = width b.height = height b.Styles.containerStyle = b.Styles.containerStyle. Width(width - b.Styles.containerStyle.GetHorizontalFrameSize()/2). Height(height - b.Styles.containerStyle.GetVerticalFrameSize()) b.viewport.Width = width - b.Styles.containerStyle.GetHorizontalFrameSize() b.viewport.Height = height - b.Styles.containerStyle.GetVerticalFrameSize() - 3 } func (b *Bubble) GetContent() string { return b.content } func (b *Bubble) SetContent(content string) { b.content = content wrappedContent := lipgloss.NewStyle().Width(b.viewport.Width - 1).Render(content) b.viewport.SetContent(wrappedContent) } func max(a, b int) int { if a > b { return a } return b } func (b *Bubble) ScrollToTop() { b.viewport.GotoTop() } func (b Bubble) View() string { scrollPercent := fmt.Sprintf("%3.f%%", b.viewport.ScrollPercent()*100) info := b.Styles.infoStyle.Render(fmt.Sprintf("%s | %s", lipgloss.NewStyle().Italic(true).Render("output"), scrollPercent)) line := strings.Repeat(" ", max(0, b.viewport.Width-lipgloss.Width(info))) footer := lipgloss.JoinHorizontal(lipgloss.Center, line, info) content := lipgloss.JoinVertical(lipgloss.Left, b.viewport.View(), footer) return b.Styles.containerStyle.Render(content) } func (Bubble) Init() tea.Cmd { return nil } func (b Bubble) Update(msg tea.Msg) (Bubble, tea.Cmd) { var ( cmd tea.Cmd cmds []tea.Cmd ) b.viewport, cmd = b.viewport.Update(msg) cmds = append(cmds, cmd) return b, tea.Batch(cmds...) } jqp-0.7.0/tui/bubbles/output/styles.go000066400000000000000000000005671472436755000177660ustar00rootroot00000000000000package output import ( "github.com/charmbracelet/lipgloss" ) type Styles struct { infoStyle lipgloss.Style containerStyle lipgloss.Style } func DefaultStyles() (s Styles) { s.infoStyle = lipgloss.NewStyle().Bold(true).Border(lipgloss.RoundedBorder()).Padding(0, 1) s.containerStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).Padding(1) return s } jqp-0.7.0/tui/bubbles/queryinput/000077500000000000000000000000001472436755000167715ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/queryinput/queryinput.go000066400000000000000000000052141472436755000215470ustar00rootroot00000000000000package queryinput import ( "container/list" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/noahgorstein/jqp/tui/theme" ) type Bubble struct { Styles Styles textinput textinput.Model history *list.List historyMaxLen int historySelected *list.Element } func New(jqtheme theme.Theme) Bubble { s := DefaultStyles() s.containerStyle.BorderForeground(jqtheme.Primary) ti := textinput.New() ti.Focus() ti.PromptStyle.Height(1) ti.TextStyle.Height(1) ti.Prompt = lipgloss.NewStyle().Bold(true).Foreground(jqtheme.Secondary).Render("jq > ") return Bubble{ Styles: s, textinput: ti, history: list.New(), historyMaxLen: 512, } } func (b *Bubble) SetBorderColor(color lipgloss.TerminalColor) { b.Styles.containerStyle = b.Styles.containerStyle.BorderForeground(color) } func (b Bubble) GetInputValue() string { return b.textinput.Value() } func (b *Bubble) RotateHistory() { b.history.PushFront(b.textinput.Value()) b.historySelected = b.history.Front() if b.history.Len() > b.historyMaxLen { b.history.Remove(b.history.Back()) } } func (Bubble) Init() tea.Cmd { return textinput.Blink } func (b *Bubble) SetWidth(width int) { b.Styles.containerStyle = b.Styles.containerStyle.Width(width - b.Styles.containerStyle.GetHorizontalFrameSize()) b.textinput.Width = width - b.Styles.containerStyle.GetHorizontalFrameSize() - 1 } func (b Bubble) View() string { return b.Styles.containerStyle.Render(b.textinput.View()) } func (b Bubble) Update(msg tea.Msg) (Bubble, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: return b.updateKeyMsg(msg) default: var cmd tea.Cmd b.textinput, cmd = b.textinput.Update(msg) return b, cmd } } func (b *Bubble) SetQuery(query string) { b.textinput.SetValue(query) } func (b Bubble) updateKeyMsg(msg tea.KeyMsg) (Bubble, tea.Cmd) { switch msg.Type { case tea.KeyUp: return b.handleKeyUp() case tea.KeyDown: return b.handleKeyDown() case tea.KeyEnter: b.RotateHistory() return b, nil default: var cmd tea.Cmd b.textinput, cmd = b.textinput.Update(msg) return b, cmd } } func (b Bubble) handleKeyUp() (Bubble, tea.Cmd) { if b.history.Len() == 0 { return b, nil } n := b.historySelected.Next() if n != nil { b.textinput.SetValue(n.Value.(string)) b.textinput.CursorEnd() b.historySelected = n } return b, nil } func (b Bubble) handleKeyDown() (Bubble, tea.Cmd) { if b.history.Len() == 0 { return b, nil } p := b.historySelected.Prev() if p != nil { b.textinput.SetValue(p.Value.(string)) b.textinput.CursorEnd() b.historySelected = p } return b, nil } jqp-0.7.0/tui/bubbles/queryinput/styles.go000066400000000000000000000003631472436755000206450ustar00rootroot00000000000000package queryinput import ( "github.com/charmbracelet/lipgloss" ) type Styles struct { containerStyle lipgloss.Style } func DefaultStyles() (s Styles) { s.containerStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()) return s } jqp-0.7.0/tui/bubbles/state/000077500000000000000000000000001472436755000156645ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/state/state.go000066400000000000000000000001451472436755000173330ustar00rootroot00000000000000package state type State uint const ( Query State = iota Running Input Output Save Loading ) jqp-0.7.0/tui/bubbles/statusbar/000077500000000000000000000000001472436755000165545ustar00rootroot00000000000000jqp-0.7.0/tui/bubbles/statusbar/statusbar.go000066400000000000000000000032001472436755000211060ustar00rootroot00000000000000package statusbar import ( "time" tea "github.com/charmbracelet/bubbletea" "github.com/noahgorstein/jqp/tui/theme" ) type Bubble struct { styles styles StatusMessageLifetime time.Duration statusMessage string statusMessageTimer *time.Timer } func (Bubble) Init() tea.Cmd { return nil } func (b Bubble) View() string { return b.styles.containerStyle.Render(b.statusMessage) } func (b *Bubble) SetSize(width int) { b.styles.containerStyle = b.styles.containerStyle.Width(width) } func (b *Bubble) hideStatusMessage() { b.statusMessage = "" if b.statusMessageTimer != nil { b.statusMessageTimer.Stop() } } func (b Bubble) Update(msg tea.Msg) (Bubble, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case statusMessageTimeoutMsg: b.hideStatusMessage() case tea.WindowSizeMsg: b.SetSize(msg.Width) } return b, tea.Batch(cmd) } func New(jqtheme theme.Theme) Bubble { styles := defaultStyles() styles.successMessageStyle = styles.successMessageStyle.Foreground(jqtheme.Success) styles.errorMessageStyle = styles.errorMessageStyle.Foreground(jqtheme.Error) b := Bubble{ styles: styles, } return b } type statusMessageTimeoutMsg struct{} func (b *Bubble) NewStatusMessage(s string, success bool) tea.Cmd { if success { b.statusMessage = b.styles.successMessageStyle.Render(s) } else { b.statusMessage = b.styles.errorMessageStyle.Render(s) } if b.statusMessageTimer != nil { b.statusMessageTimer.Stop() } b.statusMessageTimer = time.NewTimer(b.StatusMessageLifetime) // Wait for timeout return func() tea.Msg { <-b.statusMessageTimer.C return statusMessageTimeoutMsg{} } } jqp-0.7.0/tui/bubbles/statusbar/styles.go000066400000000000000000000006061472436755000204300ustar00rootroot00000000000000package statusbar import ( "github.com/charmbracelet/lipgloss" ) type styles struct { containerStyle lipgloss.Style errorMessageStyle lipgloss.Style successMessageStyle lipgloss.Style } func defaultStyles() (s styles) { s.containerStyle = lipgloss.NewStyle().PaddingLeft(1) s.errorMessageStyle = lipgloss.NewStyle() s.successMessageStyle = lipgloss.NewStyle() return s } jqp-0.7.0/tui/theme/000077500000000000000000000000001472436755000142305ustar00rootroot00000000000000jqp-0.7.0/tui/theme/theme.go000066400000000000000000000350371472436755000156710ustar00rootroot00000000000000package theme import ( "strings" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/styles" "github.com/charmbracelet/lipgloss" ) type CustomTheme struct { Primary string Secondary string Inactive string Success string Error string } var CustomThemeKeys = CustomTheme{ Primary: "primary", Secondary: "secondary", Success: "success", Inactive: "inactive", Error: "error", } const ( BLUE = lipgloss.Color("69") PINK = lipgloss.Color("#F25D94") GREY = lipgloss.Color("240") GREEN = lipgloss.Color("76") RED = lipgloss.Color("9") ) type Theme struct { Primary lipgloss.Color Secondary lipgloss.Color Inactive lipgloss.Color Success lipgloss.Color Error lipgloss.Color ChromaStyle *chroma.Style } func getDefaultTheme() Theme { theme := Theme{ Primary: BLUE, Secondary: PINK, Inactive: GREY, Success: GREEN, Error: RED, ChromaStyle: styles.Get("paraiso-light"), } if lipgloss.HasDarkBackground() { theme.ChromaStyle = styles.Get("vim") } return theme } var ( // from https://www.nordtheme.com/docs/colors-and-palettes nord7 = lipgloss.Color("#8FBCBB") nord9 = lipgloss.Color("#81A1C1") nord11 = lipgloss.Color("#BF616A") nord14 = lipgloss.Color("#A3BE8C") ) var themeMap = map[string]Theme{ "abap": { Primary: lipgloss.Color("#00f"), Secondary: lipgloss.Color("#3af"), Inactive: GREY, Success: lipgloss.Color("#5a2"), Error: lipgloss.Color("#F00"), ChromaStyle: styles.Get("abap"), }, "algol": { Primary: lipgloss.Color("#5a2"), Secondary: lipgloss.Color("#666"), Inactive: GREY, Success: lipgloss.Color("#5a2"), Error: lipgloss.Color("#FF0000"), ChromaStyle: styles.Get("algol"), }, "arduino": { Primary: lipgloss.Color("#1e90ff"), Secondary: lipgloss.Color("#aa5500"), Inactive: GREY, Success: lipgloss.Color("#5a2"), Error: lipgloss.Color("#F00"), ChromaStyle: styles.Get("arduino"), }, "autumn": { Primary: lipgloss.Color("#aa5500"), Secondary: lipgloss.Color("#fcba03"), Inactive: GREY, Success: lipgloss.Color("#009999"), Error: lipgloss.Color("#ff0000"), ChromaStyle: styles.Get("autumn"), }, "average": { Primary: lipgloss.Color("#ec0000"), Secondary: lipgloss.Color("#008900"), Inactive: GREY, Success: lipgloss.Color("#008900"), Error: lipgloss.Color("#ec0000"), ChromaStyle: styles.Get("average"), }, "base16-snazzy": { Primary: lipgloss.Color("#ff6ac1"), Secondary: lipgloss.Color("#5af78e"), Inactive: GREY, Success: lipgloss.Color("#5af78e"), Error: lipgloss.Color("#ff5c57"), ChromaStyle: styles.Get("base16-snazzy"), }, "borland": { Primary: lipgloss.Color("#00f"), Secondary: lipgloss.Color("#000080"), Inactive: GREY, Success: lipgloss.Color("#5a2"), Error: lipgloss.Color("#a61717"), ChromaStyle: styles.Get("borland"), }, "catppuccin-latte": { Primary: lipgloss.Color("#179299"), Secondary: lipgloss.Color("#1e66f5"), Inactive: GREY, Success: lipgloss.Color("#40a02b"), Error: lipgloss.Color("#d20f39"), ChromaStyle: styles.Get("catppuccin-latte"), }, "catppuccin-frappe": { Primary: lipgloss.Color("#81c8be"), Secondary: lipgloss.Color("#8caaee"), Inactive: GREY, Success: lipgloss.Color("#a6d189"), Error: lipgloss.Color("#e78284"), ChromaStyle: styles.Get("catppuccin-frappe"), }, "catppuccin-macchiato": { Primary: lipgloss.Color("#8bd5ca"), Secondary: lipgloss.Color("#8aadf4"), Inactive: GREY, Success: lipgloss.Color("#a6da95"), Error: lipgloss.Color("#ed8796"), ChromaStyle: styles.Get("catppuccin-macchiato"), }, "catppuccin-mocha": { Primary: lipgloss.Color("#94e2d5"), Secondary: lipgloss.Color("#89b4fa"), Inactive: GREY, Success: lipgloss.Color("#a6e3a1"), Error: lipgloss.Color("#f38ba8"), ChromaStyle: styles.Get("catppuccin-mocha"), }, "colorful": { Primary: lipgloss.Color("#00d"), Secondary: lipgloss.Color("#070"), Inactive: GREY, Success: lipgloss.Color("#070"), Error: lipgloss.Color("#a61717"), ChromaStyle: styles.Get("colorful"), }, "doom-one": { Primary: lipgloss.Color("#b756ff"), Secondary: lipgloss.Color("#63c381"), Inactive: GREY, Success: lipgloss.Color("#63c381"), Error: lipgloss.Color("#e06c75"), ChromaStyle: styles.Get("doom-one"), }, "doom-one2": { Primary: lipgloss.Color("#76a9f9"), Secondary: lipgloss.Color("#63c381"), Inactive: GREY, Success: lipgloss.Color("#63c381"), Error: lipgloss.Color("#e06c75"), ChromaStyle: styles.Get("doom-one2"), }, "dracula": { Primary: lipgloss.Color("#8be9fd"), Secondary: lipgloss.Color("#ffb86c"), Inactive: GREY, Success: lipgloss.Color("#50fa7b"), Error: lipgloss.Color("#f8f8f2"), ChromaStyle: styles.Get("dracula"), }, "emacs": { Primary: lipgloss.Color("#008000"), Secondary: lipgloss.Color("#a2f"), Inactive: GREY, Success: lipgloss.Color("#008000"), Error: lipgloss.Color("#b44"), ChromaStyle: styles.Get("emacs"), }, "friendly": { Primary: lipgloss.Color("#40a070"), Secondary: lipgloss.Color("#062873"), Inactive: GREY, Success: lipgloss.Color("#40a070"), Error: lipgloss.Color("#FF0000"), ChromaStyle: styles.Get("friendly"), }, "fruity": { Primary: lipgloss.Color("#fb660a"), Secondary: lipgloss.Color("#0086f7"), Inactive: GREY, Success: lipgloss.Color("#40a070"), Error: lipgloss.Color("#FF0000"), ChromaStyle: styles.Get("fruity"), }, "github": { Primary: lipgloss.Color("#d14"), Secondary: lipgloss.Color("#099"), Inactive: GREY, Success: lipgloss.Color("#099"), Error: lipgloss.Color("#d14"), ChromaStyle: styles.Get("github"), }, "github-dark": { Primary: lipgloss.Color("#d2a8ff"), Secondary: lipgloss.Color("#f0883e"), Inactive: GREY, Success: lipgloss.Color("#56d364"), Error: lipgloss.Color("#ffa198"), ChromaStyle: styles.Get("github-dark"), }, "gruvbox": { Primary: lipgloss.Color("#b8bb26"), Secondary: lipgloss.Color("#d3869b"), Inactive: GREY, Success: lipgloss.Color("#b8bb26"), Error: lipgloss.Color("#fb4934"), ChromaStyle: styles.Get("gruvbox"), }, "gruvbox-light": { Primary: lipgloss.Color("#fb4934"), Secondary: lipgloss.Color("#b8bb26"), Inactive: GREY, Success: lipgloss.Color("#b8bb26"), Error: lipgloss.Color("#9D0006"), ChromaStyle: styles.Get("gruvbox-light"), }, "hrdark": { Primary: lipgloss.Color("#58a1dd"), Secondary: lipgloss.Color("#ff636f"), Inactive: GREY, Success: lipgloss.Color("#a6be9d"), Error: lipgloss.Color("#FF0000"), ChromaStyle: styles.Get("hrdark"), }, "igor": { Primary: lipgloss.Color("#009c00"), Secondary: lipgloss.Color("#00f"), Inactive: GREY, Success: lipgloss.Color("#009c00"), Error: lipgloss.Color("#FF0000"), ChromaStyle: styles.Get("igor"), }, "lovelace": { Primary: lipgloss.Color("#b83838"), Secondary: lipgloss.Color("#2838b0"), Inactive: GREY, Success: lipgloss.Color("#009c00"), Error: lipgloss.Color("#b83838"), ChromaStyle: styles.Get("lovelace"), }, "manni": { Primary: lipgloss.Color("#c30"), Secondary: lipgloss.Color("#309"), Inactive: GREY, Success: lipgloss.Color("#009c00"), Error: lipgloss.Color("#c30"), ChromaStyle: styles.Get("manni"), }, "monokai": { Primary: lipgloss.Color("#a6e22e"), Secondary: lipgloss.Color("#f92672"), Inactive: GREY, Success: lipgloss.Color("#b4d273"), Error: lipgloss.Color("#960050"), ChromaStyle: styles.Get("monokai"), }, "monokai-light": { Primary: lipgloss.Color("#00a8c8"), Secondary: lipgloss.Color("#f92672"), Inactive: GREY, Success: lipgloss.Color("#b4d273"), Error: lipgloss.Color("#960050"), ChromaStyle: styles.Get("monokai-light"), }, "murphy": { Primary: lipgloss.Color("#070"), Secondary: lipgloss.Color("#66f"), Inactive: GREY, Success: lipgloss.Color("#070"), Error: lipgloss.Color("#F00"), ChromaStyle: styles.Get("murphy"), }, "native": { Primary: lipgloss.Color("#6ab825"), Secondary: lipgloss.Color("#ed9d13"), Inactive: GREY, Success: lipgloss.Color("#6ab825"), Error: lipgloss.Color("#a61717"), ChromaStyle: styles.Get("native"), }, "nord": { Primary: nord7, Secondary: nord9, Inactive: GREY, Success: nord14, Error: nord11, ChromaStyle: styles.Get("nord"), }, "onesenterprise": { Primary: lipgloss.Color("#00f"), Secondary: lipgloss.Color("#f00"), Inactive: GREY, Success: lipgloss.Color("#6ab825"), Error: lipgloss.Color("#f00"), ChromaStyle: styles.Get("onesenterprise"), }, "pastie": { Primary: lipgloss.Color("#b06"), Secondary: lipgloss.Color("#00d"), Inactive: GREY, Success: lipgloss.Color("#080"), Error: lipgloss.Color("#d20"), ChromaStyle: styles.Get("pastie"), }, "perldoc": { Primary: lipgloss.Color("#8b008b"), Secondary: lipgloss.Color("#b452cd"), Inactive: GREY, Success: lipgloss.Color("#080"), Error: lipgloss.Color("#cd5555"), ChromaStyle: styles.Get("perldoc"), }, "paraiso-dark": { Primary: lipgloss.Color("#48b685"), Secondary: lipgloss.Color("#5bc4bf"), Inactive: GREY, Success: lipgloss.Color("#48b685"), Error: lipgloss.Color("#ef6155"), ChromaStyle: styles.Get("paraiso-dark"), }, "paraiso-light": { Primary: lipgloss.Color("#48b685"), Secondary: lipgloss.Color("#815ba4"), Inactive: GREY, Success: lipgloss.Color("#48b685"), Error: lipgloss.Color("#ef6155"), ChromaStyle: styles.Get("paraiso-light"), }, "pygments": { Primary: lipgloss.Color("#008000"), Secondary: lipgloss.Color("#ba2121"), Inactive: GREY, Success: lipgloss.Color("#008000"), Error: lipgloss.Color("#ba2121"), ChromaStyle: styles.Get("pygments"), }, "rainbow_dash": { Primary: lipgloss.Color("#0c6"), Secondary: lipgloss.Color("#5918bb"), Inactive: GREY, Success: lipgloss.Color("#0c6"), Error: lipgloss.Color("#ba2121"), ChromaStyle: styles.Get("rainbow_dash"), }, "rrt": { Primary: lipgloss.Color("#f60"), Secondary: lipgloss.Color("#87ceeb"), Inactive: GREY, Success: lipgloss.Color("#0c6"), Error: lipgloss.Color("#f00"), ChromaStyle: styles.Get("rrt"), }, "solarized-dark": { Primary: lipgloss.Color("#268bd2"), Secondary: lipgloss.Color("#2aa198"), Inactive: GREY, Success: lipgloss.Color("#0c6"), Error: lipgloss.Color("#cb4b16"), ChromaStyle: styles.Get("solarized-dark"), }, "solarized-dark256": { Primary: lipgloss.Color("#0087ff"), Secondary: lipgloss.Color("#00afaf"), Inactive: GREY, Success: lipgloss.Color("#0c6"), Error: lipgloss.Color("#d75f00"), ChromaStyle: styles.Get("solarized-dark256"), }, "solarized-light": { Primary: lipgloss.Color("#268bd2"), Secondary: lipgloss.Color("#2aa198"), Inactive: GREY, Success: lipgloss.Color("#859900"), Error: lipgloss.Color("#d75f00"), ChromaStyle: styles.Get("solarized-light"), }, "swapoff": { Primary: lipgloss.Color("#0ff"), Secondary: lipgloss.Color("#ff0"), Inactive: GREY, Success: lipgloss.Color("#e5e5e5"), Error: lipgloss.Color("#e5e5e5"), ChromaStyle: styles.Get("swapoff"), }, "tango": { Primary: lipgloss.Color("#204a87"), Secondary: lipgloss.Color("#0000cf"), Inactive: GREY, Success: lipgloss.Color("#4e9a06"), Error: lipgloss.Color("#a40000"), ChromaStyle: styles.Get("tango"), }, "trac": { Primary: lipgloss.Color("#099"), Secondary: lipgloss.Color("#000080"), Inactive: GREY, Success: lipgloss.Color("#099"), Error: lipgloss.Color("#a61717"), ChromaStyle: styles.Get("trac"), }, "vim": { Primary: lipgloss.Color("#cd00cd"), Secondary: lipgloss.Color("#cdcd00"), Inactive: GREY, Success: lipgloss.Color("#66FF00"), Error: lipgloss.Color("#cd0000"), ChromaStyle: styles.Get("vim"), }, "visual_studio": { Primary: lipgloss.Color("#a31515"), Secondary: lipgloss.Color("#00f"), Inactive: GREY, Success: lipgloss.Color("#023020"), Error: lipgloss.Color("#a31515"), ChromaStyle: styles.Get("vs"), }, "vulcan": { Primary: lipgloss.Color("#bc74c4"), Secondary: lipgloss.Color("#56b6c2"), Inactive: GREY, Success: lipgloss.Color("#82cc6a"), Error: lipgloss.Color("#cf5967"), ChromaStyle: styles.Get("vulcan"), }, "witchhazel": { Primary: lipgloss.Color("#ffb8d1"), Secondary: lipgloss.Color("#56b6c2"), Inactive: GREY, Success: lipgloss.Color("#c2ffdf"), Error: lipgloss.Color("#ffb8d1"), ChromaStyle: styles.Get("witchhazel"), }, "xcode": { Primary: lipgloss.Color("#c41a16"), Secondary: lipgloss.Color("#1c01ce"), Inactive: GREY, Success: lipgloss.Color("#023020"), Error: lipgloss.Color("#c41a16"), ChromaStyle: styles.Get("xcode"), }, "xcode-dark": { Primary: lipgloss.Color("#fc6a5d"), Secondary: lipgloss.Color("#d0bf69"), Inactive: GREY, Success: lipgloss.Color("#90EE90"), Error: lipgloss.Color("#fc6a5d"), ChromaStyle: styles.Get("xcode-dark"), }, } // returns a theme by name, and true if default theme was returned func GetTheme(themeName string, styleOverrides map[string]string) (Theme, bool) { lowercasedTheme := strings.ToLower(strings.TrimSpace(themeName)) var isDefault bool var theme Theme if value, ok := themeMap[lowercasedTheme]; ok { theme = value isDefault = false } else { theme = getDefaultTheme() isDefault = true } theme.SetOverrides(styleOverrides) return theme, isDefault && len(styleOverrides) == 0 } func (t *Theme) SetOverrides(overrides map[string]string) { t.Primary = customColorOrDefault(overrides[CustomThemeKeys.Primary], t.Primary) t.Secondary = customColorOrDefault(overrides[CustomThemeKeys.Secondary], t.Secondary) t.Inactive = customColorOrDefault(overrides[CustomThemeKeys.Inactive], t.Inactive) t.Success = customColorOrDefault(overrides[CustomThemeKeys.Success], t.Success) t.Error = customColorOrDefault(overrides[CustomThemeKeys.Error], t.Error) } func customColorOrDefault(color string, def lipgloss.Color) lipgloss.Color { if color == "" { return def } return lipgloss.Color(color) } jqp-0.7.0/tui/utils/000077500000000000000000000000001472436755000142665ustar00rootroot00000000000000jqp-0.7.0/tui/utils/json.go000066400000000000000000000060711472436755000155720ustar00rootroot00000000000000package utils import ( "bytes" "encoding/json" "errors" "fmt" "io" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/formatters" "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" ) const FourSpaces = " " // IsValidInput checks the validity of input data as JSON or JSON lines. // It takes a byte slice 'data' and returns two boolean values indicating // whether the data is valid JSON and valid JSON lines, along with an error // if the data is not valid in either format. func IsValidInput(data []byte) (isValidJSON bool, isValidJSONLines bool, err error) { if len(data) == 0 { err = errors.New("Data is not valid JSON or NDJSON") return false, false, err } isValidJSON = IsValidJSON(data) == nil isValidJSONLines = IsValidJSONLines(data) == nil if !isValidJSON && !isValidJSONLines { err = errors.New("Data is not valid JSON or NDJSON") return false, false, err } return isValidJSON, isValidJSONLines, nil } func highlightJSON(w io.Writer, source string, style *chroma.Style) error { l := lexers.Get("json") if l == nil { l = lexers.Fallback } l = chroma.Coalesce(l) f := formatters.Get(getTerminalColorSupport()) if f == nil { f = formatters.Fallback } if style == nil { style = styles.Fallback } it, err := l.Tokenise(nil, source) if err != nil { return err } return f.Format(w, style, it) } func IsValidJSON(input []byte) error { var js json.RawMessage return json.Unmarshal(input, &js) } func IsValidJSONLines(input []byte) error { maxBufferSize := 100 * 1024 * 1024 // 100MB err := ScanLinesWithDynamicBufferSize(input, maxBufferSize, IsValidJSON) if err != nil { return err } return nil } func indentJSON(input *[]byte, output *bytes.Buffer) error { err := IsValidJSON(*input) if err != nil { return nil } err = json.Indent(output, []byte(*input), "", FourSpaces) if err != nil { return err } return nil } func prettifyJSON(input []byte, chromaStyle *chroma.Style) (*bytes.Buffer, error) { var indentedBuf bytes.Buffer err := indentJSON(&input, &indentedBuf) if err != nil { return nil, err } if indentedBuf.Len() == 0 { err := highlightJSON(&indentedBuf, string(input), chromaStyle) if err != nil { return nil, err } return &indentedBuf, nil } var highlightedBuf bytes.Buffer err = highlightJSON(&highlightedBuf, indentedBuf.String(), chromaStyle) if err != nil { return nil, err } return &highlightedBuf, nil } func Prettify(inputJSON []byte, chromaStyle *chroma.Style, isJSONLines bool) (*bytes.Buffer, error) { if !isJSONLines { return prettifyJSON(inputJSON, chromaStyle) } var buf bytes.Buffer processLine := func(line []byte) error { hightlighedLine, err := prettifyJSON(line, chromaStyle) if err != nil { return err } _, err = buf.WriteString(fmt.Sprintf("%v\n", hightlighedLine)) if err != nil { return err } return nil } const maxBufferSize = 100 * 1024 * 1024 // 100MB max buffer size err := ScanLinesWithDynamicBufferSize(inputJSON, maxBufferSize, processLine) if err != nil { return nil, err } return &buf, nil } jqp-0.7.0/tui/utils/scan.go000066400000000000000000000033141472436755000155420ustar00rootroot00000000000000package utils import ( "bufio" "bytes" "errors" "fmt" ) // ScanLinesWithDynamicBufferSize scans the input byte slice line by line, using a dynamically // increasing buffer size. It starts with an initial buffer size of 64KB and doubles the buffer // size each time a line exceeds the current buffer size, up to the specified maximum buffer size. // // If a line exceeds the maximum buffer size, it returns an error. // // The processLine function is called for each line and should return an error if processing fails. // // The function returns an error if the input exceeds the maximum buffer size or if any other // error occurs during line processing. It returns nil if all lines are processed successfully. func ScanLinesWithDynamicBufferSize(input []byte, maxBufferSize int, processLine func([]byte) error) error { scanner := bufio.NewScanner(bytes.NewReader(input)) initialBufferSize := 64 * 1024 // 64KB initial buffer size for bufferSize := initialBufferSize; bufferSize <= maxBufferSize; bufferSize *= 2 { if err := scanWithBufferSize(scanner, bufferSize, maxBufferSize, processLine); err != nil { if errors.Is(err, bufio.ErrTooLong) { // Buffer size is too small, retry with a larger buffer continue } return err } // All lines are processed successfully return nil } // Input exceeds maximum buffer size return fmt.Errorf("input exceeds maximum buffer size of %d bytes", maxBufferSize) } func scanWithBufferSize(scanner *bufio.Scanner, bufferSize, maxBufferSize int, processLine func([]byte) error) error { scanner.Buffer(make([]byte, bufferSize), maxBufferSize) for scanner.Scan() { if err := processLine(scanner.Bytes()); err != nil { return err } } return scanner.Err() } jqp-0.7.0/tui/utils/terminal.go000066400000000000000000000007571472436755000164410ustar00rootroot00000000000000package utils import ( "github.com/charmbracelet/lipgloss" "github.com/muesli/termenv" ) var termenvChromaTerminal = map[termenv.Profile]string{ termenv.Ascii: "terminal", termenv.ANSI: "terminal16", termenv.ANSI256: "terminal256", termenv.TrueColor: "terminal16m", } // returns a string used for chroma syntax highlighting func getTerminalColorSupport() string { if chroma, ok := termenvChromaTerminal[lipgloss.ColorProfile()]; ok { return chroma } return "terminal" }