Basically it's a modern w3m clone. JS is done with QuickJS; other than that, DOM/web APIs, CSS and layout are all done in Nim from scratch. (No Chromium/Firefox/WebKit involved.)
You can get it from: https://sr.ht/~bptato/chawan
Note that the Nim forum doesn't load without JS, so you'll have to put this in ~/.config/chawan/config.toml:
[[siteconf]]
host = 'forum\.nim-lang\.org'
scripting = true
cookie = "save"
If your terminal supports it, you can also look at images by adding:
[buffer]
images = true
Enjoy :)
Really cool!
I tired running Chawan on my macbook last week. I had to set some CFLAGS to get it to compile. Not sure if I got it right though cause clicking or standard key combos like ctrl-c or :q didn't work. Is there something missing on macos?
I had to set some CFLAGS to get it to compile.
Mind sharing? :)
Not sure if I got it right though cause clicking or standard key combos like ctrl-c or :q didn't work.
Mouse clicking is a hit or miss. If your terminal supports XTerm's mouse protocol, then it works. Supposedly iTerm2 does, but maybe the default terminal app doesn't.
There isn't an ex-mode, : makes plain text links clickable and q just quits the browser. Ctrl+c stops the page from loading.
hjkl + enter should be enough to navigate in general, but you can get a full keybinding list with cha -V. (There's a lot, so getting the mouse to work is beneficial. Then you can right click and get a pop up menu.)
Is there something missing on macos?
I never tested on macOS, so it's a thorny path :p but theoretically it should work.
Mind sharing? :)
Sure! First I set PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig/".
Then I did: export CFLAGS=$(pkg-config --cflags openssl libssh2 ncurses). I already had those installed previously with brew.
There isn't an ex-mode, : makes plain text links clickable and q just quits the browser. Ctrl+c stops the page from loading.
hjkl + enter should be enough to navigate in general, but you can get a full keybinding list with cha -V.
Hmmm, I tried q as well as hjkl and none of them seemed to work. Nothing changed on the page either. Perhaps it could be something with macos and ncurses or whatnot.
Then I did: export CFLAGS=$(pkg-config --cflags openssl libssh2 ncurses). I already had those installed previously with brew.
Thanks, I'll add these to the built in library lookup code then.
One question: does it also compile with pkg-config --cflags libssl libcrypto libssh2?
Hmmm, I tried q as well as hjkl and none of them seemed to work. Nothing changed on the page either. Perhaps it could be something with macos and ncurses or whatnot.
Ah, so it's a bug. What terminal are you using? Does the issue persist with iTerm2?
Ah, so it's a bug. What terminal are you using? Does the issue persist with iTerm2?
I don’t have iterm2 but I tired in vscodes terminal and it didn’t work there either. Looks like it’s not getting any keys.
I’ll try recompiling it without ncurses as maybe it’s an issue with brews ncurses. I also have ghost terminal thing.
Also I wanna use Chawan in my HNBrowser app in Figuro. Chawan does great converting the html to text friendly output. Figuro requires ARC/ORC so I’ll have to call it as an external bin.
Still waay better than deno with firefoxes readily script that compiles to a 100mb binary.
I don’t have iterm2 but I tired in vscodes terminal and it didn’t work there either. Looks like it’s not getting any keys.
Hmm... so maybe it is macOS. It would be interesting to see what happens with ghostty (which I've tested on Linux before).
ncurses
is unlikely to be the culprit, as it's not used for I/O at all.
I was thinking Terminal.App, which is notoriously broken. But it may as well be something else.
Grasping in the dark at this point, but I wonder what happens if you shrink the input buffer to one byte (from 256): sed -i '' '/ibuf: array/s/256/1/' src/local/term.nim
Also I wanna use Chawan in my HNBrowser app in Figuro. Chawan does great converting the html to text friendly output. Figuro requires ARC/ORC so I’ll have to call it as an external bin.
Note that if you're using it for rendering only, you can also throw out everything that goes into libexec and just pass the HTML directly on stdin. (Maybe run it as cha -o external.cgi-dir=[] for good measure.)
Thinking of it, some better way to split out rendering functionality would be interesting. w3m has a -halfdump mode that renders to a limited subset of HTML, and it was ported to emacs in this way. But that relies on an implementation detail.
On ORC: I had a port that kind of worked at one point, but I put it on hold after encountering https://github.com/nim-lang/Nim/issues/24596.
Still waay better than deno with firefoxes readily script that compiles to a 100mb binary.
Just for fun:
$ wget news.ycombinator.com https://raw.githubusercontent.com/mozilla/readability/refs/heads/main/Readability.js
$ cat index.html|cha -r 'eval(readFile("Readability.js")),r=new Readability(new DOMParser().parseFromString(readFile("/dev/stdin"),"text/html")).parse(),writeFile("/dev/stdout",r.textContent)'
It "works", aside from the segfault on exit :p
Hmm... so maybe it is macOS. It would be interesting to see what happens with ghostty (which I've tested on Linux before).
I tested it in ghostty and same issue. Must be a macOS issue. Same after recompiling without ncurses.
I modified the ibuf to one char and also changed:
proc readChar*(term: Terminal): char =
echo "readChar:init "
if term.ibufn == term.ibufLen:
term.ibufn = 0
term.ibufLen = term.istream.readData(term.ibuf)
echo "readChar:init ", repr term.ibuf
if term.ibufLen == -1:
stderr.writeLine("Error reading from stdin")
quit(1)
result = term.ibuf[term.ibufn]
inc term.ibufn
Running cha was printing the chars, but it still wasn't doing anything.
Thinking of it, some better way to split out rendering functionality would be interesting. w3m has a -halfdump mode that renders to a limited subset of HTML, and it was ported to emacs in this way. But that relies on an implementation detail.
Oh a halfdump mode would be awesome! I love the idea of a text gui browser for news reading. :)
Even the current --dump works better than the markdown readers or my hacked port of readability to Nim.
I tested it in ghostty and same issue. Must be a macOS issue.
It appears so :/
I modified the ibuf to one char and also changed: [...] Running cha was printing the chars, but it still wasn't doing anything.
That's a relief, it means read or poll aren't behaving strangely (if readChar gets called, at least the input loop is working). But that also makes the issue all the more puzzling...
OK, how about this:
index f28bdc0a..eea6ca89 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -778,6 +778,7 @@ proc handleMouseInput(pager: Pager; input: MouseInput) =
const MaxPrecNum = 100000000
proc handleCommandInput(pager: Pager; c: char): EmptyPromise =
+ eprint "buffer", repr pager.inputBuffer, "c", repr c
if pager.config.input.viNumericPrefix and not pager.notnum:
if pager.precnum != 0 and c == '0' or c in '1' .. '9':
if pager.precnum < MaxPrecNum: # better ignore than eval...
@@ -788,6 +789,7 @@ proc handleCommandInput(pager: Pager; c: char): EmptyPromise =
pager.notnum = true
pager.inputBuffer &= c
let action = getNormalAction(pager.config, pager.inputBuffer)
+ eprint "action", action
if action != "":
let p = pager.evalAction(action, pager.precnum)
if not pager.feednext:
(eprint writes to stderr, so you can also get a log like cha -V 2>cha.log ; cat cha.log.)