[{"data":1,"prerenderedAt":435},["ShallowReactive",2],{"navigation":3,"\u002Fblog\u002Fi-couldnt-give-up-ghostty-so-i-put-it-inside-my-app":216,"\u002Fblog\u002Fi-couldnt-give-up-ghostty-so-i-put-it-inside-my-app-surround":431},[4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212],{"title":5,"path":6,"stem":7},"You do not have time to not have tests","\u002Fblog\u002Fyou-do-not-have-time-to-not-have-tests","2.blog\u002F20211217.you-do-not-have-time-to-not-have-tests",{"title":9,"path":10,"stem":11},"Migrate Vue 2 with Vuetify and Jest to Vite and Vitest","\u002Fblog\u002Fmigrate-vue-2-with-vuetify-and-jest-to-vite-and-vitest","2.blog\u002F20220109.migrate-vue-2-with-vuetify-and-jest-to-vite-and-vitest",{"title":13,"path":14,"stem":15},"I am a Dark Matter Developer","\u002Fblog\u002Fi-am-a-dark-matter-developer","2.blog\u002F20220626.i-am-a-dark-matter-developer",{"title":17,"path":18,"stem":19},"Why using Conventional commits is useful","\u002Fblog\u002Fusing-conventional-commits","2.blog\u002F20240623.using-conventional-commits",{"title":21,"path":22,"stem":23},"Why you should make a toolbox repository","\u002Fblog\u002Fwhy-you-should-make-a-toolbox-repository","2.blog\u002F20240630.Why-you-should-make-a-toolbox-repository",{"title":25,"path":26,"stem":27},"Apache Airflow Part 1 - Why and Goals for a near Serverless ELT","\u002Fblog\u002Fapache-airflow-part-1-why-and-goals","2.blog\u002F20240710.apache-airflow-part-1-why-and-goals",{"title":29,"path":30,"stem":31},"Oh My Zsh on your server","\u002Fblog\u002Foh-my-zsh-on-your-server","2.blog\u002F20240711.oh-my-zsh-on-your-server",{"title":33,"path":34,"stem":35},"Fire tablet and YouTube Kids","\u002Fblog\u002Ffire-tablet-and-youtube-kids","2.blog\u002F20240714.fire-tablet-and-youtube-kids",{"title":37,"path":38,"stem":39},"Using Ollama and Continue as a GitHub Copilot Alternative","\u002Fblog\u002Fusing-ollama-and-continue-as-github-copilot-alternative","2.blog\u002F20240723.using-ollama-and-continue-as-github-copilot-alternative",{"title":41,"path":42,"stem":43},"Debugging Local Packages Made Easy with pnpm","\u002Fblog\u002Fdebugging-local-packages-with-pnpm-link","2.blog\u002F20250422.debugging local-packages-with-pnpm-link",{"title":45,"path":46,"stem":47},"Two Weeks with Cloudflare AI and Tools","\u002Fblog\u002Ftwo-weeks-with-cloudflare-ai-and-tools","2.blog\u002F20250509.two-weeks-with-cloudflare-aI-and-tools",{"title":49,"path":50,"stem":51},"Adding Prompts to VS Code - How I Learned to Stop Worrying and Love AI Context","\u002Fblog\u002Fadding-prompts-to-vscode","2.blog\u002F20250528.adding-prompts-to-vscode",{"title":53,"path":54,"stem":55},"My Best Practices","\u002Fblog\u002Fmy-best-practicies","2.blog\u002F20250607.my-best-practicies",{"title":57,"path":58,"stem":59},"Creating my own CLI Tool - Towles Tool","\u002Fblog\u002Ftowles-tool","2.blog\u002F20250607.towles-tool",{"title":61,"path":62,"stem":63},"Software Development Best Practices & ITIL","\u002Fblog\u002Fsoftware-engineering-and-itil-best-practices","2.blog\u002F20250612.software-engineering-and-itil-best-practices",{"title":65,"path":66,"stem":67},"Voice to Text","\u002Fblog\u002Fvoice-to-text","2.blog\u002F20250622.voice-to-text",{"title":69,"path":70,"stem":71},"Setting Up ComfyUI - A Better Alternative to Fooocus","\u002Fblog\u002Fcomfy-ui-setup","2.blog\u002F20250628.comfy-ui-setup",{"title":73,"path":74,"stem":75},"Voice to System","\u002Fblog\u002Fvoice-to-system","2.blog\u002F20250705.voice-to-system",{"title":77,"path":78,"stem":79},"Tips for Claude Code","\u002Fblog\u002Ftips-for-claude-code","2.blog\u002F20250713.tips-for-claude-code",{"title":81,"path":82,"stem":83},"Review That AI Code: Why I Read Every Line Generated Code","\u002Fblog\u002Freview-that-ai-code","2.blog\u002F20250720.review-that-ai-code",{"title":85,"path":86,"stem":87},"My Context Engineering Journey: From Dev Scripts to AI Collaboration","\u002Fblog\u002F20250803-1.my-context-engineering-journey","2.blog\u002F20250803-1.my-context-engineering-journey",{"title":89,"path":90,"stem":91},"Context Engineering at Scale: Enterprise Lessons and the Future of Development","\u002Fblog\u002F20250803-2.context-engineering-at-scale","2.blog\u002F20250803-2.context-engineering-at-scale",{"title":93,"path":94,"stem":95},"Check That Your Tools and Linters Do Not Burn Tokens","\u002Fblog\u002Fcheck-that-your-tools-and-linters-do-not-burn-tokens","2.blog\u002F20250806.check-that-your-tools-and-linters-do-not-burn-tokens",{"title":97,"path":98,"stem":99},"Markdown + AI: The Communication Protocol That Changes Everything","\u002Fblog\u002Fmarkdown-plus-ai-the-communication-protocol-that-changes-everything","2.blog\u002F20250814.markdown-plus-ai-the-communication-protocol-that-changes-everything",{"title":101,"path":102,"stem":103},"Finally: Type-Safe AI in Production (And Why I'm Here For It)","\u002Fblog\u002Ffinally-type-safe-ai-in-production-and-why-im-here-for-it","2.blog\u002F20250819.finally-type-safe-ai-in-production-and-why-im-here-for-it",{"title":105,"path":106,"stem":107},"Dotfiles: Masterpiece or Late Stage Picasso?","\u002Fblog\u002Fdotfiles-masterpiece-or-late-stage-picasso","2.blog\u002F20250822.dotfiles-masterpiece-or-late-stage-picasso",{"title":109,"path":110,"stem":111},"Beyond API Wrappers: Building State-Driven MCP Servers for Long-Horizon Agent Orchestration","\u002Fblog\u002Fbeyond-api-wrappers-mcp-servers","2.blog\u002F20250907.beyond-api-wrappers-mcp-servers",{"title":113,"path":114,"stem":115},"Why Vertical Integration Wins: A Software Engineer's Case for Owning Your Stack","\u002Fblog\u002Fwhy-i-bought-tesla-model-3-vertical-integration","2.blog\u002F20250928.why-i-bought-tesla-model-3-vertical-integration",{"title":117,"path":118,"stem":119},"The Min-Maxer's Trifecta: Building Tools for the Game You Actually Play","\u002Fblog\u002Fmin-maxer-trifecta","2.blog\u002F20251004.min-maxer-trifecta",{"title":121,"path":122,"stem":123},"Read The Source: Learning by Cutting Out The Middleman and RTFM","\u002Fblog\u002Fread-the-source","2.blog\u002F20251010.read-the-source",{"title":125,"path":126,"stem":127},"The Exponential Shift: Why AI Progress Feels Different Now","\u002Fblog\u002Fthe-exponential-shift","2.blog\u002F20251015.the-exponential-shift",{"title":129,"path":130,"stem":131},"Plan Mode for Your Problems, Edit Mode for Claude's","\u002Fblog\u002Fplan-mode-problems-edit-mode-solutions","2.blog\u002F20251019.plan-mode-problems-edit-mode-solutions",{"title":133,"path":134,"stem":135},"AWS Aurora DSQL Looked Perfect Until I Needed the Connection String","\u002Fblog\u002Faws-aurora-dsql-postgres-serverless-authentication","2.blog\u002F20251028.aws-aurora-dsql-postgres-serverless-authentication",{"title":137,"path":138,"stem":139},"Switchback: Browser History for Your Thoughts","\u002Fblog\u002Fswitchback-second-order-reasoning","2.blog\u002F20251205.switchback-second-order-reasoning",{"title":141,"path":142,"stem":143},"AI Pairing: Notes to Self","\u002Fblog\u002Fai-pairing-notes-to-self","2.blog\u002F20251216.ai-pairing-notes-to-self",{"title":145,"path":146,"stem":147},"I've Been Sleeping on Zellij","\u002Fblog\u002Fsleeping-on-zellij","2.blog\u002F20251229.sleeping-on-zellij",{"title":149,"path":150,"stem":151},"Implementing a Ralph Wiggum Loop: The Secret is Session Markers","\u002Fblog\u002Fimplementing-ralph-wiggum-loop-for-autonomous-ai-coding","2.blog\u002F20260114.implementing-ralph-wiggum-loop-for-autonomous-ai-coding",{"title":153,"path":154,"stem":155},"Goodhart's Law Ate My Context Window","\u002Fblog\u002Fgoodharts-law-ate-my-context-window","2.blog\u002F20260119.goodharts-law-ate-my-context-window",{"title":157,"path":158,"stem":159},"Claude Code's Hidden Multi-Agent System Is Real","\u002Fblog\u002Fclaude-code-hidden-multi-agent-system","2.blog\u002F20260124.claude-code-hidden-multi-agent-system",{"title":161,"path":162,"stem":163},"Free Printable Math Sheets for Kids — Number Chart, Skip Counting, Multiplication, and More","\u002Fblog\u002Ffree-printable-number-chart-and-coin-sheets","2.blog\u002F20260214.free-printable-number-chart-and-coin-sheets",{"title":165,"path":166,"stem":167},"We Are Near the End of the Exponential","\u002Fblog\u002Fnear-the-end-of-the-exponential","2.blog\u002F20260214.near-the-end-of-the-exponential",{"title":169,"path":170,"stem":171},"Free Printable Language Arts Sheets for Kids — Sight Words, Parts of Speech, Homophones, and More","\u002Fblog\u002Ffree-printable-sight-words-and-grammar-sheets","2.blog\u002F20260215.free-printable-sight-words-and-grammar-sheets",{"title":173,"path":174,"stem":175},"Interactive Code Execution with Artifacts","\u002Fblog\u002Finteractive-code-execution-with-artifacts","2.blog\u002F20260215.interactive-code-execution-with-artifacts",{"title":177,"path":178,"stem":179},"Free Printable Telling Time Worksheet for Kids — Clock Reference & Practice Sheet","\u002Fblog\u002Ffree-printable-telling-time-worksheet","2.blog\u002F20260216.free-printable-telling-time-worksheet",{"title":181,"path":182,"stem":183},"Claude Code Skills: Teaching AI Your Playbook","\u002Fblog\u002Fclaude-code-skills-guide","2.blog\u002F20260221.claude-code-skills-guide",{"title":185,"path":186,"stem":187},"Building a Multi-Agent Loan Approval System with Human-in-the-Loop","\u002Fblog\u002Fmulti-agent-loan-approval-human-in-the-loop","2.blog\u002F20260225.multi-agent-loan-approval-human-in-the-loop",{"title":189,"path":190,"stem":191},"The Inception of AI Infrastructure: Bottlenecks All the Way Down","\u002Fblog\u002Fbiggest-bottleneck-scaling-ai-compute","2.blog\u002F20260313.biggest-bottleneck-scaling-ai-compute",{"title":193,"path":194,"stem":195},"What I Tell Teams About Claude Code","\u002Fblog\u002Fwhat-i-tell-teams-about-claude-code","2.blog\u002F20260314.what-i-tell-teams-about-claude-code",{"title":197,"path":198,"stem":199},"The Hardest Part of AI Isn't the AI","\u002Fblog\u002Fthe-hardest-part-of-ai-isnt-the-ai","2.blog\u002F20260327.the-hardest-part-of-ai-isnt-the-ai",{"title":201,"path":202,"stem":203},"Claude Code Hooks: The Capability I Left on the Table","\u002Fblog\u002Fclaude-code-hooks-capability-left-on-the-table","2.blog\u002F20260401.claude-code-hooks-capability-left-on-the-table",{"title":205,"path":206,"stem":207},"Claude in a Box: Trying Computer-Use on My Laptop","\u002Fblog\u002Fclaude-computer-use-in-a-box","2.blog\u002F20260418.claude-computer-use-in-a-box",{"title":209,"path":210,"stem":211},"MCP Apps: We Spent $100B on AI to Bring Back the Iframe","\u002Fblog\u002Fmcp-ext-apps-claude-desktop-developer-mode","2.blog\u002F20260505.mcp-ext-apps-claude-desktop-developer-mode",{"title":213,"path":214,"stem":215},"I Couldn't Give Up Ghostty, So I Put It Inside My App","\u002Fblog\u002Fi-couldnt-give-up-ghostty-so-i-put-it-inside-my-app","2.blog\u002F20260710.i-couldnt-give-up-ghostty-so-i-put-it-inside-my-app",{"id":217,"title":213,"authors":218,"badge":224,"body":226,"date":420,"description":421,"extension":422,"image":423,"meta":426,"navigation":427,"path":214,"seo":428,"status":429,"stem":215,"__hash__":430},"posts\u002F2.blog\u002F20260710.i-couldnt-give-up-ghostty-so-i-put-it-inside-my-app.md",[219],{"name":220,"to":221,"avatar":222},"Chris Towles","https:\u002F\u002Ftwitter.com\u002FChris_Towles",{"src":223},"\u002Fimages\u002Fctowles-profile-512x512.png",{"label":225},"Architecture",{"type":227,"value":228,"toc":416},"minimark",[229,233,261,270,277,331,334,341,353,359,362,365,380,386,398,413],[230,231,232],"p",{},"Confession first: I built myself a dev cockpit and then didn't use it.",[230,234,235,236,243,244,248,249,254,255,260],{},"My daily setup is Bun + tmux + ",[237,238,242],"a",{"href":239,"rel":240},"https:\u002F\u002Fghostty.org",[241],"nofollow","Ghostty",": my ",[245,246,247],"code",{},"tt"," CLI (",[237,250,253],{"href":251,"rel":252},"https:\u002F\u002Fgithub.com\u002FChrisTowles\u002Ftowles-tool",[241],"towles-tool",") driving a tmux grid, one Claude Code session per pane. I've been rewriting it as a Rust + Tauri app (",[237,256,259],{"href":257,"rel":258},"https:\u002F\u002Fgithub.com\u002FChrisTowles\u002Ftowles-tool-rs",[241],"towles-tool-rs",") with embedded xterm.js terminals. Every time I tried to switch, I lasted a few minutes and went back. The app was fine. The terminals weren't. I couldn't give up Ghostty.",[230,262,263,264,269],{},"So when ",[237,265,268],{"href":266,"rel":267},"https:\u002F\u002Fmitchellh.com\u002Fwriting\u002Flibghostty-is-coming",[241],"Mitchell Hashimoto announced libghostty",", extracting Ghostty's terminal core into embeddable libraries, the fix was obvious: put Ghostty inside the app.",[230,271,272],{},[273,274],"img",{"alt":275,"src":276},"The towles-tool-rs dashboard mid-migration: sidebar of repos and agent slots, an active Claude Code session, and a branch diff panel all open at once","\u002Fimages\u002Fblog\u002F20260710-ghostty-terminal-cockpit.png",[278,279,280,295],"table",{},[281,282,283],"thead",{},[284,285,286,289,292],"tr",{},[287,288],"th",{},[287,290,291],{},"Old",[287,293,294],{},"New",[296,297,298,310,321],"tbody",{},[284,299,300,304,307],{},[301,302,303],"td",{},"Runtime",[301,305,306],{},"Bun\u002FTypeScript CLI",[301,308,309],{},"Rust",[284,311,312,315,318],{},[301,313,314],{},"Windowing",[301,316,317],{},"tmux",[301,319,320],{},"Tauri app",[284,322,323,326,328],{},[301,324,325],{},"Terminal",[301,327,242],{},[301,329,330],{},"libghostty (Ghostty's core, embedded)",[230,332,333],{},"The catch: libghostty has never been released. No version tag, no artifacts. You build it from the Ghostty repo, and the C API breaks weekly. But the core is the same code that's run the Ghostty app for years, so pin your commit and it's fine.",[230,335,336,337,340],{},"What you get in ",[245,338,339],{},"libghostty-vt",": the SIMD VT parser and the full terminal state machine (scrollback, styles, Kitty graphics, reflow on resize) plus a render-state API for writing your own renderer. No PTY, no renderer, no font shaping. You bring those.",[230,342,343,344,346,347,352],{},"My setup now: each terminal is a Rust thread owning a libghostty-vt ",[245,345,325],{}," (via the ",[237,348,351],{"href":349,"rel":350},"https:\u002F\u002Fgithub.com\u002FUzaaft\u002Flibghostty-rs",[241],"libghostty-rs"," bindings). PTY bytes in, dirty-row frames out as JSON, and the WebView just paints them on a canvas. With xterm.js all the parsing and state lived in the JS heap and died on every reload. Now it lives in Rust: reload the WebView and your shells survive.",[230,354,355],{},[273,356],{"alt":357,"src":358},"lazygit running full-width in the app, rendered from libghostty frames. The commits panel is showing this migration's own commits.","\u002Fimages\u002Fblog\u002F20260710-ghostty-terminal-lazygit.png",[230,360,361],{},"Numbers from the spike: parsing ~418 MB\u002Fs, full 200×50 render pass ~190µs. The terminal state layer is just not a cost anymore.",[230,363,364],{},"The costs were real too. I had to write a canvas renderer (a few hundred lines) and keyboard encoding (~150 lines, IME is the usual trap), and Zig joined my toolchain since the crate builds libghostty from source.",[230,366,367,368,373,374,379],{},"The whole migration took a couple hours with ",[237,369,372],{"href":370,"rel":371},"https:\u002F\u002Fclaude.com\u002Fclaude-code",[241],"Claude Code"," on Fable, and used 5% of my weekly quota. I expected it to eat the week. A few of the prompts driving it came straight from ",[237,375,378],{"href":376,"rel":377},"https:\u002F\u002Fx.com\u002Ftrq212\u002Fstatus\u002F2073100352921215386",[241],"Thariq's field guide",".",[230,381,382],{},[273,383],{"alt":384,"src":385},"Claude Code's \u002Fusage screen running inside the libghostty-backed terminal: current week (Fable) 5% used","\u002Fimages\u002Fblog\u002F20260710-ghostty-terminal-usage.png",[230,387,388,389,397],{},"After it shipped I had Fable generate a report on the change with a quiz I had to pass, becuase \"the AI did it in a day\" is worthless if I can't defend it in review. Another one of his prompts — I liked it enough it's now a ",[237,390,393,396],{"href":391,"rel":392},"https:\u002F\u002Fgithub.com\u002FChrisTowles\u002Ftowles-tool-rs\u002Fblob\u002Fmain\u002Fpackages\u002Fcore\u002Fcommands\u002F21-comprehend.md",[241],[245,394,395],{},"\u002Fcomprehend"," command"," in towles-tool-rs, my Claude Code plugin.",[399,400,402],"callout",{"icon":401},"i-lucide-graduation-cap",[230,403,404,412],{},[405,406,407],"strong",{},[237,408,411],{"href":409,"target":410},"\u002Fdownloads\u002Freports\u002Flibghostty-vt-comprehension.html","\\_blank","Read the comprehension report →"," with an interactive quiz. See if you can pass it.",[230,414,415],{},"If you've got embedded terminals and have been waiting on libghostty: it's real, it works today. Just pin your commit.",{"title":417,"searchDepth":418,"depth":418,"links":419},"",2,[],"2026-07-10","I couldn't give up Ghostty's terminal, so I embedded its unreleased terminal core in my Rust + Tauri app instead.","md",{"src":424,"alt":425},"\u002Fimages\u002Fblog\u002F20260710-ghostty-dev-cockpit.png","A pilot cockpit built from curved terminal panels glowing amber, wrapping around an empty seat in a dark hangar",{},true,{"title":213,"description":421},"published","4941k34Fp05TF336yAPFSdMBMP11l_C5OhEcc2kKuPE",[432,434],{"title":209,"path":210,"stem":211,"description":433,"status":429,"children":-1},"Karpathy is teaching the world how transformers work. Boris is shipping Claude Code. The frontier of AI just gave us its frontend story, and it's an iframe inside a chat. I built an MCP server to make sure I wasn't hallucinating.",null,1783879486803]