diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..12c94c2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,35 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + build: + name: Build and Release + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: '^1.21' + + - name: Build binaries for all platforms + run: | + GOOS=windows GOARCH=amd64 go build -o jsonviz-windows-amd64.exe + GOOS=linux GOARCH=amd64 go build -o jsonviz-linux-amd64 + GOOS=darwin GOARCH=amd64 go build -o jsonviz-darwin-amd64 + + - name: Upload releases + uses: softprops/action-gh-release@v1 + with: + files: | + jsonviz-windows-amd64.exe + jsonviz-linux-amd64 + jsonviz-darwin-amd64 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 0881875..4573f48 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,58 @@ # jsonviz -visualize json files in the terminal + +`jsonviz` is a command-line tool built in Go that visualizes JSON file structures in a navigable tree format using the `termui` library. This tool is designed to help developers and data scientists quickly understand and explore the structure of complex JSON data from the terminal. + +## Features + +- **Interactive Tree View**: Display JSON files as expandable and collapsible trees. +- **Color Coding**: Different colors for root, keys, and values to enhance readability. +- **Keyboard Navigation**: Use keyboard shortcuts to navigate through the JSON structure. +- **Resizable UI**: Adapt to terminal size changes dynamically. + +## Installation + +To install `jsonviz`, clone this repository and build the tool using Go. + +```bash +git clone https://github.com/copyleftdev/jsonviz.git +cd jsonviz +go build +``` + +## Usage + +After building the tool, you can run it by specifying the JSON file you want to visualize: + +```bash +./jsonviz path/to/your/file.json +``` + +### Keyboard Shortcuts + +- **Arrow Up (`k`)**: Move up in the tree. +- **Arrow Down (`j`)**: Move down in the tree. +- **Enter**: Expand or collapse the selected node. +- **CTRL+C**: Exit the application. + +## Requirements + +- Go 1.15 or higher +- `termui` library (automatically installed with go get) + +## Contributing + +Contributions are welcome! Feel free to submit pull requests, create issues for bugs you've found, or suggest new features. + +1. Fork the repository. +2. Create your feature branch (`git checkout -b feature/fooBar`). +3. Commit your changes (`git commit -am 'Add some fooBar'`). +4. Push to the branch (`git push origin feature/fooBar`). +5. Create a new Pull Request. + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details. + +## Contact + +- GitHub: [@copyleftdev](https://github.com/copyleftdev) diff --git a/jsonviz.go b/jsonviz.go new file mode 100644 index 0000000..6bedaba --- /dev/null +++ b/jsonviz.go @@ -0,0 +1,124 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "log" + "os" + + ui "github.com/gizak/termui/v3" + "github.com/gizak/termui/v3/widgets" +) + +type nodeValue struct { + Key string + Value interface{} +} + +var ( + rootStyle = ui.NewStyle(ui.ColorGreen) + keyStyle = ui.NewStyle(ui.ColorGreen) + valueStyle = ui.NewStyle(ui.ColorWhite) + selectedStyle = ui.NewStyle(ui.ColorYellow, ui.ColorClear, ui.ModifierBold) // Correctly apply style with modifiers +) + +func (nv nodeValue) String() string { + if nv.Key == "root" { + return fmt.Sprintf("[%s](fg:green)", nv.Key) + } + switch v := nv.Value.(type) { + case string, float64, bool, int: + return fmt.Sprintf("[%s: %v](fg:white)", nv.Key, v) + default: + return fmt.Sprintf("[%s](fg:green)", nv.Key) + } +} + +func parseJSONToTree(key string, data interface{}) *widgets.TreeNode { + node := &widgets.TreeNode{ + Value: nodeValue{Key: key}, + } + + switch v := data.(type) { + case map[string]interface{}: + children := []*widgets.TreeNode{} + for k, val := range v { + child := parseJSONToTree(k, val) + children = append(children, child) + } + node.Nodes = children + case []interface{}: + children := []*widgets.TreeNode{} + for i, val := range v { + child := parseJSONToTree(fmt.Sprintf("%s[%d]", key, i), val) + children = append(children, child) + } + node.Nodes = children + default: + node.Value = nodeValue{Key: key, Value: v} + } + + return node +} + +func main() { + if len(os.Args) < 2 { + fmt.Println("Usage: jsonviz [filename]") + os.Exit(1) + } + + filename := os.Args[1] + + fmt.Println("Initializing UI... Press CTRL+C to exit.") + + if err := ui.Init(); err != nil { + log.Fatalf("failed to initialize termui: %v", err) + } + defer ui.Close() + + jsonFile, err := os.Open(filename) + if err != nil { + log.Fatalf("failed to open JSON file: %v", err) + } + defer jsonFile.Close() + + byteValue, _ := io.ReadAll(jsonFile) + + var result interface{} + json.Unmarshal(byteValue, &result) + + root := parseJSONToTree("root", result) + tree := widgets.NewTree() + tree.Title = "JSON Visualizer (Press CTRL+C to exit)" + tree.TextStyle = ui.NewStyle(ui.ColorWhite) + tree.SelectedRowStyle = selectedStyle + tree.WrapText = false + tree.SetNodes([]*widgets.TreeNode{root}) + + x, y := ui.TerminalDimensions() + tree.SetRect(0, 0, x, y) + + ui.Render(tree) + + uiEvents := ui.PollEvents() + for { + e := <-uiEvents + switch e.ID { + case "q", "": + return + case "j", "": + tree.ScrollDown() + case "k", "": + tree.ScrollUp() + case "": + tree.ToggleExpand() + case "": + x, y := ui.TerminalDimensions() + tree.SetRect(0, 0, x, y) + ui.Render(tree) + } + + ui.Render(tree) + } +}