Manton Reece
About Photos Videos Archive 30 days 90 parks Replies Reading Search Also on Micro.blog
  • AI coding transcript

    Expanding on my short post this morning about AI-assisted coding style, I think the reason I like this iterative approach is that I don’t always know what I want the UI to look like until I build it. UI design is a bit like sculpting, slowly revealing the shape of everything.

    This may also be a difference in style between Claude Code and Codex, where Claude prefers to come up with a plan first, and Codex is fine just running with small instructions and figuring out the details as it goes.

    Here’s a transcript of my interaction with Codex for adding podcast playback to Inkwell for Mac. I’m just including my own text, not all the logging from Codex. Maybe someone will find it interesting! Years from now I can look back and marvel at how primitive this will seem.

    Here’s a screenshot of the current in-progress UI. The feature is not done. I plan to fill in the space on the right of the podcast pane with playback speed, add better download caching, and whatever else makes sense.


    Let's add a new major feature: podcast playback. When requesting entries.json, add mode=extended which will include more JSON data like this example item:

    [JSON clipped]

    Notice the "enclosure" field with "enclosure_url" and "itunes_duration". We'll want to add these to our model.

    Next, we want to add a new player pane at the bottom of the sidebar, below the tableview. For now, it will be hidden by default. If the selected sidebar item has an enclosure, we show the new player pane. Give the new player its own view controller like MBPodcastController which you can wire up as necessary where it needs to be instantiated and called. As a placeholder, just have an orange background and a button centered with an image "play.fill". We'll add more later. Also keep tracking of whether a podcast is playing. If playing, we change the image to "stop.fill". Also if playing, we keep the podcast pane shown below the tableview. If the podcast is not playing, then when the selection changes in the tableview to another row, we hide the player pane.

    -- Worked for 6m 45s --
    Let's update hasEnclosure to actually only return YES for audio enclosures. Rename it hasAudioEnclosure. Let's also keep track of the JSON field:

    "enclosure_type": "audio/mpeg",

    And use enclosure_type == "audio/mpeg" to mean audio podcast enclosures, returning YES there.

    -- Worked for 48s --
    In the podcast pane, change "stop" to "pause.fill". Set these buttons to not have a border. Also add a button to the left and to the right of play/pause for skipping back and forward. Use image names: "30.arrow.trianglehead.counterclockwise" and "30.arrow.trianglehead.clockwise". We also want a custom slider beneath the buttons that we will use to scrub through the episode and show playback progress.
    -- Worked for 1m 43s --
    On the left side of the podcast pane (see mockup where I put a black square) let's add the avatar image for the site. Use rounded corners 5px. If the avatar can't be loaded, just use a gray background. Also let's change the placeholder orange background color to a semi-transparent light gray.
    -- Worked for 1m 38s --
    Not quite right. We still want the buttons centered horizontally and the slider centered. The avatar should be off on its own on the left.
    -- Worked for 31s --
    Let's make the avatar square a little smaller. Also, can we customize the slider so there is no visible thumb?
    -- Worked for 15s --
    It's time to wire up playback. Let's use AVPlayer (AVFoundation) to instantiate the podcast from the enclosure URL. When the user clicks play, we'll play, pause, etc. Update the slider % with the playback progress. Wire up the left rewind button and right forward buttons to go 30 seconds back or forward. When we get to the end of playback, set the playback to paused.
    -- Worked for 1m 14s --
    When the slider changes, we should also scrub through the podcast playback.
    -- Worked for 28s --
    Are you sure the slider is working? Please check again.
    -- Worked for 1m 54s --
    Good. Also can you change background color to use the color named "color_podcast_background"
    -- Worked for 23s --
    Let's keep track of playback position in a new file in Application Support. Call it Podcasts.json. It should be a JSON array with each item having fields for feed ID, enclosure URL, playback_seconds, playback_percent (float 0...1), last played date. We keep this array in memory in the app and when the selection changes, we update it. That way if someone selects a different post and then comes back to the podcast, we can restore the playback position correctly. Set a timer during playback that updates the file on disk every 15 seconds. When we're writing out the new file, sort by last played date with most recent first, then truncate the array so it's only the most recent 50 items, so the array on disk never gets too large.
    -- Worked for 5m --
    This is great. One change: when we show the podcast player, update the slider to the saved position right away, don't wait until the user clicks play.
    -- Worked for 35s --
    Still not right. Do we need to call setNeedsDisplay? Visually the slider is not updating when the podcast pane is shown, until the user clicks play, and then it's correct. We want to visually update the slider right away even while paused.
    -- Worked for 38s --
    Not sure why I said to save feed_id. We mean entry_id so that each podcast episode (blog post) is tracked.
    -- Worked for 47s --
    I figured out the playback restore position problem. After we set the position, updateProgressSliderForCurrentTime is called and resets it back to 0.
    -- Worked for 27s --
    Now let's add the current playback time and the remaining time. We want to use hours:minutes:seconds. Add two text label fields: very small text label on the left directly underneath the slider for current time, and a label on the right for time remaining. Time remaining should have "-" before it. Move the slider up 10px or so to make room.
    -- Worked for 1m 36s --
    Move the new fields up about 5px. Also if there are no hours, leave it off. If the minutes are single digit, leave off the extra 0. In other words we want 1:23 for 1 minute 23 seconds, not 0:01:23 and not 01:23. Apply this formatting change to both current time and time remaining. Also update the time labels live as we're scrubbing through the slider.
    -- Worked for 41s --
    Good but slider still isn't live. Check if slider.continuous is set correctly.
    -- Worked for 16s --
    I think I found the problem. updateProgressSliderForCurrentTime is bailing right away when isScrubbing is true. Let's take a step back and rethink this flow with scrubbing. It's okay if we don't set the AVPlayer position until after scrubbing is finished, but we still need the live time labels update as the user is dragging.
    -- Worked for 33s --
    There's obviously a lag when first starting a podcast as AVPlayer loads the audio file. During this time, let's add a progress spinner to the right of the pane (so it mirrors the avatar icon) and start animating it while the audio file loads, then hide it. We can use player.status == ReadyToPlay to judge when to hide progress.
    -- Worked for 1m 18s --
    Progress spinner is way too big. Make it default small size. Doesn't need to match avatar square size.
    -- Worked for 18s --
    Something is still amiss with live tracking. I've debugged and the action scrubPlaybackPosition is not being called at all. Please double-check this, also check trackingStateChangedHandler and related callbacks.
    -- Worked for 47s --
    We've still got problems and it's our custom cell. Let's implement continueTracking:at:inView and stopTracking for our custom cell. Make sure that those fire the action. Also let's refactor MBPodcastSlider into its own file. It's okay if the cell class is in the file too with MBPodcastSlider.
    -- Worked for 1m 22s --
    → 11:02 AM, Mar 19
    Also on Bluesky
  • Wondering if I use AI-assisted coding differently than a lot of developers. I don’t chat with AI forever to come up with a perfect plan and then have it execute it. I use AI the same way I would code traditionally, iterating quickly with dozens of quick changes. Still feels like programming.

    → 9:32 AM, Mar 19
    Also on Bluesky
  • I’m testing GPT-5.4 via the API. The pricing is interesting. 5.4-mini and 5.4-nano are about 3x as expensive, but they are apparently much better, so I switched one call from 5-mini to 5.4-nano.

    Meanwhile for coding I use 5.4 high with /fast and can’t burn through tokens fast enough. Really good.

    → 8:54 AM, Mar 19
    Also on Bluesky
  • Marco Arment posted to Reddit with details on the first Overcast beta that includes transcripts. Very nicely done. The transcripts UI feels cleaner and less finicky than Apple Podcasts.

    → 10:21 PM, Mar 18
    Also on Bluesky
  • That’s a lot of green Ws. Spurs have won their last 18 of 20 games. 🏀

    A schedule displays basketball game results, teams, scores, and top performers over multiple days.
    → 10:44 AM, Mar 18
    Also on Bluesky
  • Really cool set of upcoming Micro.blog plug-ins from @rscottjones for putting different kinds of travel maps on your blog. I bet this’ll work great for my attempt to visit all Texas state parks.

    → 10:00 AM, Mar 18
    Also on Bluesky
  • Federico Viticci blogs about using Android foldables, thinking about the upcoming iPhone foldable:

    Anyway, what every foldable has also taught me is that it’s just so nice to realize you can open your phone and be taken into a quasi-tablet environment without having to go grab a separate device. This happens to me all the time: I’m doing some research and realize I want to keep my browser next to, say, ChatGPT; without interrupting what I’m doing, I can open the Z Fold and continue the task, now with two apps shown on screen.

    Also I love the name iPhone Duo.

    → 2:00 PM, Mar 17
    Also on Bluesky
  • Like the native Micro.blog client apps, Inkwell for web and macOS are open source. We aren’t great at incorporating contributions into the shipping versions, but it feels right. With AI-assisted development, the code itself is not as valuable as the whole package — design, infrastructure, etc.

    → 1:03 PM, Mar 17
    Also on Bluesky
  • Inkwell 1.0.1 is out for Mac with several bug fixes, especially for dark mode. Choose “Check for Updates” from the application menu to get the latest version.

    If you missed the announcement, I posted a video about Inkwell yesterday.

    → 11:47 AM, Mar 17
    Also on Bluesky
  • Gulf of Mexico. Heading out on a kind of crazy adventure that I’ll blog more about later. As usual with my travel, I’ve devised the most complicated trip possible. Boat, trains, planes!

    Ocean waves stretch out under a partly cloudy sky.
    → 10:31 AM, Mar 17
    Also on Bluesky
  • Inkwell for Mac

    Last week we shipped Inkwell, our new feed reader for Micro.blog. Today I’m releasing a native Mac app for Inkwell.

    You can download Inkwell directly here.

    I also recorded a 3-minute video demo of the Mac app. That’s the best way to understand the app. I’ll be updating documentation and other things soon.

    → 3:02 PM, Mar 16
    Also on Bluesky
  • Wooden rollercoaster at the Kemah Boardwalk. Walking around earlier before dinner.

    → 10:49 PM, Mar 15
    Also on Bluesky
  • They did a great job with the in memoriam this year at the Oscars. Longer and more personal stories. So many incredible people to highlight.

    → 8:02 PM, Mar 15
    Also on Bluesky
  • Inkwell for Mac will ship tomorrow. It’s my first Mac app that is sandboxed. I have to admit it is nice to do things like delete files without being extremely paranoid about deleting the right thing. It can’t do much damage, and Inkwell is well suited anyway for minimal access to the system.

    → 6:00 PM, Mar 15
    Also on Bluesky
  • Looking forward to the Oscars tonight. Managed to see almost all the best picture nominations, and a handful more in other categories. I wonder if the Oscars being later than the Golden Globes, etc. means there are more surprises because it would be “boring” for the same films to win again.

    → 5:11 PM, Mar 15
    Also on Bluesky
  • Cracking up watching the emoji bit on SNL tonight. I lost it at orange square. 🚡

    → 11:38 PM, Mar 14
    Also on Bluesky
  • Parker Ortolani blogs about the Neo:

    The MacBook Neo is the first Mac that is truly, in every sense of the word, a bicycle for the mind. It’s the first Mac that almost anyone can buy and it’s going to unleash a whole new era of creativity because of it.

    → 2:50 PM, Mar 14
    Also on Bluesky
  • Writing the app is easy. Configuring Sparkle to verify updated app signatures… Impossible! 🤪

    → 12:10 PM, Mar 14
    Also on Bluesky
  • Patrick Rhone interviewed for the People and Blogs series:

    …that’s exactly what a blog should be — a reflection of one’s interest and attention over time. A reflection of who one is right now and where they’ve been. Blogs are living things that should grow at the same rate we do.

    → 10:33 AM, Mar 14
    Also on Bluesky
  • Tetragrammaton

    I picked up Rick Rubin’s book a couple years ago. It sat on my bedside table for months and one day I will actually finish it.

    I just recently discovered his podcast Tetragrammaton. There are two episodes with Greg Brockman that are excellent. (Yes, I’m aware Greg gave a bunch of money to Trump and I’m disappointed by it.)

    The full interview is 3 hours long. The first episode has the best insight into Sam Altman’s firing that I’ve heard yet, especially around letting internal drama brew instead of resolving conflicts early. Highly recommend both episodes whether you love or hate AI.

    → 9:04 AM, Mar 14
    Also on Bluesky
Recommendations
  • Jon Hays
  • Daniel Jalkut
  • Brent Simmons
  • Vincent Ritter
  • John Gruber
  • Jean MacDonald
  • Alan Jacobs
  • Molly White
  • Ana Rodrigues
  • Om Malik
  • Adrian Vila
  • Michael Tsai
  • Dave Winer
  • Rebecca Toh
  • Ben Thompson
  • Sven Dahlstrand
  • Austin Kleon
  • Ton Zijlstra
  • Nick Heer
  • Jason Fried

Blogroll as: OPML | JSON

  • RSS
  • JSON Feed
  • Surprise me!
  • Tweets