- Getting Started
- The Reader
- Organizing Feeds
- Update Cycle
- Other Plugins
- Tweaking Feed Options
- Filtering and Sorting Items
canto-curses with no arguments will automatically start the daemon and connect to it. If you don’t have any settings, it will create some defaults for you.
You can use
:help or “?” to open help, which will list the built-in keybinds.
:help commands will list the individual commands with a brief description of what they do.
:help [command] will give detailed information about the commands.
Add some feeds
:add command can be used to add a feed. For example
:add http://example.com/rss :add http://example.com/secret.rss user=myuser password=secret
If you’re coming from another RSS reader that can export OPML, you can import your list into canto by using
canto-remote from your shell.
user@host:~$ canto-remote import /path/to/feeds.opml
You can also export your feeds with the remote as well
user@host:~$ canto-remote export > feeds.opml
With a feed selected, you can delete it with
Set your browser
Now that you’ve set up some feeds, it’d be nice to be able to read them.
:set browser firefox :set browser google-chrome-stable
If you’re a fan of text browsers, you can also tell canto to hand over the terminal
:set browser elinks :set browser.text True
You can then open a link in your browser with
:goto (which is bound to ‘g’ by default).
smartlink.py plugin was written to give finer grain control over how links are handled with the
:fetch command that can be used anywhere
:goto can be. It can be used to properly handle media like images, video, audio, or other documents if your browser isn’t the best application to use. You can enable it by copying
smartlink.py from the installed plugin directory (
/usr/lib/canto/plugins by default) into your configuration’s plugin directory
~/.config/canto/plugins and restarting canto-curses. The header of that plugin covers how to configure and use it.
In addition to using a browser, canto-curses has a built in reader that can be used to read items. It can be opened with the
:reader command, which is bound to space by default.
Inside the reader, you can toggle displayed information
:show-links (l) :show-summary (s) :show-enclosures (e)
Within the reader, you can also open specific links with your browser by doing
:goto [link number].
:fetch [link number] from the smartlink plugin will also work as expected.
A lot of feeds add extra information and easter eggs to items that you may be interested in showing in the reader. The
reader-extras.py plugin was created for just that purpose. You can enable it by copying
reader-extras.py from the installed plugin directory (
/usr/lib/canto/plugins by default) into your configuration’s plugin directory
~/.config/canto/plugins. The plugin’s header has information on how to use it.
This plugin also has a debug mode that can be used to discover this extra content.
Moving Them Up and Down
You can change the order of the feeds with the
:demote commands, which are bound to + and – by default.
Collapsing (Minimizing) Feeds
Feeds can be reduced to single line summaries using
:uncollapse. By default, ‘c’ will toggle collapse for a single feed. ‘C’ and ‘V’ will collapse and uncollapse all visible feeds. This setting is persistent.
There are a handful of commands available to categorize feeds and filter based on those categories. For example, you can categorize a feed by selecting an item in that feed and doing
You can query what categories a feed is in by selecting an item in and doing
This will also tell you what categories you’ve already established.
Once you’ve marked your feeds, you can show only those feeds by doing
And switch back to the default view with
You can remove a feed from a category with
Since 0.9.0, Canto-curses has, by default, kept updates hidden until the user requests to see them with a refresh (bound to C-r by default) or an update (bound to \ or F5 by default). A refresh actually re-fetches all data from the daemon as if you just opened canto-curses. An update integrates new items into the list based on a policy and (optionally) a timer.
The reason that updating is manual by default is that RSS items appear frequently and changing the screen can be disorienting. It’s important to know if items before or after your cursor are new or old. It’s also important for range operations behave as expected so you don’t accidentally mark a whole bunch of items ‘read’ unintentionally because they appeared a millisecond before you triggered it. This is also why, by default, new items are appended to the ends of tags, it needs to be crystal clear both where and when new items will appear.
That said, this is open to interpretation, so there are a handful of knobs to tweak.
:set update.auto.enabled [True|False] :set update.auto.interval [n seconds]
You can set these to your desired settings and Canto will automatically cause an
:update every n seconds.
In addition, you can change where new items appear when they are updated.
:set update.style [prepend|maintain|append]
Where prepend will add items to the top of the feed, maintain will actively re-sort the feed on update to give output like
:refresh would give, equal to the order of items in the source feed data, and append (the default) will add items to the end of the tag.
You can alter the way Canto-curses scrolls through items as well.
:set taglist.cursor.type [edge|top|middle|bottom]
This setting will change where the cursor start scrolling. Edge is the default, where the cursor can move freely, and the interface scrolls only when it goes off screen. Top, middle, and bottom types keep the cursor as close to that point on the screen as possible while keeping the screen full of content.
:set taglist.cursor.edge [n lines]
This setting lets you set where exactly the “edge” of the screen is compared to the actual edge of the terminal. You could also think of this as a vertical “margin”. This affects all cursor types except “middle”
:set taglist.cursor.scroll [scroll|page]
This setting lets you switch between normal scrolling or paging. This only has an effect when the cursor type is “edge”.
Canto-curses gives you a lot of flexibility with it’s appearance.
:color command gives you fine-grain control over what color everything is. In it’s most basic form, you can specify it like this:
:color unread green
Which will make unread items green, instead of blue. See
:help color for a list of what you can change and the available colors.
If your terminal supports it, and
$TERM is set to a 256-color compatible terminal, Canto natively supports 256 color, but you must specify a color by color code. A Perl script names colortest can be used to both test whether your terminal is properly setup and see what various colors look like.
user@host:~$ wget http://codezen.org/static/colortest -O colortest user@host:~$ chmod +x colortest user@host:~$ ./colortest
If this script displays more than 8 colors, you’re set and can use the simple numbers with
:color read 240
Canto-curses also gives you control over the “style” of the text displayed. Most terminals support making text bold, dim, underlined, reversed or “standout”, all of which have different meanings based on your terminal. Sometimes you have to use these to achieve a color. For example, to get a gray with only 8 colors, you have to use the color black, but make it “bold”.
:style command works similar to
:style unread bold :style read normal
:help style will give you more information on what you can change.
If you’re not satisfied with just messing with colors and styles of what already exists, then you may be interested in
default-theme.py which implements the standard Canto theme in the form of a plugin. By copying
/usr/lib/canto/plugins to your plugin directory (
~/.config/canto/plugins/) you can then change most of what Canto puts on the screen. For control over the reader, see the previous section on
To see a list of keybinds, use
:help. For help with commands, use
Using these two help pages, you can come up with your own string of commands to bind to a keybind, using
:help bind will give you more info, but in short you can invoke it with an key or simple key chord.
:bind j item-state read \& next-item :bind C-y yank-link :bind M-y yank-title :bind F1 help
The first bind will reset the ‘j’ key to “item-state read & next-item” (note the \& in the invocation, it must be escaped or Canto will interpret that as a fresh command, similar to bash’s semicolon.) so when ‘j’ is pushed, it will set the current item as read and move to the next one automatically.
The subsequent binds use Ctrl-y (C-y), Alt-y (M-y – the M is for ‘meta’) and F1.
NOTE it’s important to use
:bind in the context you want to bind a key in. So, to make a bind work in the reader, use
:bind with the reader open. For example
(with Reader open) :bind 1 goto 1 :bind 2 goto 2 ...
To bind the number keys to go to specific links in the Reader text.
ALSO NOTE keybinds are persistent by nature and will be remembered between sessions of Canto-curses.
Currently, Canto supports two forms of synchronization between machines, both a plugins. Similar to all other plugins, you can enable these by copying from the installed plugin directory (
/usr/lib/canto/plugins) into your personal configuration plugin directory (
This plugin, which requires the python3-requests package, will synchronize any items with Inoreader an online service that offers free accounts. The top of that file documents how to configure it and some caveats (such as needing a “real” Inoreader account and not an OAuth Google/Facebook login account).
Inoreader sync comes with some disadvantages, however. Because Inoreader as ad-based (with their free tier), the content they serve has ads in it. For this reason, and because Inoreader seems to have trouble fetching some feeds, Canto will still fetch data itself and try to match it with Inoreader’s data. This allows you access to the absolute most items, but at the cost that not all items will be synchronized. That means if you have multiple instances of Canto, all syncing with Inoreader, you will have unsynchronized items. Synchronization with other Inoreader clients (such as their web or mobile apps) works just fine.
To achieve perfect synchronization between multiple Cantos, you can use sync-rsync.py
rsync based plugin actually synchronizes Cantos by rsync’ing their files to a common location. This can be used to rsync files to some remote storage space you have access to (via SSH, for example), or – since rsync works locally – can be used to sync with a service like Dropbox or Google Drive, or locally mounted NFS/sshfs filesystems.
This plugin also enables you to do
canto-remote sync to trigger a sync manually.
These plugins didn’t fit into the rest of the configuration. As with the others, they can be enabled by copying them from the system plugin directory (
/usr/lib/canto/plugins) into your plugin directory (
~/.config/canto/plugins). Most of them include some amount of configuration.
This plugin will add Reddit API information to item titles. It can add the current number of upvotes, as well as the source subreddit. In addition, it adds a sort,
reddit_score_sort, which can be enabled by selecting any reddit tag and doing
:set tag.transform reddit_score_sort
This plugin allows you to automatically issue commands on startup, potentially changing configuration amongst other things. It comes with an example that allows you to change your browser settings based on shell environment.
This plugin can do search and replace on story content to clean up badly formatted titles / descriptions. Useful to deal with unwanted newlines, improperly escaped HTML, or other arbitrary search and replaces.
This plugin will set the title in most graphical terminals to “Canto” to make it easier to distinguish in your window manager. The title of the currently selected item can also be added.
This plugin uses the program
xclip to put the title or the link of an item in your X clipboard.
xclip must be in your
$PATH somewhere. It attempts to bind ‘y’ to
:yank-link and ‘Y’ to
:yank-title but won’t clobber them if you’ve set them differently.
Tweaking Feed Options
You can configure individual feed settings to tweak how often the feeds are fetched, how long their items are kept and whether unread items can be discarded. With an item in the feed selected you can do:
:set feed.rate [rate in minutes] :set feed.keep_unread [True|False] :set feed.keep_time [time in seconds]
You can also change the defaults for feeds that haven’t already been specifically configured
:set defaults.rate [rate in minutes] :set defaults.keep_unread [True|False] :set defaults.keep_time [time in seconds]
For reference, the default
rate is 10 minutes,
keep_unread is False, and
keep_time is 86400 (one day).
Filtering and Sorting Items
The items shown in canto-curses can be filtered and sorted in various ways through something calls “transforms” that can change which items are present in the feeds and what order they’re in.
Global Filters and Sorts (Persistent)
By default, there is only one useful filter defined,
filter_read which will hide all items that you’ve marked as read by opening them in the browser/reader/fetch or by using ‘r’ (setting a feed as read) or ‘R’ (setting all items as read). You can enable this filter with
:set global_transform filter_read
Or disable it with
:set global_transform None
There’s also a proof of concept
sort_alphabetical transform, and the Reddit plugin adds a
reddit_score_sort as well. Transforms can be created on the fly or combined as well. For this advanced usage, read transform.py to see the various classes and operators provided.
Feed/Tag Filters and Sorts (Persistent)
In addition to settings for all clients, you can set transform per feed / tag. For example, to use the
reddit_score_sort from the Reddit plugin (in addition to having a copy of
reddit.py in your plugin directory), select an item in a Reddit feed and do
:set tag.transform reddit_score_sort
Client Filters and Sorts (Temporary)
The final level of transforms is on the client level and can be changed with
:sort (which are all the same command). Unlike global and tag filters, these don’t persist between sessions because they aren’t written into the config. So, for example, to change to
filter_read for just the current session, you can do