Public Access
Give the phone the second design, and both heads the palette it arrives with
The Android v2 design is what this head draws now: four destinations in a bottom bar — Hosts, Terminal, Keychain, More — with snippets, SFTP, S3, logs and preferences one tap deeper behind the last. The first design's four had nothing behind them, which is what made a hub worth building. The palette moved from green-black to blue-black, and it moved in the shared project because that is where it lives and the desktop v2 specifies the same seventeen tokens. One colour changed meaning rather than value, and it is the only semantic change in the file. Green used to *be* the accent, so Ellipse.dot.live filled with Accent and "the thing to press" and "a shell is open on this host" were the same colour by construction. v2 makes the accent blue and keeps a green for status alone, which finally separates them: Live is that green and nothing merely interactive may use it. The accent is also two colours now — Accent fills, AccentText writes — because a row of chips in the fill colour is a row of things that all look like the primary action. A palette is not one file, which is the part worth knowing before the next one. Nine hex literals lived outside it: the nav bar's own label colours, the accessory keys and their Ctrl-latched state, two scrims, the window background Android paints before Avalonia has a frame, and the launcher vector. The two C# sites now resolve from the dictionary by name rather than restating it. The renderer's page cannot — it is served to a WebView over a loopback socket — so terminal.css and terminal.js keep hand-copied values and say so at both sites. ShellScreen gained More and Buckets, appended rather than slotted in. SFTP and S3 are one screen over one TransfersViewModel differing only in which picker they offer, and the kind is set by the button that navigates rather than on arrival — doing it in OnScreenChanged made every arrival at Transfers force the picker back to hosts, including the desktop's own rail arriving at a screen with a bucket already open. It refuses to change kind while a session is live, because there is one session behind both destinations and switching under it would title a screen S3 while it listed an SFTP host. What the design draws and this does not, on the usual grounds. The FORWARDING screen: nothing here forwards anything, so every toggle would be a control with no effect — it is a paragraph on the hub naming the absence, for the reason the desktop keeps TEAMS in its rail. The terminal's `23 ms · fwd 5432`. An ED25519 badge and a SHA256 line on keychain cards, which need an algorithm field and a fingerprint the item type does not have. An `agent` chip, for an agent that does not exist. Snippet run history and exit codes. The Logs FOLLOW pill, which claims a live tail over records that are written once at close and read when the screen opens, and the severity filter, which has nothing to count — that chip row is spent on the real choice, which of the two logs. S3 bucket totals and lifecycle. And the + on HOSTS, which would open a host editor this head has not got. SFTP is browse, open and delete. Both transfer commands work, and what they work against is the local pane: QueueDownloads writes to Path.Combine(LocalPath, name), and LocalPath starts at SpecialFolder.UserProfile, which on Android is the application's own private directory. A download would have reported success and left the file where the person who asked for it cannot open it, which is worse than not offering it — a refusal is visible and a file in /data/user/0/ is not. The queue is not drawn either, since nothing here can put anything in it. Both return with the document picker. The foreground service still counts zero transfers, and the reason moved rather than went away. Four defects worth naming, because three of them are the kind that compile. A Button as a ListBox ItemTemplate swallows the pointer press before the list sees it, so the files listing selected nothing and every command reading the selection did nothing — the row is a Border now and the phone-only single-tap-to-open is a Tapped handler, which also keeps a desktop single click from walking into directories. Avalonia type selectors are exact, so TextBlock.fingerprint never matched SelectableTextBlock and every fingerprint on this head rendered proportional and unwrapped: that was breaking the never-truncated rule on the host-key sheet already. The new two-level hierarchy had no handler for the system back gesture, so back left the application from a log screen. And the tab's close cross had shrunk to a 30x32 target flush against the select target, which is the one control here that ends a shell with no confirmation and no undo. Fingerprint unlock is raised on arriving at the lock screen rather than waiting for its button, which is still there. Only at launch: a lock the user asked for is not answered with an immediate request to unlock, which makes LOCK look inert and trains the reflex of authenticating at a prompt nobody asked for. And once, because a declined gesture leaves the passphrase box exactly where it was and a prompt that came back after being dismissed would be a modal you cannot get out of to type into it. Two fixes fall on the desktop. Its file listing coloured directories with Info and executables with Accent, which was blue against green and is now two steps of one blue; an executable is Live now. And a bucket's folders were drawn with a 0001-01-01 timestamp, because a prefix has no modification time — blank now, for the reason a directory's size is blank. Verified by the whole suite: 1309 tests over nineteen projects, none failing, including the layout suite that stands up real Avalonia and parses every desktop screen. Both heads build. Not verified on a device — nothing in this head ever has been; see docs/android-port.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZE3u99BNt6LzgTC5jhbz2
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
<!--
|
||||
The palette, named for what a colour is for rather than for what it looks like. Every one of these is
|
||||
from the design; the names are this codebase's, because "#0C0F0E" appearing in nine files is how a
|
||||
from the design; the names are this codebase's, because "#0E1220" appearing in nine files is how a
|
||||
surface ends up two shades off in the tenth.
|
||||
|
||||
It lives here, in the shared project, because there are now two heads drawing the same product. Two
|
||||
@@ -13,62 +13,138 @@
|
||||
Five near-black surfaces rather than one, and the difference between them is real work: the window is
|
||||
the darkest so the terminal reads as the lit thing, chrome sits one step up so the header and status
|
||||
bar frame it, and the lists sit between the two so a list does not look like part of either.
|
||||
|
||||
── v2 ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
This is the second design's palette: blue-black surfaces and a blue accent, replacing the green-black
|
||||
ones the first import shipped. Both heads move together, which is the whole reason the file is here —
|
||||
the Android v2 design and the desktop v2 design specify the same seventeen tokens, so a phone that went
|
||||
blue while the desktop stayed green would be the drift this file exists to prevent.
|
||||
|
||||
Where the design names a colour, the value below is that colour exactly. The design has seventeen
|
||||
tokens and this palette has rather more, so the rest are interpolated along the same ramp and are
|
||||
marked. That is deliberate rather than lazy: the finer distinctions here — three border weights, four
|
||||
text steps — are ones the design draws without naming, and collapsing them to match the token count
|
||||
would lose the rule each one encodes.
|
||||
-->
|
||||
<Color x:Key="CanvasColor">#0A0C0B</Color>
|
||||
|
||||
<!--
|
||||
The surfaces, darkest first. Two things changed shape rather than merely colour.
|
||||
|
||||
A field is now *inset* — darker than the canvas it sits on — where it used to be raised. That follows
|
||||
the design, which draws the search box as a well rather than as a tile, and it is the one place where
|
||||
reading the old palette's ordering across to the new one gives the wrong answer.
|
||||
|
||||
And Chrome and Panel are now the same value, because the design uses one surface for both the header
|
||||
and a card. They are kept as two names anyway: they answer different questions, and the day one of the
|
||||
two moves is the day having merged them would have to be undone across every file that used the wrong
|
||||
one.
|
||||
-->
|
||||
<Color x:Key="CanvasColor">#0E1220</Color>
|
||||
<SolidColorBrush x:Key="Canvas" Color="{StaticResource CanvasColor}" />
|
||||
<SolidColorBrush x:Key="Chrome" Color="#0D100F" />
|
||||
<SolidColorBrush x:Key="Sidebar" Color="#0C0F0E" />
|
||||
<SolidColorBrush x:Key="Panel" Color="#0F1211" />
|
||||
<SolidColorBrush x:Key="Raised" Color="#111514" />
|
||||
<SolidColorBrush x:Key="Field" Color="#121615" />
|
||||
<SolidColorBrush x:Key="Chrome" Color="#111629" />
|
||||
<SolidColorBrush x:Key="Sidebar" Color="#0F1426" />
|
||||
<SolidColorBrush x:Key="Panel" Color="#111629" />
|
||||
<SolidColorBrush x:Key="Raised" Color="#151B31" />
|
||||
<SolidColorBrush x:Key="Field" Color="#0B0F1D" />
|
||||
|
||||
<!--
|
||||
The surface a terminal, a log and a snippet's command are drawn on, and it is not one of the five
|
||||
above. The design gives every block of monospaced output this one value, warmer than the chrome around
|
||||
it, and that is what makes the lit thing read as lit. Named here rather than repeated in the renderer's
|
||||
stylesheet and in two screens, which is how the three of them would drift.
|
||||
-->
|
||||
<SolidColorBrush x:Key="TerminalSurface" Color="#171A26" />
|
||||
|
||||
<!-- Row hover, and the heavier one the chrome's own buttons use. On the phone these are press states. -->
|
||||
<SolidColorBrush x:Key="Hover" Color="#141817" />
|
||||
<SolidColorBrush x:Key="ChromeHover" Color="#1A1F1D" />
|
||||
<SolidColorBrush x:Key="Hover" Color="#151B31" />
|
||||
<SolidColorBrush x:Key="ChromeHover" Color="#1D2749" />
|
||||
|
||||
<!--
|
||||
Selection, and it is a surface rather than a highlight: the design fills a chosen host row, the current
|
||||
segment of a toggle and the current chip with this and adds no border. Distinct from Hover, which is
|
||||
what the finger is on rather than what is chosen.
|
||||
-->
|
||||
<SolidColorBrush x:Key="Active" Color="#1D2749" />
|
||||
|
||||
<!-- The fill behind a small monospaced tag — a group name, a storage class, a bucket. -->
|
||||
<SolidColorBrush x:Key="Chip" Color="#16204A" />
|
||||
|
||||
<!--
|
||||
Three border weights, and they are not interchangeable. Strong separates one region from another,
|
||||
subtle separates rows inside one region, and mid is what a control draws around itself.
|
||||
-->
|
||||
<SolidColorBrush x:Key="Border" Color="#1E2422" />
|
||||
<SolidColorBrush x:Key="BorderSubtle" Color="#171C1A" />
|
||||
<SolidColorBrush x:Key="BorderMid" Color="#2A312E" />
|
||||
<SolidColorBrush x:Key="BorderHover" Color="#3A423E" />
|
||||
<SolidColorBrush x:Key="BorderFaint" Color="#232927" />
|
||||
<SolidColorBrush x:Key="Border" Color="#1A2138" />
|
||||
<SolidColorBrush x:Key="BorderSubtle" Color="#151B31" />
|
||||
<SolidColorBrush x:Key="BorderMid" Color="#232B45" />
|
||||
<SolidColorBrush x:Key="BorderHover" Color="#2E3859" />
|
||||
<SolidColorBrush x:Key="BorderFaint" Color="#1F2740" />
|
||||
|
||||
<!--
|
||||
The text ramp. Three steps, used consistently: what you read, what you glance at, and what is there
|
||||
only so its absence would be noticed. A fourth step would be one nobody could tell from its neighbours.
|
||||
-->
|
||||
<SolidColorBrush x:Key="Text" Color="#DCE3DF" />
|
||||
<SolidColorBrush x:Key="TextDim" Color="#7E8A84" />
|
||||
<SolidColorBrush x:Key="TextFaint" Color="#566059" />
|
||||
<SolidColorBrush x:Key="TextGhost" Color="#404743" />
|
||||
<SolidColorBrush x:Key="Text" Color="#E3E7F4" />
|
||||
<SolidColorBrush x:Key="TextDim" Color="#8B93B0" />
|
||||
<SolidColorBrush x:Key="TextFaint" Color="#565E7E" />
|
||||
<SolidColorBrush x:Key="TextGhost" Color="#414A6B" />
|
||||
|
||||
<!--
|
||||
The accent, and the three colours that are allowed to disagree with it. Green means live, connected or
|
||||
yours; amber means a caveat worth reading; red means refused or destructive; blue is for the one thing
|
||||
that is neither — a directory, a distinct scope — and is deliberately rare.
|
||||
The accent, and it is two colours rather than one. The design fills with #5B8CFF — the button, the
|
||||
floating action button, a progress bar, a toggle that is on — and writes with #8FA7FF: the current
|
||||
destination in the bottom bar, a link, the text inside a chip. They are not interchangeable in either
|
||||
direction. The fill is too saturated to read as small text on a dark surface, and the text colour is
|
||||
too pale to carry a button.
|
||||
|
||||
AccentInk is what goes *on* the fill. It equals Canvas today and is named separately anyway, because
|
||||
"the darkest surface" and "the colour of text on a blue button" are two facts that happen to coincide,
|
||||
and a light theme would separate them immediately.
|
||||
-->
|
||||
<Color x:Key="AccentColor">#3CE88F</Color>
|
||||
<Color x:Key="AccentColor">#5B8CFF</Color>
|
||||
<SolidColorBrush x:Key="Accent" Color="{StaticResource AccentColor}" />
|
||||
<SolidColorBrush x:Key="AccentSoft" Color="#3CE88F" Opacity="0.35" />
|
||||
<SolidColorBrush x:Key="AccentWash" Color="#3CE88F" Opacity="0.06" />
|
||||
<SolidColorBrush x:Key="Warn" Color="#E8B44C" />
|
||||
<SolidColorBrush x:Key="WarnSoft" Color="#E8B44C" Opacity="0.35" />
|
||||
<SolidColorBrush x:Key="WarnWash" Color="#E8B44C" Opacity="0.06" />
|
||||
<SolidColorBrush x:Key="WarnText" Color="#B9A26B" />
|
||||
<SolidColorBrush x:Key="Danger" Color="#E85D5D" />
|
||||
<SolidColorBrush x:Key="DangerSoft" Color="#E85D5D" Opacity="0.3" />
|
||||
<SolidColorBrush x:Key="DangerWash" Color="#E85D5D" Opacity="0.08" />
|
||||
<SolidColorBrush x:Key="DangerText" Color="#D98A8A" />
|
||||
<SolidColorBrush x:Key="Info" Color="#5DA9E8" />
|
||||
<SolidColorBrush x:Key="AccentText" Color="#8FA7FF" />
|
||||
<SolidColorBrush x:Key="AccentInk" Color="#0E1220" />
|
||||
<SolidColorBrush x:Key="AccentSoft" Color="#5B8CFF" Opacity="0.35" />
|
||||
<SolidColorBrush x:Key="AccentWash" Color="#5B8CFF" Opacity="0.06" />
|
||||
|
||||
<!--
|
||||
The design asks for IBM Plex Mono and IBM Plex Sans. Neither ships with this application, and neither
|
||||
is on a stock Windows install or a stock Android one, so requesting them by name would render as
|
||||
whatever the font fallback chose that day — which is worse than choosing deliberately. Inter is
|
||||
embedded by both heads and is what they already draw with.
|
||||
◆ Green is no longer the accent, and that is the one semantic change in this file rather than a
|
||||
recolour.
|
||||
|
||||
It used to be both: Ellipse.dot.live filled with Accent, so "a shell is open on this host" and "this is
|
||||
the thing the screen wants you to press" were the same colour by construction. The v2 design keeps a
|
||||
green — #3DDC97, for the live dot, the sync light and the follow indicator — and makes the accent blue,
|
||||
which finally separates the two. Live is that green, and nothing that is merely interactive may use it.
|
||||
|
||||
Read the pair as: blue is what you can do, green is what is true.
|
||||
-->
|
||||
<SolidColorBrush x:Key="Live" Color="#3DDC97" />
|
||||
<SolidColorBrush x:Key="LiveWash" Color="#3DDC97" Opacity="0.12" />
|
||||
|
||||
<!--
|
||||
The three colours that are allowed to disagree with the accent. Amber means a caveat worth reading, red
|
||||
means refused or destructive, and blue-light is for the one thing that is neither — an informational log
|
||||
line, a directory, a distinct scope.
|
||||
|
||||
WarnText and DangerText are the prose weights: a wash is a very dark tint, and the signal colour that
|
||||
works as a 6-pixel dot or a one-word label is not the one that reads as a sentence on top of it. The
|
||||
design supplies both reds — #E86A6A labels the refusal and #FF8F8F writes it out — and one amber, so
|
||||
the amber prose weight is interpolated.
|
||||
-->
|
||||
<SolidColorBrush x:Key="Warn" Color="#FFB454" />
|
||||
<SolidColorBrush x:Key="WarnSoft" Color="#FFB454" Opacity="0.35" />
|
||||
<SolidColorBrush x:Key="WarnWash" Color="#FFB454" Opacity="0.12" />
|
||||
<SolidColorBrush x:Key="WarnText" Color="#E4A868" />
|
||||
<SolidColorBrush x:Key="Danger" Color="#E86A6A" />
|
||||
<SolidColorBrush x:Key="DangerSoft" Color="#E86A6A" Opacity="0.3" />
|
||||
<SolidColorBrush x:Key="DangerWash" Color="#E86A6A" Opacity="0.12" />
|
||||
<SolidColorBrush x:Key="DangerText" Color="#FF8F8F" />
|
||||
<SolidColorBrush x:Key="Info" Color="#7FB0FF" />
|
||||
|
||||
<!--
|
||||
The design asks for Instrument Sans and Fira Code, as the one before it asked for IBM Plex. Neither
|
||||
ships with this application, and neither is on a stock Windows install or a stock Android one, so
|
||||
requesting them by name would render as whatever the font fallback chose that day — which is worse than
|
||||
choosing deliberately. Inter is embedded by both heads and is what they already draw with.
|
||||
|
||||
The stack ends in the generic `monospace` rather than a Windows face, which is what makes it work on
|
||||
both: Android has no Cascadia Mono or Consolas and resolves the generic name to its own mono face.
|
||||
|
||||
Reference in New Issue
Block a user