Files
MeshTerm/meshterm/core/winterminal.py
T
Jean-Pierre MartineauandClaude Opus 5 2d31810801 MeshTerm gets its own window on a Mac, and leaves yours alone
No Mac monospace face carries the Braille Patterns block, so Terminal --
which falls back generously rather than drawing a box -- borrows the
charts and the map's terrain from a proportional face and lands them at
the wrong width. Nothing announces itself as broken; it is just crooked,
and a reader with nothing to compare against never finds out why.

Fixing it means a font, and a font means a profile. The question that
shapes this module is whose terminal it is. MeshTerm's palette is the
16-colour VT set, crude on purpose, and nobody should have to read their
mail in it -- so nothing here touches the default profile or edits their
preferences. It adds a profile named MeshTerm and opens our own window in
it; theirs keeps its translucency, and quitting leaves nothing behind.

That is also the only route that works at all. Terminal rewrites its
preferences from memory when it quits, so a file written while it runs --
and MeshTerm always runs inside it -- is discarded later. Handing Terminal
a .terminal file lets Terminal do the importing.

The profile is generated from _VT_SLOTS rather than shipped, so the window
cannot disagree with the screen it holds. The NSColor and NSFont archives
are pinned to bytes read back off a profile that was installed on a real
Mac and confirmed rendering, not to a reading of the format.

relaunch.py is the half that was never Windows-specific -- own_command,
child_environment and its _PYI trap, the wanted size -- moved out of
winterminal so both terminals share one copy.

Not yet wired into startup: the launch itself wants a Mac to confirm, and
that machine is asleep.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 13:35:29 -04:00

111 lines
4.5 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Reopening the session in Windows Terminal, which is where MeshTerm looks like itself.
MeshTerm is drawn with emoji icons, braille charts and powerline path chips. Whether any
of that reaches the screen is decided by the *terminal*, not by MeshTerm and not really by
the font either: a modern terminal, asked for a character its font lacks, quietly borrows
the glyph from another font on the machine. That is why the app looks right in Windows
Terminal, in VS Code's terminal, and on macOS and Linux — and why it looks right there
even though the font the reader chose usually holds almost none of it. Measured on a
development machine, the everyday choices — Hack Nerd Font, JetBrains Mono, Fira Code,
Source Code Pro — carry no braille at all, and no monospace font anywhere carries emoji.
The classic Windows console does not do that. It draws what its one font holds and boxes
for the rest, so on that host the emoji cannot be shown *at all* — the only two fonts on a
Windows machine with emoji glyphs, Segoe UI Emoji and Segoe UI Symbol, are proportional,
and a console will not take a proportional font. No font choice fixes it, which leaves
exactly one thing that does: a different terminal.
So when MeshTerm finds itself in that console and Windows Terminal is installed — it is on
every Windows 11 machine, and a free install on 10 — it offers to reopen itself there.
One keypress, and the app looks the way it was drawn instead of the way conhost can
manage. Declining falls back to :mod:`meshterm.core.consolefont`, which makes the best of
the console the reader chose to stay in.
"""
from __future__ import annotations
import os
import shutil
import subprocess
import sys
from .relaunch import REOPENED_ENV, child_environment, own_command, wanted_size
__all__ = [
"REOPENED_ENV",
"available",
"child_environment",
"own_command",
"reopen",
]
def available() -> str | None:
"""The path to ``wt.exe``, or ``None`` when Windows Terminal is not installed.
Found on ``PATH``, where Windows puts an execution alias for it under
``%LOCALAPPDATA%/Microsoft/WindowsApps``.
"""
if sys.platform != "win32" or os.environ.get(REOPENED_ENV):
return None
return shutil.which("wt.exe")
def reopen() -> bool:
"""Start this session again in Windows Terminal, in the same directory.
The new window is not a child in any meaningful sense — the caller exits immediately
afterwards and the two never talk — so nothing is waited on and no pipes are held.
Returns:
Whether Windows Terminal was started. ``False`` leaves the caller exactly where it
was, which is why the offer is only ever a detour and never a dead end.
"""
terminal = available()
if terminal is None:
return False
try:
# `--` ends wt's own option parsing, so MeshTerm's flags reach MeshTerm rather
# than being read as Windows Terminal's (verified: `--mock` and `--profile x`
# arrive intact). `-d` keeps the working directory, which relative paths given
# on the command line depend on.
subprocess.Popen( # noqa: S603 - the argv is ours, not the reader's
[
terminal,
"-w",
"new",
"--size",
_wanted_size(),
# Otherwise the tab is titled with the executable's full path, which on a
# downloaded build is a line of Downloads folder. The icon is not ours to
# set: Windows Terminal takes that from a profile, and a bare command line
# is not one, so it gets the generic console glyph.
"--title",
"MeshTerm",
"-d",
os.getcwd(),
"--",
*own_command(),
],
env=child_environment(),
close_fds=True,
)
return True
except OSError:
return False
def _wanted_size() -> str:
"""The window to ask Windows Terminal for, as ``cols,rows``.
Asked for, because without ``-w new`` the session lands as a *tab* in whatever window
happens to be open and inherits its size, and without ``--size`` a new window takes the
reader's global launch size, which is a setting about their shell and not about this
app. The dimensions themselves are
:func:`~meshterm.core.relaunch.wanted_size`'s, shared with every other terminal
MeshTerm reopens itself in; only the spelling is Windows Terminal's.
"""
cols, rows = wanted_size()
return f"{cols},{rows}"