Repository navigation
Releases: CarlOpenLab/term-fleet
Release list
v0.4.12
The lease's edges: this machine's own shell, a shell nobody holds, a shell that is gone
- 释放控制权 is not offered on a shell of this machine's. The button was drawn
for every shell the window was driving, including the ones running here — and
on those there is nobody to hand the lease to. The release drops it and the hub
hands it to no one, so the only thing the button did was stop the window in
front of the shell from taking the typing of the person sitting at it. It is
drawn for the shell this window drives on another machine, where the lease
goes back to the machine the shell runs on and that machine can type again. The
browser client drew it on the same wrong rule and now draws it on the same
right one. - A shell that has ended keeps its tab, and the tab finally knows it. The hub
forgets a session the moment its process exits — before the exit frame and
before the session list — so no summary ever saysalive=false, and the
desktop tab, which learned liveness from the attach summary, sat over a dead
shell offering 结束 for a process that was gone, with no way to close the tab.
The exit frame is the only place a tab hears it now: 已退出 arrives with its
exit code in the tooltip, the tab dims with the exit's colour on its dot, its
row offers 关闭 where a running shell's row offers 结束, and the band offers
关闭标签 without repeating the sentence. Nothing is sent to a shell that has
ended — no keystrokes, no resize: a resize answeredno_sessionwas a red toast
raised by the act of reading a shell's last lines. - The hub's refusals say who holds the shell, or that nobody does. A frame
that needs the lease was answered "another client controls s1" whatever the
state was, including a shell the same client had just let go — a sentence with
no client to name, which sends the person reading it looking for somebody who is
not there. The hub now tells a free lease from a lost one: the refusal is
nobody holds s1; claim it first when there is nobody to name, and a frame from
a client one handoff behind is told the lease has moved rather than that someone
took it. The browser client's 结束 was the case that mattered: it claimed only
when somebody else held the lease, so on a released shell the close was refused
and 结束 did nothing at all. It claims in every case now, stealing only from a
live holder. - A shell nobody holds is marked read-only, in both clients. A free lease is
not the same fact as a taken one, but it is the same answer at the keyboard,
and the desktop drew nothing for it: no mark on the pane, no band, and a lease
panel that could not be drawn at all. The pane's 只读 chip, the band
(只读:当前无人控制, with the way in beside it) and the panel (当前无人控制这个终端,
取得控制权后你就能继续输入。) are drawn from one answer now, on shells that have
nobody in them as well as shells that have somebody. Rows read 无人控制 where
they used to read 控制中 for a shell no one is driving, their holder comes from
the tab's own lease frames rather than from a broadcast that can be a push
behind, and the tooltip says 控制端 本窗口 for a shell this window drives instead
of denying it. A lease the window has not heard about yet is marked with
nothing at all: the word arrives in a role frame, and a window that read that
silence as 只读 would flash the mark over every shell a person opens.
v0.4.11
Direction C, and a light with one meaning
designs/ui-redesign/ held three directions and C — Editor Native — is the one
that was picked, so both clients are drawn from it now rather than from the
system they happened to share: one violet accent for what is chosen or in
control, paper-flat surfaces at 6/10/14px, pills instead of underlines, and the
same near-black terminal sheet in both appearances. The one piece of chrome that
is not words — the light along a shell somebody else is typing into — now means
one thing only.
- Both clients take the C direction. The accent moves from teal to violet:
0.4.9 had moved the desktop off its violet onto the browser's teal so the two
would match, and the direction that was then chosen is violet, so both are
violet now. Tabs are pills on a raised strip instead of underlines, the
composer is one bordered field that lights around the caret rather than a card
with three controls in it, the key bar is one row of equal keys grouped by
hairlines, and the sidebar, the session inspector and the settings window are
redrawn on the same tokens. Colour means one thing each — accent for chosen,
amber for read-only, red for gone, grey for exited — and where the desktop
drew decoration that carried nothing, it draws nothing. - The terminal's sixteen colours are the app's. The browser client's theme
and the desktop's carry the same numbers now: the cursor and the ANSI magenta
are the accent's violet, the selection is that violet at 28%, and the sheet is
the same#0d0d12in both appearances. A shell should look like it belongs to
the window around it, and ANSI's sixteen are drawn for a dark ground — which is
why the light appearance keeps a dark terminal rather than inverting one. - The light along a taken shell is drawn under one condition, and it reads as
light. It used to come on for any shell another client held, including one on
a machine the person is not sitting at — a light for a process that is
nobody-here's, which is the ordinary case rather than news. It is drawn only
for a shell this machine runs with somebody else on its keyboard, which is
the one thing the words on screen do not say from across the room; a shell on
another machine keeps its 只读 chip, because that is a fact about the pane you
are looking at. The effect is one family of colours instead of a spectrum (the
old cut ran violet, pink, coral and peach around the edge — a second and louder
vocabulary for a fact the pane was already stating in words) and it is a band
with no line anywhere in it, falling off on both sides of the pane's edge, the
way the platform's own agent-takeover glow is drawn, rather than a coloured
border painted on the pane. Where the desktop asks for less motion the colours
stop travelling and the light stays. - The takeover offer says what pressing it does to the other side. The panel
under a shell that is not yours names the holder, says that your typing is not
reaching the shell, and now says what happens to them: they go read-only and
keep watching the terminal afterwards. A button that takes a keyboard away
from somebody without saying so is one people learn not to press.
Fixed
- 释放控制权 was offered by a window that held nothing. The browser client
setshiddenon the button, andhiddenis a baredisplay: nonefrom the
browser's own stylesheet, which loses to any class that sets a display — and
every control in that client does. The button sat in the header of a window
with no session at all. The per-class workarounds the stylesheet had
accumulated (.tf-hero[hidden],.tf-inspect[hidden], …) are one[hidden]
rule now, so the next control that hides itself stays hidden. - The browser client's status line went stale after the session list changed,
counting sessions the sidebar no longer showed, and the session inspector could
not be closed — the button that dismissed it reopened it on the next render.
Both follow the person now: the count follows the roster, and whether the
inspector is open is a choice that is remembered while the page is.
v0.4.10
Four separate acts, and the ones that were missing
A client watching a shell could take it over but never give it back, and the
button that said 关闭标签页 ended the process instead. Both are fixed, and the
four things a client can do to a shell are now four buttons on both clients.
- 释放控制权 exists. The release frame was in the protocol and no client
ever sent it. A client holding the lease is offered 释放 in the header and in
the banner; pressing it gives the lease up, leaves the process running and
leaves the tab watching it. The hub hands the lease to the machine the shell
runs on, so the shell is not left mute. - The hub was throwing a release away.
handleReleasedropped the lease and
then askedgiveAwayto hand it to the machine's own window — which is the
client that had just released it, becausedrophad just added it to the
watchers. A release from the machine's own window was a no-op, and that
window is the one that releases most, since it holds its own shells by
default. - The 会话 panel's × closed the tab and ended the shell. It was wired to the
same act as 结束, so "close this tab" killed a process on a machine somewhere
— for every client watching it, not only the window that clicked. It closes
the tab now, and 结束 has its own button on the row of the shell it ends. - A tab whose shell exited is no longer removed. The tab is where the
shell's last output is read, so ending a session is not a reason to lose it.
The browser client already behaved this way; the desktop app's reconcile did
not. - The desktop app catches up with the browser client's layout. It gains the
tab strip, the command composer with its history bar, and the 280px session
inspector, and its sidebar and terminal take the same measurements the
browser client uses. Without a composer the window had no way to send Esc,
Tab, Ctrl or an arrow key at all, because a terminal is a display surface and
cannot produce them. - The app icon is transparent.
resources/icon.pnghad an opaque dark plate
composited behind the logo, which is what put a dark square in the macOS Dock
and the Windows taskbar. Every icon is rendered from the one transparent
source now, and a test reads the pixels so it cannot come back. - The release pipeline builds the frontend. The
clijob never ran
build:web, so every CLI artifact published so far shipped without the
browser client embedded —tfdanswered the browser with nothing. - The macOS firewall prompt is explained before it appears. The 本机服务
switch says that the system asks once about incoming connections and that
clicking 允许 settles it for good. A service that failed to start is stated in
the status bar rather than only logged, and the background update check starts
off instead of calling out on every launch.
v0.4.9
One design, two clients
The browser client and the desktop app are drawn from the same palette now. A
shell in a tab and a shell in the native window say the same things in the same
words, with the same colours, at the same sizes: one accent for what is chosen
or in control, amber for read-only, red for gone, grey for exited.
- The desktop app's palette moves off its violet onto the teal the browser
client uses, and its terminal takes the same sixteen ANSI colours, so a shell
belongs to the window around it rather than looking like a program that
wandered in. The header band is 44px and the status line 30px, the heights the
browser client uses, and the status line states the connection as a chip — a
dot in a wash — the way the browser does. - A shell in the sidebar carries its state as a badge — 控制中, 只读, 已退出 —
with the holder's name beside it, and the row's tooltip carries the numbers
the row has no room for. The empty window says what the machine it would open
a shell on is doing, rather than showing an empty rectangle. - The browser client is redrawn on the same system: a cold near-black ground
with hairlines instead of borders, a sidebar whose rows are one badge and one
number, tabs whose underline is their state, and a right-hand session
inspector holding what the daemon said about the shell on screen. A dark and a
light appearance are both complete, and the choice is remembered per device. - The composer gained a strip of the commands this tab has sent — real history,
newest first, one tap back into the box — and says on the same line what the
Enter key will do. The relay's connect screen names the app, says why it is
asking, and reports an empty token next to the field instead of doing nothing. - The phone layout is a drawer and a bigger key bar rather than a second design,
with touch targets at 44px and the terminal kept whole.
Fixed
- The browser client's empty state kept a stale line about a previous machine
after the roster changed, and said nothing at all once the machine had
sessions to show.
v0.4.8
Desktop app
- Fixed a race on the relay client's roster callback. The window hands the fleet
link the place its roster goes, and the link's read loop reads it as frames
arrive: a window opening while the relay was already pushing a roster read and
wrote the same field from two goroutines. It is guarded now, both where it is
set and where it is called, so 设置 → 远程 → 应用 on a live link cannot race an
arriving roster frame.
v0.4.7
Desktop app
- A new terminal opens in this machine's home directory, not wherever the app
happened to be started. Started by the window server — Finder, the Windows
shell — that place is the root of the filesystem, so every new shell opened in
/: a directory nobody chose and everybody leaves again.-cwdstill says
otherwise, a client that asks for a directory keeps what it asked for, and a
shell started on another machine opens in that machine's home, which is the
only home that means anything there. The same applies to terminals started from
a phone or a browser.
v0.4.6
Desktop app
- The lease follows the shell on screen. A window that opened a shell on another
machine and then moved the screen on — to 本机, or to any other shell — used to
leave that machine showing 正在被控制 with a keyboard that no longer worked.
Switching away lets the lease go, the machine whose shell it is takes it back
the way it does when a holder's lease lapses, and coming back to that shell
drives it again without a click. The connection to that machine stays: it is
the lease that moves, not the link. - Picking a machine in the 机器 panel brings one of its shells on screen when this
window has one open there, so 切到本机 means the screen as well as the panels:
two panes and a pane that disagree about what is being worked on is one answer
too many. - The hub hands a lease nobody holds — released, let go by a detach, or simply
lapsed — to the machine's own window when it is watching. A shell of a machine's
is that machine's to type in, so a client that stops driving it cannot leave it
mute with nothing on screen to say why.
v0.4.5
Desktop app
- Control belongs to the window that reached the machine. Opening a shell another
machine was running left this window read-only — a spectator of a shell it had
just asked to work in — with the glow and 拿回控制权 drawn on the controlling
machine while the machine being driven kept the keyboard. A tab on another
machine now takes the lease as it opens: this window types, and the machine
whose shell it is goes read-only behind the glow, saying who is typing into it.
拿回控制权 there is still one click, and the hub always honours it. - 会话 lists the picked machine's shells and nothing else. A tab on another
machine used to be a row in this machine's list — it looked like one of this
machine's shells — and picking it moved the panels to that machine, so
switching tabs landed somewhere else entirely. - The panel under the glow offers 接管 on a shell of another machine's rather than
拿回控制权, which is what taking one is when the lease was never this window's.
v0.4.4
Desktop app
- Fixed the 会话 panel not switching shells. Clicking a shell another machine was
running, when this window already had a tab for it, did nothing at all: the
click was swallowed and the pane stayed where it was, so a machine's shells —
the tabs opened to watch them — could not be walked between, while this
machine's own tabs could. The panel now builds one list either way and picking
a row selects its tab, opening one only when the shell has no tab yet. - The 会话 panel says which shell is on screen, with the same accent bar the
picked machine carries, and picking a shell on another machine moves the 机器
and 会话 panels with it rather than leaving them on a machine that is no longer
on screen. ⌘⇧] and ⌘⇧[ walk this window's tabs, wherever each shell runs.
v0.4.3
Brand
- The project wears the mascot: a periwinkle planet with a cream face, a ring and
a terminal prompt on its brow. It replaces the mark cut out of the logo sheet,
and it is used as drawn rather than recoloured per surface — which is why the
app icon, the README's logo and the browser favicon are now the same picture on
the shell's ink. The mascot's face is cream: on a white tile, or on the
transparent ground a README has on GitHub's light theme, it would run into the
page.resources/brand/make-assets.pyrebuilds all three from the master, and
takes a new render as its argument when the art changes again.
Desktop app
-
The ring around a shell being driven from elsewhere is quieter: a thinner line,
a slower flow and a dimmer bloom than the chat-glow example's composer ring,
which is drawn to be looked at. (The artist's own reference for this state is
still pending — this is the window's own reading of "a signal, not a show".) -
A machine taking its shell back ends the connection, on the machine that was
driving it: the tabs it had open there close, the link to that machine is
dropped, the panels return to this machine, and the status bar says who took
the shell back. Before this, the driving window was simply left read-only, with
a tab on screen that could not type and no word about why. -
The panel under the glow — who holds a shell, and 保持只读 / 拿回控制权 — has a
width of its own and shorter words. It hangs from the top of a pane, and a pane
can be narrower than a sentence: it used to run past the terminal it was about. -
The 机器 panel is a list you switch between rather than a nest of shells. This
machine comes first and is picked by default; picking another machine makes it
the subject of the 会话 panel below and of that panel's 新建标签页, which now
starts a shell on that machine over the relay and opens it here. The relay
itself has no row any more — it is the pipe, not a machine — and its state
moved to the line the header and status bar already carry. -
Reaching the other machines no longer needs a secret typed anywhere. 设置 → 远程
lost the 客户端令牌 field: a window asks its relay for a short-lived control
token, proving itself with the device credential it already joins the fleet
with, and that is what its client link presents. Picking a machine the window
cannot reach yet asks first, in a dialog, so "this window is about to ask the
relay for reach" is something a person sees and can decline. The fleet's shared
token is unchanged and is now only what a browser or a phone is given:
tfrelay -control-token-ttl(default 15m) bounds what a machine gets, revoking
a machine at the relay takes its credentials away at once, and the machine's own
允许被控制 switch is unchanged.