--- title: This blog has secrets tags: - thisSite - graphql - markdown - podcast - grats - sqlite - eslint summary: Shh… this blog has secret features notion_id: 295376e2-3751-8004-88dc-cb537a673cf2 summary_image: >- https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/4145ce31-6858-4285-b4a9-89d8a28b8ff0/Screenshot_2026-01-12_at_8.23.41_PM.png --- My blog has a bunch of non-obvious features which I’ve added because I love to over-engineer stuff. Here’s a list of eleven hidden features I think are most interesting. ## Read posts in alternate formats If you add `.md` or `.mp3` to the URL of any post/note it will render that post as either a markdown file or an AI text-to-speech audio file. For example [secrets.md](https://jordaneldredge.com/secrets.md). ![Screenshot\_2026-01-12\_at\_8.23.41\_PM.png](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/4145ce31-6858-4285-b4a9-89d8a28b8ff0/Screenshot_2026-01-12_at_8.23.41_PM.png) ## Listen to the blog as a podcast I prefer to listen to text content rather than read it. If you do too, you can subscribe to [this podcast](https://jordaneldredge.com/tts-podcast/) which consists of each post on the blog read by AI. You can also find a small “Listen” button at the top of each post. ![Screenshot\_2024-12-08\_at\_12.webp](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/c1bd5bb1-aa22-4ce4-9509-970689dd8c73/Screenshot_2024-12-08_at_12.webp) ## Query the blog using GraphQL To play with my project [Grats](https://grats.capt.dev/) I slapped a few `@gqlType` annotations on this blog’s TypeScript models and exposed them as a GraphQL API. You can play with an [interactive query UI using GraphiQL](https://jordaneldredge.com/api/graphql/). ![Screenshot\_2026-01-12\_at\_8.23.11\_PM.png](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/6bdcbb5a-b1ba-419e-9171-ed2fb9460c45/Screenshot_2026-01-12_at_8.23.11_PM.png) ## Embedded cross-page audio player Since as early as 2008 this blog has included embedded audio files. When I rewrote the blog using Next.js I took the opportunity to [add a custom audio player](https://jordaneldredge.com/blog-audio-player/). The main benefit is that the player persists in the footer meaning that you can start playing a track and then navigate to a new page without the audio stopping. ![Screenshot\_2025-03-22\_at\_11.webp](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/24f780c0-2c82-4f77-a206-1f1b50af8c3f/Screenshot_2025-03-22_at_11.webp) ## Sophisticated search DSL The content of this blog is cached in a SQLite database to enable dynamically building different lists. I also leverage SQLite’s FTS (full text search) feature to power the [search page](https://jordaneldredge.com/search/). What you may not know is that the search input accepts structured searches using a custom search grammar with full support for boolean operators. So if you want to find posts that have embedded videos but _don’t_ have either embedded tweets or the tag “graphql” you could search for: [`has:video NOT (has:tweet OR #graphql)`](https://jordaneldredge.com/search/?q=has%3Avideo%20NOT%20\(has%3Atweet%20OR%20%23graphql\)). This query is parsed to an AST and then compiled to a SQLite query mixing FTS and structured conditions. You can play with a debug version of the search parser which shows the parsed AST/SQLite query at the [debug search](https://jordaneldredge.com/debug/search/) page. One feature I like about the search input is that while the query will always fall back to a text search, if you use syntax it can’t understand, it will show a small warning. ![Screenshot\_2026-01-12\_at\_9.08.59\_PM.png](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/ffdbf393-65e6-49af-a4e0-6bcd26b2abbf/Screenshot_2026-01-12_at_9.08.59_PM.png) ## PageRank algorithm As part of building the “related” section at the bottom of each posts, I wanted to ensure I was listing related posts, but also the “best” related posts. But what makes a post “best”? I figured I’d take a page (heh) out of Google’s book and tried implementing the famous [PageRank](https://en.wikipedia.org/wiki/PageRank) algorithm. It analyzes the tags and interlinking between posts as well as a few hard coded inflows of “juice” for posts which have been linked to from sites like Hacker News to try to compute an objective rank for each post. I’m not sure how well I’ve been able to do here. You can see all my posts/notes together in PageRank order here: ## GitHub powered comments Its nice to allow users to comment on your posts. But people don’t want to create accounts for your random blog, and anonymous comments are a disaster. To get around this, I implemented an entirely client-side comment system piggybacking off of GitHub issues. [Some posts](https://jordaneldredge.com/search/?q=has%3Acomments) are tagged with a specific GitHub issue. On page load the browser makes an unauthenticated request to the GitHub API to fetch all comments on that issue and then renders them on the page using React. I can then use GitHub’s moderation tools to control which comments stay up, and I get to leverage GitHub’s existing user authentication, where most of my readers already have accounts. ![Screenshot\_2026-01-12\_at\_9.30.08\_PM.png](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/1ef85fa2-5ae2-47bd-ba93-033124e7af14/Screenshot_2026-01-12_at_9.30.08_PM.png) ## Embedded animated cursors In my post [Rendering Animated .ani Cursors in the Browser](https://jordaneldredge.com/rendering-animated-ani-cursors-in-the-browser/) I share how I built an NPM library to render an old style of Windows animated cursors in the browser. To add to the fun, I configured that specific page to have a custom animated cursor using the library. [Screen\_Recording\_2026-01-12\_at\_9.20.45\_PM.mov](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/c1584764-bb1b-43fb-8408-4eae0e25042f/Screen_Recording_2026-01-12_at_9.20.45_PM.mov) ## ESLint errors in code examples My post [Interesting Bugs Caught by ESLint’s no-constant-binary-expression](https://jordaneldredge.com/interesting-bugs-caught-by-eslints-no-constant-binary-expression/) is mostly a bunch of examples of code that trigger the lint rule and I wanted each example to not only show the snippet but also show _where_ the error gets reported. To enable this I wrote a custom syntax highlighter which runs ESLint on the snippet and then inserts custom syntax tokens for the ranges that ESLint reports as errors. ![Screenshot\_2026-01-12\_at\_9.17.25\_PM.png](https://pub-d4cecb3d578a4c0a8939680792e49682.r2.dev/notion-mirror/84ebb48c-616a-4f51-ae9a-991a4e0a7e9b/5cf198d2-ec7e-40e6-b18f-ec3c745baa60/Screenshot_2026-01-12_at_9.17.25_PM.png) ## React rendered markdown Unlike many markdown-based sites which render markdown to HTML and then style it, this site parses posts (encoded as markdown) to an AST and then uses a recursive React component to render that markdown to the page. This enables me to have custom defined interactive elements inside my markdown. Such as the above mentioned audio player, Next.js smart links which preload content, embedded YouTube videos and more. I wrote about this in [A nice way to render Markdown in React apps](https://jordaneldredge.com/markdown-react/). ## Personal paste bin My site has an admin side that can be authenticated to using passkeys. Once logged in, you can create pastes. - If the paste filename is `.md` you can view it as a rendered markdown file: [sample.md](http://sample.mdhttps//jordaneldredge.com/paste/2/) - If the paste filename is `.html` you can view it as an HTML document: [webamp.html](https://jordaneldredge.com/paste/9/webamp.html) This last feature is great for vibe coding little single purpose apps, like this spelling app my wife made for our daughter to practice her weekly spelling word list: ## Conclusion What is a personal website but a little garden in which to over-engineer and play with ideas? If you want to take a peek at how any of this works, you can find my [blog’s code on GitHub](https://github.com/captbaritone/jordaneldredge.com).