Repaint both heads in the v5 palette and embed its three fonts

This commit is contained in:
2026-08-07 15:25:51 +02:00
parent 49645db680
commit bea0279937
16 changed files with 611 additions and 99 deletions
+47 -20
View File
@@ -110,11 +110,14 @@
<!-- <!--
A chip: a bordered scrap of text stating one fact. The design uses them for tags, roles, scopes and A chip: a bordered scrap of text stating one fact. The design uses them for tags, roles, scopes and
shortcut hints, which have nothing in common except that each is a noun the eye should be able to skip. shortcut hints, which have nothing in common except that each is a noun the eye should be able to skip.
CornerRadius 6 rather than v2's 4: v3 draws every small chip — a tag, a pin badge — at 5-6, distinct
from the 10-12 the cards and fields moved to, so a chip keeps reading as a chip beside either.
--> -->
<Style Selector="Border.chip"> <Style Selector="Border.chip">
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" /> <Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="4" /> <Setter Property="CornerRadius" Value="6" />
<Setter Property="Padding" Value="6,2" /> <Setter Property="Padding" Value="6,2" />
<Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="VerticalAlignment" Value="Center" />
</Style> </Style>
@@ -147,7 +150,7 @@
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" /> <Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="4" /> <Setter Property="CornerRadius" Value="6" />
<Setter Property="Padding" Value="6,2" /> <Setter Property="Padding" Value="6,2" />
<Setter Property="MinHeight" Value="0" /> <Setter Property="MinHeight" Value="0" />
<Setter Property="Foreground" Value="{StaticResource TextDim}" /> <Setter Property="Foreground" Value="{StaticResource TextDim}" />
@@ -185,7 +188,7 @@
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" /> <Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="7" /> <Setter Property="CornerRadius" Value="10" />
<Setter Property="Foreground" Value="{StaticResource TextDim}" /> <Setter Property="Foreground" Value="{StaticResource TextDim}" />
</Style> </Style>
<Style Selector="Button.ghost:pointerover /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="Button.ghost:pointerover /template/ ContentPresenter#PART_ContentPresenter">
@@ -199,25 +202,45 @@
<Setter Property="Foreground" Value="{StaticResource TextFaint}" /> <Setter Property="Foreground" Value="{StaticResource TextFaint}" />
</Style> </Style>
<!--
The primary action, and since v3 filled with a gradient rather than a flat colour: the design draws
every accent button top-to-bottom from a lighter violet into the accent proper, and lifts it off the
surface with a soft violet glow instead of a border. Both live in Palette.axaml as AccentGradient and
AccentGlow, named there rather than here for the reason every other accent value is — see the remark
on the pair in that file.
BoxShadow is set here rather than on the Button itself: this rule already targets the template's
ContentPresenter for Background for the Fluent-override reason explained above, and ContentPresenter
carries its own BoxShadow property in Avalonia 11, so the glow and the fill live on the same element
and move together.
-->
<Style Selector="Button.accent /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="Button.accent /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Accent}" /> <Setter Property="Background" Value="{StaticResource AccentGradient}" />
<Setter Property="BoxShadow" Value="{StaticResource AccentGlow}" />
<Setter Property="BorderThickness" Value="0" /> <Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="7" /> <Setter Property="CornerRadius" Value="10" />
<Setter Property="Foreground" Value="{StaticResource AccentInk}" /> <Setter Property="Foreground" Value="{StaticResource AccentInk}" />
</Style> </Style>
<Style Selector="Button.accent:pointerover /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="Button.accent:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Accent}" /> <Setter Property="Background" Value="{StaticResource AccentGradient}" />
<Setter Property="Opacity" Value="0.85" /> <Setter Property="Opacity" Value="0.85" />
<Setter Property="Foreground" Value="{StaticResource AccentInk}" /> <Setter Property="Foreground" Value="{StaticResource AccentInk}" />
</Style> </Style>
<!-- <!--
A disabled accent button keeps its shape and loses its fill. Fluent's disabled state greys the A disabled accent button keeps its shape and loses its fill. Fluent's disabled state greys the
foreground and leaves the background, which on a green slab is unreadable rather than merely dim. foreground and leaves the background, which on a purple slab is unreadable rather than merely dim.
BoxShadow is cleared here too, and that is new alongside the gradient: the base rule now sets a glow
that reads as "this is lit and wants pressing", which is the opposite of what a disabled control
should say. Left unset it would still apply — the base rule's Setter is still in effect wherever a
more specific one does not override it — so the glow has to be named here, as the empty string
BoxShadows parses to "no shadows", to go away.
--> -->
<Style Selector="Button.accent:disabled /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="Button.accent:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{StaticResource BorderFaint}" /> <Setter Property="BorderBrush" Value="{StaticResource BorderFaint}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="BoxShadow" Value="" />
<Setter Property="Foreground" Value="{StaticResource TextFaint}" /> <Setter Property="Foreground" Value="{StaticResource TextFaint}" />
</Style> </Style>
@@ -225,7 +248,7 @@
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{StaticResource DangerSoft}" /> <Setter Property="BorderBrush" Value="{StaticResource DangerSoft}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="7" /> <Setter Property="CornerRadius" Value="10" />
<Setter Property="Foreground" Value="{StaticResource Danger}" /> <Setter Property="Foreground" Value="{StaticResource Danger}" />
</Style> </Style>
<Style Selector="Button.danger:pointerover /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="Button.danger:pointerover /template/ ContentPresenter#PART_ContentPresenter">
@@ -530,7 +553,7 @@
<Setter Property="Background" Value="{StaticResource Field}" /> <Setter Property="Background" Value="{StaticResource Field}" />
<Setter Property="BorderBrush" Value="{StaticResource Border}" /> <Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="4" /> <Setter Property="CornerRadius" Value="10" />
<Setter Property="Foreground" Value="{StaticResource Text}" /> <Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="SelectionBrush" Value="{StaticResource AccentSoft}" /> <Setter Property="SelectionBrush" Value="{StaticResource AccentSoft}" />
<Setter Property="CaretBrush" Value="{StaticResource Accent}" /> <Setter Property="CaretBrush" Value="{StaticResource Accent}" />
@@ -628,7 +651,7 @@
<Setter Property="Background" Value="{StaticResource Raised}" /> <Setter Property="Background" Value="{StaticResource Raised}" />
<Setter Property="BorderBrush" Value="{StaticResource Border}" /> <Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="10" /> <Setter Property="CornerRadius" Value="12" />
<Setter Property="Padding" Value="12,10" /> <Setter Property="Padding" Value="12,10" />
<Setter Property="Width" Value="232" /> <Setter Property="Width" Value="232" />
<Setter Property="Margin" Value="0,0,10,10" /> <Setter Property="Margin" Value="0,0,10,10" />
@@ -724,14 +747,16 @@
stacked controls is fourteen. stacked controls is fourteen.
Raised on Sidebar, which is the one pairing that works in this column. Panel and Chrome are the same Raised on Sidebar, which is the one pairing that works in this column. Panel and Chrome are the same
value as each other and near enough Sidebar to vanish against it, and Field is the well a control sits value as each other and near enough Sidebar to vanish against it. Field no longer reads as a well
in — a card drawn in it would read as one enormous input. beneath either: v3 gives a field and a card the same fill, where a field used to sit visibly below
one — see the remark on Field in Palette.axaml. What still marks a field as a field here is its
border and its focus state, not a darker surface underneath it.
--> -->
<Style Selector="Border.section"> <Style Selector="Border.section">
<Setter Property="Background" Value="{StaticResource Raised}" /> <Setter Property="Background" Value="{StaticResource Raised}" />
<Setter Property="BorderBrush" Value="{StaticResource Border}" /> <Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="10" /> <Setter Property="CornerRadius" Value="12" />
<Setter Property="Padding" Value="12" /> <Setter Property="Padding" Value="12" />
</Style> </Style>
@@ -831,12 +856,14 @@
decoration that reads as information. decoration that reads as information.
A style rather than a value converter so that the two colours are the palette's, once. A converter A style rather than a value converter so that the two colours are the palette's, once. A converter
would have had to name #3DDC97 in C#, which is how a green ends up one shade off in the fourth place would have had to name the hex value in C#, which is how a green ends up one shade off in the fourth
it appears. place it appears — and it has already moved once, from v2's #3DDC97 to this design's #34D399, without
touching this file.
Live rather than Accent, and that is a v2 change with meaning rather than a recolour. The accent is Live rather than Accent, and that is a v2 change with meaning rather than a recolour, one that survives
blue now, and the green survives as the colour of a fact — see the remark on Live in Palette.axaml. the accent moving again from blue to purple in v3: the green is the colour of a fact regardless of what
Filling this with Accent would say a connected host is a thing to press. hue the accent currently is — see the remark on Live in Palette.axaml. Filling this with Accent would
say a connected host is a thing to press.
--> -->
<Style Selector="Ellipse.dot"> <Style Selector="Ellipse.dot">
<Setter Property="Width" Value="6" /> <Setter Property="Width" Value="6" />
@@ -925,7 +952,7 @@
<Style Selector="ComboBox"> <Style Selector="ComboBox">
<Setter Property="Background" Value="{StaticResource Field}" /> <Setter Property="Background" Value="{StaticResource Field}" />
<Setter Property="BorderBrush" Value="{StaticResource Border}" /> <Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="CornerRadius" Value="4" /> <Setter Property="CornerRadius" Value="10" />
<Setter Property="Foreground" Value="{StaticResource Text}" /> <Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="MinHeight" Value="30" /> <Setter Property="MinHeight" Value="30" />
<Setter Property="FontSize" Value="13" /> <Setter Property="FontSize" Value="13" />
@@ -933,7 +960,7 @@
<Style Selector="NumericUpDown"> <Style Selector="NumericUpDown">
<Setter Property="Background" Value="{StaticResource Field}" /> <Setter Property="Background" Value="{StaticResource Field}" />
<Setter Property="BorderBrush" Value="{StaticResource Border}" /> <Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="CornerRadius" Value="4" /> <Setter Property="CornerRadius" Value="10" />
<Setter Property="Foreground" Value="{StaticResource Text}" /> <Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="MinHeight" Value="30" /> <Setter Property="MinHeight" Value="30" />
<Setter Property="FontSize" Value="13" /> <Setter Property="FontSize" Value="13" />
+15 -7
View File
@@ -93,17 +93,25 @@ internal static class Program
/// <summary>Used by the designer as well as by <see cref="Main"/>.</summary> /// <summary>Used by the designer as well as by <see cref="Main"/>.</summary>
/// <remarks> /// <remarks>
/// <c>WithInterFont</c> registers Inter; it does not make it the default, and until this line the /// <c>WithInterFont</c> still registers Inter, and it is still what the layout suite pins: see
/// application shipped a font it then declined to use — falling back to Segoe UI on Windows and to /// HeadlessApp. What changed is which face answers when nothing more specific is asked for. The v5
/// whatever fontconfig offered on Linux. Almost nothing visible moves, because App.axaml sets /// design specifies Montserrat, and unlike the substitution this comment used to explain, Montserrat
/// <c>MonoFont</c> on essentially every control that draws text, but the fallback behind those is now /// now ships embedded in the shell project rather than being requested by a name the machine may not
/// a font that travels with the build rather than one the machine is assumed to have. The layout /// have — see DodoSSH.Client.Shell/Assets/Fonts and its csproj entry. Inter stays registered and is
/// suite pins the same family, and has to: see HeadlessApp. /// named as a fallback, so a glyph Montserrat does not cover still resolves to a font that travels
/// with the build rather than to whatever the platform's default sans happens to be.
/// </remarks> /// </remarks>
public static AppBuilder BuildAvaloniaApp() => public static AppBuilder BuildAvaloniaApp() =>
AppBuilder.Configure<DodoSshApp>() AppBuilder.Configure<DodoSshApp>()
.UsePlatformDetect() .UsePlatformDetect()
.WithInterFont() .WithInterFont()
.With(new FontManagerOptions { DefaultFamilyName = "avares://Avalonia.Fonts.Inter/Assets#Inter" }) .With(new FontManagerOptions
{
DefaultFamilyName = "avares://DodoSSH.Client.Shell/Assets/Fonts#Montserrat",
FontFallbacks =
[
new FontFallback { FontFamily = new FontFamily("avares://Avalonia.Fonts.Inter/Assets#Inter") },
],
})
.LogToTrace(); .LogToTrace();
} }
@@ -0,0 +1,93 @@
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,93 @@
Copyright 2024 The Montserrat.Git Project Authors (https://github.com/JulietaUla/Montserrat.git)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -68,6 +68,15 @@
--> -->
<AvaloniaResource Include="WebAssets/**" /> <AvaloniaResource Include="WebAssets/**" />
<AvaloniaResource Include="Theme/**" /> <AvaloniaResource Include="Theme/**" />
<!--
Montserrat, JetBrains Mono and Material Icons, embedded rather than requested by name for the same
reason WebAssets is embedded: a font resolved from the machine is whatever that machine happens to
have, and a phone and a desktop asked the same question would not get the same answer. Shared here
so the desktop head's avares://DodoSSH.Client.Shell/Assets/Fonts URIs are the same ones the palette
and Program.cs reference; see Theme/Palette.axaml's MonoFont and IconFont keys.
-->
<AvaloniaResource Include="Assets/Fonts/**" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+133 -67
View File
@@ -25,127 +25,186 @@
marked. That is deliberate rather than lazy: the finer distinctions here — three border weights, four 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 text steps — are ones the design draws without naming, and collapsing them to match the token count
would lose the rule each one encodes. would lose the rule each one encodes.
── v3 ──────────────────────────────────────────────────────────────────────────────────────────────
The third design moves again, and further in the same direction v2 moved in: v1 was green-black, v2
blue-black, and this one near-black — the surfaces are lower-contrast against true black than either
predecessor, and the accent turns purple, sitting where the blue used to. Both heads still move
together for the reason given above. Where a name below still carries a v2-era comment explaining a v2
fact in v2's numbers, that comment is a record of what v2 changed and why, not a claim about the
current values — it is left as history except where it stated something the current palette makes
false outright, which is called out where it happens.
--> -->
<!-- <!--
The surfaces, darkest first. Two things changed shape rather than merely colour. The surfaces, darkest first. Two things changed shape rather than merely colour, in v2, and the second
of the two has since changed back.
A field is now *inset* — darker than the canvas it sits on — where it used to be raised. That follows A field became *inset* in v2 — darker than the canvas it sat on — where it had been raised before that.
the design, which draws the search box as a well rather than as a tile, and it is the one place where That followed the v2 design, which drew the search box as a well rather than as a tile. It does not
reading the old palette's ordering across to the new one gives the wrong answer. follow this one: the v3 design gives a card and a field the same fill, rgb(16,16,25), and marks a field
as editable with a 1.5px accent-coloured inset border instead of a darker well. Raised and Field are
kept as separate keys anyway, for the same reason Chrome and Panel are — the day one of the two needs
to move again, having merged them would have to be undone everywhere that used the wrong one.
And Chrome and Panel are now the same value, because the design uses one surface for both the header Chrome and Panel remain the same value for that reason: 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 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 two moves is the day having merged them would have to be undone across every file that used the wrong
one. one.
--> -->
<Color x:Key="CanvasColor">#0E1220</Color> <Color x:Key="CanvasColor">#05050A</Color>
<SolidColorBrush x:Key="Canvas" Color="{StaticResource CanvasColor}" /> <SolidColorBrush x:Key="Canvas" Color="{StaticResource CanvasColor}" />
<SolidColorBrush x:Key="Chrome" Color="#111629" /> <SolidColorBrush x:Key="Chrome" Color="#10111E" />
<SolidColorBrush x:Key="Sidebar" Color="#0F1426" /> <SolidColorBrush x:Key="Sidebar" Color="#0D0D16" />
<SolidColorBrush x:Key="Panel" Color="#111629" /> <SolidColorBrush x:Key="Panel" Color="#10111E" />
<SolidColorBrush x:Key="Raised" Color="#151B31" /> <SolidColorBrush x:Key="Raised" Color="#101019" />
<SolidColorBrush x:Key="Field" Color="#0B0F1D" /> <SolidColorBrush x:Key="Field" Color="#101019" />
<!-- <!--
The surface a terminal, a log and a snippet's command are drawn on, and it is not one of the five 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 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 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. stylesheet and in two screens, which is how the three of them would drift.
Not one of the design's named tokens in v3 either — the mock this palette was drawn from is a Hosts
screen with no terminal on it — so the value is interpolated the same way it was for v2: warmer and a
touch lighter than the new Chrome, on the same ramp as Raised and Field rather than the old blue-black
one.
--> -->
<SolidColorBrush x:Key="TerminalSurface" Color="#171A26" /> <SolidColorBrush x:Key="TerminalSurface" Color="#18161F" />
<!-- Row hover, and the heavier one the chrome's own buttons use. On the phone these are press states. --> <!-- Row hover, and the heavier one the chrome's own buttons use. On the phone these are press states. -->
<SolidColorBrush x:Key="Hover" Color="#151B31" /> <SolidColorBrush x:Key="Hover" Color="#14151E" />
<SolidColorBrush x:Key="ChromeHover" Color="#1D2749" /> <SolidColorBrush x:Key="ChromeHover" Color="#1E1E2C" />
<!-- <!--
Selection, and it is a surface rather than a highlight: the design fills a chosen host row, the current 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 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. what the finger is on rather than what is chosen.
--> -->
<SolidColorBrush x:Key="Active" Color="#1D2749" /> <SolidColorBrush x:Key="Active" Color="#1E1E2C" />
<!-- The fill behind a small monospaced tag — a group name, a storage class, a bucket. --> <!-- The fill behind a small monospaced tag — a group name, a storage class, a bucket. -->
<SolidColorBrush x:Key="Chip" Color="#16204A" /> <SolidColorBrush x:Key="Chip" Color="#1A1A28" />
<!-- <!--
Three border weights, and they are not interchangeable. Strong separates one region from another, 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. subtle separates rows inside one region, and mid is what a control draws around itself.
BorderFaint changes relationship in v3: in v2 it sat above Border, lighter than it — a fact this
comment did not previously state because nothing here described it. In this design BorderSubtle and
BorderFaint are both a step darker than Border rather than one of them being a step lighter, so the
weight now runs, faintest to strongest: BorderFaint, BorderSubtle, Border, BorderMid, BorderHover — a
single ramp rather than Border sitting in the middle of two that disagree about which side it is on.
--> -->
<SolidColorBrush x:Key="Border" Color="#1A2138" /> <SolidColorBrush x:Key="Border" Color="#1E1E2C" />
<SolidColorBrush x:Key="BorderSubtle" Color="#151B31" /> <SolidColorBrush x:Key="BorderSubtle" Color="#17172A" />
<SolidColorBrush x:Key="BorderMid" Color="#232B45" /> <SolidColorBrush x:Key="BorderMid" Color="#24243A" />
<SolidColorBrush x:Key="BorderHover" Color="#2E3859" /> <SolidColorBrush x:Key="BorderHover" Color="#2C2C3E" />
<SolidColorBrush x:Key="BorderFaint" Color="#1F2740" /> <SolidColorBrush x:Key="BorderFaint" Color="#141420" />
<!-- <!--
The text ramp. Three steps, used consistently: what you read, what you glance at, and what is there 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. only so its absence would be noticed. A fourth step would be one nobody could tell from its neighbours.
── The ramp is white-topped, and every step below it moved with the top. ───────────────────────────── ── The ramp is white-topped, and every step below it moved with the top. ─────────────────────────────
The design's #E3E7F4 is a blue-tinted white, and against these blue-black surfaces the tint costs This was v2's reasoning, told in v2's numbers, and is left as a record of that change rather than
contrast twice: once because it is darker than white, and once because it shares a hue with what it is updated in place: the design's #E3E7F4 was a blue-tinted white, and against v2's blue-black surfaces
drawn on. Pure white against #0E1220 is 18.3:1, where #E3E7F4 was 15.5:1. the tint cost contrast twice — once because it was darker than white, and once because it shared a hue
with what it was drawn on. Pure white against v2's canvas, #0E1220, was 18.6:1, where #E3E7F4 had been
15.5:1.
Raising only the top would have been the wrong half of the change. The three steps are a ramp rather Raising only the top would have been the wrong half of that change. The three steps are a ramp rather
than three colours, and their job is that a glance can tell them apart — so lifting Text and leaving than three colours, and their job is that a glance can tell them apart — so lifting Text and leaving
TextDim where it was would have widened the first gap and left the second two crowded at the bottom. TextDim where it was would have widened the first gap and left the second two crowded at the bottom.
Every step is raised by roughly what the top gained, which keeps the intervals the design chose and Every step was raised by roughly what the top gained, which kept the intervals the design chose and
brings the lower steps up to where a secondary label is comfortably readable rather than merely brought the lower steps up to where a secondary label was comfortably readable rather than merely
present. TextDim clears 9:1 now, against 6.4:1 before. present. TextDim cleared 9.8:1 at that point, against 6.4:1 before it.
── v3 ──────────────────────────────────────────────────────────────────────────────────────────────
The canvas darkens again, past v2's blue-black to near-black, which raises every one of these ratios
again without any of them moving: pure white against the new #05050A canvas is 20.3:1. TextDim,
TextFaint and TextGhost are repicked to the design's own neutral-grey values rather than re-derived
from v2's blue-tinted ones, because the design supplies its own steps this time — #EDEDF5, #9C9EB4 and
#7C7F98 read against the new canvas at roughly 17.5:1, 7.7:1 and 5.2:1, in the same descending order the
three names promise. The design also uses two deeper steps, #6D6F84 and #5D5F74, for distinctions this
ramp does not yet draw; they are not added as keys until a screen needs them, on the same reasoning as
the paragraph above the border ramp — a resource nothing reads is not free, because the next person to
touch this file has to work out whether it is dead or merely not yet wired up.
--> -->
<SolidColorBrush x:Key="Text" Color="#FFFFFF" /> <SolidColorBrush x:Key="Text" Color="#FFFFFF" />
<SolidColorBrush x:Key="TextDim" Color="#B4BCD4" /> <SolidColorBrush x:Key="TextDim" Color="#EDEDF5" />
<SolidColorBrush x:Key="TextFaint" Color="#7B849F" /> <SolidColorBrush x:Key="TextFaint" Color="#9C9EB4" />
<SolidColorBrush x:Key="TextGhost" Color="#5A6280" /> <SolidColorBrush x:Key="TextGhost" Color="#7C7F98" />
<!-- <!--
The accent, and it is two colours rather than one. The design fills with #5B8CFF — the button, the The accent, and it is two colours rather than one. The design fills with #5D42DE — the button, the
floating action button, a progress bar, a toggle that is on — and writes with #8FA7FF: the current floating action button, a progress bar, a toggle that is on — and writes with #A78BFA: the current
destination in the bottom bar, a link, the text inside a chip. They are not interchangeable in either 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 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. too pale to carry a button.
AccentInk is what goes *on* the fill. It equals Canvas today and is named separately anyway, because AccentInk is what goes *on* the fill, and it no longer equals Canvas the way it did through v1 and v2:
"the darkest surface" and "the colour of text on a blue button" are two facts that happen to coincide, the new accent is dark enough on its own that white carries better on it than the near-black the app
and a light theme would separate them immediately. otherwise writes on. "The darkest surface" and "the colour of text on the accent" were two facts that
happened to coincide before; this design is the light theme the old comment predicted would separate
them, arriving by a different route.
AccentGradient and the glow beneath it are new in v3 — the design draws its primary buttons with a
top-to-bottom gradient rather than a flat fill, lighter violet into the accent proper, and lifts them
off the surface with a soft violet-tinted shadow rather than a border. Both are named here rather than
inlined in App.axaml for the same reason every other accent value is: one button styled from the wrong
purple is how a screenshot next to another screenshot finds the seam.
--> -->
<Color x:Key="AccentColor">#5B8CFF</Color> <Color x:Key="AccentColor">#5D42DE</Color>
<SolidColorBrush x:Key="Accent" Color="{StaticResource AccentColor}" /> <SolidColorBrush x:Key="Accent" Color="{StaticResource AccentColor}" />
<SolidColorBrush x:Key="AccentText" Color="#8FA7FF" /> <SolidColorBrush x:Key="AccentText" Color="#A78BFA" />
<SolidColorBrush x:Key="AccentInk" Color="#0E1220" /> <SolidColorBrush x:Key="AccentInk" Color="#FFFFFF" />
<SolidColorBrush x:Key="AccentSoft" Color="#5B8CFF" Opacity="0.35" /> <SolidColorBrush x:Key="AccentSoft" Color="#5D42DE" Opacity="0.35" />
<SolidColorBrush x:Key="AccentWash" Color="#5B8CFF" Opacity="0.06" /> <SolidColorBrush x:Key="AccentWash" Color="#5D42DE" Opacity="0.06" />
<LinearGradientBrush x:Key="AccentGradient" StartPoint="0%,0%" EndPoint="0%,100%">
<GradientStop Color="#8D51E7" Offset="0" />
<GradientStop Color="#5D42DE" Offset="1" />
</LinearGradientBrush>
<BoxShadows x:Key="AccentGlow">0 0 22 0 #598D51E7</BoxShadows>
<!-- <!--
◆ Green is no longer the accent, and that is the one semantic change in this file rather than a ◆ Green is still not the accent, and purple replacing blue in v3 does not reopen that question.
recolour.
It used to be both: Ellipse.dot.live filled with Accent, so "a shell is open on this host" and "this is Blue was the accent through v1 and v2; purple is the accent now. Either way, Ellipse.dot.live keeps its
the thing the screen wants you to press" were the same colour by construction. The v2 design keeps a own colour rather than borrowing the accent's, which is the separation this comment originally recorded
green — #3DDC97, for the live dot, the sync light and the follow indicator — and makes the accent blue, when the accent first stopped being green: "a shell is open on this host" and "this is the thing the
which finally separates the two. Live is that green, and nothing that is merely interactive may use it. screen wants you to press" read as two different colours by construction, not by coincidence of which
two hues the current design happens to pick.
Read the pair as: blue is what you can do, green is what is true. Live is #34D399 in this design — the design's own green, a shade cooler than v2's #3DDC97 — for the
live dot, the sync light and the follow indicator. Nothing that is merely interactive may use it.
Read the pair as: purple is what you can do, green is what is true.
--> -->
<SolidColorBrush x:Key="Live" Color="#3DDC97" /> <SolidColorBrush x:Key="Live" Color="#34D399" />
<SolidColorBrush x:Key="LiveWash" Color="#3DDC97" Opacity="0.12" /> <SolidColorBrush x:Key="LiveWash" Color="#34D399" Opacity="0.12" />
<!-- <!--
The three colours that are allowed to disagree with the accent. Amber means a caveat worth reading, red 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 means refused or destructive, and blue-light is for the one thing that is neither — an informational log
line, a directory, a distinct scope. line, a directory, a distinct scope.
Info is left at its v2 value, #7FB0FF, rather than moved: it was already meant to read as distinct from
the accent, and now that the accent has moved to purple a blue that used to sit close to it on the wheel
sits further from it instead — the separation this colour exists for gets stronger for free.
WarnText and DangerText are the prose weights: a wash is a very dark tint, and the signal colour that 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 works as a 6-pixel dot or a one-word label is not the one that reads as a sentence on top of it. Danger
design supplies both reds — #E86A6A labels the refusal and #FF8F8F writes it out — and one amber, so keeps its v2 values outright — the design does not revisit red — so DangerSoft, DangerWash and
the amber prose weight is interpolated. DangerText stay derived from the same #E86A6A they always were. Warn's fill and wash move to the
design's own #F59E0B, and WarnText to the design's own #F5B942 rather than the interpolated value v2
carried, because this design supplies a prose weight where v2's did not name one.
--> -->
<SolidColorBrush x:Key="Warn" Color="#FFB454" /> <SolidColorBrush x:Key="Warn" Color="#F59E0B" />
<SolidColorBrush x:Key="WarnSoft" Color="#FFB454" Opacity="0.35" /> <SolidColorBrush x:Key="WarnSoft" Color="#F59E0B" Opacity="0.35" />
<SolidColorBrush x:Key="WarnWash" Color="#FFB454" Opacity="0.12" /> <SolidColorBrush x:Key="WarnWash" Color="#F59E0B" Opacity="0.12" />
<SolidColorBrush x:Key="WarnText" Color="#E4A868" /> <SolidColorBrush x:Key="WarnText" Color="#F5B942" />
<SolidColorBrush x:Key="Danger" Color="#E86A6A" /> <SolidColorBrush x:Key="Danger" Color="#E86A6A" />
<SolidColorBrush x:Key="DangerSoft" Color="#E86A6A" Opacity="0.3" /> <SolidColorBrush x:Key="DangerSoft" Color="#E86A6A" Opacity="0.3" />
<SolidColorBrush x:Key="DangerWash" Color="#E86A6A" Opacity="0.12" /> <SolidColorBrush x:Key="DangerWash" Color="#E86A6A" Opacity="0.12" />
@@ -153,17 +212,24 @@
<SolidColorBrush x:Key="Info" Color="#7FB0FF" /> <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 Montserrat, JetBrains Mono and Material Icons are embedded font files rather than names handed to the
ships with this application, and neither is on a stock Windows install or a stock Android one, so font fallback system, and that is a change from every design before this one. v1 asked for IBM Plex,
requesting them by name would render as whatever the font fallback chose that day — which is worse than v2 for Instrument Sans and Fira Code, and neither shipped with the application nor came stock on a
choosing deliberately. Inter is embedded by both heads and is what they already draw with. Windows or an Android install, so requesting either by name would have rendered as whatever fallback
the platform chose that day — worse than substituting deliberately. Inter stood in for both heads' sans
and the generic `monospace` stood in for the mono stack, and that substitution was the reversible
stand-in this comment used to describe.
The stack ends in the generic `monospace` rather than a Windows face, which is what makes it work on It no longer needs to be: the three faces this design names ship as TTFs under Assets/Fonts in this
both: Android has no Cascadia Mono or Consolas and resolves the generic name to its own mono face. project, embedded the same way WebAssets is, and resolved by the same avares:// URI scheme Inter
already used. MonoFont below is JetBrains Mono first, with the old system-mono stack kept after it as a
Named as a resource rather than repeated, because the substitution is the sort of thing that gets fallback for whatever glyph JetBrains Mono does not cover. IconFont is new: the design's icons are drawn
reversed later and should be reversible in one place. See docs/design-import-gaps.md. from Material Icons by codepoint rather than by name, and screens adopt that per glyph as they are
touched rather than all at once — this resource only makes the face available. Program.cs makes the
equivalent change for the sans stack, with Montserrat first and Inter kept as its fallback; see the
remarks on BuildAvaloniaApp.
--> -->
<FontFamily x:Key="MonoFont">ui-monospace,Cascadia Mono,Consolas,monospace</FontFamily> <FontFamily x:Key="MonoFont">avares://DodoSSH.Client.Shell/Assets/Fonts#JetBrains Mono,ui-monospace,Cascadia Mono,Consolas,monospace</FontFamily>
<FontFamily x:Key="IconFont">avares://DodoSSH.Client.Shell/Assets/Fonts#Material Icons</FontFamily>
</ResourceDictionary> </ResourceDictionary>
@@ -41,8 +41,8 @@ internal static class HeadlessApp
/// On a machine with no fonts at all it does not merely differ, it stops: <c>FontManager</c> throws /// On a machine with no fonts at all it does not merely differ, it stops: <c>FontManager</c> throws
/// "Default font family name can't be null or empty" during <c>AppBuilder.SetupUnsafe</c>, before any /// "Default font family name can't be null or empty" during <c>AppBuilder.SetupUnsafe</c>, before any
/// test body runs. That is how a container CI runner reports all sixty-eight of these as failures with /// test body runs. That is how a container CI runner reports all sixty-eight of these as failures with
/// not one assertion among them. Naming Inter costs the runner nothing, because the font travels /// not one assertion among them. Naming a font costs the runner nothing, because it travels inside the
/// inside the package rather than on the host. /// package rather than on the host — true of Inter before this and of Montserrat now.
/// </para> /// </para>
/// </remarks> /// </remarks>
public static AppBuilder BuildAvaloniaApp() => public static AppBuilder BuildAvaloniaApp() =>
@@ -50,13 +50,27 @@ internal static class HeadlessApp
.UseSkia() .UseSkia()
.UseHeadless(new AvaloniaHeadlessPlatformOptions { UseHeadlessDrawing = false }) .UseHeadless(new AvaloniaHeadlessPlatformOptions { UseHeadlessDrawing = false })
.WithInterFont() .WithInterFont()
.With(new FontManagerOptions { DefaultFamilyName = InterFamily }); .With(new FontManagerOptions
{
DefaultFamilyName = DefaultFamily,
FontFallbacks = [new FontFallback { FontFamily = new FontFamily(InterFamily) }],
});
/// <summary>The family <c>WithInterFont</c> registers.</summary> /// <summary>The family <c>Program.BuildAvaloniaApp</c> asks the font manager to default to.</summary>
/// <remarks> /// <remarks>
/// Kept in step with <c>Program.BuildAvaloniaApp</c> by hand, and it matters that they agree: a /// Kept in step with <c>Program.BuildAvaloniaApp</c> by hand, and it matters that they agree: a
/// harness pinning a different default from the one the application runs with would certify heights /// harness pinning a different default from the one the application runs with would certify heights
/// for a window nobody sees, which is the failure the opening paragraph of this file is about. /// for a window nobody sees, which is the failure the opening paragraph of this file is about. It was
/// Inter until the v3 design; now it is Montserrat, embedded the same way, and Inter moves down to
/// <see cref="InterFamily"/> as the fallback both <c>BuildAvaloniaApp</c> methods name it as.
/// </remarks>
internal const string DefaultFamily = "avares://DodoSSH.Client.Shell/Assets/Fonts#Montserrat";
/// <summary>The family <c>WithInterFont</c> registers.</summary>
/// <remarks>
/// No longer the default — see <see cref="DefaultFamily"/> — but still registered and still named
/// here, because it is still the fallback both heads' <c>BuildAvaloniaApp</c> fall back to, and a test
/// measuring a glyph Montserrat does not cover has to fall back to the same font the application does.
/// </remarks> /// </remarks>
internal const string InterFamily = "avares://Avalonia.Fonts.Inter/Assets#Inter"; internal const string InterFamily = "avares://Avalonia.Fonts.Inter/Assets#Inter";
} }