Fx – Terminal JSON Viewer
fx.wtf
fx.wtf
I've taken `fx` back to the drawing board and completely rewritten it from the ground up. Excited to share what's new:
1. *Going Big*: `fx` now gracefully handles even the most massive JSON files.
2. *A New TUI Look*: Dive deep into your data with a revamped terminal interface—now with themes!
3. *Swift Navigation with Dig Fuzzy Search*: Feeling lost in JSON? Just type `.` and navigate with ease.
4. *Powerful Regex Search*: Scan across your entire JSON content with precision.
5. *Elegant Long String Wraps*: No more cut-offs. Your strings wrap beautifully now.
6. *All Things JSON*: Added love for comments, trailing commas, and JSON streams.
Pouring my heart and soul into this rewrite has been a journey to make `fx` faster and more powerful. If you find value in what I've crafted and want to support its future, consider sponsoring on GitHub.
Would love to hear your thoughts and feedback!
Built something similar, but with love for all things CUE (also works for JSON/Yaml) (https://youtu.be/XNBqBWO4y08)
Yours looks to have a better object navigation, I might have to be inspired by it!
How do you like Bubbletea? We went with tcell/tview (fork) for more control of the UI & event handling
I've always used jq for viewing json, but it's the wrong tool for my use. I basically want to explore json interactively, not parse it specifically. So the workflow is to keep "jq"ing it, and modifying my jq query until I find what I'm looking for.
Your tool looks like it allows interactive exploration.
- Some indication of how far down you've scrolled would be useful for "situational awareness". Perhaps a percentage number in the status bar (like emacs/vim?)
- I miss not having the Home/End keys bound to the goto top/bottom actions
- The cursor behavior when pressing PageDn/PageUp throws me off.
Emacs when pressing PageDn: Cursor ends up at the top of the window.
Vim: Cursor stays at the current position.
notepad.exe: Cursor ends up in the middle of the window.
Fx: Cursor ends up at the bottom of the window.
- The . navigation is very neat with the tab completion. Probably stupid observation: If you remove the alphabet key shortcuts you wouldn't need the (non-discoverable) dot. :)
Yes pgdown behaviour need to be refactored. Home/end is easy to add.
> If you remove the alphabet key shortcuts you wouldn't need the (non-discoverable) dot. :)
I did not get this one.
I never could get used to jq's syntax so instead I've just been grepping. But this looks to be for jq what htop is to top, which is great for exploring response data.
Is there a commonly-used grammar for "json with comments and trailing commas" or does everyone just try to copy the jsonc from vscode?
CUE will happily take your JSON with comments and extra commas, or no commas too (lists still need them), most keys can be unquoted, and it's just a one-liner to get valid json or yaml from it
CUE is most often used as a CLI, so no Go is needed, that's really for advanced cases. Being Go, you can compile to SO or WASM, so can use it from pretty much any language.
The lack of LSP is being actively worked on right now, I'm hoping they'll have something before the year is out
In a Cue document how do you specify the location of the Cue schema? As far as I can tell you can't.
And yes I am aware that the concept of a "document" and a "schema" are basically merged in Cue, but that doesn't mean I don't want to do this.
> CUE is most often used as a CLI, so no Go is needed
Only because that's the only way you can use it.
> Being Go, you can compile to SO or WASM
Compiling to a shared library is awkward because you then need to have the Go toolchain installed. WASM might be an option ... I couldn't find out how Go actually works in WASM though given that it needs a GC and threads, and WASM currently has neither. Do they ship their own WASM GC?
Either way, it's of no practical use since nobody has actually done that. Look at the languages supported by Jsonnet for comparison: https://jsonnet.org/ref/bindings.html
Python, C, Rust (twice), Go (twice), Lua, Node, PHP, Ruby, Haskell.
If I'm loading a config file from Python I don't want to have to mess around with `subprocess.run()` to read it. It's a solvable problem... but today, it's a bit of a turn-off.
CUE, running in the browser: https://cuelang.org/play/?id=#cue@export@cue
I see you are not interested in CUE and the benefits it offers, despite being a young language without all the ecosystem yet
We subprocess.run because there are many problems better solved in CUE that make our other code so much simpler. I can't even tell you the 10s of thousands of lines I don't have to write, both python and other config languages require way more verbosity. There are upsides for the downsides to early adoption
CUE has proper dependency management* so you specify the schema through dependencies and imports, like we do with most languages we use. JSON and Yaml are notable exceptions to this, and things like what XML and JSONNet do, referring to an http (or remote) location that may or may not serve the same content between requests, make for irreproducibility.
* or will soon, there will be an experimental version in the next cue release, and hof has had it for 2+ years now
Ah cool, I will check it out later then. Good to know people are working on the issue.
> CUE, running in the browser
No no. By "that" I mean "provide libraries for other languages that wrap it using WASM".
> I see you are not interested in CUE and the benefits it offers
That's a bad attitude. Would I have learnt how to use Cue and about these limitations because I wasn't interested? You just don't want to admit that these are valid concerns.
Subprocess.run is clearly not the ideal way to interface with a configuration language. You can do it if you have to. But you really shouldn't.
Imagine if the only JSON parser in the world was written in OCaml and every program that wanted to read JSON had to run some OCaml program to convert it into another format that they could load. Obviously not a good state of affairs.
I think we were probably talking past each other, my bad for continuing it
> Subprocess.run is clearly not the ideal way to interface with a configuration language.
100% agreement, but still better than the alternatives we had for addressing a complex problem.
yea, the interfaces to other languages is part of building out that ecosystem. The language is still changing a bit, but should hopefully stabilize in 0.8 or 0.9 (0.7 is going to bring significant performance improvements)
Yaml is another interesting case where there is inconsistent support and different defaults, depending on the library or tool. Config glues our world together, it's a complex mess because of where it sits. Everything we have today is cobbled together. To me, CUE offers a really great theory and foundation if we want to do something about it as an industry. Lord knows we're all suffering in YamHell
> You just don't want to admit that these are valid concerns.
I have my complaints and frustrations for sure, and I have been direct with the CUE team (in private) about some of the more sensitive issues, there are some comments on github and in public-ish documents if you're interested, mostly around the module proposal, which is in much better shape than anything written. The YT recordings are where the latest can be learned
> echo '{"name": "world"}' | fx 'x => x.name' 'x => `Hello, ${x}!`'
Wow, that is so nice. Having to memorize JQ syntax is such a pain.
echo '{"name": "hello"}\n{"name": "world"}' | fx '.name'
would output what? My guess is hello world but it might be hello\nworld. echo '{"name": "world"}' | fx 'x => x.name' 'x => `Hello, ${x}!`'
Is immediately grokkable compared to jq's syntax.I love jq, I think it's extremely powerful, but I have to load up the documentation in a side window every time I have use it.
echo '{"name": "world"}' | jq '"Hello \(.name)"' fx '_.groupBy("commit.author.name")' '_.mapValues(size)'
'_.toPairs' '_.sortBy(1)' '_.reverse' '_.take(10)' '_.fromPairs'
To do if in jq, it will take me much more time and extensive documentation reading.I'm looking forward to your jq version. I love jq and use it all the time, but for anything of that complexity, I'd probably just use Python.
group_by(.commit.author.name) | map({key: first.commit.author.name, value: length}) | sort_by(.value) | reverse[:10] | from_entriesYou can argue that's the case for any language, but it's the fundamental reason DSLs rarely capture the mainstream for on-the-fly utilities. Leveraging existing knowledge of a more heavily used syntax really helps here. Look at awk for example - it's been around forever, and despite its power and ubiquity in OS installs, you'll still see sed in many many more bash scripts because it's leaning on widely used familiar syntax rather than a DSL.
That said - the fx example has "x => Hello, ${x}!" where I would expect "x => `Hello, ${x}!`", so that does indicate to me that fx has its own gotchas lurking - this example could just be contrived to look intuitive.
I always found the `jq` syntax required many round-trips to the mail and more trial and error to get the syntax right.
I always found the `jqc
But is jq interactive?
go install github.com/antonmedv/fx@latest /one/two
/one/three/four
/one/three/five
/six
I ended up converting it to a JSON with a one-off Python script, and then using fx for the viewing. It worked very well! Thank you for the tool!(Incidentally, I’ve been writing my own viewer [0] to satisfy my original need more straightforwardly, but that’s still in very early stages.)
[1] I don't but tempted to try, like its data-types concept
There are many JSON viewers, I would think that inspecting with collapsable sections ought to be right in VS Code with LSP/treesitter, no extra tools needed.
What's more interesting to me is what can you do with the JSON in these tools, validate, filter, transform, chain...?
Do you mean to say "efficiency" or "performance"? I dont see both working like that
Might wanna change to something else.
One thing that isn't clear to me is how to actually use the themes you can display with fx --themes.
FX_THEME=2 fx data.jsonWhat is the website https://fx.wtf/ built-in?
https://fx.wtf/getting-started#json-processing
It's hard to tell from the docs if there is any JS involved if you just pipe JSON data to fx. I would hope not, since it mentions being written in Go for performance.
Edit: Answered my own question. I installed it into a podman container with nothing else in it, and it printed this when trying to execute js:
Node.js or Deno is required to run fx with reducers.$ cat file.json | npx fx .field
A bit surprising, I wonder if they implemented it in JS too, or somehow managed to distribute their Go program on npm.
Edit: They indeed have a separate non-interactive version in JS: https://github.com/antonmedv/fx/tree/master/npm
Someone mention Visidata[2]? VisiData is also a TUI that is great on tabular data, and it can work with json. If your JSON is mostly tabular in nature, Visidata does a great job at showing that data and allowing you to explore it. A lot of json I deal with is tabular-like data. There is a great tutorial [3], that can help you get your bearings with Visidata. Once you understand those basics you might want to look at this thread [4] for what commands you can use with json.
[1] Ultimate Plumber: https://github.com/akavel/up
[2] Visidata: https://github.com/saulpw/visidata
[3] "Intro to VisiData Tutorial": https://jsvine.github.io/intro-to-visidata/ by Jeremy Singer-Vine
[4] "Is it possible to "flatten" structured data (like JSON?)": https://github.com/saulpw/visidata/discussions/1605