Experimental browser for the Atmosphere
{ "uri": "at://did:web:hugeblank.dev/com.whtwnd.blog.entry/3lodneqnvk22h", "cid": "bafyreidwi4hjeyoshfmhcakrnjduy6qib6gxhkgmaqctcjgmo65ylorcbm", "value": { "$type": "com.whtwnd.blog.entry", "blobs": [ { "name": "image.png", "blobref": { "$type": "blob", "ref": { "$link": "bafkreieomwddmbelvw6x4gbygm25nxnjvygwvgvu4psb3wt5kemfnrq2ia" }, "mimeType": "image/png", "size": 75877 }, "encoding": "image/png" }, { "name": "image.png", "blobref": { "$type": "blob", "ref": { "$link": "bafkreiesmxvueri2vuvbx3im3fqzzyskxs6yb2ikjcqksq5ejagv3rrqse" }, "mimeType": "image/png", "size": 275869 }, "encoding": "image/png" } ], "theme": "github-light", "title": "Tegami - A Love Letter to Love Letters", "content": "<link rel=\"post\" href=\"https://bsky.app/profile/hugeblank.dev/post/3lofcumxroc2r\"/>\n\n[Tegami](https://tegami.hugeblank.dev/) | [GitHub](https://github.com/hugeblank/tegami/) | [My Blog](https://blog.hugeblank.dev/) | [Me](https://hugeblank.dev/)\n\nIf you're not interested in the background for why Tegami came to be, skip to [here](#user-content-tegami)\n\n# Prologue\n\nIf you read my first blog post about [WhiteBreeze](https://github.com/hugeblank/whitebreeze), and made it all the way to the end, you might remember this:\n\n> I just got back from a 2 month trip to Japan, visiting 20+ cities as far south as Nagasaki and as far north as Hakodate. I owe it to the people and experiences I had on that trip to share it, so please look forward to the page being occupied by loads more articles!\n\nIt's been 6 months since I wrote that and there's still no Japan post... why?\n\nThe truth is, I totally could have wrote a blog post in that time but I had this additional idea sitting in my head that I just couldn't shake the itch to make. There was someone I met at the end of my trip, who shared their instagram with me. I wanted to write something specifically to them, before writing a blog post about my journey, because they were really kind and wrapped a nice little bow on the whole adventure. To be clear, it wasn't going to be a love letter, the title was purely a hook to get a prospective reader interested. Regardless, I wanted to write something to them, and I knew that I wasn't going to be able to do it purely constrained to Instagram's dinky DM functionality. \n\nI sat on the idea until about a month ago when I finally got the courage (and will) to make the site, and write the letter. I knew I wanted the letter to come first because I wanted to contextualize the upcoming blog post for them. I also had this dilemma for a bit where I thought about using a different service instead of writing my own, but I wasted too much time pondering to the point that I **owed** them a bespoke site for how long I had gone without contacting them.\n\n# Tegami <a name=\"tegami\"></a>\n\n手紙 (pronounced tegami) means letter, it's a very inspired name. It is a small web app for writing and self-hosting password protected letters and unlisted blog posts. My goal was for it to look substantially better than a simple text message, so I used a markdown renderer with some custom juice that allows for embedding of more than just images, but also audio and video files. I also wanted it to be really easy for anyone to set up, just like WhiteBreeze. \n\nIt differs from WhiteBreeze in a couple ways. First, it doesn't use ATProto or WhiteWind at all. All data is stored locally, not on any PDS or anything like that. The second difference is it uses React, specifically [jebsite-template](https://github.com/Lustyn/jebsite-template) by my friend [justy](https://bsky.app/profile/did:plc:75ura2nftpim57bgdewtcpmo). Prior to this project React was completely foreign to me. The template was a great launch point and helped me rapidly assemble the project. Let's take a look at the site.\n\n## Features & Usage\n\nSome of these features were enforced/constrained by the template, I will make sure to mention which ones they are when they come up.\n\n### Admin Panel\n\n\nAs the host of the site to get started you navigate to `/admin`. From there you log in using credentials set up in the `.env` file. After logging in you gain access to this dashboard, where you can see all existing letters, their ID, when they were created and last modified, and can delete them. Also prominent at the top is the create letter button which generates a 10 digit hex number, and creates the letter directory using that number. This is where all the media, letter markdown, and key goes. The header also populates with the `Admin` link in the top right, that takes you to this dashboard. In the future I plan on swapping out the IDs for a proper title. I haven't gotten around to it, and moreover haven't written enough letters to warrant it.\n\n### Letter Editor\n\n\nWhen clicking into one of the letters from the dashboard you are presented with a preview and editor pane, in addition to the key input, media popup button, and a share button that was implemented after this screenshot was taken. Letters get autosaved too, and the status icon is shown to the left of the key. In the screenshot it's an emoji, but I found that lucide, an icon library was provided in the template, and have since moved to using it. This was something I wasn't really sure I'd end up implementing, since I could have just used some external editor and then uploaded letters as a zip, but ultimately I decided that it would be much cleaner to do the letter writing within the app.\n\n### Media Browser\n\n\nThis is the popup that is shown when the \"Media\" button in the editor gets pressed. Similar to the letters themselves, uploaded media gets a random 10 digit hexadecimal name when uploaded. A preview of the media is provided, with 2 buttons; a button that puts the media at the bottom of the page, and a delete button. In the future I plan on the link to the media be inserted wherever the cursor is, but for now, the bottom is generally fine. One of the things that I ensured when designing this popup was that the delete buttons remain are in the same position relative to all other media cards. This way if you have multiple media to delete in a row, the button doesn't jump around. \n\nYou can also see that in this specific letter, a video is uploaded. The markdown syntax for videos is the exact same as images; ``. The markdown renderer I'm using has this nifty feature that allows you to pass components for each html tag, so I was able to hook into the `<img>` tag and extend it's functionality, replacing it with a `<video>` or `<audio>` tag depending on the media mime type provided by the server.\n\n---\n\nThese last two screenshots are pages that are accessible to visitors of a tegami instance.\n\n### Home Page\n\nAssuming that you end up hosting your own tegami instance, this is what users will see, unless you directly send them a share link to a letter. The two components to access a letter, are the letter ID, and the access key that you create by writing one in the letter editor. If there is no key, then the letter can be openly accessed by anyone who has the link, or is really good at guessing. Realistically the home page is seldom meant to be seen, but if it is, visitors always will have a place to go, assuming they've already opened a letter. The opened letters list gets its keys from local storage, which is where keys are saved after a letter is first unlocked.\n\n### Letter Viewer\n\n\nUnsurprisingly, this looks exactly like the letter editor admin page, without the editor.\n\n## Additional Details\n\nIf you've written a blog post using WhiteWind at all you'll be familiar with the letter editor and media browser. I took a lot of inspiration from WhiteWind in that regard, as besides the locking feature, the concept of a letter and the concept of a blog post are largely the same. The only real difference is I think that tegami's UI is a lot cleaner, for a couple reasons. First off, the template starts with tailwind, so it was a no-brainer to use the typography extension alongside the markdown renderer. Tailwind also has relatively sane defaults, dark mode, and a sans serif font. In addition, I used components for buttons and inputs from a UI library called shad-cn, which itself has sane defaults and a lot of customizability within the components.\n\nThe API that handles creating, unlocking, saving, and deleting letters uses a tool provided by the template called TRPC. Basically, it handles object structure validation and parsing on both the client and the server. It's an incredible bit of kit and the tanstack react integration is incredibly helpful. The template comes with a basic example, and from that and looking over the docs I was able to do what I wanted so quickly and easily.\n\n# Final Thoughts\n\nI really like React, and finally understand it! I'll probably be rewriting WhiteBreeze and GrayHaze using this template too. I've already rewritten one of my older projects, and it was *so* easy. I'm also having a lot of fun writing self-hosted projects, I don't think I'll be stopping any time soon. If you're into the idea of self-hosting and writing your own letters please give tegami a shot! I've linked the github at the very top, instructions on how to get it running are in the readme. \n\nOnel last thing, I wrote a letter for you! Open it [here](https://tegami.hugeblank.dev/open/b65665fb1a), the password is `aketekudasai`", "createdAt": "2025-05-06T10:03:41.767Z", "visibility": "public" } }