<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gemron Guo</title>
    <description>The latest articles on DEV Community by Gemron Guo (@gemron_guo_d4c2e5a892fb40).</description>
    <link>https://dev.to/gemron_guo_d4c2e5a892fb40</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1979086%2F671d4b09-1372-4a6a-bbfb-086f954df134.jpg</url>
      <title>DEV Community: Gemron Guo</title>
      <link>https://dev.to/gemron_guo_d4c2e5a892fb40</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gemron_guo_d4c2e5a892fb40"/>
    <language>en</language>
    <item>
      <title>One PTY, two screens: choosing who controls terminal size</title>
      <dc:creator>Gemron Guo</dc:creator>
      <pubDate>Mon, 07 Sep 2026 11:56:02 +0000</pubDate>
      <link>https://dev.to/gemron_guo_d4c2e5a892fb40/one-pty-two-screens-choosing-who-controls-terminal-size-3hde</link>
      <guid>https://dev.to/gemron_guo_d4c2e5a892fb40/one-pty-two-screens-choosing-who-controls-terminal-size-3hde</guid>
      <description>&lt;p&gt;A browser terminal can look responsive while showing the wrong grid.&lt;/p&gt;

&lt;p&gt;Imagine a desktop displaying 140 columns and a phone displaying 48. Both clients are attached to the same running command-line application. If each emulator independently resizes itself to its container, the application still receives only one terminal size. Cursor positioning and line wrapping can then disagree between the application and one of its viewers.&lt;/p&gt;

&lt;p&gt;This is a state-ownership problem hiding inside a layout problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate three sizes
&lt;/h2&gt;

&lt;p&gt;There are three quantities worth naming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Container size:&lt;/strong&gt; the pixels available in one browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proposed grid:&lt;/strong&gt; the rows and columns that fit that container at its current font size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accepted grid:&lt;/strong&gt; the rows and columns the host has applied to the shared terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Windows, &lt;a href="https://learn.microsoft.com/en-us/windows/console/resizepseudoconsole" rel="noopener noreferrer"&gt;ResizePseudoConsole&lt;/a&gt; takes the pseudoconsole handle and a character-grid size. It changes the dimensions seen by attached console applications. Meanwhile, &lt;a href="https://xtermjs.org/docs/api/terminal/classes/terminal/#resize" rel="noopener noreferrer"&gt;xterm.js resize&lt;/a&gt; changes a client emulator's grid.&lt;/p&gt;

&lt;p&gt;Calling the browser method does not, by itself, coordinate the host or the other browser.&lt;/p&gt;

&lt;p&gt;A useful invariant is: every viewer of the same terminal should render the host's accepted grid. A narrower container can scroll horizontally. It should not silently invent a different number of columns for the same live output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose an ownership policy
&lt;/h2&gt;

&lt;p&gt;Several policies are reasonable, with different costs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fixed desktop grid&lt;/td&gt;
&lt;td&gt;Stable desktop layout&lt;/td&gt;
&lt;td&gt;Phones need horizontal scrolling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smallest attached viewport&lt;/td&gt;
&lt;td&gt;Content fits every viewer&lt;/td&gt;
&lt;td&gt;One passive phone can shrink everyone's terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active viewer owns the grid&lt;/td&gt;
&lt;td&gt;Layout follows the person interacting&lt;/td&gt;
&lt;td&gt;Changing devices changes the grid for every viewer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The third policy needs an explicit distinction between &lt;strong&gt;reporting a size&lt;/strong&gt; and &lt;strong&gt;claiming control&lt;/strong&gt;. A background window opening a sidebar is a size report. An intentional interaction with the terminal can be a claim.&lt;/p&gt;

&lt;p&gt;Do not make every resize observer callback a claim. Otherwise, background layout changes can take ownership back immediately after the user switches devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small state machine
&lt;/h2&gt;

&lt;p&gt;The core decision can be expressed separately from networking and rendering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ViewportState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Grid&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mayResize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;viewer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is illustrative policy code, not a complete terminal server. The surrounding handler needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Authenticate the connection and validate positive integer dimensions within supported limits.&lt;/li&gt;
&lt;li&gt;Serialize ownership and resize decisions for each terminal.&lt;/li&gt;
&lt;li&gt;Reject passive reports from viewers that do not own it.&lt;/li&gt;
&lt;li&gt;Apply the requested grid to the host terminal.&lt;/li&gt;
&lt;li&gt;After success, commit the accepted state and broadcast it to every viewer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Clients then resize their emulators from that accepted event. Keeping a local proposal separate avoids treating an unacknowledged request as an accomplished resize.&lt;/p&gt;

&lt;p&gt;An owner change can matter even if the dimensions stay identical. Deduplicating messages only by rows and columns would lose that handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disconnects and failure cases
&lt;/h2&gt;

&lt;p&gt;When an owner disconnects, clearing ownership while retaining the last accepted grid avoids a surprise resize for remaining readers. The next intentional interaction can claim ownership. A reconnecting client should obtain the current grid before presenting the session as ready.&lt;/p&gt;

&lt;p&gt;Failed host resizes should leave the previously accepted grid authoritative. Rapid claims from two clients need a defined order. If the protocol uses multiple channels or asynchronous handlers, include a revision number or another ordering mechanism; broadcasting dimensions alone does not establish that order.&lt;/p&gt;

&lt;p&gt;Sharing terminal dimensions also does not solve input ownership. Allowing two clients to type into the same process is a separate product decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checks that reveal the bug
&lt;/h2&gt;

&lt;p&gt;A useful manual exercise is to attach a wide desktop and a narrow phone, then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interact on the phone and confirm both emulators adopt the same accepted dimensions.&lt;/li&gt;
&lt;li&gt;Resize a passive desktop panel and confirm it does not steal ownership.&lt;/li&gt;
&lt;li&gt;Switch control back to the desktop while a full-screen terminal application is open.&lt;/li&gt;
&lt;li&gt;Disconnect the active viewer and check that the remaining screen keeps its grid.&lt;/li&gt;
&lt;li&gt;Simulate a rejected resize and verify that clients keep the last accepted state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are suggested checks, not a report of a new device test run. They target the boundary between local layout and shared state, where ordinary single-window testing misses the problem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI disclosure: This article was generated by an AI agent from the author's terminal-workbench implementation notes, with technical claims checked against the relevant source code and the API documentation linked above. The code sample is illustrative.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Stopped Losing Track of Claude Code and Codex Sessions</title>
      <dc:creator>Gemron Guo</dc:creator>
      <pubDate>Sun, 02 Aug 2026 06:15:53 +0000</pubDate>
      <link>https://dev.to/gemron_guo_d4c2e5a892fb40/how-i-stopped-losing-track-of-claude-code-and-codex-sessions-4egg</link>
      <guid>https://dev.to/gemron_guo_d4c2e5a892fb40/how-i-stopped-losing-track-of-claude-code-and-codex-sessions-4egg</guid>
      <description>&lt;p&gt;Using one coding agent is simple. Using several across multiple projects is where the real mess begins.&lt;/p&gt;

&lt;p&gt;I often had Claude Code and Codex running side by side. One agent was working, another was waiting for approval, and yesterday’s useful session was buried under a different path, branch, or terminal window.&lt;/p&gt;

&lt;p&gt;The agents were capable. My workspace was not.&lt;/p&gt;

&lt;p&gt;That frustration led me to build &lt;strong&gt;Termexo&lt;/strong&gt;, a local-first Windows workbench for coding agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem was not the model
&lt;/h2&gt;

&lt;p&gt;The hardest part was no longer generating code. It was keeping track of context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which agent is still working?&lt;/li&gt;
&lt;li&gt;Which one needs my approval?&lt;/li&gt;
&lt;li&gt;Where did I leave yesterday’s session?&lt;/li&gt;
&lt;li&gt;Which project, path, and branch did that terminal belong to?&lt;/li&gt;
&lt;li&gt;Which model profile was active?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A pile of terminal windows can answer all of those questions—but only if you remember everything yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I wanted instead
&lt;/h2&gt;

&lt;p&gt;I wanted one recoverable workspace where I could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run Claude Code and Codex in real PTY terminals;&lt;/li&gt;
&lt;li&gt;arrange terminals in custom grids;&lt;/li&gt;
&lt;li&gt;see when an agent needs attention;&lt;/li&gt;
&lt;li&gt;search and resume native sessions;&lt;/li&gt;
&lt;li&gt;restore a workspace after restarting the app;&lt;/li&gt;
&lt;li&gt;switch Claude-compatible model profiles without rebuilding environment variables;&lt;/li&gt;
&lt;li&gt;keep API keys in Windows Credential Manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That became Termexo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why keep the native CLI?
&lt;/h2&gt;

&lt;p&gt;Termexo does not replace Claude Code or Codex with a custom chat interface. The real CLI remains visible and usable.&lt;/p&gt;

&lt;p&gt;That matters because the terminal is still the source of truth. Existing commands, hooks, approvals, keyboard shortcuts, and session behavior continue to work. Termexo focuses on the coordination layer around those tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  A workspace should be recoverable
&lt;/h2&gt;

&lt;p&gt;A useful coding-agent session should not disappear just because the app restarted or a terminal was closed.&lt;/p&gt;

&lt;p&gt;Termexo treats terminals, layouts, projects, and native agent sessions as parts of the same workspace. The goal is simple: when you return, you should be able to understand what was happening and continue without rebuilding the entire setup from memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attention should be visible
&lt;/h2&gt;

&lt;p&gt;When several agents are active, I do not want to stare at every terminal. Termexo can surface approval requests and completion events through system notifications and taskbar attention indicators.&lt;/p&gt;

&lt;p&gt;This sounds small, but it changes the workflow. You can focus on one task while another agent works, then switch only when the second task actually needs you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local-first by default
&lt;/h2&gt;

&lt;p&gt;Termexo does not require a Termexo account. Workspaces and settings stay on the machine, and credentials are stored through Windows Credential Manager.&lt;/p&gt;

&lt;p&gt;The project is Windows-only today. It is still evolving, but it already solves the problem that originally pushed me to build it: losing track of several useful coding-agent sessions at once.&lt;/p&gt;

&lt;p&gt;If you regularly run multiple coding agents, I would love to know which part of that workflow wastes the most time for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://www.termexo.com/" rel="noopener noreferrer"&gt;termexo.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/gemron/Termexo" rel="noopener noreferrer"&gt;gemron/Termexo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Product Hunt: &lt;a href="https://www.producthunt.com/products/termexo?launch=termexo" rel="noopener noreferrer"&gt;Termexo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;— 爱玩科技&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
