I'm mostly writing this here for my future self.
All right, so first off, those Spotify assholes can go fuck themselves. With that motion carried, let's get down to business.
I'm working on a longer version of this post, but I think I'm at the point where I can manage a hyper-compressed version.
You have many ordinary music files. You want to listen to the same library on all your devices. You want the option to create and edit playlists on any device, with changes immediately reflected everywhere. You want to download a subset of the library onto any given device for offline listening, with the option to stream the rest of it. You want gapless album playback because we're not living in a drafty cave in 2003. You want sovereign autonomy, and also maybe the option to share your library with some friends and family.
You are NOT asking for too much.
The task will stretch your abilities, but there's a solid chance that it will not exceed them.
Here's how to Do The Thing.
- A Computer
- A normal physical computer on your home network with a bunch of disk to store music files, which is either always on or able to wake up upon network requests. You can dual-task a computer that's already doing other stuff, this doesn't need to be its only job. If you're installing Linux on something, make sure your media is stored on a separate disk partition from your OS and software, in case you need to rebuild the system at some point.
- Tailscale
- The only piece of actual black magic involved here. Sign up, and install it on all your mobile/laptop devices and your server. You'll get magic hostnames and IP addresses that let any connected devices securely talk to each other no matter where they are, without having to open a port on your router or anything. Now your music streaming/downloading is perfect inside your house, and possible anywhere with internet.
- If you want to get friends and family onto your server, invite them to your account. Eventually (> 3 users) this costs money, but there's a free re-implementation of the coordinating server called Headscale if you're willing to pay labor instead of cash.
- Navidrome
- There are many server apps that can speak "the Subsonic API" for serving personal music streams/downloads to an ecosystem of client apps. Navidrome is the one that has the momentum. It also has ok documentation, multi-library support (for your friends and family), good performance and restrained resource usage, and easy operational characteristics.
- Something to manage (and possibly sync) your library
- Navidrome only does the "scan and serve" part; it leaves library management to you, and you can't just add tracks from any random client device, you need to actually get organized files onto the disk somehow.
- If the scarred husk of iTunes is still working for you, just use that on your main desktop and then use
rsyncor something to sync your library to your server (if your desktop isn't your server). If you're on MusicBee or foobar2000 or something, just use that and sync. - I wish I had some less fuzzy advice here, but this is the one part of all this that's actually still low-key irreducibly complicated. I will get back to you on this one if I can derive a better answer.
- Feishin on any desktop computer
- It's fucking nice.
- Yeah, it's a RAM-guzzling Electron app, but so are spotify and qobuz and probably deezer or whatever. Doesn't matter. If you put enough effort into making things convenient and comfortable and modestly attractive, you CAN counterbalance the Electron Tax and come out on top of the available native apps, and these folks have done it.
- Be sure to configure the keyboard shortcuts to match your preferences, turn on the media hotkeys, and maybe install the
mpvhelper tool (not required, but it can potentially give slightly better sound quality if it's one of your main listening computers).
- Arpeggi on iOS things
- It's fucking nice. It's literally the best music player I've used in a decade.
- Unfortunately it's not in the app store yet because it's in a long-running beta; you have to follow the "testflight" link from the subreddit to install it. It's worth it.
- If you want a backup, you can try Narjo or Flo or Substreamer or Bragi or iSub or Amperfy or Halpoplayer or Cadence or Dromio or Musiver or Soundwaves etc. etc. etc. There's a bunch of Subsonic clients out there, though most are kind of crusty by now.
- ?? Symfonium or Dsub?? on Android things
- I don't have an android thing and cannot vouch for anything here. Symfonium seems broadly adored by everyone who doesn't mind the one-time $5 or whatever.
More Busriders Shenanigans, Probably
Oct. 19th, 2024 04:26 pmSo if you have access to an Apache2 server that allows .htaccess overrides and has mod_actions turned on, you can make a single CGI script take over the whole URL hierarchy for an entire site. (Or just for a subtree of it, although the app would need to be aware and ready for that.)
In short, you make a new directory called __internal (or something) at the top of your site, and put your CGI executable in there with a filename of my-app.cgi (or something). Then you make TWO .htaccess files.
The root-level .htaccess disables special handling for bare directories, then tells the server to unconditionally use your CGI script to handle every URL pointing into your site, without consideration for whether a path would otherwise aim at a file on disk.
# Root-level .htaccess file
Options -Indexes
DirectoryIndex disabled
Action my-app "/__internal/my-app.cgi" virtual
SetHandler my-app
AcceptPathInfo on # that's the default, but still
That CGI path in the Action directive needs to be a URL path pointed at somewhere reachable on your site, rather than a path on disk. That's kind of odd, and it hung me up for a while when I was trying to get this working! But the upshot is, we now need a second .htaccess in that __internal directory that un-does everything we did in the root-level .htaccess so that the server can actually resolve that script. (Otherwise you end up in a recursive loop and the site doesn't work.)
# .htaccess file in /__internal
Options +ExecCgi -Indexes
SetHandler None
AddHandler cgi-script .cgi
Ta-daaaa! Now your program can handle all the top-level routing for your site, using CGI vars like REQUEST_URI to reconstruct the original request and do your routing. (And don't worry about needing to keep __internal private or anything, it just needed some kind of weird name to avoid trampling on any of your app's real URL paths.)
As mentioned previously, this year I switched to hosting eardogger.com in what's either a highly unconventional environment or an unusually conventional environment, depending on your perspective. This has mostly gone completely fine! However, I did have one incident several weeks ago, and it was a funny one.
I was out reading webcomics on my phone, and got creepy 500 errors on Eardogger; when I got home, the logs showed a Resource temporarily unavailable error when trying to access the database.
⁉️ (Metal Gear Solid guard alert noise)
All right, first off: That database isn't a remote server; it's a file on the local disk. If THAT's "unavailable," something's very wrong. A quick web search indicated that error comes from the operating system itself, not anything in my tech stack (like sqlite maybe). At some point, I visited a page on the site, then tried to run a command in my SSH session:
$ ls
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: Resource temporarily unavailable
Hahahaha holy shit.
Ok anyway, long story short: I was hitting my user's process limit and being prevented from spawning new processes or threads. The 500s were happening when concurrent DB reads would have made the reader pool spawn a new thread, and it hit the wall instead.
A given user may only run a certain number of processes at once on this server. Eardogger is at maximum a single process instance, so I thought I was fine. But then my web host upgraded my server's OS, which changed the process limit accounting to also include sub-process threads. And Eardogger IS multi-threaded.
How multi-threaded, exactly? Well, I was using the Tokio multi-threaded runtime with default configuration. And it turns out the default behavior is to immediately spawn one worker thread per logical CPU core...
...on what turns out to be a 128-core web server. The process limit (not advertised, but support will divulge if you ask) is 25.
I wouldn't want that even if it WAS allowed!! This app has like three goddamn users! I made my thread pool configurable and set it to single digits, and that immediately banished the errors and the shell lockups. 🌈 As a bonus, it also cut the app's cold startup time from "barely perceptible" to "legit gone" — apparently spawning more than a hundred threads on startup takes a noticeable amount of time, but Rust is so fast in general that it covered most of that sin and I wasn't immediately suspicious.
Lessons learned:
- Resource heuristics that inspect the local system's capacity are a huge red flag, because they act normal on your laptop and then go berserk elsewhere. Set your ceiling explicitly!
topandpson Linux don't list threads by default, you have to use an extra argument to see that.- Wow.
EDIT: But actually, in most modern deployment scenarios, your server's CPU and memory resources really are much closer to the scene on your laptop, since the standard practice is to slice up computer resources into tiny single-purpose shards via containers or VMs. Like, Ruth was showing me something from her work that we would both consider "fairly extreme" in terms of resource allocation, and I was like "oh yeah, that's a whole lot of laptop... but it ain't a web server, you know?" So yes, this whole problem did in fact stem directly from my runtime environment being weirdly atavistic, and most people are probably fine with Tokio's default behavior.
The Rewrite
May. 9th, 2024 10:30 pmI run Eardogger.com, the web's favorite unpopular bookmarking tool for binge-reading webcomics. It's about four years old, and it's currently built on Typescript, Node.js, the Express web framework (with some add-ons), and PostgreSQL.
I've been rewriting the whole thing in a new Pile of Stuff — Rust, the Axum web framework (and the Tokio + Tower + Hyper ecosystem it's built on), Sqlite, and my experimental FastCGI/HTTP reverse-compatibility layer. (Yup: this was the secret endgame for that whole project.)
All features are complete. I've got the rewrite deployed in realistic hosting to do some soak testing, and it's working amazingly well. Most of the remaining to-dos are either rewriting my integration tests, or ancillary stuff like backup jobs, release scripting, and data import scripting.
A rewrite, huh? 😒
( yeah dude )
Also it was fun, right?
Haha yeah, it totally was.
Axum is really nice to work with! It combines some of my favorite parts of Express (Eardogger's old framework) and Bevy (the game engine I've been working with on another project). And the way it's able to give strong type guarantees to handlers (despite being flexible and comfy to work with) means the code can inherently represent a bunch of assumptions that otherwise I would have to remember somehow; that should be really nice for future maintenance after I haven't touched it in two years.
I'm doing the database stuff in sqlx, which is mostly pretty great. I'm using the compile-time-checked query macros; it's true, they add some significant extra build weirdness, but once again, it's all about the maintenance — those automatic checks let me skip a huge amount of otherwise-manual testing. During development, it caught a ton of bugs and schema problems before they could even take root.
For templates I'm using minijinja, and it's all right. I've never met a template language I love yet, so it's par for the course. I investigated a lot of alternatives, and I think the design constraints of this one seemed easiest to live with.
And using busriders makes it feel like I'm somehow beating the system. 😛 I love it.
Here's the code for the rewrite, if anyone's interested.
Why Git is hard
Sep. 24th, 2023 01:13 pmJulia Evans’ StrangeLoop 2023 keynote was about digging into the different reasons a tool can be hard to learn, and it was a real good talk! At the end, as a tossed-off addendum to a conclusion about continuing to learn things, she said “I still don’t know why Git is hard.”
I happen to have thoughts about that one!
I have had to teach a fair number of (generally clever and persistent) people how to get around in Git or how to use its more advanced features. While doing so, I have often failed to get the basics to stick, which is incredibly aggravating to someone who prides themselves on explaining things. Sometimes this devolves into me talking about wave/particle duality as a crucial metaphor for getting through a rebase intact, and everyone in the room looking at me like my second head just tried to convert them to Gnosticism.
So, I’ve spent some time thinking about this before this weekend.
( Read more... )
I'm brushing up on my Ember this weekend (for reasons), which is something like 50% re-learning syntax and standard library stuff, and 50% just stabilizing my mental models for how stuff fits together.
So since I'm thinking about the experiences I had when learning this stuff the first time around, here's the thing I most wish I'd been told basically before doing anything else:
Ember-style Handlebars isn't a text-processing language.
Every* other templating language I've met is content-agnostic and only does naïve text transformations, and if you go into Ember expecting that, shit like this looks absolutely, comprehensively haunted:
<button
{{on "click" this.saveProfile}}
>
save
</button>
First time I encountered that I felt physically queasy, bc it looks like that on call will be replaced by some run of text that causes the element to get a click handler, and there's z e r o imaginable safe-and-sane way to implement that.
But in fact it's fine — that template directive doesn't get replaced by text at all, because .hbs fucking cheats! It's fully aware of the semantic, structural, and runtime rules of its HTML content, and it gets compiled into live JavaScript instead of a static HTML fragment string. So that "on" thing is a separate piece of syntax called a "modifier," and instead of inserting something into the HTML text, it waits 'til that <button> has a DOM element and then uses .addEventListener on it.
Anyway, tbh I'm not 100% sure what I would have told myself to save me the motion sickness. Maybe just: .hbs cheats and knows how browsers work, so some of its syntax acts on DOM APIs instead of on raw text.
* I guess the one exception is HAML, but HAML is also just generally deranged.
VM wrangling, CSS rodeo
Apr. 14th, 2019 08:53 pmWell I already posted about it on the one community where anyone's likely to give a damn, but I built a thing that'll stand up a working Dreamwidth dev instance on your laptop for you. Fill out the config file, vagrant up and answer "1" to its question, go into energy saver and tell your computer to not go to sleep for a while, and literally go out to lunch or something (installing perl modules is way slow for some damn reason), and you should have yourself a Fukken Livejournal when you get back.
The instructions on the wiki were honestly pretty dece, but there is no way in hell I was going to do that all manually. You never wanna be in a situation where if the shitbox VM goes down it takes part of your brain with it.
Why was I even doing this? Well, all the CSS offroading I did in my most recent journal style refresh taught me how to fix at least two or three of the top five (imo) most obnoxious pieces of Dreamwidth mobile jank. I've already got PRs in to fix two of them:
...and across the two of them, I kind of snuck in a fix for another real stinker, where mobile Safari zooms the viewport to unusable hugeness as soon as you focus a form field. Maybe someone'll ask me to rebase that into a separate thing, idk.
Anyway, I feel powerful. 💪🏼
Scratch the Surface
Mar. 8th, 2019 12:11 amHi, I redid my journal style for the first time in ages! I did not even briefly consider making it any color other than baby-aspirin orange. Also I named the new one "Scratch the Surface," as an homage to my old five meg home base. (Good god, have I really been Extremely Online for 20+ years.)
This time around, I actually dug into Dreamwidth's S2 style system and made my own "theme layer" over Tabula Rasa. I've never done that before! I was vaguely aware of some kind of concept of "~layers,~" but had no idea what was actually involved until a couple days ago.
How about that S2 business, huh? Though TBH it's more like, "how about LiveJournal-descended technologies in general, huh?" Like many of those, this one is very clearly a product of a particular time and place ("people want to build complex document tree-structures and/or roll their own CSS pre-processors by composing Perl subroutine calls!"), and you can't exactly call it "good" per se... but you definitely have to respect the amount of insight and thoroughness that went into it. I can recognize at least five or six of the specific template language grievances that drove the design here — they're ones I've wrestled before without seeing any good way around them, and S2 has at least semi-reasonable answers. But also, WOW. The language design is usable and the docs really aren't that bad, and ultimately I spent like 80% of my time wrestling with CSS anyway, but I kind of feel like I just french kissed a coelacanth.
Anyway, this was a great chance to fix some things that have always bugged me about most of the built-in layouts, especially on mobile. New one should be fairly solid, unless I use a table in a post, in which case I probably deserve what I get in the first place.
Garbage Book and FMP live on
Jan. 25th, 2019 09:55 pmK, idk if y’all have ever messed around with the iOS “Shortcuts” app, but it turns out it is Legit.
So. On my Mac, I use a pair of oddball note-taking systems that I made out of baling wire, scrap lumber, and keyboard shortcuts over a decade ago and have been using daily ever since:
- “Garbage Book” is just a clone of Apple Notes,* except that it uses my normal text editor instead of a separate app that I don’t like as much.
“FMP”** is, uh, baroque. There's a hotkey to get a text field that appends to a special text file. If I append a line that starts with something like
^didread, it’ll eventually get moved to a file likedidread.txt(courtesy of a second hotkey that refreshes everything). And there’s yet another hotkey for quickly opening one of those note files by name.(I do realize what that all sounds like to a normal person, but trust me, it's a specialized ADHD workaround that solves some particular annoyances for me in a really sweet way.)
Since both sets of notes are just plain text files in a synced folder, I've always been able to access them on my phone. But it was way more awkward and slow, and I've always been on the lookout for ways to improve it.
Anyway, yesterday my dad was having brain surgery (he's doing fine, everything went great), and here's a thing about when someone close to you has major surgery: you sit around for a long-ass time, and you can't concentrate on any actual responsibilities for the whole duration (basically because the entire part of your mind that gives a flying fuck about responsibilities is wholly occupied with reacting whenever it's time to do something for said loved one). But a really frivolous and finicky project can be a great way to not obsess at 20,000 RPM over hypothetical stuff that you can't do anything about, so I decided to crack open that Shortcuts app that came out with iOS 12 and see if there was anything to it.
THERE IS. The card-based editing interface is weird as heck (it has some real strengths for programming on a touchscreen, but it also makes refactoring a pain in the ass, and was probably not intended for use on an iPhone SE), and the lack of some basic niceties like and/or expressions is pretty embarrassing (you can implement boolean logic yourself by adding more variables, but it looks like legit hell), but behind all that is a remarkably functional little scripting language with a coherent and consistent core and really unmatched discoverability.
It's probably even nicer for scripting apps that provide "real" Shortcuts actions, but even with just iA Writer's URL-based scripting and the built-in actions, I was able to completely*** re-implement both Garbage Book and FMP on my telephone over the course of two afternoons!
I now have little buttons on my "today" view for starting a new Garbage Book page, appending a line to my dump file, opening an FMP ^caret-tag file by name, and sorting ^caret-tag lines out of the dump file and into their real homes. (That was the one I didn't think would even be possible, but I totally did it!) And it all plays nicely with the edifice of Mac scripts that I've been using since '06.
Anyway, very satisfying results, a very impressive tool, also A+ distraction for that mandatory fret-in-the-hospital downtime.
_____
* Except this was ca. 2007 so it was more like a clone of Notational Velocity.
** Fast Memo Pencil? Fiendish Master Plan? Free Mashed Potatoes?
*** Wellll mostly completely. There's one thing I couldn't do on my phone from the original Garbage Book script: include part of the first line of text in the filename. That was always a nice feature, but c'est la vie; it's just a problem of different editing paradigms. In BBEdit I start by opening an empty editor window and typing for a while before hitting the Garbage Book hotkey to save, but in iA Writer I have to start by creating the file, so there's no first line of text to grab. I guess the editor itself could update the filename (Notes does this!), but it would have to know about my timestamped naming scheme to do that.
( This is long, and it's about troubleshooting an exotic driver issue. )
Of course, I'm probably going to replace the damn thing eventually anyway because the download speeds are so heinously slow. Earlier I used connection sharing via Ethernet to plug the PC into my MacBook's wireless, and it was literally like ten times faster.
Well, still, I was offended that I couldn't understand what was going on and I really wanted to win, so I guess it was worth it just for that.
I finally switched from bash to zsh, and it was totally worth it.
I was paranoid about how much time it was going to take, but it turns out the trick is to do as little as possible, and most of your bash customizations will still work. It basically went like:
- Refactor your dotfiles to just isolate any bash-specific stuff that's in there.
.bash_profileshould consist ofsource ~/.bashrc;.bashrcshould have your little handful of bash-specific configurations followed bysource ~/.profile; and.profileshould have all the aliases and simple functions that will work in both. - Install oh-my-zsh and assemble a really simple .zshrc file. At the bottom, put
source ~/.profileto catch all your old bash aliases and functions. - Look through the list of themes on the wiki and find something that looks familiar enough that you won't feel weird. I went with
muse. - Maybe make one or two edits to that theme and save it as a custom one. I looked through
lib/git.zshand figured out how to add an indicator that my git branch is ahead or behind.
In the end, I got pretty much all I needed out of the box and only had to make about a four-line edit to the muse theme. Better tab completion, and I can spend about 90% less time running git status at work.
I realize this is of limited interest to this blog but oh well.
New gadgetry
Nov. 30th, 2012 12:22 amListen though, I kind of love the iPad. ( Contains too many words about gadgets. )
Evil is a Feature
Aug. 12th, 2012 02:27 am@Skud: but the sites i know that do that best (eg. @dreamwidth, @Pinboard) also have a very human/community feel that i don't see here
@nfagerlund: @Skud I feel that. W/ DW, I was immediately like “seems legit; I can tell who these folk are & how big it can get.” A.n is like Diaspora.
@nfagerlund: @Skud …by which I mean their story is critically incomplete in some way I can’t yet put my finger on.
@Skud: @nfagerlund yeah, i get that feeling too. which is not necessarily an impediment to their platform taking off...
@Skud: @nfagerlund ... but does mean that my gut feeling about them is a bit nervous
Yeah, so
So Diaspora was meant to be Facebook without all the evil, right? Here's the problem with that: Facebook without the evil is NOTHING.
Because what the hell even IS Facebook? The answer changes significantly every nine or ten months. I joined it in January 2005 because it was a visual address book to my college, and I needed that when I was looking for a new sublet. Then it turned into a walled-garden email replacement. Then it turned into Flickr for spring break photos, then it finally managed to replace the .plan file, then it was also Livejournal for about a month, then it tried to be Craigslist for a summer, then it was Twitter with less focus, then it was a platform for shitty little games that you pay real money in order to not have to play. Now it's just sort of an undifferentiated mishmash, although it seems to be turning into Tumblr lately, mostly to accomodate George Takei.
Obviously there is no common thread. Facebook's soul is not in what it does for you or allows you to do. The product itself, the THING that Diaspora tried to copy, is frankly irrelevant. The one thing that has always made Facebook Facebook is that fucking practically everybody you know is on the goddamn thing, and they got there because Facebook was persistently and craftily evil.
The original short-lived college-scope restrictions on the thing were brilliant, because they made people let their guards down, join up, and put their personal info in. That made it easier for peoples' friends in other colleges to find them, which anchored them further in. All the effort to make it difficult to add people to your normal email address book meant that you were signing in on a regular basis to get/send messages, and would be more likely to see new friend requests and other activity, which would keep you interested. Let's not even get into that Zynga Skinner box. Etc. etc. etc. The only reason you're on Facebook now is because they were evil, and although you'd leave if your friends left, they're all still on there because of the evil. The fact that everyone is there makes Facebook a horrible place most of the time, but it also makes it indispensable, and the fact that you can't properly export your information makes it non-disposable and non-replaceable.
I don't think the people behind Diaspora ever understood any of that. They thought people were on Facebook because Facebook was a good app, and people actually wanted some atrocity that was kind of like Tumblr/Flickr/Twitter/LJ/toilet-graffiti/emotionally-abusive-Gameboy except worse. That's manifestly not the case. People want everyone they know in one place, and the only way to give them that is to be evil. Which makes it impossible to replace Facebook with any less-evil alternative -- whatever eventually kills Facebook will win by being either MORE evil, or more SOPHISTICATEDLY evil. And since Diaspora was unable to compete with Facebook, it found itself competing with all the non-Facebook focussed-purpose services like Twitter and Flickr and DW and Tumblr, and it since it was built to be worse than all of them, you probably still aren't using it. Of course, you're probably not using Dreamwidth, either. You probably ARE using Twitter, and I'll be interested to hear app.net's plan for dealing with the fact that 80% of Twitter joined Twitter because all their friends were on Twitter.
Yes, this is depressing and annoying. Whatever, let me have my moment of explanatory triumph.
(no subject)
May. 30th, 2012 11:43 pmI think the last time I cracked open some compu-tower ribs was at the yarn shop back in 2009. We had these three mildly crappy Dell P4 towers for running the POS system and everything, but they were acting like they were the ultra crappy kind. Just slow as hell and unresponsive and bullshit. Stev and Hannah were all angsting about whether we could afford new ones and how to go about getting them, and I was like, "lemme take a look." Reader, these 3Ghz machines were choking along on a half-gig of RAM each. I got on Newegg, and was like "Gimme the credit card, I'll get you some rejuvenated computers for like $140."
Once the sticks arrived, I got the backroom and cafe computers beefed up and breathing easy in like 20 minutes. Then I upgraded the main counter PC, tried to power it back up, and got nuthin'. I cracked it open again to make sure I hadn't left anything loose, and we were all like "Uh, was that creepy amber light there the last time?" It was totally not.
I searched the internet for the manual again, and it turned out the amber light on the mobo meant "power fault," usually a bunk power supply. So I grabbed a screwdriver and tore the fucker out, tossed it in my backpack, got on my motorbike, and jetted up Milwaukie to Free Geek, where they confirmed it was toast and sold me a better one (for the $15 I had liberated from the petty cash drawer). Which totally solved the problem, and left me feeling like quite the techno-samurai moto-badass.
And then the whole shop folded explosively in August and we were all out of work, but at least the computers were non-bullshit for as long as the shop survived.
I like using spoiler text! Spoiler text doesn't like iOS. Or vice-versa, whatever.
Anyway, I found a trick today that works great on both desktop and mobile, and which will work without Javascript, which means I can use it on my Dreamwidth! Unfortunately, you don't get the benefit unless you're viewing the entry in my style, and I don't get the benefit unless you write your spoiler tags such that they'll work the same way on my reading page. Frustrating. But it degrades gracefully to normal-acting spoiler text, so what the hell, I'll use it anyway.
It goes a little like this:
Go here to add custom CSS to your style, and add the following:
a.spoiler, a.spoiler:link, a.spoiler:visited {color: #3a3a3a; background-color: #3a3a3a; text-decoration: none;}
a.spoiler:active, a.spoiler:hover {color: #000 !important; background-color: #e7e7e7 !important; text-decoration: none;}
Then, you can make spoiler text like this:
Normal text, <a href="##" class="spoiler" style="color: #3a3a3a; background-color: #3a3a3a">spoiler text</a>, normal text.
Normal text, spoiler text, normal text.
Honestly they should have put a <spoiler> tag in HTML 5, but this will do for now. Sorta. I guess. And no, I don't know how to make spoiler text work with both screenreaders and iPads. :(
(no subject)
Oct. 13th, 2011 01:58 amSpreadsheet dorks: Numbers or the new Excel? I actually quite liked Excel '07 for Windows, and thought Numbers '09 felt kind of flimsy by comparison, but I'm still on the fence a bit, because it's $150 vs. a combined $30 for both Mac and iOS. Plus, I don't reeeeeally need that awesome bolt-on regex substitution function anymore, since it was mostly a workaround for being stranded on computers without proper text editors or language runtimes.
I recommend this thingamajob.
Apr. 16th, 2011 06:44 pmSo I grabbed a new iPad text editor to play with! It’s called iaWriter, and you might have heard of it, since apparently it’s the one Stephen Fry uses or something. I like it a lot. It can’t replace PlainText, because its syncing and organizing are kind of barely sufficient (verging on crappy); it’s completely unusable for making frequent updates to a blizzard of smallish documents, and appears to be meant for working diligently on four or five large files, which will occasionally be switched out. It’s a text drafter, I think, not a text editor. Anyway, the upshot is that I seem to be able to write fiction with it at speed, which is one hell of a feature and which I always found too frustrating to keep trying for on Simplenote and PlainText.
The two real standout touches are that it uses a big ugly font that’s very readable no matter how I’m holding the device, and it extends the iPad keyboard with a row of extra keys – things like always-on apostrophe and quote keys, and cursor keys that can navigate by letter or word. It sounds trivial, but it ends up making a pretty huge difference, and I’m inclined to attribute its surprising usefulness to the amount of friction that takes away. I realize most iPad apps can’t do that with the keyboard, because it makes it take up like 3/5 of the whole screen, but it’s damn nice if you need to type for an hour.
It also has two standout annoyances: there’s a big old scrolling dead zone on either edge of the page, and it doesn’t switch the keys back after you type an apostrophe with the normal punctuation keyboard. Yes, you have a built-in key for that, but that’s no reason to punish me for picking up a little muscle memory elsewhere, geez.
I think I paid like a buck for it? It’s definitely worth checking out, even if you already have a main text editor.
(Oh, and apropos of nothing, apparently it saves newly created files as Little-Endian UTF-16. Who in the world uses that for ANYTHING? Weird!)
Firefox 4's Panorama feature is misguided
Jan. 15th, 2011 06:16 pmThe Mac Twitter client rumble is on again
Jan. 10th, 2011 10:04 pm- It's got support for lists! I think it's the first Twitter app for Mac that does the list thing and yet doesn't seem misguided and broken on a totally fundamental level. (Nambu's probably the closest behind, but I really can't stand it.)
- Unfortunately, it doesn't expose them flatly the way Twitterrific for iPad does, so you have to drill down every time to get to them. Bogus.
- Not down with this slide-and-stack UI nonsense they've got going on. Honestly, what the hell is supposed to be happening in this metaphor? That this stack keeps getting taller and taller every time I change views makes me nervous, like I'm going to have to clean it all up someday when the pile tips over and gets all over the foyer. I am only 30% joking, here.
- On the other hand, I thought the Twitter for iPad UI was godawful at first, but they improved it pretty dramatically in the minor updates. Maybe they'll make it more spatially coherent in a month or three.
- I've gotten used to Itsy's inline images, which make TfM's popups seem rather outdated in comparison.
- Ha—when I unplugged my monitor, the window stayed taller than my MacBook's screen; now it won't auto-shrink, and there's no way to grab the resize control. Enjoy the land beyond the bottom of the screen, little window! Go where no man has gone before!
- Hmm, the keyboard shortcuts seem pretty rich. That's nice.
- It's still got the ability to do multiple tweet windows, which was always nice to have when I needed it. Does any other app else do that, or is that still Tweetie-only?
- But DO NOT WANT tweet windows that hover over the top of everything. Eew. I understand the problem they were trying to solve, but I disapprove. I'd prefer that they come to the fore whenever any Twitter window was active but act like normal windows the rest of the time.
- It's still tough to tell whether the main window is active, but that's less annoying than it was when it looked like it should dim the way normal Leopard/Snow Leopard windows do.
- Feels pretty fast!
- I can't remember: Was there ever any actual apology for that debacle where MacHeist and Atebits sold vaporware sneak peeks of Tweetie 2 and basically never delivered? *checks* Hahaha—looks like there's this, and also that, for whatever those're worth.
