In my last post, I had a comparison of two different versions of the scripts portion of my site’s package.json file. You may have wondered, “how did he so easily compare his current site build script with a site build script version from long ago?”
The answer is using git diff with not only a filename for the file to compare, but with commit IDs of the two commits in question. It looks like this:
And if you use a difftool for comparing changes, just change diff to difftool in the above:
Then your difftool of choice opens and you can compare the two versions of the same file from two different commits side by side:
In order to find commit IDs, the command git log will do the trick. The commit at the top is your latest commit. You can even search for a particular phrase with git log, which I did in order to find my first Pagefind implementation.
I just had to page down a bit to get to where I first finished adding Pagefind prior to using Astro-Pagefind and then grab that commit ID for the comparison:
And, finally, in the interest of fairness, I dug through my git history and found what it takes to implement pagefind index build into the site build process, and it’s much simpler than what I wrote previously. Most of what I had in my build script for Pagefind prior to Astro-Pagefind was copying files to the public directory so Pagefind would function in dev mode. All you really need to integrate pagefind directly is something like this:
Git remains awesome, and its flexibility in allowing you to use helper apps of your choice does too.
Pagefind is a static search library that you install locally to your project, that runs and builds its index locally after your static site is compiled, and that provides both a JavaScript search tool and an HTML UI. It’s also open source and free to download and use on your site.
You can watch Liam Bigelow of CloudCannon presenting how Pagefind works and how it’s scaleable on YouTube in his video from HugoConf 2022. That name “HugoConf” may give you pause if you don’t use Hugo as your static site generator (I don’t – I use Astro), but it’s actually a hint to the fact that Pagefind is completely static platform agnostic and will work regardless of you static site builder.
I recommend you watch the video and see for yourself how lean Pagefind runs and then marvel over the fact that it’s free and ready to use on your static site. In the words of Bryce Wray, Pagefind is quite a find for site search. Bryce also has a really good article on integrating Pagefind called Sweeter searches with Pagefind.
Initially when I started using Pagefind on my websites, I used it as per the Pagefind docs. In terms of adding Pagefind search to a page, you drop in the following to your HTML template:
That actually works quite well as is, but there’s even better news for you if you use Astro to build your static sites: Sergey Shishkin’s Astro-Pagefind Astro integration.
Astro-Pagefind lets you make life easy for yourself when adding Pagefind to your astro site by integrating it so that you can drop it into your Astro templates as a component.
It really is that easy. Here’s the entirety of my search page Astro template, and it’s almost completely CSS to make the Pagefind UI look how I want:
As I said, almost all CSS. Everything in the <style></style> block overrides Pagefind’s default UI appearance. Basically I just used Chrome’s inspector tools to look at different elements and figure out which ones applied to me and needed to be overwritten (as well as seeing what the names of the elements were as written to the page).
A nice quality of life enhancement using Astro-Pagefind over directly integrating Pagefind is that your build scripts don’t have to include anything about Pagefind at all. Astro-Pagefind takes care of that for you. Before Astro-Pagefind, I had to make sure to include Pagefind commands in my build scripts.
Site build script when using direct Pagefind integration:
Site build script when using Astro-Pagefind:
To be fair to Pagefind, I think the build script could actually be slightly simpler. I think the reason I’m copying the output to public/_pagefind is because I was trying to trick Pagefind into working in dev mode before I discovered Astro-Pagefind.
The BEST part about using Astro-Pagefind instead of just integrating Pagefind the original way though is the ability to have search work even when running in Astro dev server mode (ie, while working on the site). Normally in order to see search working and see what effect your CSS or other changes have had on your Pagefind integration, you’d have to recompile and run a preview server with every change. With Astro-Pagefind, you just run in dev mode as you’d normally do when working on your site, and any changes you make that affect your Pagefind UI appearance will be available instantaneously when you make them.
The trick Astro-Pagefind uses to do this is to use your last compiled site in your dist folder (or whatever you call yours) and pulls the already-built Pagefind index from there. It’s super slick and it’s really changed how I work whenever I am adding search to an Astro site.
Considering Pagefind’s speed, scalability, and ease of integration, I don’t know of any better options for static site search. And if you’re using Astro, install Astro-Pagefind to your project and be amazed at how something that once was a big downside to static site builds is now an advantage.
One of the beautiful things about CSS grid (and in my opinion, there are many) is the ability to name grid sections (template areas) and then assign the children (the grid items) to those sections. This means you can do useful things like change where a grid item displays when the site is viewed on smaller screens.
I used this capability recently when doing a redesign of Friends with Brews. Previously, the navigation menu was just four links: Home, The Friends, The Brews, and Episodes. I had links for our RSS feed and other ways to subscribe on the home page underneath the introduction paragraph. The reason I did this is so that on small screens, I could just reduce the size of the text and still fit all four links in the header without having to really change the layout much (although with the two links on either side of the logo arranged in rows, one over the other, instead of all four links on one row).
Once I added transcripts to the site, the number of links I needed started growing. And I already wanted to link directly to the Follow page listing the various ways to subscribe to the podcast, along with our Mastodon account. So I knew I needed to make the site navigation menu more flexible. I decided to make use of the strategy I’ve employed on this site’s menu and just shuffle the menu to the bottom of the page for small screens narrower than 900 pixels. In addition, the Friends with Brews menu would switch from being a horizontal, page-wide list of links to a vertical list when the viewport is less than 900 pixels wide.
CSS Grid makes this trivial. It might not seem intuitive to put vertical page sections in a grid, given that they’ll just lay out vertically one over the other with straight html and no CSS at all, but doing so allows you to name sections and thus move them around in the order of the page stack.
In wide browsers, the menu appears right under the site header, with the menu items laid out horizontally.
For views narrower than 900px wide, the menu shifts down to the bottom of the page, with menu links stacked vertically.
Here’s the html for my Base.astro page layout that makes up the base of every page on Friends with Brews:
Don’t worry too much about <Header />, <Menu />, <slot />, and <Footer />, except to take it as how the page is laid out in full-sized view: site header, then menu, then main content, and then the footer.
And here’s the CSS for the div with the ID wrapper-grid:
You can see I have set up grid template areas named header, menu, main, and footer. The desired correlation to the html sections should be clear. But this just sets up the fact that those template areas exist. In order to determine what html goes in what grid template area, each of those sections needs to state their membership using the grid-area property.
To move the menu to the bottom for narrow screens, I use a simple media query to rearrange the grid areas inside the wrapper-grid div.
Tada! Instant re-placement (not replacement) of the menu to the bottom of the page!
As far as switching the menu from horizontal to vertical layout, that’s because the nav ul is also a grid, and it’s laid out with grid-template-columns: repeat(6, 1fr); in wide view, and grid-template-rows: repeat(6, 1fr); in narrow view. It’s as simple as switching from column to row view using another media query for narrow screens.
I love Drafts for writing blog posts, podcast episode show notes, and more. I used to also use it for saving self-written tech-related documentation in, but now that is primarily in Obsidian. But Drafts is great for “writing writing”. Workspaces, actions, themes, templates, and huge amounts of built-in functionality make it a flexible, capable writing tool. It’s also super keyboard shortcut friendly, which is a huge must-have for me.
And now it’s even better, because it has everyone’s favorite buzzword, ChatGPT!
This is actually a pretty cool feature, assuming you’re experienced with ChatGPT and know its strengths and weaknesses and how to dig the truth out from its inevitable lies. I’ve talked about MacGPT and its inline mode before, and using ChatGPT in Drafts is very much like using MacGPT’s inline mode. It’s very easy to create actions to define things, check spelling, summarize a selection, or to write code to match requirements specified in a prompt. In fact, Greg Pierce, creator of Drafts, has written some actions to do those very things.1
I made some quick short videos showing these actions in… well… action. Have a look. By the way, I modified the OpenAI: Translate Selection action to include Japanese as an option. As written, it included Spanish, French, German, English.
Footnotes
Look for the actions posted by agiletortoise in those search results. ↩
If you’re reading this thinking “this site looks a little bit different”, you’re correct. What started off as an exercise in improving my pagination pager for my blog posts became a bit of a site redesign including moving menu links out of the header into a standalone menu, a new (work-in-progress) pager, and having the home page act as a links/summary page instead of being the full page 1 of my paginated blog posts.
The home page change is probably the most obvious to most people, even though the menu change is the one I personally think improves things the most. Whereas previously I did a couple minor hacks with my pagination logic to have the home page show the first page of blog posts, this isn’t really how Astro likes to handle pagination.
Astro expects a [bracket] syntax page that acts as the page that will page through a given collection of items. Because of this, implementing anything more than a simple “more posts” link on the home page became difficult for me. I wrestled with figuring out how to get the home page the information passed via the page prop like Astro does on a proper paginated page.1
In the end, I gave up and decided to do what Astro seems to think is the right way to set up a blog site, which is to have a landing home page and keep the paginated blog posts off of it. That doesn’t mean completely avoiding linking to posts, as you can see from the fact that I have a list of the 5 most recent posts on the home page. But these aren’t the complete posts like on the paginated blog post page, and they (obviously) aren’t paginated. Links to the 5 most recent posts are there, and that’s it.
Personally I don’t really know why Astro doesn’t have the ability to handle routing pagination with the index page or home page as the first page of items, but they don’t. None of the popular sites I enjoy that have daily articles have a landing page with posts hidden behind a link. They all just get down to business and show the visitor the first page of posts, with a pager to move on to older content. Regardless, it’s not a hill I want to die on for this site. I do have a project in the works where it may become more of an issue, but I’ll cross that bridge when I come to it.
Bridges and hills. Who knew there would be so much geography involved in running a web site?
Ordering a Menu
Previously on this incarnation of my website (since converting it to Astro), the site header combined the site title (featuring my weird head) and the menu. The menu was just laid out horizontally and didn’t change for smaller screens other than to scale down enough to fit.
Besides not being very elegant, this menu structure also has the downside of featuring inherent non-expandability. This is a convoluted way of saying there was no way to cram more onto that menu. I knew that at some point I wanted a side menu that would disappear for small screens, but I wasn’t sure how it should look and if I really wanted a hamburger menu that would reveal a hidden mobile menu.
Enter Ben Smith, whose website I stumbled across somehow, probably in the Astro Discord Showcase channel. I genuinely feel bad about how much I stole his menu ideas, but it looks good and functions even better. Instead of a hamburger menu, Ben chose an approach of having a hamburger menu looking icon next to the site title for small screens, but having it just jump the visitor down to the same menu as on the full-size site, except now located down at the bottom of the page. I loved it. I stole it. I’m sorry and thank you?
Lest the reader think I also stole the icon idea, you can see from the image of the old menu above that I was already using a menu with icons. In fact, way back when my site was written with Hugo, I had a menu that was nothing BUT icons. I think it’s fair to say this isn’t a unique idea.
Also lest the reader wonders whether or not I realize the limitations of my design skills and whether or not I think my site looks stunningly professional, the answers are yes, I do realize my limitations, and no, I don’t overestimate my site’s appearance. My best is not very good when it comes to UI design or even design of any kind, and I do understand that.
I’m not going to go into the old Pocket Sized Podcast days, but just know that I was publicly criticized by a few listeners for my artwork design skills, or lack thereof. 🤷♂️ You can’t win them all. Hopefully this site is at least tolerable enough that people won’t take time out of their day to chase me down and tell me how bad it is.
Human communication is weird. It’s so nebulous and imprecise and dependent on visual cues like eye-to-eye contact and physical observation of the party we’re communicating with. I often used to imagine how much life would be easier if we could do direct data dumps into each other’s brains, like computers sending information to each other.
How funny it is to me then that machine learning is making advances by using large language models to let computers parse incoming information through the context of human language in order to let systems with different APIs and data formats communicate with each other. It turns out that the future of computer communication may well be based on the less precise and more misunderstanding-prone human forms of verbal communication.
All philosophical musing aside, the point is that I’ve been using ChatGPT to do computer related tasks for me like write AppleScripts, bash scripts, and detail nuances of Swift code. These are the types of things I believe the GPT LLMs are best at. I don’t like arguing with it to make it do things it’s not good at or isn’t supposed to do, and I understand and share the social and ethical concerns voiced by people like Timnit Gebru and Emily M. Bender. In addition, there’s the question of resource usage, and LLM training requires tons of it.
That’s a lot of off-topic information to prepend onto what is basically a post about what ChatGPT tools and applications I use, but I think it’s important to think about these things and not just blindly fall into the AI craze and believe everything people like Sam Altman want us to believe.
GPT apps
“Blah, blah, blah,” you say, “what about the apps?” Fine. Let’s talk about some ChatGPT apps I like.
MacGPT
By far the GPT app I use the most is MacGPT by Jordi Bruin. MacGPT primarily runs as a menubar app, but it also features global mode (a pop up query field that looks like Spotlight) and inline mode (letting you get responses from ChatGPT right inside your text documents and text fields).
As an example of inline mode, I’m going to trigger MacGPT to have ChatGPT tell me if Python 3 supports async/await right here inside this post.
Does Python 3 have async/await functionality?+gpt
Yes, starting from Python 3.5, async/await functionality is available in Python. It allows for asynchronous programming, which can greatly improve the performance of certain types of applications. The async/await keywords are used to define asynchronous functions and to wait for the results of asynchronous operations.
Here’s what that looks like in action:
MacGPT allows you to use either a native Mac UI for interacting with ChatGPT or the OpenAI web view for ChatGPT. Both have pros and cons - the OpenAI web view allows you to keep your history available there for access across all platforms, while the native UI looks nicer and (hopefully) in the future will have greater customization. I’d like to see an even wider menubar view, and complete font customization including font, font size, and bubble colors.
Both views allow for copying of conversations, but the native UI copy function copies the entire conversation in the window including both your prompts and ChatGPT’s replies, while the web view only allows you to copy a ChatGPT response and doesn’t include the preceding prompt that you entered.
MacGPT isn’t perfect, but given the low cost of entry (it’s basically donation-ware) and the pace at which Jordi has added updates, I have high hopes for its future.
Machato
I recently stumbled across another Mac ChatGPT app called Machato. Machato takes a different approach than MacGPT in that it just runs as a normal Mac app and doesn’t have menubar, inline, or global (floating textbox) functionality. I miss MacGPT’s menubar aspect when using Machato, but it has some compelling features of its own.
Machato lets you create multiple conversations so you can categorize by topic, and it also allows you to create folders to further organize them. It also has a very handy ability to branch a conversation so that you can pick a point to split off into a new conversation. All you have to do is hover over a prompt or response and click the “Branch from here” icon. It’s super handy.
Machato also lets you pick the output type for copied conversations – Markdown, LaTeX, or plain text.
And finally, Machato keeps track of your usage costs per day, week, month, or all time. I would love to see MacGPT steal this feature.
I’m kind of torn between MacGPT and Machato, because Machato has the superior UI and functionality for serious “help me with my programming” use, for example, but MacGPT has the nice always-there presence with its menubar component and the ability to pop that open and closed with a keyboard shortcut.
Petey
Another interesting entry into the ChatGPT space is Petey. Petey is for iOS and (get this) Apple Watch. Yep, you can ask ChatGPT things from your watch. You can share the results to messages or mail on the watch, or copy and paste them by sending the answer to your iPhone with one tap, and copying the answer from there.
On iOS, it’s not quite as interesting to me as the next app in my list, because it doesn’t let you do multiple conversations or look at the history as easily. You can ask questions, get answers (and have them read back to you), and copy and paste them, but that’s about it.
Petey is well-designed, playful, and fun. For more background and information about Petey, you can hear it directly from app developer Hidde van der Ploeg1 on episode 60 of the Launched podcast.
Petey is $4.99 and has an in-app subscription, but if you have your own OpenAI API key, you don’t need to pay for the subscription.
GeePeeTee
GeePeeTee is Jordi Bruin’s iOS companion app to MacGPT, but it has some interesting features that MacGPT doesn’t. The most interesting feature to me is Conversations, similar to the same feature in Machato. My assumption is he plans to bring that to MacGPT at some point, but I haven’t bothered him to ask yet.
GeePeeTee doesn’t have a watchOS app, but it is better for iPhone use than Petey. Either way, you can’t go wrong. GeePeeTee is free on the App Store, but you need an API key.
Summarium
ChatGPT can be an effective tool to learn things and shorten writing or programming time if used wisely. It’s also wildly overhyped and has replaced crypto as the buzzword de jour, but the result is that lots of developers are writing apps against its API. You can probably guess by the fact I own and am talking about them that I like all the above apps (I do). I recommend you give them a try if you’re a Mac and iOS user who has room for ChatGPT in your workflow.
Footnotes
No, I have no clue how to say that, but Clay Daly certainly must. ↩
I inadvertently lied to you. I bragged about using a bash script to get my current external IP address and putting it in my menu bar with One Thing, and about scheduling that to happen on a predetermined frequency by creating a LaunchDaemon plist and registering it with launchctl. But there’s just one problem.
It didn’t actually work.
After a bunch of digging around, I figured out the reason was I was making some extremely basic scripting errors, but I’m glad it happened because along the way I also learned about some changes to how launchctl works in current macOS versions, and also about a program called LaunchControl.
My primary error was that I forgot about how the Unix PATH and execution environment interaction works. Basically in Unix, your PATH variable dictates what directories the shell will look in to find the application or script you are telling it to execute, whether in a script or at the command prompt. I forgot that just because I could run my script at the command prompt and have it work, that didn’t meant that it would execute in the same environment with the same PATH as mine when executed as a LaunchAgent entry, even though the LaunchAgent is in my user directory’s Library/LaunchAgents directory. Therefore it could neither find one-thing (located in /usr/local/bin) in order to run that, nor could it find node.js which one-thing relies on (also in /usr/local/bin).
The solution was to add /usr/local/bin to the PATH variable for the environment that launchd runs my script in.
Very simple.
Along the way though, before I realized my remedial error, I was worried that Ventura was causing permission errors or blocking executing of the script by launchd. As I researched this possible angle, I discovered a program called LaunchControl. LaunchControl describes itself this way:
LaunchControl is a fully-featured launchd GUI allowing you to create, manage and debug system- and user services on your Mac.
As you may remember, launchd handles User Agents (~/Library/LaunchAgents), Global Agents (/Library/LaunchAgents), Global Daemons (/Library/LaunchDaemons). LaunchControl allows easy viewing, execution, and modification of all of these in one location.
It also allows for easy editing of the file being run by the Agent or Daemon, which makes trying things simple.
You don’t need LaunchControl to create and edit Agents and Daemons. You can create and edit plist files manually and many (most?) people do. But LaunchControl makes it easy in the same way that most people would much rather use VSCode or some other context-aware IDE to work on projects than to do it in TextEdit. It’s a powerful tool that makes something that is a chore easier and less error prone, and that seems like a good thing to me.