The site uses cookies that you may not want. Continued use means acceptance. For more information see our privacy policy.

TT-RSS Grid CSS

A DIY style improvement for the feedreader I use.

I’m a big believer in RSS feeds. My vehicle for consuming them is TT-RSS on Debian. And while I liked the UI well enough, on some days I’d see the flood of feed items in the list and found myself tending toward reading aversion, where I didn’t want to look at each item and would mark a lot as seen even though I hadn’t actually paid much attention.

That’s good and bad. If I don’t have the bandwidth for it today, I doubt I’ll find the extra bandwidth to go back through older items tomorrow, so wiping the slate is for the best. On the other hand, over longer terms if I’m not consuming feeds, it’s better to stop receiving them (or filter them more heavily (trim the fat)). That’s a calculus and decision of its own, for another day.

But I also thought the interface might be part of the issue. A big list with each item squished between other items tends to be harder for me to scan for keywords, to pick out the parts I want. So I decided to try the new-fangled grid-related styles to make the interface look a little different, and I could see if it made a difference in my ability to consume my RSS feeds.

As you can see from the comparison image, this does trade off compactness for the layout, but I find it easier to scroll and have less visible at once. It’s probably slightly worse in terms of picking what to read next from starred items, because you can’t see them all at once, but if you’re planning to read them all anyway, perhaps the order isn’t as important.

Because I do all of my modifications in Stylus, it is easy for me to swap between my grid style and my older style. Anyway, here’s the code for the grid part. Note that you may need to change some settings for TT-RSS to use this, particularly the “Group by feed” option. (It may work without it, and certainly a modified version could, but I didn’t test any variety of configurations, only what I use.)

Ahem, the code:

/* The main headline area.*/
#headlines-frame {
    display: grid !important;
    /* Use 5 columns; could be tweaked depending on your screen. */
    grid-template-columns: repeat(5, 1fr) !important;
    grid-auto-rows: min-content !important;
    column-gap: 0px !important;
    align-items: start !important;
    justify-content: space-around !important;
    row-gap: 2rem !important;
    margin-block-start: 1rem !important;
}

/* If you include .feed-title in this selector, it will make the feed group act as a horizontal divider.
    You should also enable it for the next one, which makes sure it doesn't take up too much space. */
/*.feed-title,*/
.whiteBox,
#headlines-spacer {
    grid-column: 1 / -1 !important;
}

/*.feed-title,*/
#headlines-spacer {
    max-height: 2em !important;
}

div.feed-title {
    border-width: 1px 1px 1px 1px !important;
    margin-inline: 1rem !important;
}

/* You'd disable these if you enable .feed-title above. */
.feed-title {
    /* You could span more than one, in which case it will stick out in the grid.
        Disabling the grid-column rule would treat the .feed-title as a .hl (headline),
        placing it in the next available grid slot. */
    grid-column: 1 / span 1 !important;
    padding-inline-end: 1.4rem !important;
    display: grid !important;
    grid-template-columns: 1fr auto !important;
    grid-template-rows: auto 1fr !important;
    height: 100% !important;
}

/* Positions the feed's icon in the center of the grid cell. */
.feed-title div:first-child {
    display: grid !important;
    grid-area: 2 / 1 / -1 / -1 !important;
    align-self: center !important;
    justify-self: center !important;
    justify-content: space-around !important;
    align-content: space-around !important;
    vertical-align: middle !important;
    height: 150px !important;
}

.feed-title .icon {
    vertical-align: middle !important;
}

/* Place the title in the upper-left of the cell. */
.feed-title .title {
    grid-area: 1 / 1 / 1 / 2 !important;
}

/* Place the catchup button in the top-right of the cell. */
.feed-title .catchup {
    grid-area: 1 / 2 / 1 / -1 !important;
}

/* The headlines rule.
    It defines its own grid to position the contents.
    The default (light.css and night.css) themes only give it a bottom border.
*/
div.hl {
    grid-column: auto !important;
    display: grid !important;
    height: 100% !important;
    width: 90% !important;
    grid-template-columns: 1fr 1fr 2rem !important;
    grid-template-rows: 1fr auto !important;
    column-gap: 1em !important;
    place-items: start start !important;
    margin: 0 0.25em !important;
    padding: 0.25em !important;
    border-radius: 6px !important;
    border-width: 1px 1px 1px 1px !important;
}

/* Positioning for the item's headline, author, and tags, which are members of this element. */
div.hl > div.title {
    grid-column: 1 / -2 !important;
    /* Setting the height to 100% helps to maximize the clickable link area of the cell. */
    height: 100% !important;
}

/* The headline buttons are positioned in top-right (like the catchup for the feed title).  */
.hl > .left {
    display: grid !important;
    /* Make it a vertical-axis grid. Perhaps should include auto, in case the number of buttons changes (see .pub-pic below.) */
    grid-template-rows: repeat(2, 1fr);
    height: 4rem !important;
    /* First row, third column. */
    grid-row: 1 / 1 !important;
    grid-column: 3 / -1 !important;
    align-items: stretch !important;
    align-content: space-between !important;
}

/* Remove to show the feed button in grid tiles. I don't currently use it, so I hide it. */
.pub-pic {
    display: none !important;
}

/* I prefer the star to be the top item and the checkbox to be under it. */
.hl > .left > .marked-pic {
    order: -1 !important;
}

/*  */
span.hl-content {
    display: grid !important;
    grid-template-columns: 1fr !important;
    grid-template-rows: 1fr auto !important;
    row-gap: 1em !important;
}

/* Show the title which can be multiline.
    The width bit should be removed for more general use. */
span.hl-content > .title {
    display: inline-block !important;
    word-wrap: normal !important;
    white-space: normal !important;
    width: 10em !important;
    grid-row-start: 1 !important;
    grid-row-end: 1 !important;
}

/* Show the full author which can be multiline.
    This style rule goes a bit further than it should, mixing
    with some of my personal style stuff, so may need to be tweaked
    for more general use. (width, line height, particularly.) */
span.hl-content > span.author {
    display: inline-block !important;
    grid-row-start: 2 !important;
    grid-row-end: 2 !important;
    row-gap: 1em !important;
    width: 14em !important;
    line-height: 1em !important;
    word-wrap: normal !important;
    white-space: normal !important;
}

/* Place the score icon and feed icon in the bottom right. */
.hl > .right {
    grid-column: 3 / -1 !important;
    justify-self: end !important;
}

/* Hide these items if they don't contain anything. They are a content preview and the labels. */
span.preview:empty,
span[class^="HLLCTR-"]:empty {
    display: none !important;
}

It’s worth noting I didn’t spend a ton of time cleaning this up, either when I made it or when I decided to post about it, so it may have some rough edges. It didn’t take very long to go from idea to implementation, perhaps a couple of hours, which is a sign of how much easier and more useful CSS has become with things like the grid here. Now, there are things it’s still hard to do with the grid.

For example, without changes to markup, I couldn’t come up with an easy way to make the .feed-title elements fill the rest of the row when there aren’t enough items to do so. I’m pretty sure that could be done if there were separate divs for each feed’s contents, and perhaps it could be done if the grid had pseudo-classes (e.g., :first-row-member), but otherwise I’m not sure how you could do it. (I don’t know if it would have looked better anyway.)

So far I judge it a worthwhile change, as I have found it easier to scan the feeds even if I don’t see all the items at once. It’s more natural for my eyes to jump from item to item, and each one taking up more space gives the illusion that I’m making more progress by looking over each item.

How an Alsa Configuration Change Broke a Game

A look at tracking down a broken game to an alsa configuration change.

“LYNE” (lynegame.com) is a neat little puzzle game I’ve been playing. But I recently updated alsa-lib, audio library, and it quit working (well, it quit working, then I tracked down why). It would load the initial screen, then when I selected resolution, etc., it would crash.

I had turned off sound for the first time the last game I had played, so at first I thought maybe that broke it. I looked around and found its configuration file, tried flipping the mute-sound bit, no dice. There was also a Player.log file (part of the Unity3D kit the game uses), but I didn’t look at it initially or it would have saved some time.

Instead, I used gdb to attach to LYNE and see what the crash was (Player.log contained the backtrace, hence it could have saved this step). It showed the problem stemming from libasound.so.2 from the Steam runtime. I ignored the exact calls, which also might have saved some time in figuring it out.

First thing, I downgraded alsa-lib packages, and the game worked. So I knew where the problem was, I thought. I tried a few other Unity3D-using games with the bad alsa-lib, and they worked fine. Weird. Also, how could a system lib be causing a problem when the game uses the steam-runtime version?

I had left libasound2 at the older version and used apt-mark to hold it, but I had left libasound2-data (config files) unheld, so when I saw that I could upgrade it independently, I decided to give it a try. Aha! With the supposedly-working version of the library installed, but with the new configuration files it crashed.

So I got a diff of the config files, which is when I got hinted back to the actual breaking calls: they involved something called device name hinting, and the config file’s only changes were adding (or modifying) device name hints.

A bit more digging, I found Github: Valve Software: steam-runtime: 23 January 2015: “segfault in ALSA snd_device_name_hint(), backport fix?”. A sadly old, known, fixable issue which had suddenly, by a mere config file change, come to bite me. I left a comment and also sent a message to the developer of “LYNE” letting them know that the other Unity3D games avoid the crash, so maybe there’s a way to fix it without Valve fixing the runtime library.

Main lessons? If you see a logfile near a problem, give it a look (kudos to Unity3D for apparently throwing a similar log in with every game that uses it). If you see a call name associated with the problem, look at it first (might not have helped here, but it’s a good practice to follow). If a seemingly harmless change happens to a conf file, it could actually break another piece of software due to an old runtime library. Oy vey.

Messing with TT-RSS in Python

Some sample code I wrote to feel my way around the TT-RSS API in Python.

I use the feed reader TT-RSS to read RSS/ATOM feeds. It’s a good reader, and I also use TTRSS-Reader for Android (there are others, which I’ve not tried yet) on my (ancient) Android mobile device to quickly go through incoming feed items. But I have some itches to the web interface that I am considering scratching, so I decided to see how hard it is to start building a simpler interface.

One of the things I knew from setting up the Android application is that TT-RSS has an API, which is where I started. It communicates via JSON, which is great because it is ubiquitous and easy to use.

You have to have a session to talk to the API, so I built a class to handle all requests and the session:

class TTRSSSession():
    def __init__(self, url, user, password):
        self.session_id = None
        self.url = url
        self.user = user
        self.password = password
        self._login()

    def headlines(self, feed):
        data = {
            "feed_id": feed,
            "limit": 10,
            #"skip": 11,
        }
        resp = self._request('getHeadlines', data)
        return resp

    def _login(self):
        login_data = {
            "user": self.user,
            "password": self.password,
        }
        result = self._request("login", login_data)
        self.session_id = result['session_id']

    def _request(self, op, data):
        request_data = {
            "op": op,
        }
        if not self.session_id and op != "login":
            raise OpException("Tried to execute without a session/login.")
            return None
        elif self.session_id and op != "login":
            request_data['sid'] = self.session_id

        request_data = dict_join((request_data, data))
        request_data_j = json.dumps(request_data).encode('utf-8')
        response = urllib.request.urlopen(self.url, data=request_data_j)
        r_data_j = response.read()
        encoding = chardet.detect(r_data_j)['encoding']
        r_data_j = r_data_j.decode(encoding, 'replace')
        r_data = json.loads(r_data_j)
        if r_data['status'] != 0:
            raise OpException(
                "Bad request: {0}".format(r_data['content']['error']))
            return None
        # FIXME handling multiple parts (seq = #)?
        return r_data['content']

That’s the main guts. This isn’t very advanced/functional yet, but it’s enough for me to mess with. I also built a very simple Flask application that uses this class so I can begin sketching some UI I might want (though for now it’s very bare bones, just showing the result of TTRSSSession.headlines()).

I was going to use Django, but I think it’s too heavy for what I want out of it. Flask is very light.

A sample invocation of the class:

feeds = {
    'FRESH': -3,
    'STARRED': -1,
    'ALL': -4,
}
url = 'http://example.com/tt-rss/api/'
user = 'random_hacker'
passw = 'xyzzy'
ttr = TTRSSSession(url, user, passw)
ttr.headlines(feeds['ALL'])

The headlines() method will return the first ten items in the feed containing all items (a special feed that throws everything read and unread together).

As you can see in the class’ _request() method, I haven’t added support for pulling multiple calls together. That will likely require a JavaScript frontend to integrate with my Flask application (or the data would all get pulled by the backend first, which seems wrong). And that begs the question of why not to forgo Python altogether and just write this as a pure client-side JavaScript thing?

Maybe I will. For now I am just messing around. Depending, I might prefer to use a Python backend to help filter or manage some of the items automatically. So at this point the versatility seems useful. Also, I always feel slightly silly when I find myself doing something that feels too close to rewriting part of an application that already (and still will) have that part, so who knows. I may just use this to learn more about Flask.