This is a place where I write what I built, what I made or what I worked on. It's the changelog.md of my work life.
Signed up for Ahrefs and fixed the SEO issues it reported on Darkmatter’s website:
h1
tag.Presented Darkmatter to a small enthusiastic crowd in Astro’s weekly community call on Discord.
I was a bit nervous, because I got a spontaneous invitation and I wasn’t prepared for this at all. I drafted up some talking points and waited for my turn to speak.
Surprisingly, it went very well. I kept the whole speech to around 10 minutes, which covered literally every aspect of Darkmatter. People were commenting how much they liked it and they generally seemed exciting to check it out. One person even wanted to record a video about it, which would be quite cool!
Rebuilt the license manager Rails app from scratch with a significantly reduced scope. It’s no longer built as a SaaS, so I don’t need to implement authentication, user management and make the features generic enough to adapt to any app.
Instead, I built a small app that works with Darkmatter and can support my other apps, which all have a similar pricing model.
It also includes a customer portal, where users can log in to manage their licenses. They can deactivate devices, buy more seats or renew their license for another year.
My new plan is to use this for Darkmatter for a while, integrate it into Lotus, Rosefinch and Linkjar and only then consider the possibility of opening it to everyone.
Published a newsletter and a blog post about latest Darkmatter updates: dark mode and license manager.
Built a small Rails app to handle license keys for Darkmatter. I considered using Keygen, but I wanted an app that integrates with Paddle and offers an SDK to quickly integrate licensing into all my apps.
It has some nice features:
I plan to use it in Darkmatter at first and then integrate it into my other apps, Rosefinch, Lotus and Linkjar.
If everything goes well, I will launch it as a standalone product.
Shipped Darkmatter 0.10.0 with support for:
There’s a new “Publish changes” button in the top right corner, which opens a simple dialog to commit and push your changes to git.
You don’t need to go back to terminal to run git commit
and git push
after writing in Darkmatter anymore. This is super convenient, because the entire content editing workflow can happen inside Darkmatter end to end.
In previous versions of Darkmatter, you’d need to start the Astro dev server manually, otherwise Darkmatter wouldn’t open the entry page in the browser.
Now Darkmatter starts the Astro dev server on a random port if it’s not running already. That way, there’s always a dev server to preview your content in and you don’t have to jump between Darkmatter and terminal.
Added a way to customize the entry URLs of each content collection in Darkmatter.
Darkmatter assumes that each entry can be viewed at /[collection name]/slug
URL, but that’s not always the case. For example, I have a “posts” content collection, but posts are displayed on a /blog
page. If I try to open any post in browser from Darkmatter, it’ll show a 404.
To fix that, there’s a new defineDarkmatterCollections()
function in darkmatter-sdk package, which can configure the right base path for each collection.
import {z as zod, defineCollection} from 'astro:content';
import {defineDarkmatterCollections} from 'darkmatter-sdk';
const posts = defineCollection({
schema: zod.object({
title: zod.string()
});
});
export const collections = {posts};
export const darkmatter = defineDarkmatterCollections({
posts: {
basePath: '/blog'
}
});
Now Darkmatter would correctly open a /blog/[slug]
route for each entry.
Added an RSS feed for the worklog you’re reading. Christopher, a reader of Darkmatter’s newsletter, emailed me asking about it, so I figured why not and shipped it.
Now I’ve got one RSS feed for my blog posts and one exclusively for worklogs. I added different <link rel="alternate">
tags for them, so your RSS reader should auto-detect the right RSS feed based on the page URL you’re giving it.
Using the same technique to implement text fields, I also added support for date time fields. It’s the same zod.date()
field, but with an option to set the time, which will be saved to frontmatter as ISO 8601 timestamp.
import {z as zod, defineCollection} from 'astro:content';
import {dateTime} from 'darkmatter-sdk';
const posts = defineCollection({
schema: zod.object({
title: zod.string(),
date: dateTime()
});
});
Shipped support for multi-line text field in Darkmatter.
This was tricky, because field types are inferred from the Zod schema, which doesn’t have a zod.string()
that somehow hints that a string needs to have multiple lines.
import {z as zod, defineCollection} from 'astro:content';
const posts = defineCollection({
schema: zod.object({
title: zod.string(),
description: zod.string()
});
});
To work around this limitation, I published a darkmatter-sdk package that exposes a text()
function. It returns the same ZodString
object, but Darkmatter detects when text()
is used instead of zod.string()
and marks the field as multi-line text.
import {z as zod, defineCollection} from 'astro:content';
import {text} from 'darkmatter-sdk';
const posts = defineCollection({
schema: zod.object({
title: zod.string(),
description: text()
});
});
Released a massive v0.9.0 update to Darkmatter:
zod.or()
.zod.date()
fields, which are now saved as calendar dates (e.g. 2023-05-15
) instead of ISO 8601 timestamps.I worked on keyboard navigation in Darkmatter and decided it’s be a good idea to design macOS-like focus rings.
It may not be a 1:1 replica, but I think it’s pretty close. Another one of those details that make my Electron apps feel more like native ones.
Added support for editing union fields in Darkmatter, which are defined by zod.or()
function.
defineCollection({
schema: zod.object({
title: zod.string(),
date: zod
.string()
.or(zod.date())
.transform(date => new Date(date))
})
});
Darkmatter detects the field type for the current value among the allowed types specified by the schema. It even lets you switch the field type if needed.
Sent out a newsletter and published a blog post about internationalization support in Darkmatter.
Shipped i18n support in Darkmatter, which allows content in Astro websites to have translations in multiple languages.
I wasn’t sure how to implement it at first, since there’s no API or configuration for it, just a set of recommendations from Astro on how to set it up with existing building blocks. I’d have to resort to detecting if folders are named after language codes, which sounded brittle.
However, I decided to give this shot, because this approach doesn’t require any work from the user of Darkmatter and it just magically detects content collection to have translated content.
No regrets so far, but I’ll see how it goes as more people use it. Big fan of that “Language” switch though.
Built a small app I called “selfie” to take screenshots of my vadimdemedes.com and getdarkmatter.dev websites to use them as Open Graph images.
I implemented it earlier as an Astro integration, but that implementation started to annoy the hell out of me. It required manually running a script before pushing to a git repository, otherwise screenshots would be missing or outdated.
This new app looks at the sitemap and screenshots all the pages it has found every time I deploy. It’s written in Node.js, hosted on Railway and uses Redis both for background workers and screenshot storage. Perhaps it could grow to a standalone product some day, who knows.
Sent out a newsletter and published a blog post about Darkmatter. Talked about recent updates in the 0.6.0 version.
Released Darkmatter 0.6.0 with support for data collections, new Astro assets API, native app menu and ability to open multiple project windows at once.
Updated Darkmatter to support Astro data collections.
Implemented support for the new Astro assets API in Darkmatter.