apply jet brains nerd font + image fix
what in the fahhh
This commit is contained in:
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "brotli==1.2.0",
|
||||
# "fonttools==4.63.0",
|
||||
# ]
|
||||
# ///
|
||||
"""Rebuild Wakey's self-hosted terminal webfont from Nerd Fonts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import tarfile
|
||||
import tempfile
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
NERD_FONTS_VERSION = "v3.4.0"
|
||||
ARCHIVE_SHA256 = "ef552a3e638f25125c6ad4c51176a6adcdce295ab1d2ffacf0db060caf8c1582"
|
||||
WOFF2_SHA256 = "50680a64466fbcc3eff68e00f84505f55681c86f8450c042416af5a8dbc1cce1"
|
||||
FONT_FILE = "JetBrainsMonoNerdFontMono-Regular"
|
||||
ARCHIVE_URL = (
|
||||
"https://github.com/ryanoasis/nerd-fonts/releases/download/"
|
||||
f"{NERD_FONTS_VERSION}/JetBrainsMono.tar.xz"
|
||||
)
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
OUTPUT_DIR = ROOT / "ui/src/assets/fonts/jetbrains-mono-nerd"
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as source:
|
||||
for chunk in iter(lambda: source.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def verify_sha256(path: Path, expected: str) -> None:
|
||||
actual = sha256(path)
|
||||
if actual != expected:
|
||||
raise RuntimeError(
|
||||
f"checksum mismatch for {path}\nexpected: {expected}\nactual: {actual}"
|
||||
)
|
||||
|
||||
|
||||
def download(url: str, destination: Path) -> None:
|
||||
request = urllib.request.Request(url, headers={"User-Agent": "wakey-font-preparer"})
|
||||
with (
|
||||
urllib.request.urlopen(request, timeout=60) as response,
|
||||
destination.open("wb") as output,
|
||||
):
|
||||
shutil.copyfileobj(response, output)
|
||||
|
||||
|
||||
def extract_member(archive: tarfile.TarFile, name: str, destination: Path) -> None:
|
||||
source = archive.extractfile(name)
|
||||
if source is None:
|
||||
raise RuntimeError(f"missing {name} in Nerd Fonts archive")
|
||||
with source, destination.open("wb") as output:
|
||||
shutil.copyfileobj(source, output)
|
||||
|
||||
|
||||
def install_file(source: Path, destination: Path) -> None:
|
||||
"""Atomically replace an asset without exposing a partial file to Vite."""
|
||||
temporary = destination.with_name(f".{destination.name}.tmp")
|
||||
try:
|
||||
shutil.copyfile(source, temporary)
|
||||
os.chmod(temporary, 0o644)
|
||||
temporary.replace(destination)
|
||||
finally:
|
||||
temporary.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.TemporaryDirectory(prefix="wakey-terminal-font.") as temp:
|
||||
work_dir = Path(temp)
|
||||
archive_path = work_dir / "JetBrainsMono.tar.xz"
|
||||
ttf_path = work_dir / f"{FONT_FILE}.ttf"
|
||||
woff2_path = work_dir / f"{FONT_FILE}.woff2"
|
||||
license_path = work_dir / "OFL.txt"
|
||||
|
||||
print(f"Downloading Nerd Fonts {NERD_FONTS_VERSION} JetBrainsMono archive")
|
||||
download(ARCHIVE_URL, archive_path)
|
||||
verify_sha256(archive_path, ARCHIVE_SHA256)
|
||||
|
||||
with tarfile.open(archive_path, "r:xz") as archive:
|
||||
extract_member(archive, ttf_path.name, ttf_path)
|
||||
extract_member(archive, license_path.name, license_path)
|
||||
|
||||
# FontTools otherwise stamps the current time into the head table,
|
||||
# making equivalent rebuilds produce different WOFF2 files.
|
||||
font = TTFont(ttf_path, recalcTimestamp=False)
|
||||
font.flavor = "woff2"
|
||||
font.save(woff2_path)
|
||||
verify_sha256(woff2_path, WOFF2_SHA256)
|
||||
|
||||
install_file(woff2_path, OUTPUT_DIR / woff2_path.name)
|
||||
install_file(license_path, OUTPUT_DIR / license_path.name)
|
||||
|
||||
print(f"Prepared {OUTPUT_DIR / f'{FONT_FILE}.woff2'}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+7
-6
@@ -15,12 +15,13 @@
|
||||
"@base-ui/react": "^1.4.0",
|
||||
"@fontsource-variable/inter": "^5.2.8",
|
||||
"@tailwindcss/vite": "^4.2.2",
|
||||
"@xterm/addon-clipboard": "^0.2.0",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-unicode-graphemes": "^0.4.0",
|
||||
"@xterm/addon-unicode11": "^0.9.0",
|
||||
"@xterm/addon-web-links": "^0.12.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"@xterm/addon-clipboard": "0.3.0-beta.290",
|
||||
"@xterm/addon-fit": "0.12.0-beta.290",
|
||||
"@xterm/addon-image": "0.10.0-beta.290",
|
||||
"@xterm/addon-unicode-graphemes": "0.5.0-beta.290",
|
||||
"@xterm/addon-unicode11": "0.10.0-beta.290",
|
||||
"@xterm/addon-web-links": "0.13.0-beta.290",
|
||||
"@xterm/xterm": "6.1.0-beta.290",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^1.8.0",
|
||||
|
||||
Generated
+61
-36
@@ -18,23 +18,26 @@ importers:
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2([email protected](@types/[email protected])([email protected]))
|
||||
'@xterm/addon-clipboard':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
specifier: 0.3.0-beta.290
|
||||
version: 0.3.0-beta.290(@xterm/[email protected])
|
||||
'@xterm/addon-fit':
|
||||
specifier: ^0.11.0
|
||||
version: 0.11.0
|
||||
specifier: 0.12.0-beta.290
|
||||
version: 0.12.0-beta.290(@xterm/[email protected])
|
||||
'@xterm/addon-image':
|
||||
specifier: 0.10.0-beta.290
|
||||
version: 0.10.0-beta.290(@xterm/[email protected])
|
||||
'@xterm/addon-unicode-graphemes':
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0
|
||||
specifier: 0.5.0-beta.290
|
||||
version: 0.5.0-beta.290(@xterm/[email protected])
|
||||
'@xterm/addon-unicode11':
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.0
|
||||
specifier: 0.10.0-beta.290
|
||||
version: 0.10.0-beta.290(@xterm/[email protected])
|
||||
'@xterm/addon-web-links':
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0
|
||||
specifier: 0.13.0-beta.290
|
||||
version: 0.13.0-beta.290(@xterm/[email protected])
|
||||
'@xterm/xterm':
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
specifier: 6.1.0-beta.290
|
||||
version: 6.1.0-beta.290
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
@@ -833,23 +836,38 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
|
||||
'@xterm/addon-clipboard@0.2.0':
|
||||
resolution: {integrity: sha512-Dl31BCtBhLaUEECUbEiVcCLvLBbaeGYdT7NofB8OJkGTD3MWgBsaLjXvfGAD4tQNHhm6mbKyYkR7XD8kiZsdNg==}
|
||||
'@xterm/addon-clipboard@0.3.0-beta.290':
|
||||
resolution: {integrity: sha512-ky44+eNKIxL9clMgrWCBRSRe1Y6Oj+ayNavySfFioScxpuyicu/GYaF7n97eqW3FW0eClElCtsK+wJ6AGqnJmw==}
|
||||
peerDependencies:
|
||||
'@xterm/xterm': ^6.1.0-beta.290
|
||||
|
||||
'@xterm/[email protected]1.0':
|
||||
resolution: {integrity: sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==}
|
||||
'@xterm/[email protected]2.0-beta.290':
|
||||
resolution: {integrity: sha512-mJ77s82VhARA8XhuWcUnWYKAZkEDzSnCzgBrT9qjHJlHWr+G5duDZcOBnxnVVNtp21LDKaONr3c7IxuLii1XNg==}
|
||||
peerDependencies:
|
||||
'@xterm/xterm': ^6.1.0-beta.290
|
||||
|
||||
'@xterm/addon-[email protected].0':
|
||||
resolution: {integrity: sha512-9+/CqwbKcnlkJU4d3wIgO+wjsL8f6vyz+UwUWLu6nADQz8Gr8ONqGCJfdDjIdI+yYZLABQqQy47FzEM6AWELjw==}
|
||||
'@xterm/addon-[email protected]0':
|
||||
resolution: {integrity: sha512-gzQfH3tACsdf52n0lY3QMwLY7HcII1ce+IvzphWs9UucQIZ3rBKjEkYz4sxBhhEP1Eb6dNn1uerrfoJECmcyGg==}
|
||||
peerDependencies:
|
||||
'@xterm/xterm': ^6.1.0-beta.290
|
||||
|
||||
'@xterm/addon-unicode[email protected].0':
|
||||
resolution: {integrity: sha512-FxDnYcyuXhNl+XSqGZL/t0U9eiNb/q3EWT5rYkQT/zuig8Gz/VagnQANKHdDWFM2lTMk9ly0EFQxxxtZUoRetw==}
|
||||
'@xterm/addon-unicode[email protected]0':
|
||||
resolution: {integrity: sha512-c0hF4HJrbZUlb1cVXNldl6YRf+Udoe3nc80djNnxSzjHgmryT09DhL4U4IDcCqSkzgwMENnN65mexlYKyNXNBw==}
|
||||
peerDependencies:
|
||||
'@xterm/xterm': ^6.1.0-beta.290
|
||||
|
||||
'@xterm/addon-[email protected].0':
|
||||
resolution: {integrity: sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==}
|
||||
'@xterm/addon-[email protected]0':
|
||||
resolution: {integrity: sha512-tSmvf6hX1tT4RAgqbC8moZc/2oLR3qkaAL0xHaSMPd5bch2wkSvjfTxDfb3cfXJLyKCkWR+uwH54JYNOnCTW5g==}
|
||||
peerDependencies:
|
||||
'@xterm/xterm': ^6.1.0-beta.290
|
||||
|
||||
'@xterm/[email protected].0':
|
||||
resolution: {integrity: sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==}
|
||||
'@xterm/[email protected]0':
|
||||
resolution: {integrity: sha512-p0OL4dPrmETt+6sFUniSjiV2g5nV35ckipVZQv1uuZ8GKtHYvlDTJj4Fr+cHzrsNmDhceaaiIW6fMPynJtCm1w==}
|
||||
peerDependencies:
|
||||
'@xterm/xterm': ^6.1.0-beta.290
|
||||
|
||||
'@xterm/[email protected]':
|
||||
resolution: {integrity: sha512-lbzQN1cx0h9uLUWQM7l6HxjP0qPMoqhc1ahlwmqgbSALpTeOUAQfMhMhHr0TSGRIcv7S4grvhhzQCb6teFrZ7Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
|
||||
@@ -1439,9 +1457,6 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-5xVjhUZlHHeuO2W7w2rDFj/Kl1xLX+HjZxdOQwCsUOifl6UaoH1o1wsbsTMz+r0aeC7gCijvru02j6TfKZWzKg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
@@ -2847,19 +2862,31 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@xterm/addon-clipboard@0.2.0':
|
||||
'@xterm/addon-clipboard@0.3.0-beta.290(@xterm/[email protected])':
|
||||
dependencies:
|
||||
js-base64: 3.8.1
|
||||
'@xterm/xterm': 6.1.0-beta.290
|
||||
|
||||
'@xterm/[email protected]1.0': {}
|
||||
'@xterm/[email protected]2.0-beta.290(@xterm/[email protected])':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.290
|
||||
|
||||
'@xterm/addon-[email protected]': {}
|
||||
'@xterm/addon-[email protected](@xterm/[email protected])':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.290
|
||||
|
||||
'@xterm/addon-unicode[email protected]': {}
|
||||
'@xterm/addon-unicode[email protected](@xterm/[email protected])':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.290
|
||||
|
||||
'@xterm/addon-[email protected]': {}
|
||||
'@xterm/addon-[email protected](@xterm/[email protected])':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.290
|
||||
|
||||
'@xterm/[email protected]': {}
|
||||
'@xterm/[email protected](@xterm/[email protected])':
|
||||
dependencies:
|
||||
'@xterm/xterm': 6.1.0-beta.290
|
||||
|
||||
'@xterm/[email protected]': {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
@@ -3403,8 +3430,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
|
||||
Binary file not shown.
@@ -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,16 @@
|
||||
# JetBrainsMono Nerd Font Mono
|
||||
|
||||
`JetBrainsMonoNerdFontMono-Regular.woff2` is the regular monospaced variant
|
||||
from Nerd Fonts v3.4.0 (JetBrains Mono v2.304), converted from the release TTF
|
||||
to WOFF2 without subsetting.
|
||||
|
||||
Source archive:
|
||||
https://github.com/ryanoasis/nerd-fonts/releases/download/v3.4.0/JetBrainsMono.tar.xz
|
||||
|
||||
The font is distributed under the SIL Open Font License in `OFL.txt`.
|
||||
|
||||
Rebuild the checked-in webfont with:
|
||||
|
||||
```sh
|
||||
./scripts/prepare_terminal_font.py
|
||||
```
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { ClipboardAddon } from "@xterm/addon-clipboard";
|
||||
import { FitAddon } from "@xterm/addon-fit";
|
||||
import { ImageAddon } from "@xterm/addon-image";
|
||||
import { Unicode11Addon } from "@xterm/addon-unicode11";
|
||||
import { UnicodeGraphemesAddon } from "@xterm/addon-unicode-graphemes";
|
||||
import { WebLinksAddon } from "@xterm/addon-web-links";
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { loadTerminalFontFamily } from "@/terminal/terminalFonts";
|
||||
|
||||
type Props = {
|
||||
agents: Agent[];
|
||||
@@ -158,6 +160,7 @@ export function TerminalPage({
|
||||
const [sessionTitles, setSessionTitles] = useState<Record<string, string>>(
|
||||
{},
|
||||
);
|
||||
const [terminalFontFamily, setTerminalFontFamily] = useState<string>();
|
||||
const [operatorId] = useState(terminalOperatorId);
|
||||
|
||||
activeSessionRef.current = session;
|
||||
@@ -304,16 +307,26 @@ export function TerminalPage({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hostRef.current) return;
|
||||
let cancelled = false;
|
||||
void loadTerminalFontFamily().then((fontFamily) => {
|
||||
if (!cancelled) setTerminalFontFamily(fontFamily);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hostRef.current || !terminalFontFamily) return;
|
||||
const terminal = new XTerm({
|
||||
cursorBlink: true,
|
||||
convertEol: false,
|
||||
fontFamily: '"SFMono-Regular", Consolas, "Liberation Mono", monospace',
|
||||
fontFamily: terminalFontFamily,
|
||||
fontSize: 14,
|
||||
lineHeight: 1.1,
|
||||
// xterm uses this width for its internal scrollbar as well as the
|
||||
// overview ruler. Reserve a larger touch target on coarse pointers.
|
||||
overviewRuler: {
|
||||
scrollbar: {
|
||||
width: window.matchMedia("(pointer: coarse)").matches ? 22 : 16,
|
||||
},
|
||||
scrollback: 5000,
|
||||
@@ -331,6 +344,16 @@ export function TerminalPage({
|
||||
terminal.unicode.activeVersion = "11";
|
||||
terminal.loadAddon(new UnicodeGraphemesAddon());
|
||||
terminal.loadAddon(new WebLinksAddon());
|
||||
terminal.loadAddon(
|
||||
new ImageAddon({
|
||||
// // Keep image terminals useful on phones without accepting the addon's
|
||||
// // much larger default decode and retained-canvas memory ceilings.
|
||||
// pixelLimit: 4 * 1024 * 1024,
|
||||
// sixelSizeLimit: 8 * 1024 * 1024,
|
||||
// iipSizeLimit: 8 * 1024 * 1024,
|
||||
// storageLimit: 32,
|
||||
}),
|
||||
);
|
||||
const fit = new FitAddon();
|
||||
terminal.loadAddon(new ClipboardAddon());
|
||||
terminal.loadAddon(fit);
|
||||
@@ -425,13 +448,6 @@ export function TerminalPage({
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
void document.fonts.ready.then(() => {
|
||||
if (cancelled) return;
|
||||
fit.fit();
|
||||
lastTerminalSizeRef.current = { rows: 0, cols: 0 };
|
||||
sendResize();
|
||||
});
|
||||
|
||||
const input = terminal.onData((data) => {
|
||||
const socket = socketRef.current;
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
@@ -482,7 +498,7 @@ export function TerminalPage({
|
||||
terminalRef.current = null;
|
||||
fitRef.current = null;
|
||||
};
|
||||
}, [connect, detachTransport, sendResize]);
|
||||
}, [connect, detachTransport, sendResize, terminalFontFamily]);
|
||||
|
||||
async function start() {
|
||||
if (!selectedAgentId || !terminalRef.current) return;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "Wakey JetBrainsMono Nerd Font Mono";
|
||||
src: url("../assets/fonts/jetbrains-mono-nerd/JetBrainsMonoNerdFontMono-Regular.woff2")
|
||||
format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import "./terminalFonts.css";
|
||||
|
||||
const TERMINAL_WEB_FONT = "Wakey JetBrainsMono Nerd Font Mono";
|
||||
const TERMINAL_FALLBACK_FONTS =
|
||||
'"SFMono-Regular", Consolas, "Liberation Mono", monospace';
|
||||
|
||||
let terminalFontsPromise: Promise<string> | undefined;
|
||||
|
||||
/**
|
||||
* Resolves the terminal font stack only after browser glyph metrics are ready.
|
||||
* xterm caches those metrics when it opens, so a late webfont load permanently
|
||||
* misaligns cells for that terminal instance.
|
||||
*/
|
||||
export function loadTerminalFontFamily(): Promise<string> {
|
||||
terminalFontsPromise ??= document.fonts
|
||||
.load(`400 14px "${TERMINAL_WEB_FONT}"`, "\uE0B0")
|
||||
.then((faces) => {
|
||||
if (faces.length === 0) {
|
||||
throw new Error("terminal webfont was not registered");
|
||||
}
|
||||
return `"${TERMINAL_WEB_FONT}", ${TERMINAL_FALLBACK_FONTS}`;
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
console.warn(
|
||||
"Could not load terminal webfont; using system monospace",
|
||||
error,
|
||||
);
|
||||
return TERMINAL_FALLBACK_FONTS;
|
||||
});
|
||||
return terminalFontsPromise;
|
||||
}
|
||||
Reference in New Issue
Block a user