cockpit-project / cockpit

Cockpit is a web-based graphical interface for servers.
http://www.cockpit-project.org/
GNU Lesser General Public License v2.1
10.64k stars 1.07k forks source link

Allow font of terminal to be changed #16583

Open gbraad opened 2 years ago

gbraad commented 2 years ago

It would be nice if the font that is used for the terminal can be changed. When for instance a airline/powerline is used, the glyphs aren't properly rendered as the default font might not have these.

image

Since the size can be changed, it might be possible to allow free-editing of the font, pretty much like vscode or codesandbox allows with comma separation, or provide several well-known options (which might avoid the need to sanitize input).

garrett commented 2 years ago

So I think this issue is actually several:


Powerline

I think we're going to add the new RedHatMono font as the default here. I'm guessing it doesn't user powerlines, so we might need to fork it and run it through a patcher tool to add powerlines. https://redhatofficial.github.io/RedHatFont/

(* nerd-fonts has a patcher tool @ https://github.com/ryanoasis/nerd-fonts#font-patcher)

Or perhaps we could use CSS font subsetting to monkey-patch all fonts with powerline support in-browser?

There's an article explaining this @ https://www.sitepoint.com/joy-of-subsets-web-fonts/

The gist is:

  1. Import the usual fonts
  2. Import the patch font, in this case a Cockpit-local version of https://github.com/powerline/powerline/raw/develop/font/PowerlineSymbols.otf
  3. Make sure PowerlineSymbols is the first font in the font stack... anything it doesn't cover (which is most things) will fall through to the next fonts specified in the stack

This is probably what we want to do, as long as the height metrics match. (Otherwise I might need to adjust the powerline.otf for RedHatMono by default and then we might need to ship two. Or see if we can use size-adjust (spoiler: probably not, as it doesn't work on Safari yet and it might not work as expected anyway).


Switcher UI

If we do have a font switcher, we might need to rethink the settings anyway and perhaps just move to the menu redesign outright (perhaps with a bar when embedded in a page and then have an option to turn off the bar otherwise). In other words: A font switcher would add more widgets and our current UI won't scale.

Also, a font switcher should have system-available fonts in addition to RedHatMono (which we should have soon), and we'd have to detect the fonts. We'd probably have to have a shortlist of fonts to vet (on macOS, Windows, and various Linuxes) and have native monospace font as well. And then possibly a way to have a manual font name. (This sounds like an autocomplete widget would make sense here.)

gbraad commented 2 years ago

Saving the preference

Totally

(in a cookie)

Not so much...

Cookies to store preferences is probably not the best idea. These get sent back and forth with each request and respons. It would be better to utilize session storage, or local storage.

It might even be possible in that case to sync the storage from the cockpit backend to the frontend, to allow this kind of setting to persist between sessions on different machines. But that is a detail and improvement. Cookies are bad... But usable for token and authentication information to binds a session. Not so much for settings unless they are needed to be shared with the backend as state.

garrett commented 2 years ago

Cookies to store preferences is probably not the best idea. These get sent back and forth with each request and respons. It would be better to utilize session storage, or local storage.

Sure, I mean local storage in the browser, not on the server. We do these elsewhere in Cockpit.

It might even be possible in that case to sync the storage from the cockpit backend to the frontend, to allow this kind of setting to persist between sessions on different machines. But that is a detail and improvement.

No, it's not. We specifically do not set Cockpit-specific settings on the host. We only ever save system-related data. This is why I was talking about browser storage, as we use that for other Cockpit-specific settings.

(I meant browser's local storage independent of technology, not necessarily a cookie.)

Not so much for settings unless they are needed to be shared with the backend as state.

User preference settings are a state that need to be shared with the backend.

bassemkaroui commented 1 year ago

Hi guys, is there a solution, even not out of the box, for cockpit's font issue or we have to wait ? Thanks

garrett commented 1 year ago

You'd think you could do this with Stylus with custom CSS...

...however, the terminal uses Canvas, so this won't work... at least after the canvas is rendered.

Here's what it the DOM looks like in Firefox's developer tools:

image


In other words, it's trickier than what you'd expect.


Oddly, the font stack is "Menlo, Monaco, Consolas, monospace"... And I see this is in pkg/lib/console.css and in pkg/lib/cockpit-components-terminal.jsx (Those are macOS and Windows fonts, with a fallback to just monospace. I guess it was copied from some example or something.)

It's possible that it's pulling in the font and rendering it. So perhaps a CSS override would work, if it's done before the page loads and renders. Swapping it out after rendering doesn't work.

Or, we could try to load system fonts that are more optimal by adjusting the font stack, including powerline fonts (on the system where the browser is), if they're there. That should provide better defaults without having to do the font switching.

psztoch commented 1 year ago

IntelliJ Mono font is popular among my friends - https://www.jetbrains.com/lp/mono/ It has support for Powerline and foreign languages.

BrainStone commented 1 year ago

I'd be very interested in this too

gbraad commented 1 year ago

In a more recent version this seems to work for me: image

marvinruder commented 1 year ago

In a more recent version this seems to work for me: image

@gbraad That's interesting, I cannot reproduce that with the latest version of cockpit-ws (294.1). Still only font-family: Menlo, Monaco, Consolas, monospace; are set. Which version are you using on which OS/browser, and which special characters are supported there? Can you see which fonts are used to render them?

gbraad commented 1 year ago

For example, I am using https://github.com/spotsnel/cockpit-tailscale (start from GitPod: https://gitpod.io/#https://github.com/spotsnel/cockpit-tailscale). This a F38 docker image and I open this on Vivaldi (a chrome-based browser) on Windows due to cross-platform work.

The image installs:

cockpit-ws-294.1-1.fc38.x86_64                       79/99
$ sudo passwd gitpod
$ npm run cockpit

but similarly, a local install on my T14 or T460p behave the same (based on the same install images).


Windows 10

Vivaldi: image image

Firefox: image Note: interesting glitch (dark mode not working as expected)?!?

~I'll also check on my F38 install, as this might well be OS specific.~


Linux (Fedora 38)

Vivaldi: Screenshot from 2023-06-27 22-04-54 Screenshot from 2023-06-27 22-07-23

Firefox: Screenshot from 2023-06-27 22-08-32


Mobile browsers

Vivaldi: Screenshot_20230627-221159_Vivaldi Browser

Firefox: Screenshot_20230627-221430_Firefox


So, as you can see. My original problem is actually solved, as the glyphs (mostly) render correctly. Except for mobile, though even this is more than 50% there.

without having to do the font switching.

I am in this case less concerned about the actual font.

marvinruder commented 1 year ago

Thanks for the insights! I can reproduce the prompt above, that looks fine, however I cannot figure out which font is used to render the arrowheads there, as they are not part of any of the standard monospace fonts I can find in the CSS. But those appear to be the only working characters. All other glyphs like icons for OS, git provider, the home directory, CPU, RAM, network, battery state etc. are still not supported. This is reproducable in the gitpod by switching to the ~/.oh-my-zsh directory – this is shown on the right side:

image

My personal prompt looks even worse at the moment:

image

So from my perspective it would still be relevant to have the possibility to either be able to provide a custom font or have Cockpit use a font supporting more than just the basic Powerline characters.

gbraad commented 1 year ago

however I cannot figure out which font is used to render the arrowheads there

As you can see, in some of the targets it does render the lock, but on mobile not at all. On Linux I can check but expect this to also work.

So, yeah. it looks like special glyphs are problematic, while the powerline-specific glyphs work.


Checked Linux just to be sure:

Vivaldi: image

Firefox: image

Note: Vivaldi does not render the special glyphs.


On Windows, the Git branch glyph does render: image

So it still really depends on the OS+browser how this renders. For my machines this does not seem to be overall an issue, except for mobile browsers... but these are known to have limited font support.

saturated-white commented 1 year ago

Since this is a complex topic and there are many unanswered questions, I'll try to share my knowledge…

Font-Rendering (in Browsers)

Rendering of unknown characters in browsers varies from vendor to vendor, but basically all of them use the first available (locally installed or hosted) font.

What @gbraad found was, that some browser-vendors provide an override, that when a glyph in the used font is not available, it goes to the next font in the font-family list for that glyph.
The problem with that solution is that typefaces are inconsistent when it comes to missing characters: Some leave them empty, so the above mentioned override can work, others fill that missing glyph w/ a placeholder, so the browser render the placeholder instead of trying the next font in the list.

Fonts in Cockpit

Cockpit styles are provided in form of pre-built static CSS files and as it turns out each Linux distribution has their own preference of what to set and then built in to those CSS files. (just checked misc distributions; and this may be also the reason, why some have more problems than others.)

Complications w/ previous suggested solutions

  1. Agree on one font and sugar it w/ powerline etc. glyphs: Even if we add all thinkable symbols in the world, there sill remains the problem, that every user prefers a different typeface. Additionally some character-codes used locally may conflict w/ the then provided one of Cockpit.
  2. Add a selector to the UI: Seems a "lot" of planning+work… Add logic to fetch-fonts for the drop-down options, add a whole design of storing host-based settings on the server, etc. (Maybe there is already some stuff there and I missed it. Anyhow I don't think it's a "5min thing".) - Also issues of point no.3 apply.
  3. Add a config flag: Adding a config-file to /etc/cockpit/<SOMETHING> and use its value shouldn't be too difficult to implement, but we shouldn't use system-fonts (.ttf, etc.) and use web-fonts (.woff) instead (Just Google it! - Don't want ot open that can of worms here. :wink: ) - Therefore we would have to figure out a mechanism to host those web-fonts.
  4. Use a browser-plugin: @garrett pretty much already summed it up! - Since it's a canvas it's pretty tricky/undoable. (Also I don't think slapping a style via plugin on top is a real solution, but just a dirty bodge.)

(My) Suggested Solution

NOTE: This suggested solution only works on desktop-browsers, since at least I don't know of any mobile-browser that allows for customization of any fonts. Therefore, if the default monospace font on mobile is problematic, I'd suggest to use a query-selector for mobile and use @garrett suggested sugared font on mobile only.

marvinruder commented 1 year ago

@saturated-white Safari on macOS also offers no possibility to change the default monospace font, so this issue is not restricted to mobile devices.

Therefore I like the option to use a config flag, perhaps in combination with a CSS class holding the font-family value which can easily be overridden by clients if desired.

garrett commented 1 year ago

Sadly, these suggestions would not work.

I'm happy that it's working in some places (as seen in the screenshots above), but, yes, this still needs work to work everywhere. If anyone figures out a solution and sends a PR, it'd be great to look at.

It looks like this is a problem in XTerm.js (which is what Cockpit uses in the stack) loading things in canvas, and there's a workaround @ https://www.npmjs.com/package/xterm-webfont which would need to be loaded and configured.

Perhaps the best solution is still:

Note:

Meanwhile, there are a lot of powerline-related issues and PRs at Xterm.js: https://github.com/xtermjs/xterm.js/issues?q=powerline (only 2 open currently; just saying that a lot of people have been hard at work on it, and that's why we're seeing it work in some places)

saturated-white commented 1 year ago

Thanks @marvinruder , I wasn't aware that Apple removed this feature from Safari.

I'm not going to dispute any of the "would not work" bullet-points, despite some look more like "design-decisions" by the Cockpit-Team to me. - Therefore they are still valid in their own way.

But I think I was misunderstood regarding one thing: "Fonts per distribution doesn't matter on the Cockpit server side of things." - I just wanted to point out, that for example Ubuntu (e.g. v20.04) ships their official package of Cockpit w/ "Menlo, Monaco, Consolas, monospace" and not the "RedHatFont". Therefore it may be another factor, why it may only work for some people in this issue discussion.

Regarding "best solution" by @garrett : I'd also be for a new font w/ complete support of the Powerline glyphs then. (If I read RedHatFont's License correctly, there shouldn't be a legal problem to create a new Powerline glyphs sugared font under a different name.)

garrett commented 1 year ago

more like "design-decisions" by the Cockpit-Team to me

Which ones? Not a single one in that list is a decision decision. They're all due to technical reasons. This is just how the web and web browsers and UNIX permissions.

(I'd know. I'm the designer on the team and I've been using Linux and doing web stuff since 1996.)

Ubuntu (e.g. v20.04) ships their official package of Cockpit w/ "Menlo, Monaco, Consolas, monospace" and not the "RedHatFont".

This is news to me. It would break our screenshot testing, if it were the case. Where are you seeing this?

I can 100% guarantee Ubuntu doesn't ship those fonts. Menlo and Monaco are proprietary macOS fonts; Consolas is proprietary Windows.

https://en.wikipedia.org/wiki/Menlo_(typeface)

I guess they're possibly listed in the font stack somewhere? Possibly just in the terminal widget? But mention of a font doesn't mean it is shipped or being used.

If I read RedHatFont's License correctly

SIL Open Font License is pretty much the de facto open source font license; there's nothing specific to RedHatFont. It's approved by FSF, OSI, and DFSG.

The rename requirement is due to how fonts are implemented on computers. They're unversioned, so you reference a font by name. If the font is different with the same name, it will cause unexpected things to happen. Therefore: Rename.

https://en.wikipedia.org/wiki/SIL_Open_Font_License

Even still: It is possible to patch a font at runtime and expose the same name to the browser. The font name needs to be different, but the CSS can "monkeypatch" / "hotpatch" it live. We've extended fonts like this within Cockpit for various reasons (adding icons back when we used icon fonts, swapping out the slashed zero with an unslashed one, etc.).

saturated-white commented 1 year ago

You cannot really detect fonts like that (query selector) on the web.

We actually had no choice in a legacy closed-source project, but to add/implement such "query selector". - Maybe the term was chosen badly, since it is a combination of: HTTP-Headers, checking misc avail. CSS-Selectors and checking misc avail. JS functions/objects. - This solutions seems to work pretty stable for months now, since we had no customer complaints regarding that.

We are not going to remove monospace font support across Cockpit just for the terminal (nor is it necessary to do so).

The "we are not going to" part clearly sounds like a design-choice.😉

We cannot serve a system file directly; there are file permissions and such.

Maybe I misinterpreted the "system" part of that sentence, but in my previous post I tried to suggest following:

This solution should address following problems/points:

  1. This should not cause any permission issues, since cockpit already access e.g. their static CSS-files. (The only problem that I can see occur is that sys-admins set a wrong seLinux context, but when the system-owner/admin doesn't understand their own system, there's IMHO a much bigger problem.)
  2. A system-admin is usually responsible to provide the necessary infrastructure, therefore it should be in their respective area of responsibility to either install or deny the Cockpit user that additional font(s).
  3. Through the manual process of copying the fonts to a specific destination, the responsibility of hosting a possible copyrighted font falls in the hands of the end-user and not the Cockpit package-maintainers.

(I'd know. I'm the designer on the team and I've been using Linux and doing web stuff since 1996.)

Respect!! - I'm not that long in that business, but still developing for the Web since 2001 and using Linux since 2002.

This is news to me. It would break our screenshot testing, if it were the case. Where are you seeing this?

Maybe I find time to re-confirm it by re-installing the system, but basically we currently need a plain Ubuntu 20.04 server for in-house integration testing, since that customer only support that selected system in their environment. For that we just used the official Ubuntu 20.04 server ISO, installed the latest updates, cockpit and a few libraries needed for that mentioned integration test. And when connecting to Cockpit of that machine, the Dev-Tools state:

.console-ct {
  font-family: Menlo, Monaco, Consolas, monospace;
  /* … */
}

To clarify: They don't "host" any of the declared fonts, they seemingly just changed the CSS and hope the user has one of the listed fonts on their system.

They're unversioned, so you reference a font by name. …

Yeah, additionally to the version-state, there's actually an additional problem, that W3C doesn't specify what has precedence, when a font is locally installed + served by the website. Therefore some browser (on some systems) load the local font, whereas others load the web-hosted one.

It is possible to patch a font at runtime and expose the same name to the browser.

You're absolutely right, but my personal experience has shown, that monkey-patches usually bite you in the a*** at some point. Therefore I'd still suggest to go the renamed+extended font route.

LittleNewton commented 3 months ago

I had the same issue recently. 2024 March.

garrett commented 3 months ago

@LittleNewton: Which issue? That you cannot change the font? (Which is expected, as that's not an issue; there isn't a font switcher.) Or that powerline looks wrong for you?

Could you share more details, like your system information (OS of server with Cockpit and client OS with the browser), browser and version, Cockpit version, etc.

For reference, here's Cockpit with powerline installed on my Fedora 40 beta system using Firefox 124.0.2 (server and client are the same; I'm running Cockpit locally):

image

LittleNewton commented 3 months ago

@garrett, I am sorry for not providing details about my shell profile and OS internals.

I am using zsh as my shell. My personal profile is built based on Oh-My-Zsh. All the details about my zsh config can be found here.

Here is my Windows client's (Windows Terminal) display: image

Cockpit in chrome display: image

I hope those information would be helpful.

PS. I'd like to contribute to the functionality of cockpit. However, I need to become more familiar with Web UI design.