Merge branch 'master' into configurable-hotkeys

This commit is contained in:
Levi Portenier 2026-02-04 11:31:16 -07:00 committed by GitHub
commit 974ca4ecc6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1490 additions and 787 deletions

1531
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-term"
version = "0.1.0"
version = "1.0.5"
authors = ["Jeremy Soller <jeremy@system76.com>"]
edition = "2024"
license = "GPL-3.0-only"

View file

@ -0,0 +1,41 @@
(
name: "Seal Brown Amber",
background: "#623004",
foreground: "#FFBF00",
cursor: "#FFBF00",
dim_foreground: "#cfa02e",
normal: (
black: "#2b1503",
red: "#c1311c",
green: "#8bab6c",
yellow: "#FFBF00",
blue: "#4b5f8a",
magenta: "#7a4a8f",
cyan: "#6b8f8a",
white: "#f1e6c8",
),
bright: (
black: "#4a2a10",
red: "#e0482f",
green: "#a9c98c",
yellow: "#ffd54a",
blue: "#6f86c9",
magenta: "#9b6fc2",
cyan: "#8fc9c3",
white: "#ffffff",
),
dim: (
black: "#1e0f02",
red: "#8f2414",
green: "#6e8a55",
yellow: "#c99a28",
blue: "#39476b",
magenta: "#5e3a70",
cyan: "#4f6f6b",
white: "#cdbf9e",
),
)

12
debian/changelog vendored
View file

@ -1,3 +1,15 @@
cosmic-term (1.0.5) noble; urgency=medium
* Epoch 1.0.5 version update
-- Jeremy Soller <jeremy@system76.com> Fri, 30 Jan 2026 17:16:48 -0700
cosmic-term (1.0.4) noble; urgency=medium
* Epoch 1.0.4 version update
-- Jeremy Soller <jeremy@system76.com> Wed, 21 Jan 2026 10:18:52 -0700
cosmic-term (0.1.0) jammy; urgency=medium
* Initial release.

32
flake.lock generated
View file

@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1746232882,
"narHash": "sha256-MHmBH2rS8KkRRdoU/feC/dKbdlMkcNkB5mwkuipVHeQ=",
"lastModified": 1766309749,
"narHash": "sha256-3xY8CZ4rSnQ0NqGhMKAy5vgC+2IVK0NoVEzDoOh4DA4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7a2622e2c0dbad5c4493cb268aba12896e28b008",
"rev": "a6531044f6d0bef691ea18d4d4ce44d0daa6e816",
"type": "github"
},
"original": {
@ -34,22 +34,6 @@
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1744536153,
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
@ -59,14 +43,16 @@
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs_2"
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1746239644,
"narHash": "sha256-wMvMBMlpS1H8CQdSSgpLeoCWS67ciEkN/GVCcwk7Apc=",
"lastModified": 1766630657,
"narHash": "sha256-wW15buPGU29v0XuAmDkc30+d5j4Tmg/V8AkpHH+hDWY=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "bd32e88bef6da0e021a42fb4120a8df2150e9b8c",
"rev": "3bf67c5e473f29ca79ff15904f3072d87cf6d087",
"type": "github"
},
"original": {

View file

@ -2,7 +2,10 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay.url = "github:oxalica/rust-overlay";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
flake-utils.lib.eachDefaultSystem
@ -15,7 +18,7 @@
in
with pkgs;
{
formatter = nixpkgs.legacyPackages.x86_64-linux.nixpkgs-fmt;
formatter = nixpkgs.legacyPackages.${system}.nixpkgs-fmt;
devShells.default = mkShell {
buildInputs = [
rust-bin.stable.latest.default

View file

@ -1,9 +1,9 @@
settings = Nastavení
appearance = Vzhled
theme = Téma
theme = Motiv
match-desktop = Podle systému
dark = Tmavé
light = Světlé
dark = Tmavý
light = Světlý
file = Soubor
quit = Ukončit
import = Importovat
@ -44,7 +44,7 @@ splits = Rozdělení
focus-follow-mouse = Zaměření psaní sleduje myš
advanced = Pokročilé
show-headerbar = Zobrazit hlavičku
show-header-description = Hlavičku je možné odkrýt z kontextového menu.
show-header-description = Hlavičku je možné odkrýt z kontextového menu
find-placeholder = Najít...
find-previous = Najít předchozí
find-next = Najít další

View file

@ -30,7 +30,7 @@ new-profile = Új profil
make-default = Beállítás alapértelmezettként
working-directory = Munkakönyvtár
hold = Maradjon nyitva
remain-open = Maradjon nyitva a gyermekfolyamat kilépése után.
remain-open = Maradjon nyitva a gyermekfolyamat kilépése után
## Settings
@ -51,7 +51,7 @@ opacity = Háttér átlátszósága
### Font
font = Betűtípus
advanced-font-settings = Haladó betűtípus-beállítások
advanced-font-settings = Speciális betűtípus-beállítások
default-font = Betűtípus
default-font-size = Betűméret
default-font-stretch = Betűszélesség
@ -67,11 +67,11 @@ focus-follow-mouse = A fókusz követi az egeret gépelés közben
### Advanced
advanced = Haladó
advanced = Speciális
show-headerbar = Fejléc megjelenítése
show-header-description = Fejléc megjelenítése a jobb gombos menüből.
show-header-description = Fejléc megjelenítése a jobb gombos menüből
# Find
find-placeholder = Keresés...
find-placeholder = Keresés
find-previous = Előző találat
find-next = Következő találat
@ -84,7 +84,7 @@ file = Fájl
new-tab = Új lap
new-window = Új ablak
profile = Profil
menu-profiles = Profilok...
menu-profiles = Profilok
close-tab = Lap bezárása
quit = Kilépés
@ -108,12 +108,12 @@ previous-tab = Előző lap
split-horizontal = Vízszintes felosztás
split-vertical = Függőleges felosztás
pane-toggle-maximize = Panel maximalizálása
menu-color-schemes = Színsémák...
menu-settings = Beállítások...
menu-about = A COSMIC Terminál névjegye...
menu-password-manager = Jelszavak...
menu-color-schemes = Színsémák
menu-settings = Beállítások
menu-about = A COSMIC Terminál névjegye
menu-password-manager = Jelszavak
passwords-title = Jelszavak
add-password = Jelszó hozzáadása
password-input = Jelszó
password-input-description = Leírás
open-link = Link megnyitása
open-link = Hivatkozás megnyitása

View file

@ -0,0 +1,79 @@
syntax-light = Skema warna terang
new-terminal = Terminal baru
repository = Repositori
support = Dukungan
color-schemes = Skema warna
cosmic-terminal = Terminal COSMIC
rename = Ganti nama
export = Ekspor
delete = Hapus
import = Impor
import-errors = Galat impor
profiles = Profil
name = Nama
command-line = Baris perintah
tab-title = Judul tab
tab-title-description = Timpakan judul tab bawaan
add-profile = Tambahkan profil
new-profile = Profil baru
make-default = Jadikan bawaan
working-directory = Direktori kerja
hold = Tahan
remain-open = Tetap buka setelah proses anak berakhir.
settings = Pengaturan
appearance = Tampilan
theme = Tema
match-desktop = Cocokkan desktop
dark = Gelap
light = Terang
syntax-dark = Skema warna gelap
default-zoom-step = Langkah pembesar
opacity = Opasitas latar belakang
font = Huruf
advanced-font-settings = Pengaturan huruf lanjutan
default-font = Huruf
default-font-size = Ukuran huruf
default-font-stretch = Peregangan huruf
default-font-weight = Bobot huruf normal
default-dim-font-weight = Bobot huruf redup
default-bold-font-weight = Bobot huruf tebal
use-bright-bold = Jadikan teks tebal lebih terang
splits = Pemisahan
focus-follow-mouse = Fokus pengetikan mengikuti tetikus
advanced = Lanjutan
show-header-description = Tampilkan tajuk dari menu klik kanan.
show-headerbar = Tampilkan tajuk
find-placeholder = Temukan...
find-previous = Temukan sebelumnya
find-next = Temukan selanjutnya
file = Berkas
new-tab = Tab baru
new-window = Jendela baru
profile = Profil
menu-profiles = Profil...
close-tab = Tutup tab
quit = Keluar
edit = Sunting
copy = Salin
paste = Tempel
select-all = Pilih semua
find = Temukan
clear-scrollback = Hapus riwayat gulir
open-link = Buka Tautan
view = Tampilan
zoom-in = Teks lebih besar
zoom-reset = Ukuran teks bawaan
zoom-out = Teks lebih kecil
next-tab = Tab selanjutnya
previous-tab = Tab sebelumnya
split-horizontal = Pemisahan horisontal
split-vertical = Pemisahan vertikal
pane-toggle-maximize = Alihkan ke maksimal
menu-color-schemes = Skema warna...
menu-settings = Pengaturan...
menu-about = Tentang Terminal COSMIC...
menu-password-manager = Kata Sandi...
passwords-title = Kata Sandi
add-password = Tambahkan Kata Sandi
password-input = Kata Sandi
password-input-description = Deskripsi

79
i18n/kk/cosmic_term.ftl Normal file
View file

@ -0,0 +1,79 @@
name = Аты
delete = Өшіру
repository = Репозиторий
support = Қолдау
settings = Баптаулар
appearance = Сыртқы түрі
theme = Тақырып
match-desktop = Жұмыс үстеліне сәйкес келу
dark = Қараңғы
light = Жарық
file = Файл
new-tab = Жаңа бет
new-window = Жаңа терезе
close-tab = Бетті жабу
quit = Шығу
edit = Түзету
copy = Көшіру
paste = Кірістіру
select-all = Барлығын таңдау
view = Көрініс
menu-settings = Баптаулар...
export = Экспорттау
import = Импорттау
default-zoom-step = Масштабтау қадамдары
find-placeholder = Табу...
find-previous = Алдыңғысын табу
find-next = Келесісін табу
find = Табу
cosmic-terminal = COSMIC терминалы
new-terminal = Жаңа терминал
color-schemes = Түстер схемалары
rename = Атын өзгерту
import-errors = Импорттау қателері
profiles = Профильдер
command-line = Командалық жол
tab-title = Бет атауы
tab-title-description = Әдепкі бет атауын алмастыру
add-profile = Профильді қосу
new-profile = Жаңа профиль
make-default = Әдепкі қылып орнату
working-directory = Жұмыс бумасы
hold = Ұстап тұру
remain-open = Туынды процесс аяқталғаннан кейін ашық күйде қалу.
syntax-dark = Қараңғы түстер схемасы
syntax-light = Ашық түстер схемасы
opacity = Фон мөлдірлігі
font = Қаріп
advanced-font-settings = Қаріптің қосымша баптаулары
default-font = Қаріп
default-font-size = Қаріп өлшемі
default-font-stretch = Қаріпті созу
default-font-weight = Қалыпты қаріптің қалыңдығы
default-dim-font-weight = Күңгірт қаріптің қалыңдығы
default-bold-font-weight = Жуан қаріптің қалыңдығы
use-bright-bold = Жуан мәтінді жарықтау қылу
splits = Бөліктер
focus-follow-mouse = Теру фокусы тышқан соңынан ереді
advanced = Қосымша
show-headerbar = Тақырыптаманы көрсету
show-header-description = Оң жақ батырма мәзірі арқылы тақырыптаманы көрсету.
profile = Профиль
menu-profiles = Профильдер...
clear-scrollback = Айналдыру тарихын тазарту
open-link = Сілтемені ашу
zoom-in = Үлкенірек мәтін
zoom-reset = Әдепкі мәтін өлшемі
zoom-out = Кішірек мәтін
next-tab = Келесі бет
previous-tab = Алдыңғы бет
split-horizontal = Горизонталды бөлу
split-vertical = Вертикалды бөлу
pane-toggle-maximize = Толық экран режимін ауыстыру
menu-color-schemes = Түс схемалары...
menu-about = COSMIC терминалы туралы...
menu-password-manager = Парольдер...
passwords-title = Парольдер
add-password = Пароль қосу
password-input = Пароль
password-input-description = Сипаттама

View file

@ -0,0 +1,79 @@
new-terminal = 새 터미널
quit = 종료
rename = 이름 바꾸기
theme = 테마
appearance = 외관
name = 이름
light = 라이트
delete = 삭제
repository = 저장소
support = 지원
dark = 다크
match-desktop = 시스템과 동기화
settings = 설정
file = 파일
cosmic-terminal = COSMIC 터미널
color-schemes = 색 구성표
import-errors = 가져오기 오류
import = 가져오기
profiles = 프로필
command-line = 명령줄
syntax-light = 라이트모드 색 구성표
default-font-weight = 일반 글꼴 굵기
add-profile = 프로필 추가
find-next = 다음 찾기
splits = 분할
zoom-in = 텍스트 크게
select-all = 모두 선택
previous-tab = 이전 탭
show-headerbar = 헤더 표시
new-window = 새 창
zoom-out = 텍스트 작게
split-vertical = 세로로 분할
syntax-dark = 다크모드 색 구성표
menu-profiles = 프로필...
tab-title-description = 기본 탭 제목 재정의
menu-about = COSMIC 터미널 정보...
remain-open = 자식 프로세스 종료 후 열린 상태를 유지합니다.
menu-color-schemes = 색 구성표...
working-directory = 작업 디렉터리
opacity = 배경 투명도
tab-title = 탭 제목
zoom-reset = 기본 텍스트 크기
passwords-title = 암호
edit = 편집
copy = 복사
pane-toggle-maximize = 최대화 전환
export = 내보내기
password-input = 암호
close-tab = 탭 닫기
default-dim-font-weight = 희미한 글꼴 굵기
advanced = 고급
use-bright-bold = 굵은 텍스트를 더 밝게 표시
default-bold-font-weight = 굵은 글꼴 굵기
show-header-description = 오른쪽 클릭 메뉴를 통해 헤더를 나타냅니다.
password-input-description = 설명
default-font = 글꼴
paste = 붙여넣기
menu-settings = 설정...
add-password = 암호 추가
view = 보기
default-font-stretch = 글꼴 늘이기
hold = 유지
menu-password-manager = 암호...
find-previous = 이전 찾기
default-font-size = 글꼴 크기
split-horizontal = 가로로 분할
find-placeholder = 찾기...
focus-follow-mouse = 마우스가 위치한 곳에 입력
advanced-font-settings = 고급 글꼴 설정
font = 글꼴
next-tab = 다음 탭
default-zoom-step = 확대/축소 간격
clear-scrollback = 이전 출력 내역 지우기
open-link = 링크 열기
make-default = 기본으로 설정
new-profile = 새 프로필
find = 찾기
profile = 프로필
new-tab = 새 탭

View file

@ -71,3 +71,5 @@ menu-password-manager = Slaptažodžiai...
passwords-title = Slaptažodžiai
add-password = Pridėti Slaptažodį
password-input = Slaptažodis
default-font-stretch = Šrifto tempimas
password-input-description = Aprašymas

0
i18n/ms/cosmic_term.ftl Normal file
View file

View file

@ -80,7 +80,7 @@ find-next = Zoek volgende
file = Bestand
new-tab = Nieuw tabblad
new-window = Nieuw venster
new-window = Nieuw venster openen
profile = Profiel
menu-profiles = Profielen...
close-tab = Sluit tabblad
@ -89,7 +89,7 @@ quit = Beëindig
## Edit
edit = Bewerk
copy = Kopieer
copy = Kopiëer
paste = Plak
select-all = Selecteer alles
find = Zoek
@ -107,7 +107,7 @@ split-horizontal = Splits horizontaal
split-vertical = Splits verticaal
pane-toggle-maximize = Gemaximaliseerd
menu-color-schemes = Kleurenpaletten...
menu-settings = Instellingen...
menu-settings = Instellingen
menu-about = Over COSMIC Terminal…
support = Ondersteuning
repository = Bibliotheek

0
i18n/pa/cosmic_term.ftl Normal file
View file

0
i18n/ti/cosmic_term.ftl Normal file
View file

View file

@ -9,9 +9,9 @@ new-terminal = Новий термінал
## Color schemes
color-schemes = Схеми кольорів
color-schemes = Кольорові схеми
rename = Перейменувати
export = Експортувати
export = Експорт
delete = Вилучити
import = Імпортувати
import-errors = Помилки імпорту
@ -21,7 +21,7 @@ import-errors = Помилки імпорту
profiles = Профілі
name = Назва
command-line = Командний рядок
tab-title = Назва вкладки
tab-title = Заголовок вкладки
tab-title-description = Замінити типовий заголовок вкладки
add-profile = Додати профіль
new-profile = Новий профіль
@ -41,27 +41,27 @@ theme = Тема
match-desktop = Відповідно системі
dark = Темна
light = Світла
syntax-dark = Темна схема кольорів
syntax-light = Світла схема кольорів
syntax-dark = Темна колірна схема
syntax-light = Світла колірна схема
default-zoom-step = Зміна масштабу
opacity = Непрозорість тла
### Font
font = Шрифт
advanced-font-settings = Розширені налаштування шрифтів
advanced-font-settings = Розширені налаштування шрифту
default-font = Шрифт
default-font-size = Розмір шрифту
default-font-stretch = Розтягнення шрифту
default-font-weight = Звичайна товщина шрифту
default-dim-font-weight = Товщина тьмяного шрифту
default-bold-font-weight = Товщина жирного шрифту
use-bright-bold = Збільшити яскравість жирного шрифту
default-font-weight = Звичайне накреслення шрифту
default-dim-font-weight = Приглушене накреслення шрифту
default-bold-font-weight = Жирне накреслення шрифту
use-bright-bold = Зробити жирний текст яскравішим
### Splits
splits = Розділення
focus-follow-mouse = Ділянка під мишею отримує фокус для введення даних
focus-follow-mouse = Фокус введення слідує за мишею
### Advanced
@ -105,15 +105,15 @@ previous-tab = Попередня вкладка
split-horizontal = Розділити горизонтально
split-vertical = Розділити вертикально
pane-toggle-maximize = Перемкнути розгортання
menu-color-schemes = Схеми кольорів...
menu-color-schemes = Кольорові схеми...
menu-settings = Налаштування...
menu-about = Про Термінал COSMIC...
repository = Репозиторій
support = Підтримка
clear-scrollback = Очистити текст поза межами екрану
clear-scrollback = Очистити журнал прокрутки
menu-password-manager = Паролі...
passwords-title = Паролі
add-password = Додати пароль
password-input = Пароль
password-input-description = Опис
open-link = Відкрити посилання
open-link = Відкрити ланку

0
i18n/uz/cosmic_term.ftl Normal file
View file

View file

@ -22,9 +22,9 @@ profiles = 配置文件
name = 名称
command-line = 命令行
tab-title = 标签标题
tab-title-description = 覆盖默认标签标题
add-profile = 创建配置文件
new-profile = 新建配置文件
tab-title-description = 覆盖默认标签标题
add-profile = 添加配置
new-profile = 新建配置
make-default = 设为默认配置
working-directory = 工作目录
hold = 保留
@ -67,10 +67,10 @@ focus-follow-mouse = 聚焦窗口跟随鼠标
advanced = 高级
show-headerbar = 显示标题栏
show-header-description = 右键菜单显示标题栏。
show-header-description = 右键菜单提供显示标题栏选项
# Find
find-placeholder = 查找...
find-previous = 上一个
find-previous = 查找上一个
find-next = 查找下一个
# Menu
@ -79,11 +79,11 @@ find-next = 查找下一个
## File
file = 文件
new-tab = 新建标签
new-tab = 新建标签
new-window = 新建窗口
profile = 配置文件
profile = 配置
menu-profiles = 配置文件...
close-tab = 关闭标签
close-tab = 关闭标签
quit = 退出
## Edit
@ -98,11 +98,11 @@ clear-scrollback = 清除滚动缓冲区
## View
view = 视图
zoom-in = 大字体
zoom-in = 大字体
zoom-reset = 默认字体大小
zoom-out = 缩小字体
next-tab = 下一个标签
previous-tab = 上一个标签
next-tab = 下一个标签
previous-tab = 上一个标签
split-horizontal = 水平分割
split-vertical = 垂直分割
pane-toggle-maximize = 切换最大化
@ -111,3 +111,9 @@ menu-settings = 设置...
menu-about = 关于 COSMIC 终端...
repository = 仓库
support = 支持
open-link = 打开链接
menu-password-manager = 密码…
passwords-title = 密码
add-password = 添加密码
password-input = 密码
password-input-description = 描述

View file

@ -2934,6 +2934,15 @@ impl Application for App {
self.core.window.show_context = !self.core.window.show_context;
self.pane_model.update_terminal_focus();
#[cfg(feature = "password_manager")]
if ContextPage::PasswordManager == context_page {
if self.core.window.show_context {
self.password_mgr.pane = Some(self.pane_model.focused());
return self.password_mgr.refresh_password_list();
} else {
self.password_mgr.clear();
}
}
return self.update_focus();
} else {
self.context_page = context_page;
@ -2968,12 +2977,8 @@ impl Application for App {
#[cfg(feature = "password_manager")]
if ContextPage::PasswordManager == context_page {
if self.core.window.show_context {
self.password_mgr.pane = Some(self.pane_model.focused());
return self.password_mgr.refresh_password_list();
} else {
self.password_mgr.clear();
}
self.password_mgr.pane = Some(self.pane_model.focused());
return self.password_mgr.refresh_password_list();
}
}
Message::UpdateDefaultProfile((default, profile_id)) => {

View file

@ -204,7 +204,7 @@ pub fn menu_bar<'a>(
responsive_menu_bar()
.item_height(ItemHeight::Dynamic(40))
.item_width(ItemWidth::Uniform(240))
.item_width(ItemWidth::Uniform(320))
.spacing(4.0)
.into_element(
core,

View file

@ -246,6 +246,7 @@ pub struct Terminal {
pub url_regex_search: RegexSearch,
pub regex_matches: Vec<alacritty_terminal::term::search::Match>,
pub active_regex_match: Option<alacritty_terminal::term::search::Match>,
pub active_hyperlink_id: Option<String>,
bold_font_weight: Weight,
buffer: Arc<Buffer>,
is_focused: bool,
@ -335,6 +336,7 @@ impl Terminal {
Ok(Self {
active_regex_match: None,
active_hyperlink_id: None,
url_regex_search: url_regex_search(),
regex_matches: Vec::new(),
bold_font_weight: Weight(bold_font_weight),
@ -887,6 +889,28 @@ impl Terminal {
flags |= Flags::UNDERLINE;
}
}
if let Some(active_id) = &self.active_hyperlink_id {
let mut matches_active = indexed
.cell
.hyperlink()
.is_some_and(|link| link.id() == active_id);
if !matches_active
&& indexed.cell.flags.intersects(
Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER,
)
&& indexed.point.column.0 > 0
{
matches_active = grid[Point::new(
indexed.point.line,
Column(indexed.point.column.0 - 1),
)]
.hyperlink()
.is_some_and(|link| link.id() == active_id);
}
if matches_active {
flags |= Flags::UNDERLINE;
}
}
let metadata = Metadata::new(bg, fg)
.with_flags(flags)

View file

@ -343,11 +343,7 @@ where
let location = terminal
.viewport_to_point(TermPoint::new(row as usize, TermColumn(col as usize)));
if terminal
.regex_matches
.iter()
.any(|bounds| bounds.contains(&location))
{
if get_hyperlink(&terminal, location).is_some() {
return mouse::Interaction::Pointer;
}
}
@ -731,66 +727,75 @@ where
state.scrollbar_rect.set(Rectangle::default())
}
// Draw cursor
// Draw cursor (only when not scrolled, as cursor is at bottom of active area)
{
let cursor = terminal.term.lock().renderable_content().cursor;
let col = cursor.point.column.0;
let line = cursor.point.line.0;
let color = terminal.term.lock().colors()[NamedColor::Cursor]
.or(terminal.colors()[NamedColor::Cursor])
.map(|rgb| Color::from_rgb8(rgb.r, rgb.g, rgb.b))
.unwrap_or(Color::WHITE); // TODO default color from theme?
let width = terminal.size().cell_width;
let height = terminal.size().cell_height;
let top_left = view_position
+ Vector::new((col as f32 * width).floor(), (line as f32 * height).floor());
match cursor.shape {
CursorShape::Beam => {
let quad = Quad {
bounds: Rectangle::new(top_left, Size::new(1.0, height)),
..Default::default()
};
renderer.fill_quad(quad, color);
}
CursorShape::Underline => {
let quad = Quad {
bounds: Rectangle::new(
view_position
+ Vector::new(
(col as f32 * width).floor(),
((line + 1) as f32 * height).floor(),
),
Size::new(width, 1.0),
),
..Default::default()
};
renderer.fill_quad(quad, color);
}
CursorShape::Block if !state.is_focused => {
let quad = Quad {
bounds: Rectangle::new(top_left, Size::new(width, height)),
border: Border {
width: 1.0,
color,
let term = terminal.term.lock();
let display_offset = term.grid().display_offset();
let cursor = term.renderable_content().cursor;
drop(term);
// Skip drawing cursor when scrolled - the cursor is below the visible viewport
if display_offset > 0 {
// Cursor is off-screen when scrolled up
} else {
let col = cursor.point.column.0;
let line = cursor.point.line.0;
let color = terminal.term.lock().colors()[NamedColor::Cursor]
.or(terminal.colors()[NamedColor::Cursor])
.map(|rgb| Color::from_rgb8(rgb.r, rgb.g, rgb.b))
.unwrap_or(Color::WHITE); // TODO default color from theme?
let width = terminal.size().cell_width;
let height = terminal.size().cell_height;
let top_left = view_position
+ Vector::new((col as f32 * width).floor(), (line as f32 * height).floor());
match cursor.shape {
CursorShape::Beam => {
let quad = Quad {
bounds: Rectangle::new(top_left, Size::new(1.0, height)),
..Default::default()
},
..Default::default()
};
renderer.fill_quad(quad, Color::TRANSPARENT);
}
CursorShape::HollowBlock => {
let quad = Quad {
bounds: Rectangle::new(top_left, Size::new(width, height)),
border: Border {
width: 1.0,
color,
};
renderer.fill_quad(quad, color);
}
CursorShape::Underline => {
let quad = Quad {
bounds: Rectangle::new(
view_position
+ Vector::new(
(col as f32 * width).floor(),
((line + 1) as f32 * height).floor(),
),
Size::new(width, 1.0),
),
..Default::default()
},
..Default::default()
};
renderer.fill_quad(quad, Color::TRANSPARENT);
};
renderer.fill_quad(quad, color);
}
CursorShape::Block if !state.is_focused => {
let quad = Quad {
bounds: Rectangle::new(top_left, Size::new(width, height)),
border: Border {
width: 1.0,
color,
..Default::default()
},
..Default::default()
};
renderer.fill_quad(quad, Color::TRANSPARENT);
}
CursorShape::HollowBlock => {
let quad = Quad {
bounds: Rectangle::new(top_left, Size::new(width, height)),
border: Border {
width: 1.0,
color,
..Default::default()
},
..Default::default()
};
renderer.fill_quad(quad, Color::TRANSPARENT);
}
CursorShape::Block | CursorShape::Hidden => {} // Block is handled seperately
}
CursorShape::Block | CursorShape::Hidden => {} // Block is handled seperately
}
}
@ -1009,12 +1014,15 @@ where
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
state.modifiers = modifiers;
if modifiers.contains(Modifiers::CTRL) || terminal.active_regex_match.is_some() {
if modifiers.contains(Modifiers::CTRL)
|| terminal.active_regex_match.is_some()
|| terminal.active_hyperlink_id.is_some()
{
//Might need to update the url regex highlight,
//so we need to calculate the mouse position
let location = if let Some(p) = cursor_position.position() {
let x = (p.x - layout.bounds().x) - self.padding.left;
let y = (p.y - layout.bounds().y) - self.padding.top;
let location = if let Some(p) = cursor_position.position_in(layout.bounds()) {
let x = p.x - self.padding.left;
let y = p.y - self.padding.top;
//TODO: better calculation of position
let col = x / terminal.size().cell_width;
let row = y / terminal.size().cell_height;
@ -1152,18 +1160,30 @@ where
} else {
TermSide::Right
};
let selection = match click_kind {
ClickKind::Single => {
Selection::new(SelectionType::Simple, location, side)
// Check if shift is pressed and there's an existing selection to extend
if state.modifiers.shift() {
let mut term = terminal.term.lock();
if let Some(ref mut selection) = term.selection {
selection.update(location, side);
} else {
term.selection = Some(Selection::new(
SelectionType::Simple,
location,
side,
));
}
ClickKind::Double => {
Selection::new(SelectionType::Semantic, location, side)
}
ClickKind::Triple => {
Selection::new(SelectionType::Lines, location, side)
}
};
{
} else {
let selection = match click_kind {
ClickKind::Single => {
Selection::new(SelectionType::Simple, location, side)
}
ClickKind::Double => {
Selection::new(SelectionType::Semantic, location, side)
}
ClickKind::Triple => {
Selection::new(SelectionType::Lines, location, side)
}
};
let mut term = terminal.term.lock();
term.selection = Some(selection);
}
@ -1311,29 +1331,38 @@ where
self.mouse_inside_boundary = Some(mouse_is_inside);
}
}
if let Some(p) = cursor_position.position() {
if let Some(p_global) = cursor_position.position() {
let bounds = layout.bounds();
let x = (p.x - bounds.x) - self.padding.left;
let y = (p.y - bounds.y) - self.padding.top;
//TODO: better calculation of position
let col = x / terminal.size().cell_width;
let row = y / terminal.size().cell_height;
let location = terminal
.viewport_to_point(TermPoint::new(row as usize, TermColumn(col as usize)));
update_active_regex_match(
&mut terminal,
Some(location),
Some(&state.modifiers),
);
let col_row_opt = if let Some(p) = cursor_position.position_in(bounds) {
let x = p.x - self.padding.left;
let y = p.y - self.padding.top;
//TODO: better calculation of position
let col = x / terminal.size().cell_width;
let row = y / terminal.size().cell_height;
let location = terminal.viewport_to_point(TermPoint::new(
row as usize,
TermColumn(col as usize),
));
update_active_regex_match(
&mut terminal,
Some(location),
Some(&state.modifiers),
);
Some((col, row))
} else {
None
};
if is_mouse_mode {
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
if let Some((col, row)) = col_row_opt {
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
}
} else {
let handled_buffer_drag = update_buffer_drag(
state,
&mut terminal,
buffer_size,
p,
p_global,
bounds,
self.padding,
0.0,
@ -1345,6 +1374,7 @@ where
start_scroll,
}) = state.dragging.as_mut()
{
let y = p_global.y - bounds.y - self.padding.top;
let start_y = *start_y;
let start_scroll = *start_scroll;
let scroll_offset = terminal.with_buffer(|buffer| {
@ -1359,9 +1389,9 @@ where
state.autoscroll.stop();
} else {
if state.autoscroll.is_active() {
state.autoscroll.update_pointer(p);
state.autoscroll.update_pointer(p_global);
} else {
state.autoscroll.start(p);
state.autoscroll.start(p_global);
}
shell.request_redraw(RedrawRequest::NextFrame);
}
@ -1374,8 +1404,8 @@ where
Event::Mouse(MouseEvent::WheelScrolled { delta }) => {
if let Some(p) = cursor_position.position_in(layout.bounds()) {
if is_mouse_mode {
let x = (p.x - layout.bounds().x) - self.padding.left;
let y = (p.y - layout.bounds().y) - self.padding.top;
let x = p.x - self.padding.left;
let y = p.y - self.padding.top;
//TODO: better calculation of position
let col = x / terminal.size().cell_width;
let row = y / terminal.size().cell_height;
@ -1449,6 +1479,9 @@ fn get_hyperlink(
terminal: &std::sync::MutexGuard<'_, Terminal>,
location: TermPoint,
) -> Option<String> {
if let Some(link) = osc8_hyperlink_at(terminal, location) {
return Some(link.uri().to_string());
}
if let Some(match_) = terminal
.regex_matches
.iter()
@ -1461,6 +1494,37 @@ fn get_hyperlink(
}
}
fn get_hyperlink_id(
terminal: &std::sync::MutexGuard<'_, Terminal>,
location: TermPoint,
) -> Option<String> {
osc8_hyperlink_at(terminal, location).map(|link| link.id().to_string())
}
fn osc8_hyperlink_at(
terminal: &std::sync::MutexGuard<'_, Terminal>,
location: TermPoint,
) -> Option<alacritty_terminal::term::cell::Hyperlink> {
let term = terminal.term.lock();
if location.line >= term.screen_lines() || location.column.0 >= term.columns() {
return None;
}
let grid = term.grid();
let cell = &grid[location];
if let Some(link) = cell.hyperlink() {
return Some(link);
}
if cell
.flags
.intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER)
&& location.column.0 > 0
{
let left = TermPoint::new(location.line, TermColumn(location.column.0 - 1));
return grid[left].hyperlink();
}
None
}
fn update_active_regex_match(
terminal: &mut std::sync::MutexGuard<'_, Terminal>,
location: Option<TermPoint>,
@ -1473,6 +1537,9 @@ fn update_active_regex_match(
return;
}
let allow_hyperlink = modifiers
.map(|mods| mods.contains(Modifiers::CTRL))
.unwrap_or(false);
//Require CTRL for keyboard and mouse interaction
if let Some(modifiers) = modifiers {
if !modifiers.contains(Modifiers::CTRL) {
@ -1480,16 +1547,37 @@ fn update_active_regex_match(
terminal.active_regex_match = None;
terminal.needs_update = true;
}
if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
return;
}
} else if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
let Some(location) = location else {
if terminal.active_regex_match.is_some() {
terminal.active_regex_match = None;
terminal.needs_update = true;
}
if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
return;
};
if allow_hyperlink {
let next_hyperlink_id = get_hyperlink_id(terminal, location);
if terminal.active_hyperlink_id != next_hyperlink_id {
terminal.active_hyperlink_id = next_hyperlink_id;
terminal.needs_update = true;
}
} else if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
if let Some(match_) = terminal
.regex_matches
.iter()