Manton Reece
About Photos Videos Archive 30 days 90 parks Replies Reading Search Also on Micro.blog
  • Apple’s Mac app notarization is throwing errors for me today, maybe networking problems, so I guess it’s time to take a break from work.

    → 1:32 PM, Mar 22
    Also on Bluesky
  • Loops adds starter kits. I’ve been poking around the Loops API to see if I should support this in the way we support Bluesky starter packs. Probably will wait to see what Mastodon does.

    → 10:24 AM, Mar 22
    Also on Bluesky
  • Trying to be better about cleaning up unused Git branches in a new project, before they get out of control. Micro.blog has hundreds of stale branches because I haven’t taken the time to sort out if there are a few I might want to keep, just in case.

    → 8:42 AM, Mar 22
    Also on Bluesky
  • NetNewsWire via AppleScript via MCP… I wonder what the future of scriptability is. We’ve got AppleScript, Shortcuts, App Intents, and MCP. But meanwhile you have agents which are fine just firing up command-line tools.

    → 5:41 PM, Mar 21
    Also on Bluesky
  • Finished reading: To Shape a Dragon’s Breath by Moniquill Blackgoose. Starts with a girl discovering a dragon egg, but really a story about indigenous people, race, class, and colonialism. 📚

    → 2:01 PM, Mar 21
    Also on Bluesky
  • We are pretty much done clearing out my mom’s house. I’ve found many little memories, keepsakes, books, and other things. One surprise was this letter my mom wrote from Greece in 1980. Preserving it here on my blog as a snapshot in time.

    → 10:42 AM, Mar 21
    Also on Bluesky
  • I blogged briefly last year about feature creep when there is no cost to adding features with AI. Need to keep reminding myself of this. I’ve been adding podcast support to Inkwell, but it’s not intended to be a podcast player that can do everything… Just the right set of features.

    → 8:31 AM, Mar 21
    Also on Bluesky
  • Nick Heer in a fediverse post:

    Do I trust this computer?, my iPhone asks me for what has to be the thousandth time using the same computer logged into the same Apple ID. Enter your passcode, it demands once again, reflecting the hardware, software, and services working together in a way only Apple can deliver.

    Another one is charging my Apple Watch via the Mac and needing to confirm if I trust this accessory. Too secure! Only Apple could do this.

    → 1:00 PM, Mar 20
    Also on Bluesky
  • This is my new favorite article from The Onion. Southwest Airlines Begins Assigning Chores:

    For instance, everyone in row 18 might now be in charge of trash on this flight, while those in the fire exit rows will push the beverage cart. Chore assignments will be clearly printed on all boarding passes for ticketed travelers 2 years old and above. Those flying standby may refer to the chore wheels posted at fore and aft of the plane. Of course, you will have the option to pay extra if you want a more comfortable job.

    → 12:00 PM, Mar 20
    Also on Bluesky
  • It’s 2026 and AppKit is the best way to build Mac apps. Even a part-time luddite like me is surprised by this. SwiftUI is great for new programmers who don’t want to embrace AI-assisted coding, but old school developers should not feel any guilt sticking with AppKit. It’s still the gold standard.

    → 10:36 AM, Mar 20
    Also on Bluesky
  • Amazon is working on a new phone:

    According to people familiar with the new phone, “integrating artificial intelligence capabilities into the device” has been a central focus, which could mean “Transformer” may rely on mini apps like those available in ChatGPT, rather than a fully-fledged app store.

    This might work. Alexa and mini apps could be the core UI, while still allowing Android apps and the web where needed.

    → 10:19 AM, Mar 20
    Also on Bluesky
  • OpenAI is planning to unify its apps into a single app. I don’t love this. Codex is excellent on its own, and Atlas already feels cluttered. From the WSJ:

    OpenAI is planning to unify its ChatGPT app, coding platform Codex and browser into a desktop “superapp,” a step to simplify the user experience and continue with efforts to focus on engineering and business customers.

    Chief of Applications Fidji Simo will oversee the change and focus on helping the company’s sales team market the new product.

    → 8:35 AM, Mar 20
    Also on Bluesky
  • My NCAA bracket could’ve started off better, but caught the last couple minutes of Spurs/Suns, so good. Officially back in the playoffs. 🏀

    → 10:37 PM, Mar 19
    Also on Bluesky
  • Just realized today that we hadn’t released the Epilogue update for Android that improves book search. It has been done for a while. Finally out now! Lots of apps and versions to juggle right now.

    → 8:00 PM, Mar 19
    Also on Bluesky
  • Just released Inkwell for Mac 1.0.2, improving a few things and fixing bugs, especially a potential hang on launch. There’s also a help page with full release notes for each version.

    → 3:17 PM, Mar 19
    Also on Bluesky
  • 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
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