Take control of your web.
Weaver is a qt5-webengine browser with most of the user control provided via a local tcp socket connection. Weaver focuses on displaying web pages and offering highly customizable and scriptable user control mechanisms. There is no gui other than the windows that display web content. All behavior is controlled by sending commands to the socket connection and / or running userscripts (example scripts include extensive key bindings, hinting, and styling).
location.href = "weaver://<command>" + WeaverPID location.href = "weaver://<command>/<args, even with spaces>" + WeaverPID
Commands can also be passed to the socket (default localhost:9090) in the same
format as the weaver url scheme above, just without the leading
weaver: or the
trailing WeaverPID. A front-end script is provided to facilitate sending
commands to the socket, but feel free to roll-your-own. But if you do, you may
want to first review the examples section below.
The weaverServer process must be running in the background before the front-end
script can send commands. The front-end script will launch the server if passed
-d flag (alternatively, you can run WeaverServer, which is installed to
the /lib/ directory, directly):
Note that all browser windows are controlled by this initial server process, not
the front-end client. If gui processes need to be launched from a certain
environment, it's this
weaver -d command that must be launched in that manner.
Subsequent calls to send socket commands can come from anywhere. For a
specific example, if you are running i3 or sway, only the
weaver -d command
must be launched with one of the following:
swaymsg exec 'weaver -d' i3msg exec 'weaver -d'
The examples below do not start with obvious things like how to open a url. This is intentional. The list starts with commands that are "simplest" in terms of being the same when given from the front-end script versus directly to the socket. As you work through the list, you will learn how these two mechanisms diverge. Opening a url with the front-end script works likely just as you would expect, but sending it directly to the socket has some caveats that we'll work up to below.
Note that the netcat implementation used for the examples below has the
reasonable behavior of exiting when it gets a EOF on stdin. If you're netcat
implementation does not behave in this way by default, you may need to add the
appropriate flag (
-c for gnu-netcat or
-N for openbsd-netcat ...
alternatively you can use
socat - in place of
echo //list | nc localhost 9090
List windows with different format - e.g., just the title:
echo //list/%1 | nc localhost 9090
Close the second window in the list
weaver //target/2 //close
echo //target/2 | nc localhost 9090; echo //close | nc localhost 9090
Note 1: windows will be renumbered after this as the
//targetcommand pulls the window from position 2 and puts it in position 1, then the
//closecommand closes the window at position 1 and all remaining windows shift down to fill the space in the list.
Note 2: all content sent on a single socket connection is seen as a single command, so if you use netcat or a related tool, you cannot echo more than one command at once. This is one of the benefits of the weaver script as the script creates one connection for each command line argument.
Open duckduckgo.com on the third window in the list
weaver //target/3 duckduck.com
echo //target/3 | nc localhost 9090; echo //open/duckduckgo.com | nc localhost 9090
Note 3: here's another benefit of the script: the script appropriately prepends
//opento any non-command input so you can give it urls, search terms, or filenames as you'd expect to be able to.
Open a local file
echo //open/file:///full/path/to/mydocument.pdf | nc localhost 9090
echo //open/file://$(realpath mydocument.pdf) | nc localhost 9090
Note 4: yeah, the script really helps with this too by prepending
//open/file://and the appropriate path.
weaver //js/location.href //sleep //js
echo //js/location.ref | nc localhost 9090; sleep 0.1; echo //js | nc localhost 9090
Note 5: the
//jscommand behaves differently when provided arguments versus when not. With arguments it begins running the script, without arguments it checks for the results of the last script run on the targetted window.
Note 6: the
//sleepcommand is not actually a server command, but is handled by the weaver script. By default it sleeps for 0.1s, but other durations can be provided as
//sleep/3to sleep for 1.5s or 3s respectively. For
location.hrefyou'd be fine a vast majority of the time with no
//sleepcommand at all, but for more complex js calls that return values, the
//jscall to get the result may report that there is not yet any data available unless you add a short
//sleep(or just wait a moment before running the second
If all you want is the url, you're better off using info command rather than js
echo //info/%2 | nc localhost 9090
The info command has various options. Get all the text content of the page:
weaver //action/SelectAll //info/%8
Open several urls in one go:
weaver calendar.google.com weaver //target/0 darksky.net //target/0 bbs.archlinux.org
Note 7: the target command can be given a window index, or if passed 0, as above, it will create a new window to be the target of subsequent commands.
Now that you've gotten a bit of the flavor of how commands work, here's the reference list of all currently available commands. Arguments in brackets are optional. Below the table are notes on a few of the commands that may require further explanation.
||action name||Run the specified WebAction in the target window|
||N/A||Close the currently targetted window|
||[format]||Display list of downloads following format|
||N/A||Open this README on the project webpage|
||[format]||Provide information about the focused window following format|
||N/A||Launch a devtools / inspector page for the target window|
||[script content]||Alias for
||[script content]||W/ args, send script to target to run; w/o, check for results|
||[format]||List every open window with it's index and
||url / search||Open the provided url, or send provided text to the search page|
||name||Create a new profile named name in the targetted window|
||N/A||Shut down the weaver server|
||index||Target the window by index or create a new window if 0|
||user-agent-string||Sets a new user-agent-string for the targetted window|
The list of available actions and their names can be found here. For example
//action/Back would navigate back one step in the history.
Format for the download list should first be customized in your
The sample config file provided in the examples directory documents the format
specifiers that can be used. The
//downloads command only requires a format
string as an argument if you want to override what is in the config file.
Either of these commands can be used without arguments and will provide a useful
default. But a format string can be provided to customize the output. Format
specifiers for this string are in the table below. The default for
//list just prepends a window number (that can be used for
|%3||Url scheme (e.g., "http")|
|%4||Url host / domain name|
|%6||Url file name|
|%7||Url query string|
|%8||All selected text on the page|
the script you provide, then you can use just one call to this command. For
example, to popup an alert in the currently targetted window you'd use
//js/alert('Hello World');. This would need to be quoted appropriately to be
used on the command line to prevent the shell from griping about the
weaver "//js/alert('Hello World');"
If you want to get the result of the script which is the value of the last line
of the script, you need to make a second call to the
//js command but with no
parameters to request any result data available. The
//js command will either
return a message that no data is (yet) available, which means the script is
still running, or it will return the result (which may be an empty string).
For trivial js calls like
//js/location.href you could pass both commands at
once and likely have success:
weaver //js/location.href //js
However for any substantial script, there is potential that the results might
not be available yet for the second command. It's recommended to either wait a
moment between entering the two, or use the front-end script's built in
weaver "//js/longFunction()" weaver //js
weaver "//js/longFunction()" //sleep //js
The above assumes
longFunction() was already defined on the page, perhaps by a
The profile command should be provided with a name as a parameter. This name
will identify the alternative profile to be used in the target window. It will
also define the on-disk name of the profile. If this is an empty string, it
will result in a RAM-only profile in which no content is stored to disk. This
is often called private mode or incognito mode (thus the
//icognito alias for
Note, however, that this change in profile applies only to the targetted window. Any subsequently created windows will revert to using the default profile.
Target must be passed an integer parameter and this must either match the index
//list output to identify a target window, or it must be 0 to
specify that a new window should be created and targetted for subsequent
Note that window indices are dynamic. Internally a list of open windows is
maintained. The order of this list can be changed explicitly by //target/
commands, but can also be changed every time a window is focused or brought to
the front. In these cases, the focused / targetted window is popped from it's
previous position in the list and prepended to be at the first position. So if
you check a window's index from the
//list command, then cycle through
windows, that index will no longer point to the same window.
If no target is explicitly specified, most commands will act on the first window in the list (generally the most-recently focused weaver window). Some commands (those that will change window content like close, open, action, etc) will only act on either the currently focused or explicilty targetted windows.
Please see the well commented config.conf in the examples directory. Copy this
file (or create your own) under
userscripts (and actually a lot more as userscripts can access the
scheme to send browser commands).
Examples of some of what can be done with userscripts are provided in the *.js files in the examples directory. These include key bindings, hinting, image viewing, and some custom CSS. User submissions of other interesting scripts are most welcome on the ticketting system.
Google services' sign in page actively blocks connections from browsers with QtWebEngine in their user agent string. You can quite easily use google services (calendar, gmail, etc) in weaver, but you need to do the following steps just once first.
- Set the user agent to a non-webengine string
- Log in to a google service
- Reset the user agent or just close the ua-spoofing window
- From then on you will be able to access google services with out any spoofing
The following command will do step 1 and allow you to do step 2:
weaver //target/0 "//ua/Mozilla/5.0 (Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0" calendar.google.com
There most definitely are some, and there will be more. If you see them before I do, feel free to submit detailed descriptions via the ticketting system.
Current issues under investigation
Accepting a download results in a WebPage::onLoadFinished(false) and "[ERROR] " in the title
- This is entirely begnin and just a cosmetic annoyance.
- There is no actual 'ERROR' from the page.
Note: this should be fixed now, but still needs testing
Shift+Insert pastes clipboard rather than primary selection in Qt. This is far from ideal. Options under consideration:
- Override event handler to catch this key and ... still not sure how to paste primary
2+helper: add weaver://selection type command that returns primary selection content. Then in keys.js handle S+insert ...
Note: middle-click still pastes the primary selection. Perhaps a workaround for shift-insert is not worth the effort.
Intel graphics systems do not get along well with chromium / webengine as discussed here:
For now ... weaver may have to be periodically restarted.