What Am I Working On?

Muscadine

c#f#

Muscadine is a modern web app (inspired by Twitter) for capturing the in-the-moment thoughts of a single user about a variety of specific topics.

As I consume various media or work on various projects, I've noticed that I tend to have a lot of tweet-sized ideas about those subjects. But because those thoughts are separated by lengths of time (since I only have a few hours a week to devote to any particular hobby), my Twitter timeline appears aimless and disconnected.

Thus, Muscadine allows me to associate each microblog entry with a specific media item or project. Microblogs for a specific item can be viewed as a whole or the entire feed can be intermixed to show thoughts over time while still associating each one with a particular thing.

Future Directions

I can imagine automatic cross-posting to Twitter or even implementing the Mastodon specification to join that federated microblog system.

I also think that a system for publishing longer essay-level non-micro blogs is inevitable, but I want to actually build up a backlog of essays to publish before starting that work.

The publishing interface is a little rough today. One day, I'd like to polish that if only as a treat for myself (since I'm the only that ever uses it). And, if there's any enthusiasm from others, possibly even rework the data model to be multi-user.

About the Name

In recent years, I've started choosing wine/grape varietals as project code names.

Muscadine grapes are the grapes most common to my region of the United States and are the grapes most often used to produce local wines in this area. It seemed imminently suitable for the code name for my home page since they are, indeed, the grapes of my home.

Additionally, muscadine wine is not the best tasting wine you'll ever find but it has unabashedly local character. I expect my home page to feel much the same.

Muscadine

c#f#

Muscadine is a modern web app (inspired by Twitter) for capturing the in-the-moment thoughts of a single user about a variety of specific topics.

As I consume various media or work on various projects, I've noticed that I tend to have a lot of tweet-sized ideas about those subjects. But because those thoughts are separated by lengths of time (since I only have a few hours a week to devote to any particular hobby), my Twitter timeline appears aimless and disconnected.

Thus, Muscadine allows me to associate each microblog entry with a specific media item or project. Microblogs for a specific item can be viewed as a whole or the entire feed can be intermixed to show thoughts over time while still associating each one with a particular thing.

Future Directions

I can imagine automatic cross-posting to Twitter or even implementing the Mastodon specification to join that federated microblog system.

I also think that a system for publishing longer essay-level non-micro blogs is inevitable, but I want to actually build up a backlog of essays to publish before starting that work.

The publishing interface is a little rough today. One day, I'd like to polish that if only as a treat for myself (since I'm the only that ever uses it). And, if there's any enthusiasm from others, possibly even rework the data model to be multi-user.

About the Name

In recent years, I've started choosing wine/grape varietals as project code names.

Muscadine grapes are the grapes most common to my region of the United States and are the grapes most often used to produce local wines in this area. It seemed imminently suitable for the code name for my home page since they are, indeed, the grapes of my home.

Additionally, muscadine wine is not the best tasting wine you'll ever find but it has unabashedly local character. I expect my home page to feel much the same.

Activity

I've started the C# port that I talked about 9 months ago.

My actual job has been extremely demanding this year so I haven't had much extra bandwidth to spend on coding, which means that the project is taking forever.

But, as far as it goes, I'm happy with it. I think the code is shaping up to be fairly high quality and more adaptable than the original.

I'm not sure how much of that is "I have some expertise with C# while I was learning F# as I did the first version", how much of it is "I did this already and know what my mistakes work", and how much of it is "the rewrite is still extremely early and small; it will get worse as it gets more complete".

Probably a dose of all three...but hopefully much less of the latter.

The various sections of this website have dramatically lost pace with reality. A lot of that is because I just didn't do a good job at making it easy to update (one of the things I want to fix with the second version) -- so much that I'm using other systems to track it (such as StoryGraph for books and Backloggd for games.

I'm not sure how much effort I want to spend over in those systems organizing things if I want to eventually manage them over here again -- or if I want to figure out how to sync those systems to this website in v2.

Until then, I've decided to be comfortable with everything being a mess.

The book section of this website is a disaster because the book section of my life is a disaster.

I'm tracking things in a Notion database I built, the random books I've bought that have been added to my Kindle library, and Story Graph. I just don't know where this website fits in.

Ideally, Story Graph will add an API one day. Until then, I am thinking of just ripping the book section out. It turns out, I don't really have many thoughts about books mid-read and I don't really enjoy writing reviews when I'm done with them. When I do have something to say, I'm just sticking it on Mastodon. So the books section here is extremely vestigial and wasted.

At the end of the day, I was not as erudite as I'd hoped to be and I built an entire feature as a monument to that shame.

So if I delete the feature, at least I can just lurk quietly in the background without publicly claiming that it's taking me years to read Spock's World just because I never update my actual website.

I have done nothing on this site since...well, probably since July 2023 (the date of the last update here).

I just don't want to deal with the code, to be honest.

At this point, I am thinking of doing a rewrite in C# and switch around some of the database object structures since I now have a better handle on how it should all fit together.

I still want to make this a real ActivityPub site. In particular, I haven't even been posting on this site because I know no one will see it. If I were able to push the posts to Mastodon and other Fediverse friends more easily, maybe that would be different.

But that particular feature is such a long road and the current codebase is just not cut out for it.

That said, a rewrite would also be a fairly long road and I have many other things holding my interest right now.

So this site is just going to languish with no new features for a while, I think.

It's been just over a month since I last posted about some of the features I want to add here. There are a couple of tentpole features I want to add before I implement ActivityPub support:

  1. I want to be able to add n arbitrary images to microblog posts
  2. I want to be able to support microblog posts that are not about a specific item (unlike the microblog entry you are reading now which is tied to the Muscadine project)

For (1), there are a lot of ways to go about it but I've decided that I want to make a first class "image library" in the same way that I already have a project library and a book library and a game library.

Then, on any given microblog entry, I'd just choose an existing image from the library. I'm hopeful this will make it easier to build a library of standard reaction gifs or something.

I've finally done the work to add the image library. I can now add, edit, view, and delete images in that library. I can't use them for anything yet; but they exist.

I think the next steps on images will be:

  • Migrate the "cover images" for the existing items to be in the image library
  • Add support for images to microblogs

And that will wrap up the image library feature. Then it'll be on to feature (2) which should, probably be more straightforward.

I'm still using F#, by the way. I don't think I did a very good job building an F# foundation, but I've learned a lot. And I don't think the educational or fun value of rewriting in C# is there yet.

So progress continues.

Finally taking a look at adding more features to this site. In particular, I want to add support for putting images in these blog entries (right now, I can only add cover images to the larger items).

The way I want to do that is by adding an image library feature where I can tag the images (since everything else in this data model is tagged) and use some combination of "date added", "image title text", and "tag" search to find images from the libraries to include in the blog entries.

At that point, it doesn't make sense for the cover images to be separate so I want to migrate them into the image library.

It is not at all obvious how to slot this in to the current codebase. Some of that is just because I didn't design it with foresight, but a lot of it is just that I don't really know how to lay out and design code in F#.

To some extent, doing this in F# was a vanity project. It lets me put F# in my resume and on GitHub. But it's also a learning project: I've never written anything big in F# and I don't have any mentors but I'd like to know it better.

What I am discovering is that I know a lot about what not to do but I'm not sure I've zeroed in on better patterns and practices.

So I need to weigh the vanity-aspects of F# vs having my GitHub full of (frankly) middling-quality code vs being able to execute faster if I wrote it in a technology that I'm more of an expert in.

Just as an exercise, I might re-do the frontend in C# and leave the backend in F# for now. If nothing else, it'll give me an opportunity to learn Razer which I've never actually used.

One great thing about my website being a custom app that I built from scratch: if there's some little thing about it that's bugging me, I can just hop in and fix it in a few minutes.

One less-than-great thing: if I want a big new feature for my website, I'm the idiot who has to go implement it.

You (hopefully) won't be able to tell, but I spent the day completely revamping the backend UI for this site.

When I first built this site, I built the admin interface first so I would be able to put data into the database so I would be able to then build the frontend site around it.

That means that the admin backend was really just a couple pages with HTML tables thrown on them.

A single HTML table was becoming a little difficult to scroll through as I've added more content, so it was time for a change.

The admin interface is now built inside the same layout as the frontend interface. I've also broken it up into multiple sections for each type of thing that I track; conveniently, I can now use the sidebar for navigation within the admin interface as well.

At some point, I want to add a real blogging engine to this site. At that point, I'll really need some admin UI so while it's functionally identical to when I started, this really was a necessary precursor to future enhancements.

It also means that posting and updating items is more visually pleasant for me. So that's a nice change as well.

iPhones print this site completely differently depending on the orientation of the phone when you push the print button.

I have no idea how to debug this.

Fortunately, this has to be the edgiest of all edge cases and I can live with it.

But it's just so unsatisfying!

Since my resume is now just another part of this site, it inherits most of the styling.

I wasn't really happy with the way the font worked on my resume, so I've given the site a slightly new coat of paint.

At some point, I'll find some random header with the wrong font and I will be sad. But until then, I think it's looking pretty good!

I haven't really done any work on the site proper, but I did decide to move my resume into it.

Giraffe is pretty nice for this kind of thing.

In addition to just a straightforward tech-stack port, I've done a full content revision, which is a challenge because my career has really been 18 years doing the same job (just via increasing difficulty levels).

So I added some subsections to better tell that story and ended up needing to restructure the document and completely change the generated output. Again, Giraffe works really well for that.

So it took a fair bit of agonizing revision, but I finally don't hate my resume.

It's part of this project, so it seemed worth noting here.

Some of the half-baked plans I have for this site include an even-more half-baked plan to build my own OIDC provider.

Towards that end, I updated the session cookie keys to be a little less ephemeral.

This won't affect anyone but me (since I'm the only person who needs to log in to this site) but it's nice to not have my cookie reset every time I do a deploy or the webserver restarts.

It was also a nice low-impact, low-effort change -- I decided to Just Use Azure (tm) because .NET provides a very easy path for this with Azure Key Vaults and Storage Accounts -- which I hope will provide some much-needed momentum.

Had big plans to spend my December break adding some features to the site.

In no particular order, I want to:

  1. Add support for microblog entries that aren't tied to a particular item
  2. Expand those free microblog entries into maxiblog entries for more essay-like content with a traditional weblog frontend
  3. Implement ActivityPub support

I'm wavering a little bit on (3) since I think it's probably just as valid for Mastodon to add support for subscribing to RSS feeds. But that doesn't seem like it's going to happen and there are possible dividends from just joining the fediverse. I'm still not sure yet.

I also want to spend some time looking at BookWyrm and seeing what the overlap/divergence between that and my implementation of books is.

So (3) is a much bigger job than (1) or (2).

Not that it matters. I haven't really opened the code up at all this month. I guess I have on more week? Seems unlikely.

On top of all of that, I still need to work on the backend UI as well. That'll be especially important if I start using the site a little bit like twitter.

Things to do. Things to do.

While I remain happy with the user-facing portion of this site, the backend UI is a bit thrown together and I'm starting to add enough items to make it a pain to use.

I guess it's time to start thinking about a real approach to editing.

I still haven't answered any questions about what I want this site to become.

But I realized that I forgot to add the X-Clacks-Overhead header to all of my responses, so I've fixed that (and added a few other novelty headers) so at least I'm not letting the site just rot.

For the first time since I started this project, I suddenly find that I am not quite sure what I want this website to be.

It was originally going to be an in-the-moment, somewhat curated, snapshot of what I'm doing with my life.

But I'm not sure what that means. The original goal was going to be largely centered around microblog updates (like this one) where I could share thoughts tied to a specific item.

But what if I'm having a bad day and read a trashy space opera in an afternoon but don't have anything to say about it (or any energy to log in and say it)?

Should I add it to the site later just as a "I have read this" tracker? Should I add a star ratings feature so I can rate items? Should I add a "review" section separate from the microblogs?

Specifically for books, should I make this a Goodreads clone? But isn't Goodreads good enough? Maybe not for the non-books.

But I don't always want to put a star rating on something. I certainly don't always want to write a prose review. Would their absence be interpreted as meaningful?

But ultimately, I can't decide if that would be an evolution for this site's theme, purpose and design...or a step backwards.

It opens up some presentational challenges as well. The original site design was very purposeful and that's a fairly major expansion of purpose.

So this will be something to think about in the weeks ahead.

I wanted to link to that big entry down below but to do that, I needed to add permalinks for microblogs.

That exists now. See?

That last "micro"blog was a doozy. Maybe I don't need to add a "real" blog engine after all.

Since this is my site, the character counter is just a suggestion, after all. Hmm.

One issue that I've had since the beginning was "How do I represent the different item types (book, game, project) in a strongly typed way while also reducing the amount of boilerplate I need?"

In C#, I'd just make some interfaces and an object hierarchy and call it a day. And while one can do that in F#, it's against the spirit of what I'm trying to learn.

I went down a few paths (including some pretty gnarly heroic reflection code, immortalized in this "delete all the old code" commit) but nothing ever really felt right. I ultimately just ended up copy-pasting a bunch of the same code into Book.fs, Game.fs, and Project.fs and moved on.

I've slowly been solving this, starting a couple weeks ago when I added some discriminated unions to represent each type (in this commit). That ended up with a lot of similar code in pattern matching blocks, but I felt okay about it because the compiler was going to help me out by noticing missed patterns.

Then, last weekend, I watched a talk from Mads Torgersen on "The functional journey of C#". He made a point that a big difference between OO and functional programming is that OO lets you put new behaviors in one place (encapsulated in the base class, perhaps) while functional programming makes you spread it out all over the place and duplicate things in each function. But the functional approach is more extensible since all the behavior is out in the open.

He said it better. Go watch the talk.

Anyway, I've taken that to heart and done a great big refactor today.

Before, I really wanted to be able to have a list of, like, FormField<'t> to support things like FormField<string> and FormField<DateTimeOffset> and FormField<string option>.

This is fine as far as it goes but:

  1. You can't put those in the same list because they're all different types
  2. You can't pattern match over the generic type so it's hard to do things with them

This wouldn't have been so bad in C#. I'd just have a non-generic FormField interface or something. I don't know. But in F#, it's kind of a drag.

So I embraced what Mads was saying about duplicating behaviors as needed. I now have a new "FormField" concept where each FormField is a discriminated union of everything that a form field could ever be.

I wrapped up a bunch of pattern matched functions to do things with these and it's kind of annoying but really not that bad. The compiler and tooling helps a lot here.

I'm pretty pleased with it.

I know this is far from perfect and not what an F# master would write.

But I think this is a solid improvement and shows how much I'm growing into this new-to-me language and paradigm.

I can only imagine six months from now when all of this seems really gross to me and I have much better ways to accomplish all of it.

I can't wait.

Today's the day.

I've spent the week polishing all the rough edges I could find.

I've got a commit ready to go to change the index route from "Under Construction" to...well...this.

I still want to write up something to put on social media to tell people about it (I am kind of proud of how this has turned out!) and then I can push that commit.

It's time.

Not sure if the first post-launch feature will need to be a no-context microblogging feature (possibly published to twitter) or a real blogging engine. Either way: do I actually have worth saying?

I've created a colophon. The site is feature complete! I'm not happy with the copy at all, though. So I need to spend a few days thinking about the writing. But I'll probably launch next weekend! Exciting stuff.

Added visible tags and individual item pages today. v1 of the site is nearly complete (indeed, I almost thought it was done until I remembered...) except for the colophon.

One more static page and then I'll just need to look at it very hard before making any small tweaks and going live.

I can't believe it's almost finished!

I got the "list" pages working under the "What am I...?" buttons in the sidebar. I am so excited about it. It looks so good.

Things are really coming together over here and I am starting to feel pretty proud of how it's turning out.

I did a very small amount of refactoring cleanup. The code is really disorganized and it's not at all clear to me how to improve it.

More importantly, though: I did decide to add Markdown support. It was very easy thanks to the Markdig project.

The front page now has dynamic microblogs (like this one!) on it.

I'm still debating if I'm going to support markdown in these or not. The time to make that decision one way or another is fast approaching.

The project code is also really starting to get away from me: a lot of that is to be expected since I'm learning F# as I go, but it might be time for a major cleanup refactor soon. I just need to learn more about what that should look like.

I've now got all of the static content on the main "About" page laid out in a responsive fashion. Will start working on the dynamic stuff next time.

It's looking good! I'm almost tempted to disable links to pages that don't exist yet and put this up instead of my "under construction" banner.

Almost.

I started working on the user-facing site today! I've got the main layout built out and it's responsive and everything. One day, you may even see this very message in that layout! Exciting stuff.

I've put some real data into the backend so I'll actually have something to look at when I start styling the frontend. Probably next weekend. We'll see.

Today, I am "feature complete" for the backend: which has allowed me to write this, the inaugural microblog in the production database.

Next up, it's all about presenting the data in a pleasant frontend!