Merge remote-tracking branch 'upstream/main' into jacob/uucode
commit
c67f51f3ee
|
|
@ -36,13 +36,13 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- name: Setup Nix
|
||||
uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
|
|||
|
|
@ -83,13 +83,13 @@ jobs:
|
|||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
|
|
|
|||
|
|
@ -107,12 +107,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ jobs:
|
|||
- build-bench
|
||||
- build-dist
|
||||
- build-flatpak
|
||||
- build-freebsd
|
||||
- build-linux
|
||||
- build-linux-libghostty
|
||||
- build-nix
|
||||
|
|
@ -20,7 +21,6 @@ jobs:
|
|||
- build-macos
|
||||
- build-macos-matrix
|
||||
- build-windows
|
||||
- flatpak-check-zig-cache
|
||||
- test
|
||||
- test-gtk
|
||||
- test-gtk-ng
|
||||
|
|
@ -70,14 +70,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -101,14 +101,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -137,14 +137,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -166,14 +166,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -199,14 +199,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -243,14 +243,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -378,7 +378,7 @@ jobs:
|
|||
mkdir dist
|
||||
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
|
|
@ -474,14 +474,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -519,14 +519,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -568,14 +568,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -616,14 +616,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -674,12 +674,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -702,12 +702,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -729,12 +729,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -756,12 +756,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -783,12 +783,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -810,12 +810,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -844,12 +844,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -871,12 +871,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -906,14 +906,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -954,33 +954,6 @@ jobs:
|
|||
build-args: |
|
||||
DISTRO_VERSION=13
|
||||
|
||||
flatpak-check-zig-cache:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- name: Setup Nix
|
||||
uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
useDaemon: false # sometimes fails on short jobs
|
||||
- name: Check Flatpak Zig Dependencies
|
||||
run: nix develop -c ./flatpak/build-support/check-zig-cache.sh
|
||||
|
||||
flatpak:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
name: "Flatpak"
|
||||
|
|
@ -996,7 +969,7 @@ jobs:
|
|||
- arch: aarch64
|
||||
runner: namespace-profile-ghostty-md-arm64
|
||||
runs-on: ${{ matrix.variant.runner }}
|
||||
needs: [flatpak-check-zig-cache, test]
|
||||
needs: test
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c # v6.5
|
||||
|
|
@ -1020,14 +993,14 @@ jobs:
|
|||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
- uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -1043,3 +1016,57 @@ jobs:
|
|||
- name: valgrind
|
||||
run: |
|
||||
nix develop -c zig build test-valgrind
|
||||
|
||||
build-freebsd:
|
||||
name: Build on FreeBSD
|
||||
needs: test
|
||||
runs-on: namespace-profile-mitchellh-sm-systemd
|
||||
strategy:
|
||||
matrix:
|
||||
release:
|
||||
- "14.3"
|
||||
# - "15.0" # disable until fixed: https://github.com/vmactions/freebsd-vm/issues/108
|
||||
steps:
|
||||
- name: Checkout Ghostty
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Start SSH
|
||||
run: |
|
||||
sudo systemctl start ssh
|
||||
|
||||
- name: Set up FreeBSD VM
|
||||
uses: vmactions/freebsd-vm@05856381fab64eeee9b038a0818f6cec649ca17a # v1.2.3
|
||||
with:
|
||||
release: ${{ matrix.release }}
|
||||
copyback: false
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y \
|
||||
devel/blueprint-compiler \
|
||||
devel/gettext \
|
||||
devel/git \
|
||||
devel/pkgconf \
|
||||
graphics/wayland \
|
||||
lang/zig \
|
||||
security/ca_root_nss \
|
||||
textproc/hs-pandoc \
|
||||
x11-fonts/jetbrains-mono \
|
||||
x11-toolkits/libadwaita \
|
||||
x11-toolkits/gtk40 \
|
||||
x11-toolkits/gtk4-layer-shell
|
||||
|
||||
run: |
|
||||
zig env
|
||||
|
||||
- name: Run tests
|
||||
shell: freebsd {0}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
zig build test
|
||||
|
||||
- name: Build GTK-NG app runtime
|
||||
shell: freebsd {0}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
zig build
|
||||
./zig-out/bin/ghostty +version
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ jobs:
|
|||
fetch-depth: 0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@305bfa7ea980a858d511af4899414a84847c7991 # v1.2.16
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
- name: Setup Nix
|
||||
uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2
|
||||
uses: cachix/install-nix-action@56a7bb7b56d9a92d4fd1bc05758de7eea4a370a8 # v31.6.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -50,8 +50,6 @@ jobs:
|
|||
if ! git diff --exit-code build.zig.zon; then
|
||||
nix develop -c ./nix/build-support/check-zig-cache.sh --update
|
||||
nix develop -c ./nix/build-support/check-zig-cache.sh
|
||||
nix develop -c ./flatpak/build-support/check-zig-cache.sh --update
|
||||
nix develop -c ./flatpak/build-support/check-zig-cache.sh
|
||||
fi
|
||||
|
||||
# Verify the build still works. We choose an arbitrary build type
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@
|
|||
/po/es_BO.UTF-8.po @ghostty-org/es_BO
|
||||
/po/es_AR.UTF-8.po @ghostty-org/es_AR
|
||||
/po/fr_FR.UTF-8.po @ghostty-org/fr_FR
|
||||
/po/hu_HU.UTF-8.po @ghostty-org/hu_HU
|
||||
/po/id_ID.UTF-8.po @ghostty-org/id_ID
|
||||
/po/ja_JP.UTF-8.po @ghostty-org/ja_JP
|
||||
/po/mk_MK.UTF-8.po @ghostty-org/mk_MK
|
||||
|
|
|
|||
265
CONTRIBUTING.md
265
CONTRIBUTING.md
|
|
@ -1,9 +1,9 @@
|
|||
# Ghostty Development Process
|
||||
# Contributing to Ghostty
|
||||
|
||||
This document describes the development process for Ghostty. It is intended for
|
||||
anyone considering opening an **issue** or **pull request**. If in doubt,
|
||||
please open a [discussion](https://github.com/ghostty-org/ghostty/discussions);
|
||||
we can always convert that to an issue later.
|
||||
This document describes the process of contributing to Ghostty. It is intended
|
||||
for anyone considering opening an **issue**, **discussion** or **pull request**.
|
||||
For people who are interested in developing Ghostty and technical details behind
|
||||
it, please check out our ["Developing Ghostty"](HACKING.md) document as well.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
|
|
@ -49,13 +49,16 @@ Please be respectful to maintainers and disclose AI assistance.
|
|||
|
||||
## Quick Guide
|
||||
|
||||
**I'd like to contribute!**
|
||||
### I'd like to contribute!
|
||||
|
||||
All issues are actionable. Pick one and start working on it. Thank you.
|
||||
If you need help or guidance, comment on the issue. Issues that are extra
|
||||
friendly to new contributors are tagged with "contributor friendly".
|
||||
[All issues are actionable](#issues-are-actionable). Pick one and start
|
||||
working on it. Thank you. If you need help or guidance, comment on the issue.
|
||||
Issues that are extra friendly to new contributors are tagged with
|
||||
["contributor friendly"].
|
||||
|
||||
**I'd like to translate Ghostty to my language!**
|
||||
["contributor friendly"]: https://github.com/ghostty-org/ghostty/issues?q=is%3Aissue%20is%3Aopen%20label%3A%22contributor%20friendly%22
|
||||
|
||||
### I'd like to translate Ghostty to my language!
|
||||
|
||||
We have written a [Translator's Guide](po/README_TRANSLATORS.md) for
|
||||
everyone interested in contributing translations to Ghostty.
|
||||
|
|
@ -64,25 +67,39 @@ and you can submit pull requests directly, although please make sure that
|
|||
our [Style Guide](po/README_TRANSLATORS.md#style-guide) is followed before
|
||||
submission.
|
||||
|
||||
**I have a bug!**
|
||||
### I have a bug! / Something isn't working!
|
||||
|
||||
1. Search the issue tracker and discussions for similar issues.
|
||||
2. If you don't have steps to reproduce, open a discussion.
|
||||
3. If you have steps to reproduce, open an issue.
|
||||
1. Search the issue tracker and discussions for similar issues. Tip: also
|
||||
search for [closed issues] and [discussions] — your issue might have already
|
||||
been fixed!
|
||||
2. If your issue hasn't been reported already, open an ["Issue Triage" discussion]
|
||||
and make sure to fill in the template **completely**. They are vital for
|
||||
maintainers to figure out important details about your setup. Because of
|
||||
this, please make sure that you _only_ use the "Issue Triage" category for
|
||||
reporting bugs — thank you!
|
||||
|
||||
**I have an idea for a feature!**
|
||||
[closed issues]: https://github.com/ghostty-org/ghostty/issues?q=is%3Aissue%20state%3Aclosed
|
||||
[discussions]: https://github.com/ghostty-org/ghostty/discussions?discussions_q=is%3Aclosed
|
||||
["Issue Triage" discussion]: https://github.com/ghostty-org/ghostty/discussions/new?category=issue-triage
|
||||
|
||||
1. Open a discussion.
|
||||
### I have an idea for a feature!
|
||||
|
||||
**I've implemented a feature!**
|
||||
Open a discussion in the ["Feature Requests, Ideas" category](https://github.com/ghostty-org/ghostty/discussions/new?category=feature-requests-ideas).
|
||||
|
||||
1. If there is an issue for the feature, open a pull request.
|
||||
### I've implemented a feature!
|
||||
|
||||
1. If there is an issue for the feature, open a pull request straight away.
|
||||
2. If there is no issue, open a discussion and link to your branch.
|
||||
3. If you want to live dangerously, open a pull request and hope for the best.
|
||||
3. If you want to live dangerously, open a pull request and
|
||||
[hope for the best](#pull-requests-implement-an-issue).
|
||||
|
||||
**I have a question!**
|
||||
### I have a question!
|
||||
|
||||
1. Open a discussion or use Discord.
|
||||
Open an [Q&A discussion], or join our [Discord Server] and ask away in the
|
||||
`#help` channel.
|
||||
|
||||
[Q&A discussion]: https://github.com/ghostty-org/ghostty/discussions/new?category=q-a
|
||||
[Discord Server]: https://discord.gg/ghostty
|
||||
|
||||
## General Patterns
|
||||
|
||||
|
|
@ -120,209 +137,3 @@ pull request will be accepted with a high degree of certainty.
|
|||
> **Pull requests are NOT a place to discuss feature design.** Please do
|
||||
> not open a WIP pull request to discuss a feature. Instead, use a discussion
|
||||
> and link to your branch.
|
||||
|
||||
# Developer Guide
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> **The remainder of this file is dedicated to developers actively
|
||||
> working on Ghostty.** If you're a user reporting an issue, you can
|
||||
> ignore the rest of this document.
|
||||
|
||||
## Including and Updating Translations
|
||||
|
||||
See the [Contributor's Guide](po/README_CONTRIBUTORS.md) for more details.
|
||||
|
||||
## Checking for Memory Leaks
|
||||
|
||||
While Zig does an amazing job of finding and preventing memory leaks,
|
||||
Ghostty uses many third-party libraries that are written in C. Improper usage
|
||||
of those libraries or bugs in those libraries can cause memory leaks that
|
||||
Zig cannot detect by itself.
|
||||
|
||||
### On Linux
|
||||
|
||||
On Linux the recommended tool to check for memory leaks is Valgrind. The
|
||||
recommended way to run Valgrind is via `zig build`:
|
||||
|
||||
```sh
|
||||
zig build run-valgrind
|
||||
```
|
||||
|
||||
This builds a Ghostty executable with Valgrind support and runs Valgrind
|
||||
with the proper flags to ensure we're suppressing known false positives.
|
||||
|
||||
You can combine the same build args with `run-valgrind` that you can with
|
||||
`run`, such as specifying additional configurations after a trailing `--`.
|
||||
|
||||
## Input Stack Testing
|
||||
|
||||
The input stack is the part of the codebase that starts with a
|
||||
key event and ends with text encoding being sent to the pty (it
|
||||
does not include _rendering_ the text, which is part of the
|
||||
font or rendering stack).
|
||||
|
||||
If you modify any part of the input stack, you must manually verify
|
||||
all the following input cases work properly. We unfortunately do
|
||||
not automate this in any way, but if we can do that one day that'd
|
||||
save a LOT of grief and time.
|
||||
|
||||
Note: this list may not be exhaustive, I'm still working on it.
|
||||
|
||||
### Linux IME
|
||||
|
||||
IME (Input Method Editors) are a common source of bugs in the input stack,
|
||||
especially on Linux since there are multiple different IME systems
|
||||
interacting with different windowing systems and application frameworks
|
||||
all written by different organizations.
|
||||
|
||||
The following matrix should be tested to ensure that all IME input works
|
||||
properly:
|
||||
|
||||
1. Wayland, X11
|
||||
2. ibus, fcitx, none
|
||||
3. Dead key input (e.g. Spanish), CJK (e.g. Japanese), Emoji, Unicode Hex
|
||||
4. ibus versions: 1.5.29, 1.5.30, 1.5.31 (each exhibit slightly different behaviors)
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> This is a **work in progress**. I'm still working on this list and it
|
||||
> is not complete. As I find more test cases, I will add them here.
|
||||
|
||||
#### Dead Key Input
|
||||
|
||||
Set your keyboard layout to "Spanish" (or another layout that uses dead keys).
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `'`
|
||||
3. Press `a`
|
||||
4. Verify that `á` is displayed
|
||||
|
||||
Note that the dead key may or may not show a preedit state visually.
|
||||
For ibus and fcitx it does but for the "none" case it does not. Importantly,
|
||||
the text should be correct when it is sent to the pty.
|
||||
|
||||
We should also test canceling dead key input:
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `'`
|
||||
3. Press escape
|
||||
4. Press `a`
|
||||
5. Verify that `a` is displayed (no diacritic)
|
||||
|
||||
#### CJK Input
|
||||
|
||||
Configure fcitx or ibus with a keyboard layout like Japanese or Mozc. The
|
||||
exact layout doesn't matter.
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||
4. Press `Enter`
|
||||
5. Verify that `こん` is displayed in the terminal.
|
||||
|
||||
We should also test switching input methods while preedit is active, which
|
||||
should commit the text:
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||
4. Press `Ctrl+Shift` to switch to another layout (any)
|
||||
5. Verify that `こん` is displayed in the terminal as committed text.
|
||||
|
||||
## Nix Virtual Machines
|
||||
|
||||
Several Nix virtual machine definitions are provided by the project for testing
|
||||
and developing Ghostty against multiple different Linux desktop environments.
|
||||
|
||||
Running these requires a working Nix installation, either Nix on your
|
||||
favorite Linux distribution, NixOS, or macOS with nix-darwin installed. Further
|
||||
requirements for macOS are detailed below.
|
||||
|
||||
VMs should only be run on your local desktop and then powered off when not in
|
||||
use, which will discard any changes to the VM.
|
||||
|
||||
The VM definitions provide minimal software "out of the box" but additional
|
||||
software can be installed by using standard Nix mechanisms like `nix run nixpkgs#<package>`.
|
||||
|
||||
### Linux
|
||||
|
||||
1. Check out the Ghostty source and change to the directory.
|
||||
2. Run `nix run .#<vmtype>`. `<vmtype>` can be any of the VMs defined in the
|
||||
`nix/vm` directory (without the `.nix` suffix) excluding any file prefixed
|
||||
with `common` or `create`.
|
||||
3. The VM will build and then launch. Depending on the speed of your system, this
|
||||
can take a while, but eventually you should get a new VM window.
|
||||
4. The Ghostty source directory should be mounted to `/tmp/shared` in the VM. Depending
|
||||
on what UID and GID of the user that you launched the VM as, `/tmp/shared` _may_ be
|
||||
writable by the VM user, so be careful!
|
||||
|
||||
### macOS
|
||||
|
||||
1. To run the VMs on macOS you will need to enable the Linux builder in your `nix-darwin`
|
||||
config. This _should_ be as simple as adding `nix.linux-builder.enable=true` to your
|
||||
configuration and then rebuilding. See [this](https://nixcademy.com/posts/macos-linux-builder/)
|
||||
blog post for more information about the Linux builder and how to tune the performance.
|
||||
2. Once the Linux builder has been enabled, you should be able to follow the Linux instructions
|
||||
above to launch a VM.
|
||||
|
||||
### Custom VMs
|
||||
|
||||
To easily create a custom VM without modifying the Ghostty source, create a new
|
||||
directory, then create a file called `flake.nix` with the following text in the
|
||||
new directory.
|
||||
|
||||
```
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
|
||||
ghostty.url = "github:ghostty-org/ghostty";
|
||||
};
|
||||
outputs = {
|
||||
nixpkgs,
|
||||
ghostty,
|
||||
...
|
||||
}: {
|
||||
nixosConfigurations.custom-vm = ghostty.create-gnome-vm {
|
||||
nixpkgs = nixpkgs;
|
||||
system = "x86_64-linux";
|
||||
overlay = ghostty.overlays.releasefast;
|
||||
# module = ./configuration.nix # also works
|
||||
module = {pkgs, ...}: {
|
||||
environment.systemPackages = [
|
||||
pkgs.btop
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The custom VM can then be run with a command like this:
|
||||
|
||||
```
|
||||
nix run .#nixosConfigurations.custom-vm.config.system.build.vm
|
||||
```
|
||||
|
||||
A file named `ghostty.qcow2` will be created that is used to persist any changes
|
||||
made in the VM. To "reset" the VM to default delete the file and it will be
|
||||
recreated the next time you run the VM.
|
||||
|
||||
### Contributing new VM definitions
|
||||
|
||||
#### VM Acceptance Criteria
|
||||
|
||||
We welcome the contribution of new VM definitions, as long as they meet the following criteria:
|
||||
|
||||
1. They should be different enough from existing VM definitions that they represent a distinct
|
||||
user (and developer) experience.
|
||||
2. There's a significant Ghostty user population that uses a similar environment.
|
||||
3. The VMs can be built using only packages from the current stable NixOS release.
|
||||
|
||||
#### VM Definition Criteria
|
||||
|
||||
1. VMs should be as minimal as possible so that they build and launch quickly.
|
||||
Additional software can be added at runtime with a command like `nix run nixpkgs#<package name>`.
|
||||
2. VMs should not expose any services to the network, or run any remote access
|
||||
software like SSH daemons, VNC or RDP.
|
||||
3. VMs should auto-login using the "ghostty" user.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,329 @@
|
|||
# Developing Ghostty
|
||||
|
||||
This document describes the technical details behind Ghostty's development.
|
||||
If you'd like to open any pull requests or would like to implement new features
|
||||
into Ghostty, please make sure to read our ["Contributing to Ghostty"](CONTRIBUTING.md)
|
||||
document first.
|
||||
|
||||
To start development on Ghostty, you need to build Ghostty from a Git checkout,
|
||||
which is very similar in process to [building Ghostty from a source tarball](http://ghostty.org/docs/install/build). One key difference is that obviously
|
||||
you need to clone the Git repository instead of unpacking the source tarball:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/ghostty-org/ghostty
|
||||
cd ghostty
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Ghostty may require [extra dependencies](#extra-dependencies)
|
||||
> when building from a Git checkout compared to a source tarball.
|
||||
> Tip versions may also require a different version of Zig or other toolchains
|
||||
> (e.g. the Xcode SDK on macOS) compared to stable versions — make sure to
|
||||
> follow the steps closely!
|
||||
|
||||
When you're developing Ghostty, it's very likely that you will want to build a
|
||||
_debug_ build to diagnose issues more easily. This is already the default for
|
||||
Zig builds, so simply run `zig build` **without any `-Doptimize` flags**.
|
||||
|
||||
There are many more build steps than just `zig build`, some of which are listed
|
||||
here:
|
||||
|
||||
| Command | Description |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| `zig build run` | Runs Ghostty |
|
||||
| `zig build run-valgrind` | Runs Ghostty under Valgrind to [check for memory leaks](#checking-for-memory-leaks) |
|
||||
| `zig build test` | Runs unit tests (accepts `-Dtest-filter=<filter>` to only run tests whose name matches the filter) |
|
||||
| `zig build update-translations` | Updates Ghostty's translation strings (see the [Contributor's Guide on Localizing Ghostty](po/README_CONTRIBUTORS.md)) |
|
||||
| `zig build dist` | Builds a source tarball |
|
||||
| `zig build distcheck` | Installs and validates a source tarball |
|
||||
|
||||
## Extra Dependencies
|
||||
|
||||
Building Ghostty from a Git checkout on Linux requires some additional
|
||||
dependencies:
|
||||
|
||||
- `blueprint-compiler` (version 0.16.0 or newer)
|
||||
|
||||
macOS users don't require any additional dependencies.
|
||||
|
||||
## Xcode Version and SDKs
|
||||
|
||||
Building the Ghostty macOS app requires that Xcode, the macOS SDK,
|
||||
and the iOS SDK are all installed.
|
||||
|
||||
A common issue is that the incorrect version of Xcode is either
|
||||
installed or selected. Use the `xcode-select` command to
|
||||
ensure that the correct version of Xcode is selected:
|
||||
|
||||
```shell-session
|
||||
sudo xcode-select --switch /Applications/Xcode-beta.app
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> Main branch development of Ghostty is preparing for the next major
|
||||
> macOS release, Tahoe (macOS 26). Therefore, the main branch requires
|
||||
> **Xcode 26 and the macOS 26 SDK**.
|
||||
>
|
||||
> You do not need to be running on macOS 26 to build Ghostty, you can
|
||||
> still use Xcode 26 beta on macOS 15 stable.
|
||||
|
||||
## Linting
|
||||
|
||||
### Prettier
|
||||
|
||||
Ghostty's docs and resources (not including Zig code) are linted using
|
||||
[Prettier](https://prettier.io) with out-of-the-box settings. A Prettier CI
|
||||
check will fail builds with improper formatting. Therefore, if you are
|
||||
modifying anything Prettier will lint, you may want to install it locally and
|
||||
run this from the repo root before you commit:
|
||||
|
||||
```
|
||||
prettier --write .
|
||||
```
|
||||
|
||||
Make sure your Prettier version matches the version of Prettier in [devShell.nix](https://github.com/ghostty-org/ghostty/blob/main/nix/devShell.nix).
|
||||
|
||||
Nix users can use the following command to format with Prettier:
|
||||
|
||||
```
|
||||
nix develop -c prettier --write .
|
||||
```
|
||||
|
||||
### Alejandra
|
||||
|
||||
Nix modules are formatted with [Alejandra](https://github.com/kamadorueda/alejandra/). An Alejandra CI check
|
||||
will fail builds with improper formatting.
|
||||
|
||||
Nix users can use the following command to format with Alejandra:
|
||||
|
||||
```
|
||||
nix develop -c alejandra .
|
||||
```
|
||||
|
||||
Non-Nix users should install Alejandra and use the following command to format with Alejandra:
|
||||
|
||||
```
|
||||
alejandra .
|
||||
```
|
||||
|
||||
Make sure your Alejandra version matches the version of Alejandra in [devShell.nix](https://github.com/ghostty-org/ghostty/blob/main/nix/devShell.nix).
|
||||
|
||||
### Updating the Zig Cache Fixed-Output Derivation Hash
|
||||
|
||||
The Nix package depends on a [fixed-output
|
||||
derivation](https://nix.dev/manual/nix/stable/language/advanced-attributes.html#adv-attr-outputHash)
|
||||
that manages the Zig package cache. This allows the package to be built in the
|
||||
Nix sandbox.
|
||||
|
||||
Occasionally (usually when `build.zig.zon` is updated), the hash that
|
||||
identifies the cache will need to be updated. There are jobs that monitor the
|
||||
hash in CI, and builds will fail if it drifts.
|
||||
|
||||
To update it, you can run the following in the repository root:
|
||||
|
||||
```
|
||||
./nix/build-support/check-zig-cache-hash.sh --update
|
||||
```
|
||||
|
||||
This will write out the `nix/zigCacheHash.nix` file with the updated hash
|
||||
that can then be committed and pushed to fix the builds.
|
||||
|
||||
## Including and Updating Translations
|
||||
|
||||
See the [Contributor's Guide](po/README_CONTRIBUTORS.md) for more details.
|
||||
|
||||
## Checking for Memory Leaks
|
||||
|
||||
While Zig does an amazing job of finding and preventing memory leaks,
|
||||
Ghostty uses many third-party libraries that are written in C. Improper usage
|
||||
of those libraries or bugs in those libraries can cause memory leaks that
|
||||
Zig cannot detect by itself.
|
||||
|
||||
### On Linux
|
||||
|
||||
On Linux the recommended tool to check for memory leaks is Valgrind. The
|
||||
recommended way to run Valgrind is via `zig build`:
|
||||
|
||||
```sh
|
||||
zig build run-valgrind
|
||||
```
|
||||
|
||||
This builds a Ghostty executable with Valgrind support and runs Valgrind
|
||||
with the proper flags to ensure we're suppressing known false positives.
|
||||
|
||||
You can combine the same build args with `run-valgrind` that you can with
|
||||
`run`, such as specifying additional configurations after a trailing `--`.
|
||||
|
||||
## Input Stack Testing
|
||||
|
||||
The input stack is the part of the codebase that starts with a
|
||||
key event and ends with text encoding being sent to the pty (it
|
||||
does not include _rendering_ the text, which is part of the
|
||||
font or rendering stack).
|
||||
|
||||
If you modify any part of the input stack, you must manually verify
|
||||
all the following input cases work properly. We unfortunately do
|
||||
not automate this in any way, but if we can do that one day that'd
|
||||
save a LOT of grief and time.
|
||||
|
||||
Note: this list may not be exhaustive, I'm still working on it.
|
||||
|
||||
### Linux IME
|
||||
|
||||
IME (Input Method Editors) are a common source of bugs in the input stack,
|
||||
especially on Linux since there are multiple different IME systems
|
||||
interacting with different windowing systems and application frameworks
|
||||
all written by different organizations.
|
||||
|
||||
The following matrix should be tested to ensure that all IME input works
|
||||
properly:
|
||||
|
||||
1. Wayland, X11
|
||||
2. ibus, fcitx, none
|
||||
3. Dead key input (e.g. Spanish), CJK (e.g. Japanese), Emoji, Unicode Hex
|
||||
4. ibus versions: 1.5.29, 1.5.30, 1.5.31 (each exhibit slightly different behaviors)
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> This is a **work in progress**. I'm still working on this list and it
|
||||
> is not complete. As I find more test cases, I will add them here.
|
||||
|
||||
#### Dead Key Input
|
||||
|
||||
Set your keyboard layout to "Spanish" (or another layout that uses dead keys).
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `'`
|
||||
3. Press `a`
|
||||
4. Verify that `á` is displayed
|
||||
|
||||
Note that the dead key may or may not show a preedit state visually.
|
||||
For ibus and fcitx it does but for the "none" case it does not. Importantly,
|
||||
the text should be correct when it is sent to the pty.
|
||||
|
||||
We should also test canceling dead key input:
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `'`
|
||||
3. Press escape
|
||||
4. Press `a`
|
||||
5. Verify that `a` is displayed (no diacritic)
|
||||
|
||||
#### CJK Input
|
||||
|
||||
Configure fcitx or ibus with a keyboard layout like Japanese or Mozc. The
|
||||
exact layout doesn't matter.
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||
4. Press `Enter`
|
||||
5. Verify that `こん` is displayed in the terminal.
|
||||
|
||||
We should also test switching input methods while preedit is active, which
|
||||
should commit the text:
|
||||
|
||||
1. Launch Ghostty
|
||||
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||
4. Press `Ctrl+Shift` to switch to another layout (any)
|
||||
5. Verify that `こん` is displayed in the terminal as committed text.
|
||||
|
||||
## Nix Virtual Machines
|
||||
|
||||
Several Nix virtual machine definitions are provided by the project for testing
|
||||
and developing Ghostty against multiple different Linux desktop environments.
|
||||
|
||||
Running these requires a working Nix installation, either Nix on your
|
||||
favorite Linux distribution, NixOS, or macOS with nix-darwin installed. Further
|
||||
requirements for macOS are detailed below.
|
||||
|
||||
VMs should only be run on your local desktop and then powered off when not in
|
||||
use, which will discard any changes to the VM.
|
||||
|
||||
The VM definitions provide minimal software "out of the box" but additional
|
||||
software can be installed by using standard Nix mechanisms like `nix run nixpkgs#<package>`.
|
||||
|
||||
### Linux
|
||||
|
||||
1. Check out the Ghostty source and change to the directory.
|
||||
2. Run `nix run .#<vmtype>`. `<vmtype>` can be any of the VMs defined in the
|
||||
`nix/vm` directory (without the `.nix` suffix) excluding any file prefixed
|
||||
with `common` or `create`.
|
||||
3. The VM will build and then launch. Depending on the speed of your system, this
|
||||
can take a while, but eventually you should get a new VM window.
|
||||
4. The Ghostty source directory should be mounted to `/tmp/shared` in the VM. Depending
|
||||
on what UID and GID of the user that you launched the VM as, `/tmp/shared` _may_ be
|
||||
writable by the VM user, so be careful!
|
||||
|
||||
### macOS
|
||||
|
||||
1. To run the VMs on macOS you will need to enable the Linux builder in your `nix-darwin`
|
||||
config. This _should_ be as simple as adding `nix.linux-builder.enable=true` to your
|
||||
configuration and then rebuilding. See [this](https://nixcademy.com/posts/macos-linux-builder/)
|
||||
blog post for more information about the Linux builder and how to tune the performance.
|
||||
2. Once the Linux builder has been enabled, you should be able to follow the Linux instructions
|
||||
above to launch a VM.
|
||||
|
||||
### Custom VMs
|
||||
|
||||
To easily create a custom VM without modifying the Ghostty source, create a new
|
||||
directory, then create a file called `flake.nix` with the following text in the
|
||||
new directory.
|
||||
|
||||
```
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
|
||||
ghostty.url = "github:ghostty-org/ghostty";
|
||||
};
|
||||
outputs = {
|
||||
nixpkgs,
|
||||
ghostty,
|
||||
...
|
||||
}: {
|
||||
nixosConfigurations.custom-vm = ghostty.create-gnome-vm {
|
||||
nixpkgs = nixpkgs;
|
||||
system = "x86_64-linux";
|
||||
overlay = ghostty.overlays.releasefast;
|
||||
# module = ./configuration.nix # also works
|
||||
module = {pkgs, ...}: {
|
||||
environment.systemPackages = [
|
||||
pkgs.btop
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The custom VM can then be run with a command like this:
|
||||
|
||||
```
|
||||
nix run .#nixosConfigurations.custom-vm.config.system.build.vm
|
||||
```
|
||||
|
||||
A file named `ghostty.qcow2` will be created that is used to persist any changes
|
||||
made in the VM. To "reset" the VM to default delete the file and it will be
|
||||
recreated the next time you run the VM.
|
||||
|
||||
### Contributing new VM definitions
|
||||
|
||||
#### VM Acceptance Criteria
|
||||
|
||||
We welcome the contribution of new VM definitions, as long as they meet the following criteria:
|
||||
|
||||
1. They should be different enough from existing VM definitions that they represent a distinct
|
||||
user (and developer) experience.
|
||||
2. There's a significant Ghostty user population that uses a similar environment.
|
||||
3. The VMs can be built using only packages from the current stable NixOS release.
|
||||
|
||||
#### VM Definition Criteria
|
||||
|
||||
1. VMs should be as minimal as possible so that they build and launch quickly.
|
||||
Additional software can be added at runtime with a command like `nix run nixpkgs#<package name>`.
|
||||
2. VMs should not expose any services to the network, or run any remote access
|
||||
software like SSH daemons, VNC or RDP.
|
||||
3. VMs should auto-login using the "ghostty" user.
|
||||
128
README.md
128
README.md
|
|
@ -13,7 +13,9 @@
|
|||
·
|
||||
<a href="https://ghostty.org/docs">Documentation</a>
|
||||
·
|
||||
<a href="#developing-ghostty">Developing</a>
|
||||
<a href="CONTRIBUTING.md">Contributing</a>
|
||||
·
|
||||
<a href="HACKING.md">Developing</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
|
|
@ -49,6 +51,14 @@ See the [download page](https://ghostty.org/download) on the Ghostty website.
|
|||
|
||||
See the [documentation](https://ghostty.org/docs) on the Ghostty website.
|
||||
|
||||
## Contributing and Developing
|
||||
|
||||
If you have any ideas, issues, etc. regarding Ghostty, or would like to
|
||||
contribute to Ghostty through pull requests, please check out our
|
||||
["Contributing to Ghostty"](CONTRIBUTING.md) document. Those who would like
|
||||
to get involved with Ghostty's development as well should also read the
|
||||
["Developing Ghostty"](HACKING.md) document for more technical details.
|
||||
|
||||
## Roadmap and Status
|
||||
|
||||
The high-level ambitious plan for the project, in order:
|
||||
|
|
@ -184,119 +194,3 @@ SENTRY_DSN=https://e914ee84fd895c4fe324afa3e53dac76@o4507352570920960.ingest.us.
|
|||
> stack memory of each thread at the time of the crash. This information
|
||||
> is used to rebuild the stack trace but can also contain sensitive data
|
||||
> depending when the crash occurred.
|
||||
|
||||
## Developing Ghostty
|
||||
|
||||
See the documentation on the Ghostty website for
|
||||
[building Ghostty from a source tarball](http://ghostty.org/docs/install/build).
|
||||
Building Ghostty from a Git checkout is very similar, except you want to
|
||||
omit the `-Doptimize` flag to build a debug build, and you may require
|
||||
additional dependencies since the source tarball includes some processed
|
||||
files that are not in the Git repository.
|
||||
|
||||
Other useful commands:
|
||||
|
||||
- `zig build test` for running unit tests.
|
||||
- `zig build test -Dtest-filter=<filter>` for running a specific subset of those unit tests
|
||||
- `zig build run -Dconformance=<name>` runs a conformance test case from
|
||||
the `conformance` directory. The `name` is the name of the file. This runs
|
||||
in the current running terminal emulator so if you want to check the
|
||||
behavior of this project, you must run this command in Ghostty.
|
||||
|
||||
### Extra Dependencies
|
||||
|
||||
Building Ghostty from a Git checkout on Linux requires some additional
|
||||
dependencies:
|
||||
|
||||
- `blueprint-compiler`
|
||||
|
||||
macOS users don't require any additional dependencies.
|
||||
|
||||
> [!NOTE]
|
||||
> This only applies to building from a _Git checkout_. This section does
|
||||
> not apply if you're building from a released _source tarball_. For
|
||||
> source tarballs, see the
|
||||
> [website](http://ghostty.org/docs/install/build).
|
||||
|
||||
### Xcode Version and SDKs
|
||||
|
||||
Building the Ghostty macOS app requires that Xcode, the macOS SDK,
|
||||
and the iOS SDK are all installed.
|
||||
|
||||
A common issue is that the incorrect version of Xcode is either
|
||||
installed or selected. Use the `xcode-select` command to
|
||||
ensure that the correct version of Xcode is selected:
|
||||
|
||||
```shell-session
|
||||
sudo xcode-select --switch /Applications/Xcode-beta.app
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> Main branch development of Ghostty is preparing for the next major
|
||||
> macOS release, Tahoe (macOS 26). Therefore, the main branch requires
|
||||
> **Xcode 26 and the macOS 26 SDK**.
|
||||
>
|
||||
> You do not need to be running on macOS 26 to build Ghostty, you can
|
||||
> still use Xcode 26 beta on macOS 15 stable.
|
||||
|
||||
### Linting
|
||||
|
||||
#### Prettier
|
||||
|
||||
Ghostty's docs and resources (not including Zig code) are linted using
|
||||
[Prettier](https://prettier.io) with out-of-the-box settings. A Prettier CI
|
||||
check will fail builds with improper formatting. Therefore, if you are
|
||||
modifying anything Prettier will lint, you may want to install it locally and
|
||||
run this from the repo root before you commit:
|
||||
|
||||
```
|
||||
prettier --write .
|
||||
```
|
||||
|
||||
Make sure your Prettier version matches the version of Prettier in [devShell.nix](https://github.com/ghostty-org/ghostty/blob/main/nix/devShell.nix).
|
||||
|
||||
Nix users can use the following command to format with Prettier:
|
||||
|
||||
```
|
||||
nix develop -c prettier --write .
|
||||
```
|
||||
|
||||
#### Alejandra
|
||||
|
||||
Nix modules are formatted with [Alejandra](https://github.com/kamadorueda/alejandra/). An Alejandra CI check
|
||||
will fail builds with improper formatting.
|
||||
|
||||
Nix users can use the following command to format with Alejandra:
|
||||
|
||||
```
|
||||
nix develop -c alejandra .
|
||||
```
|
||||
|
||||
Non-Nix users should install Alejandra and use the following command to format with Alejandra:
|
||||
|
||||
```
|
||||
alejandra .
|
||||
```
|
||||
|
||||
Make sure your Alejandra version matches the version of Alejandra in [devShell.nix](https://github.com/ghostty-org/ghostty/blob/main/nix/devShell.nix).
|
||||
|
||||
#### Updating the Zig Cache Fixed-Output Derivation Hash
|
||||
|
||||
The Nix package depends on a [fixed-output
|
||||
derivation](https://nix.dev/manual/nix/stable/language/advanced-attributes.html#adv-attr-outputHash)
|
||||
that manages the Zig package cache. This allows the package to be built in the
|
||||
Nix sandbox.
|
||||
|
||||
Occasionally (usually when `build.zig.zon` is updated), the hash that
|
||||
identifies the cache will need to be updated. There are jobs that monitor the
|
||||
hash in CI, and builds will fail if it drifts.
|
||||
|
||||
To update it, you can run the following in the repository root:
|
||||
|
||||
```
|
||||
./nix/build-support/check-zig-cache-hash.sh --update
|
||||
```
|
||||
|
||||
This will write out the `nix/zigCacheHash.nix` file with the updated hash
|
||||
that can then be committed and pushed to fix the builds.
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@
|
|||
// Other
|
||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||
.iterm2_themes = .{
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8b639f0c2605557bd23ba1b940842c67bbfd4ed0.tar.gz",
|
||||
.hash = "N-V-__8AAAlgXwSghpDmXBXZM4Rpd80WKOXVWTrcL0ucVmls",
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/6cdbc8501d48601302e32d6b53e9e7934bf354b4.tar.gz",
|
||||
.hash = "N-V-__8AAAtjXwSdhZq_xYbCXo0SZMqoNoQuHFkC07sijQME",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@
|
|||
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||
},
|
||||
"N-V-__8AAAlgXwSghpDmXBXZM4Rpd80WKOXVWTrcL0ucVmls": {
|
||||
"N-V-__8AAAtjXwSdhZq_xYbCXo0SZMqoNoQuHFkC07sijQME": {
|
||||
"name": "iterm2_themes",
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8b639f0c2605557bd23ba1b940842c67bbfd4ed0.tar.gz",
|
||||
"hash": "sha256-PySWF/9IAK4DZCkd5FRpiaIl6et2Qm6t8IKCTzh/Xa0="
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/6cdbc8501d48601302e32d6b53e9e7934bf354b4.tar.gz",
|
||||
"hash": "sha256-NlUXcBOmaA8W+7RXuXcn9TIhm964dXO2Op4QCQxhDyc="
|
||||
},
|
||||
"N-V-__8AAIC5lwAVPJJzxnCAahSvZTIlG-HhtOvnM1uh-66x": {
|
||||
"name": "jetbrains_mono",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
inherit name rev hash;
|
||||
url = url_without_query;
|
||||
deepClone = false;
|
||||
fetchSubmodules = false;
|
||||
};
|
||||
|
||||
fetchZigArtifact = {
|
||||
|
|
@ -162,11 +163,11 @@ in
|
|||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAAlgXwSghpDmXBXZM4Rpd80WKOXVWTrcL0ucVmls";
|
||||
name = "N-V-__8AAAtjXwSdhZq_xYbCXo0SZMqoNoQuHFkC07sijQME";
|
||||
path = fetchZigArtifact {
|
||||
name = "iterm2_themes";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8b639f0c2605557bd23ba1b940842c67bbfd4ed0.tar.gz";
|
||||
hash = "sha256-PySWF/9IAK4DZCkd5FRpiaIl6et2Qm6t8IKCTzh/Xa0=";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/6cdbc8501d48601302e32d6b53e9e7934bf354b4.tar.gz";
|
||||
hash = "sha256-NlUXcBOmaA8W+7RXuXcn9TIhm964dXO2Op4QCQxhDyc=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21a
|
|||
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||
https://github.com/jacobsandlund/uucode/archive/38b82297e69a3b2dc55dc8df25f3851be37f9327.tar.gz
|
||||
https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8b639f0c2605557bd23ba1b940842c67bbfd4ed0.tar.gz
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/6cdbc8501d48601302e32d6b53e9e7934bf354b4.tar.gz
|
||||
https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz
|
||||
https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz
|
||||
https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz
|
||||
|
|
|
|||
25
flake.lock
25
flake.lock
|
|
@ -47,6 +47,19 @@
|
|||
"url": "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1755972213,
|
||||
"narHash": "sha256-VYK7aDAv8H1enXn1ECRHmGbeY6RqLnNwUJkOwloIsko=",
|
||||
"rev": "73e96df7cff5783f45e21342a75a1540c4eddce4",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/unstable-small/nixos-25.11pre850642.73e96df7cff5/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://channels.nixos.org/nixos-unstable-small/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
|
|
@ -102,22 +115,20 @@
|
|||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742104771,
|
||||
"narHash": "sha256-LhidlyEA9MP8jGe1rEnyjGFCzLLgCdDpYeWggibayr0=",
|
||||
"lastModified": 1756000480,
|
||||
"narHash": "sha256-fR5pdcjO0II5MNdCzqvyokyuFkmff7/FyBAjUS6sMfA=",
|
||||
"owner": "jcollie",
|
||||
"repo": "zon2nix",
|
||||
"rev": "56c159be489cc6c0e73c3930bd908ddc6fe89613",
|
||||
"rev": "d9dc9ef1ab9ae45b5c9d80c6a747cc9968ee0c60",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "jcollie",
|
||||
"repo": "zon2nix",
|
||||
"rev": "56c159be489cc6c0e73c3930bd908ddc6fe89613",
|
||||
"rev": "d9dc9ef1ab9ae45b5c9d80c6a747cc9968ee0c60",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,12 @@
|
|||
};
|
||||
|
||||
zon2nix = {
|
||||
url = "github:jcollie/zon2nix?rev=56c159be489cc6c0e73c3930bd908ddc6fe89613";
|
||||
url = "github:jcollie/zon2nix?rev=d9dc9ef1ab9ae45b5c9d80c6a747cc9968ee0c60";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs";
|
||||
# Don't override nixpkgs until Zig 0.15 is available in the Nix branch
|
||||
# we are using for "normal" builds.
|
||||
#
|
||||
# nixpkgs.follows = "nixpkgs";
|
||||
flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script checks if the flatpak/zig-packages.json file is up-to-date.
|
||||
# If the `--update` flag is passed, it will update all necessary
|
||||
# files to be up to date.
|
||||
#
|
||||
# The files owned by this are:
|
||||
#
|
||||
# - flatpak/zig-packages.json
|
||||
#
|
||||
# All of these are auto-generated and should not be edited manually.
|
||||
|
||||
# Nothing in this script should fail.
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
WORK_DIR=$(mktemp -d)
|
||||
|
||||
if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then
|
||||
echo "could not create temp dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function cleanup {
|
||||
rm -rf "$WORK_DIR"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
help() {
|
||||
echo ""
|
||||
echo "To fix, please (manually) re-run the script from the repository root,"
|
||||
echo "commit, and submit a PR with the update:"
|
||||
echo ""
|
||||
echo " ./flatpak/build-support/check-zig-cache.sh --update"
|
||||
echo " git add flatpak/zig-packages.json"
|
||||
echo " git commit -m \"flatpak: update zig-packages.json\""
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Turn Nix's base64 hashes into regular hexadecimal form
|
||||
decode_hash() {
|
||||
input=$1
|
||||
input=${input#sha256-}
|
||||
echo "$input" | base64 -d | od -vAn -t x1 | tr -d ' \n'
|
||||
}
|
||||
|
||||
ROOT="$(realpath "$(dirname "$0")/../../")"
|
||||
ZIG_PACKAGES_JSON="$ROOT/flatpak/zig-packages.json"
|
||||
BUILD_ZIG_ZON_JSON="$ROOT/build.zig.zon.json"
|
||||
|
||||
if [ ! -f "${BUILD_ZIG_ZON_JSON}" ]; then
|
||||
echo -e "\nERROR: build.zig.zon2json-lock missing."
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "${ZIG_PACKAGES_JSON}" ]; then
|
||||
OLD_HASH=$(sha512sum "${ZIG_PACKAGES_JSON}" | awk '{print $1}')
|
||||
fi
|
||||
|
||||
while read -r url sha256 dest; do
|
||||
src_type=archive
|
||||
sha256=$(decode_hash "$sha256")
|
||||
git_commit=
|
||||
if [[ "$url" =~ ^git\+* ]]; then
|
||||
src_type=git
|
||||
sha256=
|
||||
url=${url#git+}
|
||||
git_commit=${url##*#}
|
||||
url=${url%%/\?ref*}
|
||||
url=${url%%#*}
|
||||
fi
|
||||
|
||||
jq \
|
||||
-nec \
|
||||
--arg type "$src_type" \
|
||||
--arg url "$url" \
|
||||
--arg git_commit "$git_commit" \
|
||||
--arg dest "$dest" \
|
||||
--arg sha256 "$sha256" \
|
||||
'{
|
||||
type: $type,
|
||||
url: $url,
|
||||
commit: $git_commit,
|
||||
dest: $dest,
|
||||
sha256: $sha256,
|
||||
} | with_entries(select(.value != ""))'
|
||||
done < <(jq -rc 'to_entries[] | [.value.url, .value.hash, "vendor/p/\(.key)"] | @tsv' "$BUILD_ZIG_ZON_JSON") |
|
||||
jq -s '.' >"$WORK_DIR/zig-packages.json"
|
||||
|
||||
NEW_HASH=$(sha512sum "$WORK_DIR/zig-packages.json" | awk '{print $1}')
|
||||
|
||||
if [ "${OLD_HASH}" == "${NEW_HASH}" ]; then
|
||||
echo -e "\nOK: flatpak/zig-packages.json unchanged."
|
||||
exit 0
|
||||
elif [ "${1:-}" != "--update" ]; then
|
||||
echo -e "\nERROR: flatpak/zig-packages.json needs to be updated."
|
||||
echo ""
|
||||
echo " * Old hash: ${OLD_HASH}"
|
||||
echo " * New hash: ${NEW_HASH}"
|
||||
help
|
||||
exit 1
|
||||
else
|
||||
mv "$WORK_DIR/zig-packages.json" "$ZIG_PACKAGES_JSON"
|
||||
echo -e "\nOK: flatpak/zig-packages.json updated."
|
||||
exit 0
|
||||
fi
|
||||
|
|
@ -61,9 +61,9 @@
|
|||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8b639f0c2605557bd23ba1b940842c67bbfd4ed0.tar.gz",
|
||||
"dest": "vendor/p/N-V-__8AAAlgXwSghpDmXBXZM4Rpd80WKOXVWTrcL0ucVmls",
|
||||
"sha256": "3f249617ff4800ae0364291de4546989a225e9eb76426eadf082824f387f5dad"
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/6cdbc8501d48601302e32d6b53e9e7934bf354b4.tar.gz",
|
||||
"dest": "vendor/p/N-V-__8AAAtjXwSdhZq_xYbCXo0SZMqoNoQuHFkC07sijQME",
|
||||
"sha256": "3655177013a6680f16fbb457b97727f532219bdeb87573b63a9e10090c610f27"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
|
|
|
|||
|
|
@ -419,6 +419,7 @@ typedef struct {
|
|||
ghostty_env_var_s* env_vars;
|
||||
size_t env_var_count;
|
||||
const char* initial_input;
|
||||
bool wait_after_command;
|
||||
} ghostty_surface_config_s;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -450,6 +451,28 @@ typedef struct {
|
|||
ghostty_config_color_s colors[256];
|
||||
} ghostty_config_palette_s;
|
||||
|
||||
// config.QuickTerminalSize
|
||||
typedef enum {
|
||||
GHOSTTY_QUICK_TERMINAL_SIZE_NONE,
|
||||
GHOSTTY_QUICK_TERMINAL_SIZE_PERCENTAGE,
|
||||
GHOSTTY_QUICK_TERMINAL_SIZE_PIXELS,
|
||||
} ghostty_quick_terminal_size_tag_e;
|
||||
|
||||
typedef union {
|
||||
float percentage;
|
||||
uint32_t pixels;
|
||||
} ghostty_quick_terminal_size_value_u;
|
||||
|
||||
typedef struct {
|
||||
ghostty_quick_terminal_size_tag_e tag;
|
||||
ghostty_quick_terminal_size_value_u value;
|
||||
} ghostty_quick_terminal_size_s;
|
||||
|
||||
typedef struct {
|
||||
ghostty_quick_terminal_size_s primary;
|
||||
ghostty_quick_terminal_size_s secondary;
|
||||
} ghostty_config_quick_terminal_size_s;
|
||||
|
||||
// apprt.Target.Key
|
||||
typedef enum {
|
||||
GHOSTTY_TARGET_APP,
|
||||
|
|
@ -680,6 +703,12 @@ typedef struct {
|
|||
uintptr_t len;
|
||||
} ghostty_action_open_url_s;
|
||||
|
||||
// apprt.action.CloseTabMode
|
||||
typedef enum {
|
||||
GHOSTTY_ACTION_CLOSE_TAB_MODE_THIS,
|
||||
GHOSTTY_ACTION_CLOSE_TAB_MODE_OTHER,
|
||||
} ghostty_action_close_tab_mode_e;
|
||||
|
||||
// apprt.surface.Message.ChildExited
|
||||
typedef struct {
|
||||
uint32_t exit_code;
|
||||
|
|
@ -693,15 +722,15 @@ typedef enum {
|
|||
GHOSTTY_PROGRESS_STATE_ERROR,
|
||||
GHOSTTY_PROGRESS_STATE_INDETERMINATE,
|
||||
GHOSTTY_PROGRESS_STATE_PAUSE,
|
||||
} ghostty_terminal_osc_command_progressreport_state_e;
|
||||
} ghostty_action_progress_report_state_e;
|
||||
|
||||
// terminal.osc.Command.ProgressReport.C
|
||||
typedef struct {
|
||||
ghostty_terminal_osc_command_progressreport_state_e state;
|
||||
ghostty_action_progress_report_state_e state;
|
||||
// -1 if no progress was reported, otherwise 0-100 indicating percent
|
||||
// completeness.
|
||||
int8_t progress;
|
||||
} ghostty_terminal_osc_command_progressreport_s;
|
||||
} ghostty_action_progress_report_s;
|
||||
|
||||
// apprt.Action.Key
|
||||
typedef enum {
|
||||
|
|
@ -786,8 +815,9 @@ typedef union {
|
|||
ghostty_action_reload_config_s reload_config;
|
||||
ghostty_action_config_change_s config_change;
|
||||
ghostty_action_open_url_s open_url;
|
||||
ghostty_action_close_tab_mode_e close_tab_mode;
|
||||
ghostty_surface_message_childexited_s child_exited;
|
||||
ghostty_terminal_osc_command_progressreport_s progress_report;
|
||||
ghostty_action_progress_report_s progress_report;
|
||||
} ghostty_action_u;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */; };
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||
A5B4EA852DFE691B0022C3A2 /* NSMenuItem+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B4EA842DFE69140022C3A2 /* NSMenuItem+Extension.swift */; };
|
||||
A5BB78B92DF9D8CE009AC3FA /* QuickTerminalSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5BB78B82DF9D8CE009AC3FA /* QuickTerminalSize.swift */; };
|
||||
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */; };
|
||||
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378D2D31D6C100931030 /* Weak.swift */; };
|
||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
||||
|
|
@ -126,6 +127,7 @@
|
|||
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */; };
|
||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */; };
|
||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3C2B37804400D21823 /* CodableBridge.swift */; };
|
||||
A5D689BE2E654D98002E2346 /* Ghostty.Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */; };
|
||||
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */; };
|
||||
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */; };
|
||||
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */; };
|
||||
|
|
@ -252,6 +254,7 @@
|
|||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||
A5B4EA842DFE69140022C3A2 /* NSMenuItem+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMenuItem+Extension.swift"; sourceTree = "<group>"; };
|
||||
A5BB78B82DF9D8CE009AC3FA /* QuickTerminalSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalSize.swift; sourceTree = "<group>"; };
|
||||
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayout.swift; sourceTree = "<group>"; };
|
||||
A5CA378D2D31D6C100931030 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = "<group>"; };
|
||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableWindowView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -637,6 +640,7 @@
|
|||
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */,
|
||||
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
|
||||
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
|
||||
A5BB78B82DF9D8CE009AC3FA /* QuickTerminalSize.swift */,
|
||||
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
|
||||
);
|
||||
path = QuickTerminal;
|
||||
|
|
@ -939,6 +943,10 @@
|
|||
A53A297B2DB2E49700B6E02C /* CommandPalette.swift in Sources */,
|
||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||
A51B78472AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift in Sources */,
|
||||
A51B78472AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift in Sources */,
|
||||
A5BB78B92DF9D8CE009AC3FA /* QuickTerminalSize.swift in Sources */,
|
||||
A51B78472AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift in Sources */,
|
||||
A5BB78B92DF9D8CE009AC3FA /* QuickTerminalSize.swift in Sources */,
|
||||
A57D79272C9C879B001D522E /* SecureInput.swift in Sources */,
|
||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||
A5593FE12DF8D74000B47B10 /* HiddenTitlebarTerminalWindow.swift in Sources */,
|
||||
|
|
@ -980,6 +988,7 @@
|
|||
A5333E232B5A219A008AEFF7 /* SurfaceView.swift in Sources */,
|
||||
A5333E202B5A2111008AEFF7 /* SurfaceView_UIKit.swift in Sources */,
|
||||
A5333E1D2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
|
||||
A5D689BE2E654D98002E2346 /* Ghostty.Action.swift in Sources */,
|
||||
A53D0C9C2B543F7B00305CE6 /* Package.swift in Sources */,
|
||||
A53D0C9B2B543F3B00305CE6 /* Ghostty.App.swift in Sources */,
|
||||
A5333E242B5A22D9008AEFF7 /* Ghostty.Shell.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -394,35 +394,69 @@ class AppDelegate: NSObject,
|
|||
// Ghostty will validate as well but we can avoid creating an entirely new
|
||||
// surface by doing our own validation here. We can also show a useful error
|
||||
// this way.
|
||||
|
||||
|
||||
var isDirectory = ObjCBool(true)
|
||||
guard FileManager.default.fileExists(atPath: filename, isDirectory: &isDirectory) else { return false }
|
||||
|
||||
|
||||
// Set to true if confirmation is required before starting up the
|
||||
// new terminal.
|
||||
var requiresConfirm: Bool = false
|
||||
|
||||
// Initialize the surface config which will be used to create the tab or window for the opened file.
|
||||
var config = Ghostty.SurfaceConfiguration()
|
||||
|
||||
|
||||
if (isDirectory.boolValue) {
|
||||
// When opening a directory, check the configuration to decide
|
||||
// whether to open in a new tab or new window.
|
||||
config.workingDirectory = filename
|
||||
} else {
|
||||
// Unconditionally require confirmation in the file execution case.
|
||||
// In the future I have ideas about making this more fine-grained if
|
||||
// we can not inherit of unsandboxed state. For now, we need to confirm
|
||||
// because there is a sandbox escape possible if a sandboxed application
|
||||
// somehow is tricked into `open`-ing a non-sandboxed application.
|
||||
requiresConfirm = true
|
||||
|
||||
// When opening a file, we want to execute the file. To do this, we
|
||||
// don't override the command directly, because it won't load the
|
||||
// profile/rc files for the shell, which is super important on macOS
|
||||
// due to things like Homebrew. Instead, we set the command to
|
||||
// `<filename>; exit` which is what Terminal and iTerm2 do.
|
||||
config.initialInput = "\(filename); exit\n"
|
||||
|
||||
|
||||
// For commands executed directly, we want to ensure we wait after exit
|
||||
// because in most cases scripts don't block on exit and we don't want
|
||||
// the window to just flash closed once complete.
|
||||
config.waitAfterCommand = true
|
||||
|
||||
// Set the parent directory to our working directory so that relative
|
||||
// paths in scripts work.
|
||||
config.workingDirectory = (filename as NSString).deletingLastPathComponent
|
||||
}
|
||||
|
||||
if requiresConfirm {
|
||||
// Confirmation required. We use an app-wide NSAlert for now. In the future we
|
||||
// may want to show this as a sheet on the focused window (especially if we're
|
||||
// opening a tab). I'm not sure.
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Allow Ghostty to execute \"\(filename)\"?"
|
||||
alert.addButton(withTitle: "Allow")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.alertStyle = .warning
|
||||
switch (alert.runModal()) {
|
||||
case .alertFirstButtonReturn:
|
||||
break
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch ghostty.config.macosDockDropBehavior {
|
||||
case .new_tab: _ = TerminalController.newTab(ghostty, withBaseConfig: config)
|
||||
case .new_window: _ = TerminalController.newWindow(ghostty, withBaseConfig: config)
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24123.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24123.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
private var previousActiveSpace: CGSSpace? = nil
|
||||
|
||||
/// The window frame saved when the quick terminal's surface tree becomes empty.
|
||||
///
|
||||
///
|
||||
/// This preserves the user's window size and position when all terminal surfaces
|
||||
/// are closed (e.g., via the `exit` command). When a new surface is created,
|
||||
/// the window will be restored to this frame, preventing SwiftUI from resetting
|
||||
|
|
@ -34,6 +34,9 @@ class QuickTerminalController: BaseTerminalController {
|
|||
|
||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||
private var derivedConfig: DerivedConfig
|
||||
|
||||
/// Tracks if we're currently handling a manual resize to prevent recursion
|
||||
private var isHandlingResize: Bool = false
|
||||
|
||||
init(_ ghostty: Ghostty.App,
|
||||
position: QuickTerminalPosition = .top,
|
||||
|
|
@ -76,6 +79,11 @@ class QuickTerminalController: BaseTerminalController {
|
|||
selector: #selector(onNewTab),
|
||||
name: Ghostty.Notification.ghosttyNewTab,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(windowDidResize(_:)),
|
||||
name: NSWindow.didResizeNotification,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -109,7 +117,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
syncAppearance()
|
||||
|
||||
// Setup our initial size based on our configured position
|
||||
position.setLoaded(window)
|
||||
position.setLoaded(window, size: derivedConfig.quickTerminalSize)
|
||||
|
||||
// Upon first adding this Window to its host view, older SwiftUI
|
||||
// seems to have a "hiccup" and corrupts the frameRect,
|
||||
|
|
@ -209,11 +217,28 @@ class QuickTerminalController: BaseTerminalController {
|
|||
}
|
||||
}
|
||||
|
||||
func windowWillResize(_ sender: NSWindow, to frameSize: NSSize) -> NSSize {
|
||||
// We use the actual screen the window is on for this, since it should
|
||||
// be on the proper screen.
|
||||
guard let screen = window?.screen ?? NSScreen.main else { return frameSize }
|
||||
return position.restrictFrameSize(frameSize, on: screen)
|
||||
override func windowDidResize(_ notification: Notification) {
|
||||
guard let window = notification.object as? NSWindow,
|
||||
window == self.window,
|
||||
visible,
|
||||
!isHandlingResize else { return }
|
||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||
|
||||
// Prevent recursive loops
|
||||
isHandlingResize = true
|
||||
defer { isHandlingResize = false }
|
||||
|
||||
switch position {
|
||||
case .top, .bottom, .center:
|
||||
// For centered positions (top, bottom, center), we need to recenter the window
|
||||
// when it's manually resized to maintain proper positioning
|
||||
let newOrigin = position.centeredOrigin(for: window, on: screen)
|
||||
window.setFrameOrigin(newOrigin)
|
||||
case .left, .right:
|
||||
// For side positions, we may need to adjust vertical centering
|
||||
let newOrigin = position.verticallyCenteredOrigin(for: window, on: screen)
|
||||
window.setFrameOrigin(newOrigin)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Base Controller Overrides
|
||||
|
|
@ -333,15 +358,17 @@ class QuickTerminalController: BaseTerminalController {
|
|||
|
||||
private func animateWindowIn(window: NSWindow, from position: QuickTerminalPosition) {
|
||||
guard let screen = derivedConfig.quickTerminalScreen.screen else { return }
|
||||
|
||||
// Grab our last closed frame to use, and clear our state since we're animating in.
|
||||
let lastClosedFrame = self.lastClosedFrame
|
||||
self.lastClosedFrame = nil
|
||||
|
||||
// Restore our previous frame if we have one
|
||||
if let lastClosedFrame {
|
||||
window.setFrame(lastClosedFrame, display: false)
|
||||
self.lastClosedFrame = nil
|
||||
}
|
||||
|
||||
// Move our window off screen to the top
|
||||
position.setInitial(in: window, on: screen)
|
||||
// Move our window off screen to the initial animation position.
|
||||
position.setInitial(
|
||||
in: window,
|
||||
on: screen,
|
||||
terminalSize: derivedConfig.quickTerminalSize,
|
||||
closedFrame: lastClosedFrame)
|
||||
|
||||
// We need to set our window level to a high value. In testing, only
|
||||
// popUpMenu and above do what we want. This gets it above the menu bar
|
||||
|
|
@ -372,7 +399,11 @@ class QuickTerminalController: BaseTerminalController {
|
|||
NSAnimationContext.runAnimationGroup({ context in
|
||||
context.duration = derivedConfig.quickTerminalAnimationDuration
|
||||
context.timingFunction = .init(name: .easeIn)
|
||||
position.setFinal(in: window.animator(), on: screen)
|
||||
position.setFinal(
|
||||
in: window.animator(),
|
||||
on: screen,
|
||||
terminalSize: derivedConfig.quickTerminalSize,
|
||||
closedFrame: lastClosedFrame)
|
||||
}, completionHandler: {
|
||||
// There is a very minor delay here so waiting at least an event loop tick
|
||||
// keeps us safe from the view not being on the window.
|
||||
|
|
@ -450,11 +481,19 @@ class QuickTerminalController: BaseTerminalController {
|
|||
}
|
||||
|
||||
private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
|
||||
// If we are in fullscreen, then we exit fullscreen. We do this immediately so
|
||||
// we have th correct window.frame for the save state below.
|
||||
if let fullscreenStyle, fullscreenStyle.isFullscreen {
|
||||
fullscreenStyle.exit()
|
||||
}
|
||||
|
||||
// Save the current window frame before animating out. This preserves
|
||||
// the user's preferred window size and position for when the quick
|
||||
// terminal is reactivated with a new surface. Without this, SwiftUI
|
||||
// would reset the window to its minimum content size.
|
||||
lastClosedFrame = window.frame
|
||||
if window.frame.width > 0 && window.frame.height > 0 {
|
||||
lastClosedFrame = window.frame
|
||||
}
|
||||
|
||||
// If we hid the dock then we unhide it.
|
||||
hiddenDock = nil
|
||||
|
|
@ -470,11 +509,6 @@ class QuickTerminalController: BaseTerminalController {
|
|||
// We always animate out to whatever screen the window is actually on.
|
||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||
|
||||
// If we are in fullscreen, then we exit fullscreen.
|
||||
if let fullscreenStyle, fullscreenStyle.isFullscreen {
|
||||
fullscreenStyle.exit()
|
||||
}
|
||||
|
||||
// If we have a previously active application, restore focus to it. We
|
||||
// do this BEFORE the animation below because when the animation completes
|
||||
// macOS will bring forward another window.
|
||||
|
|
@ -496,7 +530,11 @@ class QuickTerminalController: BaseTerminalController {
|
|||
NSAnimationContext.runAnimationGroup({ context in
|
||||
context.duration = derivedConfig.quickTerminalAnimationDuration
|
||||
context.timingFunction = .init(name: .easeIn)
|
||||
position.setInitial(in: window.animator(), on: screen)
|
||||
position.setInitial(
|
||||
in: window.animator(),
|
||||
on: screen,
|
||||
terminalSize: derivedConfig.quickTerminalSize,
|
||||
closedFrame: window.frame)
|
||||
}, completionHandler: {
|
||||
// This causes the window to be removed from the screen list and macOS
|
||||
// handles what should be focused next.
|
||||
|
|
@ -627,6 +665,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
let quickTerminalAnimationDuration: Double
|
||||
let quickTerminalAutoHide: Bool
|
||||
let quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior
|
||||
let quickTerminalSize: QuickTerminalSize
|
||||
let backgroundOpacity: Double
|
||||
|
||||
init() {
|
||||
|
|
@ -634,6 +673,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
self.quickTerminalAnimationDuration = 0.2
|
||||
self.quickTerminalAutoHide = true
|
||||
self.quickTerminalSpaceBehavior = .move
|
||||
self.quickTerminalSize = QuickTerminalSize()
|
||||
self.backgroundOpacity = 1.0
|
||||
}
|
||||
|
||||
|
|
@ -642,6 +682,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
|
||||
self.quickTerminalAutoHide = config.quickTerminalAutoHide
|
||||
self.quickTerminalSpaceBehavior = config.quickTerminalSpaceBehavior
|
||||
self.quickTerminalSize = config.quickTerminalSize
|
||||
self.backgroundOpacity = config.backgroundOpacity
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,95 +7,86 @@ enum QuickTerminalPosition : String {
|
|||
case right
|
||||
case center
|
||||
|
||||
/// Set the loaded state for a window.
|
||||
func setLoaded(_ window: NSWindow) {
|
||||
/// Set the loaded state for a window. This should only be called when the window is first loaded,
|
||||
/// usually in `windowDidLoad` or in a similar callback. This is the initial state.
|
||||
func setLoaded(_ window: NSWindow, size: QuickTerminalSize) {
|
||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||
switch (self) {
|
||||
case .top, .bottom:
|
||||
window.setFrame(.init(
|
||||
origin: window.frame.origin,
|
||||
size: .init(
|
||||
width: screen.frame.width,
|
||||
height: screen.frame.height / 4)
|
||||
), display: false)
|
||||
|
||||
case .left, .right:
|
||||
window.setFrame(.init(
|
||||
origin: window.frame.origin,
|
||||
size: .init(
|
||||
width: screen.frame.width / 4,
|
||||
height: screen.frame.height)
|
||||
), display: false)
|
||||
|
||||
case .center:
|
||||
window.setFrame(.init(
|
||||
origin: window.frame.origin,
|
||||
size: .init(
|
||||
width: screen.frame.width / 2,
|
||||
height: screen.frame.height / 3)
|
||||
), display: false)
|
||||
}
|
||||
window.setFrame(.init(
|
||||
origin: window.frame.origin,
|
||||
size: size.calculate(position: self, screenDimensions: screen.visibleFrame.size)
|
||||
), display: false)
|
||||
}
|
||||
|
||||
/// Set the initial state for a window for animating out of this position.
|
||||
func setInitial(in window: NSWindow, on screen: NSScreen) {
|
||||
// We always start invisible
|
||||
/// Set the initial state for a window NOT yet into position (either before animating in or
|
||||
/// after animating out).
|
||||
func setInitial(
|
||||
in window: NSWindow,
|
||||
on screen: NSScreen,
|
||||
terminalSize: QuickTerminalSize,
|
||||
closedFrame: NSRect? = nil
|
||||
) {
|
||||
// Invisible
|
||||
window.alphaValue = 0
|
||||
|
||||
// Position depends
|
||||
window.setFrame(.init(
|
||||
origin: initialOrigin(for: window, on: screen),
|
||||
size: restrictFrameSize(window.frame.size, on: screen)
|
||||
size: closedFrame?.size ?? configuredFrameSize(
|
||||
on: screen,
|
||||
terminalSize: terminalSize)
|
||||
), display: false)
|
||||
}
|
||||
|
||||
/// Set the final state for a window in this position.
|
||||
func setFinal(in window: NSWindow, on screen: NSScreen) {
|
||||
func setFinal(
|
||||
in window: NSWindow,
|
||||
on screen: NSScreen,
|
||||
terminalSize: QuickTerminalSize,
|
||||
closedFrame: NSRect? = nil
|
||||
) {
|
||||
// We always end visible
|
||||
window.alphaValue = 1
|
||||
|
||||
// Position depends
|
||||
window.setFrame(.init(
|
||||
origin: finalOrigin(for: window, on: screen),
|
||||
size: restrictFrameSize(window.frame.size, on: screen)
|
||||
size: closedFrame?.size ?? configuredFrameSize(
|
||||
on: screen,
|
||||
terminalSize: terminalSize)
|
||||
), display: true)
|
||||
}
|
||||
|
||||
/// Restrict the frame size during resizing.
|
||||
func restrictFrameSize(_ size: NSSize, on screen: NSScreen) -> NSSize {
|
||||
var finalSize = size
|
||||
switch (self) {
|
||||
case .top, .bottom:
|
||||
finalSize.width = screen.frame.width
|
||||
|
||||
case .left, .right:
|
||||
finalSize.height = screen.visibleFrame.height
|
||||
|
||||
case .center:
|
||||
finalSize.width = screen.frame.width / 2
|
||||
finalSize.height = screen.frame.height / 3
|
||||
}
|
||||
|
||||
return finalSize
|
||||
/// Get the configured frame size for initial positioning and animations.
|
||||
func configuredFrameSize(on screen: NSScreen, terminalSize: QuickTerminalSize) -> NSSize {
|
||||
let dimensions = terminalSize.calculate(position: self, screenDimensions: screen.visibleFrame.size)
|
||||
return NSSize(width: dimensions.width, height: dimensions.height)
|
||||
}
|
||||
|
||||
/// The initial point origin for this position.
|
||||
func initialOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch (self) {
|
||||
case .top:
|
||||
return .init(x: screen.frame.minX, y: screen.frame.maxY)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: screen.visibleFrame.maxY)
|
||||
|
||||
case .bottom:
|
||||
return .init(x: screen.frame.minX, y: -window.frame.height)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: -window.frame.height)
|
||||
|
||||
case .left:
|
||||
return .init(x: screen.frame.minX-window.frame.width, y: 0)
|
||||
return .init(
|
||||
x: screen.visibleFrame.minX-window.frame.width,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .right:
|
||||
return .init(x: screen.frame.maxX, y: 0)
|
||||
return .init(
|
||||
x: screen.visibleFrame.maxX,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .center:
|
||||
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.height - window.frame.width)
|
||||
return .init(x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2), y: screen.visibleFrame.height - window.frame.width)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,19 +94,27 @@ enum QuickTerminalPosition : String {
|
|||
func finalOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch (self) {
|
||||
case .top:
|
||||
return .init(x: screen.frame.minX, y: screen.visibleFrame.maxY - window.frame.height)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: screen.visibleFrame.maxY - window.frame.height)
|
||||
|
||||
case .bottom:
|
||||
return .init(x: screen.frame.minX, y: screen.frame.minY)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: screen.visibleFrame.minY)
|
||||
|
||||
case .left:
|
||||
return .init(x: screen.frame.minX, y: window.frame.origin.y)
|
||||
return .init(
|
||||
x: screen.visibleFrame.minX,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .right:
|
||||
return .init(x: screen.visibleFrame.maxX - window.frame.width, y: window.frame.origin.y)
|
||||
return .init(
|
||||
x: screen.visibleFrame.maxX - window.frame.width,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .center:
|
||||
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||
return .init(x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2), y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,4 +135,52 @@ enum QuickTerminalPosition : String {
|
|||
case .right: self == .top || self == .bottom
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the centered origin for a window, keeping it properly positioned after manual resizing
|
||||
func centeredOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch self {
|
||||
case .top:
|
||||
return CGPoint(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: window.frame.origin.y // Keep the same Y position
|
||||
)
|
||||
|
||||
case .bottom:
|
||||
return CGPoint(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: window.frame.origin.y // Keep the same Y position
|
||||
)
|
||||
|
||||
case .center:
|
||||
return CGPoint(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||
)
|
||||
|
||||
case .left, .right:
|
||||
// For left/right positions, only adjust horizontal centering if needed
|
||||
return window.frame.origin
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the vertically centered origin for side-positioned windows
|
||||
func verticallyCenteredOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch self {
|
||||
case .left:
|
||||
return CGPoint(
|
||||
x: window.frame.origin.x, // Keep the same X position
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||
)
|
||||
|
||||
case .right:
|
||||
return CGPoint(
|
||||
x: window.frame.origin.x, // Keep the same X position
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||
)
|
||||
|
||||
case .top, .bottom, .center:
|
||||
// These positions don't need vertical recentering during resize
|
||||
return window.frame.origin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
import GhosttyKit
|
||||
|
||||
/// Represents the Ghostty `quick-terminal-size` configuration. See the documentation for
|
||||
/// that for more details on exactly how it works. Some of those docs will be reproduced in various comments
|
||||
/// in this file but that is the best source of truth for it.
|
||||
///
|
||||
/// The size determines the size of the quick terminal along the primary and secondary axis. The primary and
|
||||
/// secondary axis is defined by the `quick-terminal-position`.
|
||||
struct QuickTerminalSize {
|
||||
let primary: Size?
|
||||
let secondary: Size?
|
||||
|
||||
init(primary: Size? = nil, secondary: Size? = nil) {
|
||||
self.primary = primary
|
||||
self.secondary = secondary
|
||||
}
|
||||
|
||||
init(from cStruct: ghostty_config_quick_terminal_size_s) {
|
||||
self.primary = Size(from: cStruct.primary)
|
||||
self.secondary = Size(from: cStruct.secondary)
|
||||
}
|
||||
|
||||
enum Size {
|
||||
case percentage(Float)
|
||||
case pixels(UInt32)
|
||||
|
||||
init?(from cStruct: ghostty_quick_terminal_size_s) {
|
||||
switch cStruct.tag {
|
||||
case GHOSTTY_QUICK_TERMINAL_SIZE_NONE:
|
||||
return nil
|
||||
case GHOSTTY_QUICK_TERMINAL_SIZE_PERCENTAGE:
|
||||
self = .percentage(cStruct.value.percentage)
|
||||
case GHOSTTY_QUICK_TERMINAL_SIZE_PIXELS:
|
||||
self = .pixels(cStruct.value.pixels)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func toPixels(parentDimension: CGFloat) -> CGFloat {
|
||||
switch self {
|
||||
case .percentage(let value):
|
||||
return parentDimension * CGFloat(value) / 100.0
|
||||
case .pixels(let value):
|
||||
return CGFloat(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// This is an almost direct port of th Zig function QuickTerminalSize.calculate
|
||||
func calculate(position: QuickTerminalPosition, screenDimensions: CGSize) -> CGSize {
|
||||
let dims = CGSize(width: screenDimensions.width, height: screenDimensions.height)
|
||||
|
||||
switch position {
|
||||
case .left, .right:
|
||||
return CGSize(
|
||||
width: primary?.toPixels(parentDimension: dims.width) ?? 400,
|
||||
height: secondary?.toPixels(parentDimension: dims.height) ?? dims.height
|
||||
)
|
||||
|
||||
case .top, .bottom:
|
||||
return CGSize(
|
||||
width: secondary?.toPixels(parentDimension: dims.width) ?? dims.width,
|
||||
height: primary?.toPixels(parentDimension: dims.height) ?? 400
|
||||
)
|
||||
|
||||
case .center:
|
||||
if dims.width >= dims.height {
|
||||
// Landscape
|
||||
return CGSize(
|
||||
width: primary?.toPixels(parentDimension: dims.width) ?? 800,
|
||||
height: secondary?.toPixels(parentDimension: dims.height) ?? 400
|
||||
)
|
||||
} else {
|
||||
// Portrait
|
||||
return CGSize(
|
||||
width: secondary?.toPixels(parentDimension: dims.width) ?? 400,
|
||||
height: primary?.toPixels(parentDimension: dims.height) ?? 800
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +95,11 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
selector: #selector(onCloseTab),
|
||||
name: .ghosttyCloseTab,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onCloseOtherTabs),
|
||||
name: .ghosttyCloseOtherTabs,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onResetWindowSize),
|
||||
|
|
@ -559,7 +564,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
closeWindow(nil)
|
||||
}
|
||||
|
||||
private func closeTabImmediately() {
|
||||
private func closeTabImmediately(registerRedo: Bool = true) {
|
||||
guard let window = window else { return }
|
||||
guard let tabGroup = window.tabGroup,
|
||||
tabGroup.windows.count > 1 else {
|
||||
|
|
@ -576,19 +581,69 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
expiresAfter: undoExpiration
|
||||
) { ghostty in
|
||||
let newController = TerminalController(ghostty, with: undoState)
|
||||
|
||||
// Register redo action
|
||||
undoManager.registerUndo(
|
||||
withTarget: newController,
|
||||
expiresAfter: newController.undoExpiration
|
||||
) { target in
|
||||
target.closeTabImmediately()
|
||||
|
||||
if registerRedo {
|
||||
undoManager.registerUndo(
|
||||
withTarget: newController,
|
||||
expiresAfter: newController.undoExpiration
|
||||
) { target in
|
||||
target.closeTabImmediately()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.close()
|
||||
}
|
||||
|
||||
private func closeOtherTabsImmediately() {
|
||||
guard let window = window else { return }
|
||||
guard let tabGroup = window.tabGroup else { return }
|
||||
guard tabGroup.windows.count > 1 else { return }
|
||||
|
||||
// Start an undo grouping
|
||||
if let undoManager {
|
||||
undoManager.beginUndoGrouping()
|
||||
}
|
||||
defer {
|
||||
undoManager?.endUndoGrouping()
|
||||
}
|
||||
|
||||
// Iterate through all tabs except the current one.
|
||||
for window in tabGroup.windows where window != self.window {
|
||||
// We ignore any non-terminal tabs. They don't currently exist and we can't
|
||||
// properly undo them anyways so I'd rather ignore them and get a bug report
|
||||
// later if and when we introduce non-terminal tabs.
|
||||
if let controller = window.windowController as? TerminalController {
|
||||
// We must not register a redo, because it messes with our own redo
|
||||
// that we register later.
|
||||
controller.closeTabImmediately(registerRedo: false)
|
||||
}
|
||||
}
|
||||
|
||||
if let undoManager {
|
||||
undoManager.setActionName("Close Other Tabs")
|
||||
|
||||
// We need to register an undo that refocuses this window. Otherwise, the
|
||||
// undo operation above for each tab will steal focus.
|
||||
undoManager.registerUndo(
|
||||
withTarget: self,
|
||||
expiresAfter: undoExpiration
|
||||
) { target in
|
||||
DispatchQueue.main.async {
|
||||
target.window?.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
|
||||
// Register redo action
|
||||
undoManager.registerUndo(
|
||||
withTarget: target,
|
||||
expiresAfter: target.undoExpiration
|
||||
) { target in
|
||||
target.closeOtherTabsImmediately()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes the current window (including any other tabs) immediately and without
|
||||
/// confirmation. This will setup proper undo state so the action can be undone.
|
||||
|
|
@ -1023,6 +1078,38 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
}
|
||||
}
|
||||
|
||||
@IBAction func closeOtherTabs(_ sender: Any?) {
|
||||
guard let window = window else { return }
|
||||
guard let tabGroup = window.tabGroup else { return }
|
||||
|
||||
// If we only have one window then we have no other tabs to close
|
||||
guard tabGroup.windows.count > 1 else { return }
|
||||
|
||||
// Check if we have to confirm close.
|
||||
guard tabGroup.windows.contains(where: { window in
|
||||
// Ignore ourself
|
||||
if window == self.window { return false }
|
||||
|
||||
// Ignore non-terminals
|
||||
guard let controller = window.windowController as? TerminalController else {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if any surfaces require confirmation
|
||||
return controller.surfaceTree.contains(where: { $0.needsConfirmQuit })
|
||||
}) else {
|
||||
self.closeOtherTabsImmediately()
|
||||
return
|
||||
}
|
||||
|
||||
confirmClose(
|
||||
messageText: "Close Other Tabs?",
|
||||
informativeText: "At least one other tab still has a running process. If you close the tab the process will be killed."
|
||||
) {
|
||||
self.closeOtherTabsImmediately()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func returnToDefaultSize(_ sender: Any?) {
|
||||
guard let defaultSize else { return }
|
||||
window?.setFrame(defaultSize, display: true)
|
||||
|
|
@ -1206,6 +1293,12 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
closeTab(self)
|
||||
}
|
||||
|
||||
@objc private func onCloseOtherTabs(notification: SwiftUI.Notification) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard surfaceTree.contains(target) else { return }
|
||||
closeOtherTabs(self)
|
||||
}
|
||||
|
||||
@objc private func onCloseWindow(notification: SwiftUI.Notification) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard surfaceTree.contains(target) else { return }
|
||||
|
|
|
|||
|
|
@ -70,4 +70,39 @@ extension Ghostty.Action {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProgressReport {
|
||||
enum State {
|
||||
case remove
|
||||
case set
|
||||
case error
|
||||
case indeterminate
|
||||
case pause
|
||||
|
||||
init(_ c: ghostty_action_progress_report_state_e) {
|
||||
switch c {
|
||||
case GHOSTTY_PROGRESS_STATE_REMOVE:
|
||||
self = .remove
|
||||
case GHOSTTY_PROGRESS_STATE_SET:
|
||||
self = .set
|
||||
case GHOSTTY_PROGRESS_STATE_ERROR:
|
||||
self = .error
|
||||
case GHOSTTY_PROGRESS_STATE_INDETERMINATE:
|
||||
self = .indeterminate
|
||||
case GHOSTTY_PROGRESS_STATE_PAUSE:
|
||||
self = .pause
|
||||
default:
|
||||
self = .remove
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state: State
|
||||
let progress: UInt8?
|
||||
|
||||
init(c: ghostty_action_progress_report_s) {
|
||||
self.state = State(c.state)
|
||||
self.progress = c.progress >= 0 ? UInt8(c.progress) : nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ extension Ghostty {
|
|||
newSplit(app, target: target, direction: action.action.new_split)
|
||||
|
||||
case GHOSTTY_ACTION_CLOSE_TAB:
|
||||
closeTab(app, target: target)
|
||||
closeTab(app, target: target, mode: action.action.close_tab_mode)
|
||||
|
||||
case GHOSTTY_ACTION_CLOSE_WINDOW:
|
||||
closeWindow(app, target: target)
|
||||
|
|
@ -543,6 +543,9 @@ extension Ghostty {
|
|||
|
||||
case GHOSTTY_ACTION_KEY_SEQUENCE:
|
||||
keySequence(app, target: target, v: action.action.key_sequence)
|
||||
|
||||
case GHOSTTY_ACTION_PROGRESS_REPORT:
|
||||
progressReport(app, target: target, v: action.action.progress_report)
|
||||
|
||||
case GHOSTTY_ACTION_CONFIG_CHANGE:
|
||||
configChange(app, target: target, v: action.action.config_change)
|
||||
|
|
@ -778,20 +781,34 @@ extension Ghostty {
|
|||
}
|
||||
}
|
||||
|
||||
private static func closeTab(_ app: ghostty_app_t, target: ghostty_target_s) {
|
||||
private static func closeTab(_ app: ghostty_app_t, target: ghostty_target_s, mode: ghostty_action_close_tab_mode_e) {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("close tab does nothing with an app target")
|
||||
Ghostty.logger.warning("close tabs does nothing with an app target")
|
||||
return
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyCloseTab,
|
||||
object: surfaceView
|
||||
)
|
||||
switch (mode) {
|
||||
case GHOSTTY_ACTION_CLOSE_TAB_MODE_THIS:
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyCloseTab,
|
||||
object: surfaceView
|
||||
)
|
||||
return
|
||||
|
||||
case GHOSTTY_ACTION_CLOSE_TAB_MODE_OTHER:
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyCloseOtherTabs,
|
||||
object: surfaceView
|
||||
)
|
||||
return
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
|
|
@ -1509,6 +1526,33 @@ extension Ghostty {
|
|||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private static func progressReport(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
v: ghostty_action_progress_report_s) {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("progress report does nothing with an app target")
|
||||
return
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||
|
||||
let progressReport = Ghostty.Action.ProgressReport(c: v)
|
||||
DispatchQueue.main.async {
|
||||
if progressReport.state == .remove {
|
||||
surfaceView.progressReport = nil
|
||||
} else {
|
||||
surfaceView.progressReport = progressReport
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private static func configReload(
|
||||
_ app: ghostty_app_t,
|
||||
|
|
|
|||
|
|
@ -504,6 +504,14 @@ extension Ghostty {
|
|||
let str = String(cString: ptr)
|
||||
return QuickTerminalSpaceBehavior(fromGhosttyConfig: str) ?? .move
|
||||
}
|
||||
|
||||
var quickTerminalSize: QuickTerminalSize {
|
||||
guard let config = self.config else { return QuickTerminalSize() }
|
||||
var v = ghostty_config_quick_terminal_size_s()
|
||||
let key = "quick-terminal-size"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return QuickTerminalSize() }
|
||||
return QuickTerminalSize(from: v)
|
||||
}
|
||||
#endif
|
||||
|
||||
var resizeOverlay: ResizeOverlay {
|
||||
|
|
|
|||
|
|
@ -329,6 +329,9 @@ extension Notification.Name {
|
|||
/// Close tab
|
||||
static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab")
|
||||
|
||||
/// Close other tabs
|
||||
static let ghosttyCloseOtherTabs = Notification.Name("com.mitchellh.ghostty.closeOtherTabs")
|
||||
|
||||
/// Close window
|
||||
static let ghosttyCloseWindow = Notification.Name("com.mitchellh.ghostty.closeWindow")
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,11 @@ extension Ghostty {
|
|||
}
|
||||
}
|
||||
.ghosttySurfaceView(surfaceView)
|
||||
|
||||
// Progress report overlay
|
||||
if let progressReport = surfaceView.progressReport {
|
||||
ProgressReportOverlay(report: progressReport)
|
||||
}
|
||||
|
||||
#if canImport(AppKit)
|
||||
// If we are in the middle of a key sequence, then we show a visual element. We only
|
||||
|
|
@ -267,6 +272,49 @@ extension Ghostty {
|
|||
}
|
||||
}
|
||||
|
||||
// Progress report overlay that shows a progress bar at the top of the terminal
|
||||
struct ProgressReportOverlay: View {
|
||||
let report: Action.ProgressReport
|
||||
|
||||
@ViewBuilder
|
||||
private var progressBar: some View {
|
||||
if let progress = report.progress {
|
||||
// Determinate progress bar
|
||||
ProgressView(value: Double(progress), total: 100)
|
||||
.progressViewStyle(.linear)
|
||||
.tint(report.state == .error ? .red : report.state == .pause ? .orange : nil)
|
||||
.animation(.easeInOut(duration: 0.2), value: progress)
|
||||
} else {
|
||||
// Indeterminate states
|
||||
switch report.state {
|
||||
case .indeterminate:
|
||||
ProgressView()
|
||||
.progressViewStyle(.linear)
|
||||
case .error:
|
||||
ProgressView()
|
||||
.progressViewStyle(.linear)
|
||||
.tint(.red)
|
||||
case .pause:
|
||||
Rectangle().fill(Color.orange)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
progressBar
|
||||
.scaleEffect(x: 1, y: 0.5, anchor: .center)
|
||||
.frame(height: 2)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the resize overlay that shows on top of a surface to show the current
|
||||
// size during a resize operation.
|
||||
struct SurfaceResizeOverlay: View {
|
||||
|
|
@ -424,6 +472,9 @@ extension Ghostty {
|
|||
|
||||
/// Extra input to send as stdin
|
||||
var initialInput: String? = nil
|
||||
|
||||
/// Wait after the command
|
||||
var waitAfterCommand: Bool = false
|
||||
|
||||
init() {}
|
||||
|
||||
|
|
@ -475,6 +526,9 @@ extension Ghostty {
|
|||
|
||||
// Zero is our default value that means to inherit the font size.
|
||||
config.font_size = fontSize ?? 0
|
||||
|
||||
// Set wait after command
|
||||
config.wait_after_command = waitAfterCommand
|
||||
|
||||
// Use withCString to ensure strings remain valid for the duration of the closure
|
||||
return try workingDirectory.withCString { cWorkingDir in
|
||||
|
|
|
|||
|
|
@ -41,6 +41,23 @@ extension Ghostty {
|
|||
|
||||
// The hovered URL string
|
||||
@Published var hoverUrl: String? = nil
|
||||
|
||||
// The progress report (if any)
|
||||
@Published var progressReport: Action.ProgressReport? = nil {
|
||||
didSet {
|
||||
// Cancel any existing timer
|
||||
progressReportTimer?.invalidate()
|
||||
progressReportTimer = nil
|
||||
|
||||
// If we have a new progress report, start a timer to remove it after 15 seconds
|
||||
if progressReport != nil {
|
||||
progressReportTimer = Timer.scheduledTimer(withTimeInterval: 15.0, repeats: false) { [weak self] _ in
|
||||
self?.progressReport = nil
|
||||
self?.progressReportTimer = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The currently active key sequence. The sequence is not active if this is empty.
|
||||
@Published var keySequence: [KeyboardShortcut] = []
|
||||
|
|
@ -142,6 +159,9 @@ extension Ghostty {
|
|||
|
||||
// A timer to fallback to ghost emoji if no title is set within the grace period
|
||||
private var titleFallbackTimer: Timer?
|
||||
|
||||
// Timer to remove progress report after 15 seconds
|
||||
private var progressReportTimer: Timer?
|
||||
|
||||
// This is the title from the terminal. This is nil if we're currently using
|
||||
// the terminal title as the main title property. If the title is set manually
|
||||
|
|
@ -348,6 +368,9 @@ extension Ghostty {
|
|||
// Remove any notifications associated with this surface
|
||||
let identifiers = Array(self.notificationIdentifiers)
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)
|
||||
|
||||
// Cancel progress report timer
|
||||
progressReportTimer?.invalidate()
|
||||
}
|
||||
|
||||
func focusDidChange(_ focused: Bool) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ extension Ghostty {
|
|||
|
||||
// The hovered URL
|
||||
@Published var hoverUrl: String? = nil
|
||||
|
||||
// The progress report (if any)
|
||||
@Published var progressReport: Action.ProgressReport? = nil
|
||||
|
||||
// The time this surface last became focused. This is a ContinuousClock.Instant
|
||||
// on supported platforms.
|
||||
|
|
|
|||
|
|
@ -407,8 +407,14 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||
self.styleMask = window.styleMask
|
||||
self.toolbar = window.toolbar
|
||||
self.toolbarStyle = window.toolbarStyle
|
||||
self.titlebarAccessoryViewControllers = window.titlebarAccessoryViewControllers
|
||||
self.dock = window.screen?.hasDock ?? false
|
||||
|
||||
self.titlebarAccessoryViewControllers = if (window.hasTitleBar) {
|
||||
// Accessing titlebarAccessoryViewControllers without a titlebar triggers a crash.
|
||||
window.titlebarAccessoryViewControllers
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
|
||||
if let cgWindowId = window.cgWindowId {
|
||||
// We hide the menu only if this window is not on any fullscreen
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
# - build.zig.zon.nix
|
||||
# - build.zig.zon.txt
|
||||
# - build.zig.zon.json
|
||||
# - flatpak/zig-packages.json
|
||||
#
|
||||
# All of these are auto-generated and should not be edited manually.
|
||||
|
||||
|
|
@ -34,8 +35,8 @@ help() {
|
|||
echo "commit, and submit a PR with the update:"
|
||||
echo ""
|
||||
echo " ./nix/build-support/check-zig-cache-hash.sh --update"
|
||||
echo " git add build.zig.zon.nix build.zig.zon.txt build.zig.zon.json"
|
||||
echo " git commit -m \"nix: update build.zig.zon.nix build.zig.zon.txt build.zig.zon.json\""
|
||||
echo " git add build.zig.zon.nix build.zig.zon.txt build.zig.zon.json flatpak/zig-packages.json"
|
||||
echo " git commit -m \"nix: update build.zig.zon.nix build.zig.zon.txt build.zig.zon.json flatpak/zig-packages.json\""
|
||||
echo ""
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ BUILD_ZIG_ZON="$ROOT/build.zig.zon"
|
|||
BUILD_ZIG_ZON_NIX="$ROOT/build.zig.zon.nix"
|
||||
BUILD_ZIG_ZON_TXT="$ROOT/build.zig.zon.txt"
|
||||
BUILD_ZIG_ZON_JSON="$ROOT/build.zig.zon.json"
|
||||
ZIG_PACKAGES_JSON="$ROOT/flatpak/zig-packages.json"
|
||||
|
||||
if [ -f "${BUILD_ZIG_ZON_NIX}" ]; then
|
||||
OLD_HASH_NIX=$(sha512sum "${BUILD_ZIG_ZON_NIX}" | awk '{print $1}')
|
||||
|
|
@ -69,27 +71,40 @@ elif [ "$1" != "--update" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
zon2nix "$BUILD_ZIG_ZON" --nix "$WORK_DIR/build.zig.zon.nix" --txt "$WORK_DIR/build.zig.zon.txt" --json "$WORK_DIR/build.zig.zon.json"
|
||||
if [ -f "${ZIG_PACKAGES_JSON}" ]; then
|
||||
OLD_HASH_FLATPAK=$(sha512sum "${ZIG_PACKAGES_JSON}" | awk '{print $1}')
|
||||
elif [ "$1" != "--update" ]; then
|
||||
echo -e "\nERROR: flatpak/zig-packages.json missing."
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
zon2nix "$BUILD_ZIG_ZON" --nix "$WORK_DIR/build.zig.zon.nix" --txt "$WORK_DIR/build.zig.zon.txt" --json "$WORK_DIR/build.zig.zon.json" --flatpak "$WORK_DIR/zig-packages.json"
|
||||
alejandra --quiet "$WORK_DIR/build.zig.zon.nix"
|
||||
prettier --write "$WORK_DIR/build.zig.zon.json"
|
||||
prettier --log-level warn --write "$WORK_DIR/build.zig.zon.json"
|
||||
prettier --log-level warn --write "$WORK_DIR/zig-packages.json"
|
||||
|
||||
NEW_HASH_NIX=$(sha512sum "$WORK_DIR/build.zig.zon.nix" | awk '{print $1}')
|
||||
NEW_HASH_TXT=$(sha512sum "$WORK_DIR/build.zig.zon.txt" | awk '{print $1}')
|
||||
NEW_HASH_JSON=$(sha512sum "$WORK_DIR/build.zig.zon.json" | awk '{print $1}')
|
||||
NEW_HASH_FLATPAK=$(sha512sum "$WORK_DIR/zig-packages.json" | awk '{print $1}')
|
||||
|
||||
if [ "${OLD_HASH_NIX}" == "${NEW_HASH_NIX}" ] && [ "${OLD_HASH_TXT}" == "${NEW_HASH_TXT}" ] && [ "${OLD_HASH_JSON}" == "${NEW_HASH_JSON}" ]; then
|
||||
if [ "${OLD_HASH_NIX}" == "${NEW_HASH_NIX}" ] && [ "${OLD_HASH_TXT}" == "${NEW_HASH_TXT}" ] && [ "${OLD_HASH_JSON}" == "${NEW_HASH_JSON}" ] && [ "${OLD_HASH_FLATPAK}" == "${NEW_HASH_FLATPAK}" ]; then
|
||||
echo -e "\nOK: build.zig.zon.nix unchanged."
|
||||
echo -e "OK: build.zig.zon.txt unchanged."
|
||||
echo -e "OK: build.zig.zon.json unchanged."
|
||||
echo -e "OK: flatpak/zig-packages.json unchanged."
|
||||
exit 0
|
||||
elif [ "$1" != "--update" ]; then
|
||||
echo -e "\nERROR: build.zig.zon.nix, build.zig.zon.txt, or build.zig.zon.json needs to be updated.\n"
|
||||
echo " * Old build.zig.zon.nix hash: ${OLD_HASH_NIX}"
|
||||
echo " * New build.zig.zon.nix hash: ${NEW_HASH_NIX}"
|
||||
echo " * Old build.zig.zon.txt hash: ${OLD_HASH_TXT}"
|
||||
echo " * New build.zig.zon.txt hash: ${NEW_HASH_TXT}"
|
||||
echo " * Old build.zig.zon.json hash: ${OLD_HASH_JSON}"
|
||||
echo " * New build.zig.zon.json hash: ${NEW_HASH_JSON}"
|
||||
echo " * Old build.zig.zon.nix hash: ${OLD_HASH_NIX}"
|
||||
echo " * New build.zig.zon.nix hash: ${NEW_HASH_NIX}"
|
||||
echo " * Old build.zig.zon.txt hash: ${OLD_HASH_TXT}"
|
||||
echo " * New build.zig.zon.txt hash: ${NEW_HASH_TXT}"
|
||||
echo " * Old build.zig.zon.json hash: ${OLD_HASH_JSON}"
|
||||
echo " * New build.zig.zon.json hash: ${NEW_HASH_JSON}"
|
||||
echo " * Old flatpak/zig-packages.json hash: ${OLD_HASH_FLATPAK}"
|
||||
echo " * New flatpak/zig-packages.json hash: ${NEW_HASH_FLATPAK}"
|
||||
help
|
||||
exit 1
|
||||
else
|
||||
|
|
@ -99,6 +114,8 @@ else
|
|||
echo -e "OK: build.zig.zon.txt updated."
|
||||
mv "$WORK_DIR/build.zig.zon.json" "$BUILD_ZIG_ZON_JSON"
|
||||
echo -e "OK: build.zig.zon.json updated."
|
||||
mv "$WORK_DIR/zig-packages.json" "$ZIG_PACKAGES_JSON"
|
||||
echo -e "OK: flatpak/zig-packages.json updated."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@
|
|||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Damyan Bogoev <damyan.bogoev@gmail.com>, 2025.
|
||||
# reo101 <pavel.atanasov2001@gmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-05-19 11:34+0300\n"
|
||||
"Last-Translator: Damyan Bogoev <damyan.bogoev@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-08-22 14:52+0300\n"
|
||||
"Last-Translator: reo101 <pavel.atanasov2001@gmail.com>\n"
|
||||
"Language-Team: Bulgarian <dict@ludost.net>\n"
|
||||
"Language: bg\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -208,12 +209,12 @@ msgstr "Позволи"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Запомни избора за това разделяне"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "За да покажеш това съобщение отново, презареди конфигурацията"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -278,15 +279,15 @@ msgstr "Копирано в клипборда"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Клипбордът е изчистен"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Командата завърши успешно"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Командата завърши неуспешно"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@
|
|||
# Copyright (C) 2025 Mitchell Hashimoto, Ghostty contributors
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Francesc Arpi <francesc.arpi@gmail.com>, 2025.
|
||||
# Kristofer Soler <31729650+KristoferSoler@users.noreply.github.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-20 08:07+0100\n"
|
||||
"Last-Translator: Francesc Arpi <francesc.arpi@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-08-24 19:22+0200\n"
|
||||
"Last-Translator: Kristofer Soler <31729650+KristoferSoler@users.noreply.github.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: ca\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -87,7 +88,7 @@ msgstr "Divideix a la dreta"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Executa una ordre…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -160,7 +161,7 @@ msgstr "Obre la configuració"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Paleta de comandes"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -208,12 +209,12 @@ msgstr "Permet"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Recorda l’opció per a aquest panell dividit"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Recarrega la configuració per tornar a mostrar aquest missatge"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -278,15 +279,15 @@ msgstr "Copiat al porta-retalls"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Porta-retalls netejat"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Comanda completada amb èxit"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Comanda fallida"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
@ -298,7 +299,7 @@ msgstr "Mostra les pestanyes obertes"
|
|||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr ""
|
||||
msgstr "Nova divisió"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ msgstr ""
|
|||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-06 14:57+0100\n"
|
||||
"PO-Revision-Date: 2025-08-25 19:38+0100\n"
|
||||
"Last-Translator: Robin <r@rpfaeffle.com>\n"
|
||||
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
|
||||
"Language: de\n"
|
||||
|
|
@ -39,7 +39,7 @@ msgstr "OK"
|
|||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr ""
|
||||
msgstr "Konfigurationsfehler"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:6
|
||||
|
|
@ -47,11 +47,14 @@ msgid ""
|
|||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Ein oder mehrere Konfigurationsfehler wurden gefunden. Bitte überprüfe "
|
||||
"die untenstehenden Fehler und lade entweder deine Konfiguration erneut oder "
|
||||
"ignoriere die Fehler."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr ""
|
||||
msgstr "Ignorieren"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
|
|
@ -86,7 +89,7 @@ msgstr "Fenster nach rechts teilen"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Einen Befehl ausführen…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -159,7 +162,7 @@ msgstr "Konfiguration öffnen"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Befehlspalette"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -207,12 +210,13 @@ msgstr "Erlauben"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Auswahl für dieses geteilte Fenster beibehalten"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
"Lade die Konfiguration erneut, um diese Eingabeaufforderung erneut anzuzeigen"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -277,15 +281,15 @@ msgstr "In die Zwischenablage kopiert"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Zwischenablage geleert"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Befehl erfolgreich"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Befehl fehlgeschlagen"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
@ -297,7 +301,7 @@ msgstr "Offene Tabs einblenden"
|
|||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr ""
|
||||
msgstr "Neues geteiltes Fenster"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgstr ""
|
|||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-28 17:46+0200\n"
|
||||
"PO-Revision-Date: 2025-08-23 17:46+0200\n"
|
||||
"Last-Translator: Miguel Peredo <miguelp@quientienemail.com>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
"Language: es_BO\n"
|
||||
|
|
@ -87,7 +87,7 @@ msgstr "Dividir a la derecha"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Ejecutar comando..."
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -160,7 +160,7 @@ msgstr "Abrir configuración"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Paleta de comandos"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -208,12 +208,12 @@ msgstr "Permitir"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Recordar su elección para esta división de ventana"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Recargar configuración para mostrar este aviso nuevamente"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -278,15 +278,15 @@ msgstr "Copiado al portapapeles"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "El portapapeles está limpio"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Comando ejecutado con éxito"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Comando fallido"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
@ -298,7 +298,7 @@ msgstr "Ver pestañas abiertas"
|
|||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr ""
|
||||
msgstr "Nueva ventana dividida"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgstr ""
|
|||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-22 09:31+0100\n"
|
||||
"PO-Revision-Date: 2025-08-23 21:01+0200\n"
|
||||
"Last-Translator: Kirwiisp <swiip__@hotmail.com>\n"
|
||||
"Language-Team: French <traduc@traduc.org>\n"
|
||||
"Language: fr\n"
|
||||
|
|
@ -88,7 +88,7 @@ msgstr "Panneau à droite"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Exécuter une commande…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -161,7 +161,7 @@ msgstr "Ouvrir la configuration"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Palette de commandes"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -209,12 +209,12 @@ msgstr "Autoriser"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Se rappeler du choix pour ce panneau"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Recharger la configuration pour afficher à nouveau ce message"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -279,15 +279,15 @@ msgstr "Copié dans le presse-papiers"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Presse-papiers vidé"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Commande réussie"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "La commande a échoué"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
@ -299,7 +299,7 @@ msgstr "Voir les onglets ouverts"
|
|||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr ""
|
||||
msgstr "Nouveau panneau"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgstr ""
|
|||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-06-29 21:15+0100\n"
|
||||
"PO-Revision-Date: 2025-08-26 15:46+0100\n"
|
||||
"Last-Translator: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>\n"
|
||||
"Language-Team: Irish <gaeilge-gnulinux@lists.sourceforge.net>\n"
|
||||
"Language: ga\n"
|
||||
|
|
@ -16,7 +16,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
|
||||
"X-Generator: Poedit 3.4.4\n"
|
||||
"X-Generator: Poedit 3.4.2\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
|
|
@ -209,12 +209,12 @@ msgstr "Ceadaigh"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Sábháil an rogha don scoilt seo"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Athlódáil an chumraíocht chun an teachtaireacht seo a thaispeáint arís"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -280,15 +280,15 @@ msgstr "Cóipeáilte chuig an ghearrthaisce"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Gearrthaisce glanta"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "D'éirigh leis an ordú"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Theip ar an ordú"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Sl (Shahaf Levi), Sl's Repository Ltd <ghostty@slsrepo.com>, 2025.
|
||||
# CraziestOwl <craziestowl@proton.me>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-13 00:00+0000\n"
|
||||
"Last-Translator: Sl (Shahaf Levi), Sl's Repository Ltd <ghostty@slsrepo."
|
||||
"com>\n"
|
||||
"PO-Revision-Date: 2025-08-23 08:00+0300\n"
|
||||
"Last-Translator: CraziestOwl <craziestowl@proton.me>\n"
|
||||
"Language-Team: Hebrew <he_IL@lists.sourceforge.net>\n"
|
||||
"Language: he\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -276,15 +276,15 @@ msgstr "הועתק ללוח ההעתקה"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "לוח ההעתקה רוקן"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "הפקודה הצליחה"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "הפקודה נכשלה"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,320 @@
|
|||
# Hungarian translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Balázs Szücs <bszucs1209@gmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-08-23 17:14+0200\n"
|
||||
"Last-Translator: Balázs Szücs <bszucs1209@gmail.com>\n"
|
||||
"Language-Team: Hungarian <translation-team-hu@lists.sourceforge.net>\n"
|
||||
"Language: hu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Terminál címének módosítása"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Hagyja üresen az alapértelmezett cím visszaállításához."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/ui/1.2/ccw-paste.blp:10
|
||||
#: src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Mégse"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "Rendben"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Konfigurációs hibák"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Egy vagy több konfigurációs hiba található. Kérjük, ellenőrizze az alábbi "
|
||||
"hibákat, és frissítse a konfigurációt, vagy hagyja figyelmen kívül ezeket a "
|
||||
"hibákat."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Figyelmen kívül hagyás"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:100
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:10
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Konfiguráció frissítése"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Felosztás felfelé"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Felosztás lefelé"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:16
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Felosztás balra"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:21
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Felosztás jobbra"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr "Parancs végrehajtása…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Másolás"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11 src/apprt/gtk/ui/1.2/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Beillesztés"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Törlés"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Visszaállítás"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Felosztás"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Cím módosítása…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Fül"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:265
|
||||
msgid "New Tab"
|
||||
msgstr "Új fül"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Fül bezárása"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Ablak"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Új ablak"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Ablak bezárása"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Konfiguráció"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Open Configuration"
|
||||
msgstr "Konfiguráció megnyitása"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr "Parancspaletta"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Terminálvizsgáló"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
#: src/apprt/gtk/Window.zig:1038
|
||||
msgid "About Ghostty"
|
||||
msgstr "A Ghostty névjegye"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:112
|
||||
msgid "Quit"
|
||||
msgstr "Kilépés"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Vágólap-hozzáférés engedélyezése"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Egy alkalmazás megpróbál olvasni a vágólapról. A vágólap jelenlegi tartalma "
|
||||
"lent látható."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Elutasítás"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Engedélyezés"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr "Választás megjegyzése erre a felosztásra"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr "Konfiguráció frissítése a kérdés újbóli megjelenítéséhez"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Egy alkalmazás megpróbál írni a vágólapra. A vágólap jelenlegi tartalma lent "
|
||||
"látható."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6 src/apprt/gtk/ui/1.2/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Figyelem: potenciálisan veszélyes beillesztés"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7 src/apprt/gtk/ui/1.2/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Ennek a szövegnek a terminálba való beillesztése veszélyes lehet, mivel "
|
||||
"néhány parancs végrehajtásra kerülhet."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47 src/apprt/gtk/Surface.zig:2531
|
||||
msgid "Close"
|
||||
msgstr "Bezárás"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Kilép a Ghostty-ból?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Ablak bezárása?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Fül bezárása?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Felosztás bezárása?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Minden terminál munkamenet lezárul."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Ebben az ablakban minden terminál munkamenet lezárul."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Ezen a fülön minden terminál munkamenet lezárul."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "Ebben a felosztásban a jelenleg futó folyamat lezárul."
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1266
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Vágólapra másolva"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr "Vágólap törölve"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr "Parancs sikeres"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr "Parancs sikertelen"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
msgstr "Főmenü"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:239
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Megnyitott fülek megtekintése"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr "Új felosztás"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr ""
|
||||
"⚠️ A Ghostty hibakereső verzióját futtatja! A teljesítmény csökkenni fog."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:775
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Konfiguráció frissítve"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:1019
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Ghostty fejlesztők"
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: Terminálvizsgáló"
|
||||
|
|
@ -2,14 +2,15 @@
|
|||
# Copyright (C) 2025 Mitchell Hashimoto, Ghostty contributors
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Andrej Daskalov <andrej.daskalov@gmail.com>, 2025.
|
||||
# Marija Gjorgjieva Gjondeva <mgjorgjieva2013@gmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-23 14:17+0100\n"
|
||||
"Last-Translator: Andrej Daskalov <andrej.daskalov@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-08-25 22:17+0200\n"
|
||||
"Last-Translator: Marija Gjorgjieva Gjondeva <mgjorgjieva2013@gmail.com>\n"
|
||||
"Language-Team: Macedonian\n"
|
||||
"Language: mk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -87,7 +88,7 @@ msgstr "Подели надесно"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Изврши команда…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -160,7 +161,7 @@ msgstr "Отвори конфигурација"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Командна палета"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -208,12 +209,12 @@ msgstr "Дозволи"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Запомни го изборот за оваа поделба"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Одново вчитај конфигурација за да се повторно прикаже пораката"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -278,15 +279,15 @@ msgstr "Копирано во привремена меморија"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Исчистена привремена меморија"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Командата успеа"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Командата не успеа"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
@ -298,7 +299,7 @@ msgstr "Прегледај отворени јазичиња"
|
|||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr ""
|
||||
msgstr "Нова поделба"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Norwegian Bokmal translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto, Ghostty contributors
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Hanna Rose <hanna@hanna.lol>, 2025.
|
||||
# Hanna Rose <me@hanna.lol>, 2025.
|
||||
# Uzair Aftab <uzaaft@outlook.com>, 2025.
|
||||
# Christoffer Tønnessen <christoffer@cto.gg>, 2025.
|
||||
# cryptocode <cryptocode@zolo.io>, 2025.
|
||||
|
|
@ -11,8 +11,8 @@ msgstr ""
|
|||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-04-14 16:25+0200\n"
|
||||
"Last-Translator: cryptocode <cryptocode@zolo.io>\n"
|
||||
"PO-Revision-Date: 2025-08-23 12:52+0000\n"
|
||||
"Last-Translator: Hanna Rose <me@hanna.lol>\n"
|
||||
"Language-Team: Norwegian Bokmal <l10n-no@lister.huftis.org>\n"
|
||||
"Language: nb\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -90,7 +90,7 @@ msgstr "Del til høyre"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Kjør en kommando..."
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -163,7 +163,7 @@ msgstr "Åpne konfigurasjon"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Kommandopalett"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -211,12 +211,12 @@ msgstr "Tillat"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Husk valget for dette delte vinduet?"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Last inn konfigurasjonen på nytt for å vise denne meldingen igjen"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -281,15 +281,15 @@ msgstr "Kopiert til utklippstavlen"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Utklippstavle tømt"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Kommando lyktes"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Kommando mislyktes"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
# Copyright (C) 2025 Mitchell Hashimoto, Ghostty contributors
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Gustavo Peres <gsodevel@gmail.com>, 2025.
|
||||
# Guilherme Tiscoski <github@guilhermetiscoski.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-06-20 10:19-0300\n"
|
||||
"Last-Translator: Mário Victor Ribeiro Silva <mariovictorrs@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-08-25 11:46-0500\n"
|
||||
"Last-Translator: Guilherme Tiscoski <github@guihermetiscoski.com>\n"
|
||||
"Language-Team: Brazilian Portuguese <ldpbr-translation@lists.sourceforge."
|
||||
"net>\n"
|
||||
"Language: pt_BR\n"
|
||||
|
|
@ -89,7 +90,7 @@ msgstr "Dividir à direita"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Executar um comando…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -162,7 +163,7 @@ msgstr "Abrir configuração"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Paleta de comandos"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -210,12 +211,12 @@ msgstr "Permitir"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Lembrar escolha para esta divisão"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Recarregue a configuração para mostrar este aviso novamente"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -280,15 +281,15 @@ msgstr "Copiado para a área de transferência"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Área de transferência limpa"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Comando executado com sucesso"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Comando falhou"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgstr ""
|
|||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-24 22:01+0300\n"
|
||||
"PO-Revision-Date: 2025-08-23 17:30+0300\n"
|
||||
"Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr\n"
|
||||
|
|
@ -88,7 +88,7 @@ msgstr "Sağa Doğru Böl"
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Bir komut çalıştır…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -161,7 +161,7 @@ msgstr "Yapılandırmayı Aç"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Komut Paleti"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -209,12 +209,12 @@ msgstr "İzin Ver"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Bu bölme için tercihi anımsa"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Bu istemi tekrar göstermek için yapılandırmayı yeniden yükle"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -279,15 +279,15 @@ msgstr "Panoya kopyalandı"
|
|||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Pano temizlendi"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Komut başarılı oldu"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Komut başarısız oldu"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@
|
|||
# Copyright (C) 2025 Mitchell Hashimoto, Ghostty contributors
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Danylo Zalizchuk <danilmail0110@gmail.com>, 2025.
|
||||
# Volodymyr Chernetskyi <19735328+chernetskyi@users.noreply.github.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-03-16 20:16+0200\n"
|
||||
"Last-Translator: Danylo Zalizchuk <danilmail0110@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-08-25 19:59+0100\n"
|
||||
"Last-Translator: Volodymyr Chernetskyi <19735328+chernetskyi@users.noreply.github.com>\n"
|
||||
"Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
|
||||
"Language: uk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -20,17 +21,17 @@ msgstr ""
|
|||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Змінити назву терміналу"
|
||||
msgstr "Змінити заголовок терміналу"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Залиште порожнім, щоб відновити назву за замовчуванням."
|
||||
msgstr "Залиште порожнім, щоб відновити заголовок за замовчуванням."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/ui/1.2/ccw-paste.blp:10
|
||||
#: src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Відмінити"
|
||||
msgstr "Скасувати"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
|
|
@ -39,7 +40,7 @@ msgstr "ОК"
|
|||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Помилки конфігурації"
|
||||
msgstr "Помилки налаштування"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:6
|
||||
|
|
@ -47,9 +48,8 @@ msgid ""
|
|||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Виявлено одну або декілька помилок у конфігурації. Будь ласка, перегляньте "
|
||||
"наведені нижче помилки і або перезавантажте конфігурацію, або проігноруйте "
|
||||
"ці помилки."
|
||||
"Виявлено одну або декілька помилок налаштування. Будь ласка, перегляньте "
|
||||
"помилки нижче і або перезавантажте налаштування, або проігноруйте ці помилки."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:9
|
||||
|
|
@ -61,35 +61,35 @@ msgstr "Ігнорувати"
|
|||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:100
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:10
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Перезавантажити конфігурацію"
|
||||
msgstr "Перезавантажити налаштування"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Розділити панель вгору"
|
||||
msgstr "Нова панель зверху"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Розділити панель вниз"
|
||||
msgstr "Нова панель знизу"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:16
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Розділити панель ліворуч"
|
||||
msgstr "Нова панель ліворуч"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:21
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Розділити панель праворуч"
|
||||
msgstr "Нова панель праворуч"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr ""
|
||||
msgstr "Виконати команду…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
|
|
@ -115,7 +115,7 @@ msgstr "Скинути"
|
|||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Розділена панель"
|
||||
msgstr "Панель"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
|
|
@ -153,16 +153,16 @@ msgstr "Закрити вікно"
|
|||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Конфігурація"
|
||||
msgstr "Налаштування"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Open Configuration"
|
||||
msgstr "Відкрити конфігурацію"
|
||||
msgstr "Відкрити налаштування"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr ""
|
||||
msgstr "Палітра команд"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
|
|
@ -182,7 +182,7 @@ msgstr "Завершити"
|
|||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Дозволити доступ до буфера обміну"
|
||||
msgstr "Надати доступ до буфера обміну"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:7
|
||||
|
|
@ -190,15 +190,15 @@ msgid ""
|
|||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Програма намагається прочитати дані з буфера обміну. Нижче показано поточний "
|
||||
"вміст буфера обміну."
|
||||
"Програма намагається прочитати дані з буфера обміну. Нижче наведено вміст "
|
||||
"буфера обміну."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Відхилити"
|
||||
msgstr "Заборонити"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
|
|
@ -210,12 +210,12 @@ msgstr "Дозволити"
|
|||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr ""
|
||||
msgstr "Запамʼятати для цієї панелі"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr ""
|
||||
msgstr "Перезавантажте налаштування, щоб показати це повідомлення знову"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
|
|
@ -223,8 +223,8 @@ msgid ""
|
|||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Програма намагається записати дані до буфера обміну. Нижче показано поточний "
|
||||
"вміст буфера обміну."
|
||||
"Програма намагається записати дані до буфера обміну. Нижче наведено вміст "
|
||||
"буфера обміну."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6 src/apprt/gtk/ui/1.2/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
|
|
@ -235,8 +235,8 @@ msgid ""
|
|||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Вставка цього тексту в термінал може бути небезпечною, оскільки виглядає "
|
||||
"так, ніби деякі команди можуть бути виконані."
|
||||
"Вставка цього тексту в термінал може бути небезпечною, бо схоже, що деякі "
|
||||
"команди можуть бути виконані."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47 src/apprt/gtk/Surface.zig:2531
|
||||
msgid "Close"
|
||||
|
|
@ -256,7 +256,7 @@ msgstr "Закрити вкладку?"
|
|||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Закрити розділену панель?"
|
||||
msgstr "Закрити панель?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
|
|
@ -272,24 +272,23 @@ msgstr "Всі сесії терміналу в цій вкладці будут
|
|||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr ""
|
||||
"Поточний процес, що виконується в цій розділеній панелі, буде завершено."
|
||||
msgstr "Процес, що виконується в цій панелі, буде завершено."
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1266
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Скопійовано в буфер обміну"
|
||||
msgstr "Скопійовано до буферa обміну"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr ""
|
||||
msgstr "Буфер обміну очищено"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr ""
|
||||
msgstr "Команда завершилась успішно"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
msgstr "Команда завершилась з помилкою"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
|
|
@ -301,7 +300,7 @@ msgstr "Переглянути відкриті вкладки"
|
|||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr ""
|
||||
msgstr "Нова панель"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
|
|
@ -311,7 +310,7 @@ msgstr ""
|
|||
|
||||
#: src/apprt/gtk/Window.zig:775
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Конфігурацію перезавантажено"
|
||||
msgstr "Налаштування перезавантажено"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:1019
|
||||
msgid "Ghostty Developers"
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ const DerivedConfig = struct {
|
|||
title_report: bool,
|
||||
links: []Link,
|
||||
link_previews: configpkg.LinkPreviews,
|
||||
scroll_to_bottom: configpkg.Config.ScrollToBottom,
|
||||
|
||||
const Link = struct {
|
||||
regex: oni.Regex,
|
||||
|
|
@ -340,6 +341,7 @@ const DerivedConfig = struct {
|
|||
.title_report = config.@"title-report",
|
||||
.links = links,
|
||||
.link_previews = config.@"link-previews",
|
||||
.scroll_to_bottom = config.@"scroll-to-bottom",
|
||||
|
||||
// Assignments happen sequentially so we have to do this last
|
||||
// so that the memory is captured from allocs above.
|
||||
|
|
@ -2280,7 +2282,8 @@ pub fn keyCallback(
|
|||
try self.setSelection(null);
|
||||
}
|
||||
|
||||
try self.io.terminal.scrollViewport(.{ .bottom = {} });
|
||||
if (self.config.scroll_to_bottom.keystroke) try self.io.terminal.scrollViewport(.bottom);
|
||||
|
||||
try self.queueRender();
|
||||
}
|
||||
|
||||
|
|
@ -2766,8 +2769,21 @@ pub fn scrollCallback(
|
|||
// that a wheel tick of 1 results in single scroll event.
|
||||
const yoff_adjusted: f64 = if (scroll_mods.precision)
|
||||
yoff
|
||||
else
|
||||
yoff * cell_size * self.config.mouse_scroll_multiplier;
|
||||
else yoff_adjusted: {
|
||||
// Round out the yoff to an absolute minimum of 1. macos tries to
|
||||
// simulate precision scrolling with non precision events by
|
||||
// ramping up the magnitude of the offsets as it detects faster
|
||||
// scrolling. Single click (very slow) scrolls are reported with a
|
||||
// magnitude of 0.1 which would normally require a few clicks
|
||||
// before we register an actual scroll event (depending on cell
|
||||
// height and the mouse_scroll_multiplier setting).
|
||||
const yoff_max: f64 = if (yoff > 0)
|
||||
@max(yoff, 1)
|
||||
else
|
||||
@min(yoff, -1);
|
||||
|
||||
break :yoff_adjusted yoff_max * cell_size * self.config.mouse_scroll_multiplier;
|
||||
};
|
||||
|
||||
// Add our previously saved pending amount to the offset to get the
|
||||
// new offset value. The signs of the pending and yoff should match
|
||||
|
|
@ -4701,10 +4717,13 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||
{},
|
||||
),
|
||||
|
||||
.close_tab => return try self.rt_app.performAction(
|
||||
.close_tab => |v| return try self.rt_app.performAction(
|
||||
.{ .surface = self },
|
||||
.close_tab,
|
||||
{},
|
||||
switch (v) {
|
||||
.this => .this,
|
||||
.other => .other,
|
||||
},
|
||||
),
|
||||
|
||||
inline .previous_tab,
|
||||
|
|
|
|||
|
|
@ -70,13 +70,15 @@ pub const Runtime = enum {
|
|||
gtk,
|
||||
|
||||
pub fn default(target: std.Target) Runtime {
|
||||
// The Linux default is GTK because it is a full featured application.
|
||||
if (target.os.tag == .linux) return .@"gtk-ng";
|
||||
|
||||
// Otherwise, we do NONE so we don't create an exe and we
|
||||
// create libghostty. On macOS, Xcode is used to build the app
|
||||
// that links to libghostty.
|
||||
return .none;
|
||||
return switch (target.os.tag) {
|
||||
// The Linux and FreeBSD default is GTK because it is a full
|
||||
// featured application.
|
||||
.linux, .freebsd => .@"gtk-ng",
|
||||
// Otherwise, we do NONE so we don't create an exe and we create
|
||||
// libghostty. On macOS, Xcode is used to build the app that links
|
||||
// to libghostty.
|
||||
else => .none,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -83,8 +83,9 @@ pub const Action = union(Key) {
|
|||
/// the tab should be opened in a new window.
|
||||
new_tab,
|
||||
|
||||
/// Closes the tab belonging to the currently focused split.
|
||||
close_tab,
|
||||
/// Closes the tab belonging to the currently focused split, or all other
|
||||
/// tabs, depending on the mode.
|
||||
close_tab: CloseTabMode,
|
||||
|
||||
/// Create a new split. The value determines the location of the split
|
||||
/// relative to the target.
|
||||
|
|
@ -701,3 +702,11 @@ pub const OpenUrl = struct {
|
|||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// sync with ghostty_action_close_tab_mode_e in ghostty.h
|
||||
pub const CloseTabMode = enum(c_int) {
|
||||
/// Close the current tab.
|
||||
this,
|
||||
/// Close all other tabs.
|
||||
other,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -447,6 +447,9 @@ pub const Surface = struct {
|
|||
|
||||
/// Input to send to the command after it is started.
|
||||
initial_input: ?[*:0]const u8 = null,
|
||||
|
||||
/// Wait after the command exits
|
||||
wait_after_command: bool = false,
|
||||
};
|
||||
|
||||
pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
||||
|
|
@ -540,6 +543,11 @@ pub const Surface = struct {
|
|||
);
|
||||
}
|
||||
|
||||
// Wait after command
|
||||
if (opts.wait_after_command) {
|
||||
config.@"wait-after-command" = true;
|
||||
}
|
||||
|
||||
// Initialize our surface right away. We're given a view that is
|
||||
// ready to use.
|
||||
try self.core_surface.init(
|
||||
|
|
|
|||
|
|
@ -116,6 +116,11 @@ pub const Application = extern struct {
|
|||
/// and initialization was successful.
|
||||
transient_cgroup_base: ?[]const u8 = null,
|
||||
|
||||
/// This is set to true so long as we request a window exactly
|
||||
/// once. This prevents quitting the app before we've shown one
|
||||
/// window.
|
||||
requested_window: bool = false,
|
||||
|
||||
/// This is set to false internally when the event loop
|
||||
/// should exit and the application should quit. This must
|
||||
/// only be set by the main loop thread.
|
||||
|
|
@ -461,7 +466,13 @@ pub const Application = extern struct {
|
|||
// If the quit timer has expired, quit.
|
||||
if (priv.quit_timer == .expired) break :q true;
|
||||
|
||||
// There's no quit timer running, or it hasn't expired, don't quit.
|
||||
// If we have no windows attached to our app, also quit.
|
||||
if (priv.requested_window and @as(
|
||||
?*glib.List,
|
||||
self.as(gtk.Application).getWindows(),
|
||||
) == null) break :q true;
|
||||
|
||||
// No quit conditions met
|
||||
break :q false;
|
||||
};
|
||||
|
||||
|
|
@ -488,7 +499,15 @@ pub const Application = extern struct {
|
|||
const parent: ?*gtk.Widget = parent: {
|
||||
const list = gtk.Window.listToplevels();
|
||||
defer list.free();
|
||||
const focused = list.findCustom(null, findActiveWindow);
|
||||
const focused = @as(?*glib.List, list.findCustom(
|
||||
null,
|
||||
findActiveWindow,
|
||||
)) orelse {
|
||||
// If we have an active surface then we should have
|
||||
// a window available but in the rare case we don't we
|
||||
// should exit so we don't crash.
|
||||
break :parent null;
|
||||
};
|
||||
break :parent @ptrCast(@alignCast(focused.f_data));
|
||||
};
|
||||
|
||||
|
|
@ -542,7 +561,7 @@ pub const Application = extern struct {
|
|||
value: apprt.Action.Value(action),
|
||||
) !bool {
|
||||
switch (action) {
|
||||
.close_tab => return Action.closeTab(target),
|
||||
.close_tab => return Action.closeTab(target, value),
|
||||
.close_window => return Action.closeWindow(target),
|
||||
|
||||
.config_change => try Action.configChange(
|
||||
|
|
@ -713,27 +732,24 @@ pub const Application = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn loadRuntimeCss(
|
||||
self: *Self,
|
||||
) Allocator.Error!void {
|
||||
fn loadRuntimeCss(self: *Self) Allocator.Error!void {
|
||||
const alloc = self.allocator();
|
||||
|
||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||
const config = self.private().config.get();
|
||||
|
||||
var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(alloc, 2048);
|
||||
defer buf.deinit(alloc);
|
||||
|
||||
const writer = buf.writer(alloc);
|
||||
|
||||
const config = self.private().config.get();
|
||||
const window_theme = config.@"window-theme";
|
||||
const unfocused_fill: CoreConfig.Color = config.@"unfocused-split-fill" orelse config.background;
|
||||
const headerbar_background = config.@"window-titlebar-background" orelse config.background;
|
||||
const headerbar_foreground = config.@"window-titlebar-foreground" orelse config.foreground;
|
||||
|
||||
try writer.print(
|
||||
\\widget.unfocused-split {{
|
||||
\\ opacity: {d:.2};
|
||||
\\ background-color: rgb({d},{d},{d});
|
||||
\\}}
|
||||
\\
|
||||
, .{
|
||||
1.0 - config.@"unfocused-split-opacity",
|
||||
unfocused_fill.r,
|
||||
|
|
@ -747,6 +763,7 @@ pub const Application = extern struct {
|
|||
\\ color: rgb({[r]d},{[g]d},{[b]d});
|
||||
\\ background: rgb({[r]d},{[g]d},{[b]d});
|
||||
\\}}
|
||||
\\
|
||||
, .{
|
||||
.r = color.r,
|
||||
.g = color.g,
|
||||
|
|
@ -759,9 +776,129 @@ pub const Application = extern struct {
|
|||
\\.window headerbar {{
|
||||
\\ font-family: "{[font_family]s}";
|
||||
\\}}
|
||||
\\
|
||||
, .{ .font_family = font_family });
|
||||
}
|
||||
|
||||
try loadRuntimeCss414(config, &writer);
|
||||
try loadRuntimeCss416(config, &writer);
|
||||
|
||||
// ensure that we have a sentinel
|
||||
try writer.writeByte(0);
|
||||
|
||||
const data = buf.items[0 .. buf.items.len - 1 :0];
|
||||
|
||||
log.debug("runtime CSS is {d} bytes", .{data.len + 1});
|
||||
|
||||
// Clears any previously loaded CSS from this provider
|
||||
loadCssProviderFromData(
|
||||
self.private().css_provider,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/// Load runtime CSS for older than GTK 4.16
|
||||
fn loadRuntimeCss414(
|
||||
config: *const CoreConfig,
|
||||
writer: *const std.ArrayListUnmanaged(u8).Writer,
|
||||
) Allocator.Error!void {
|
||||
if (gtk_version.runtimeAtLeast(4, 16, 0)) return;
|
||||
|
||||
const window_theme = config.@"window-theme";
|
||||
const headerbar_background = config.@"window-titlebar-background" orelse config.background;
|
||||
const headerbar_foreground = config.@"window-titlebar-foreground" orelse config.foreground;
|
||||
|
||||
switch (window_theme) {
|
||||
.ghostty => try writer.print(
|
||||
\\windowhandle {{
|
||||
\\ background-color: rgb({d},{d},{d});
|
||||
\\ color: rgb({d},{d},{d});
|
||||
\\}}
|
||||
\\windowhandle:backdrop {{
|
||||
\\ background-color: oklab(from rgb({d},{d},{d}) calc(l * 0.9) a b / alpha);
|
||||
\\}}
|
||||
\\
|
||||
, .{
|
||||
headerbar_background.r,
|
||||
headerbar_background.g,
|
||||
headerbar_background.b,
|
||||
headerbar_foreground.r,
|
||||
headerbar_foreground.g,
|
||||
headerbar_foreground.b,
|
||||
headerbar_background.r,
|
||||
headerbar_background.g,
|
||||
headerbar_background.b,
|
||||
}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Load runtime for GTK 4.16 and newer
|
||||
fn loadRuntimeCss416(
|
||||
config: *const CoreConfig,
|
||||
writer: *const std.ArrayListUnmanaged(u8).Writer,
|
||||
) Allocator.Error!void {
|
||||
if (gtk_version.runtimeUntil(4, 16, 0)) return;
|
||||
|
||||
const window_theme = config.@"window-theme";
|
||||
const headerbar_background = config.@"window-titlebar-background" orelse config.background;
|
||||
const headerbar_foreground = config.@"window-titlebar-foreground" orelse config.foreground;
|
||||
|
||||
try writer.writeAll(
|
||||
\\/*
|
||||
\\ * Child Exited Overlay
|
||||
\\ */
|
||||
\\
|
||||
\\.child-exited.normal revealer widget {
|
||||
\\ background-color: color-mix(
|
||||
\\ in srgb,
|
||||
\\ var(--success-bg-color),
|
||||
\\ transparent 50%
|
||||
\\ );
|
||||
\\}
|
||||
\\
|
||||
\\.child-exited.abnormal revealer widget {
|
||||
\\ background-color: color-mix(
|
||||
\\ in srgb,
|
||||
\\ var(--error-bg-color),
|
||||
\\ transparent 50%
|
||||
\\ );
|
||||
\\}
|
||||
\\
|
||||
\\/*
|
||||
\\ * Surface
|
||||
\\ */
|
||||
\\
|
||||
\\.surface progressbar.error trough progress {
|
||||
\\ background-color: color-mix(
|
||||
\\ in srgb,
|
||||
\\ var(--error-bg-color),
|
||||
\\ transparent 50%
|
||||
\\ );
|
||||
\\}
|
||||
\\
|
||||
\\.surface .bell-overlay {
|
||||
\\ border-color: color-mix(
|
||||
\\ in srgb,
|
||||
\\ var(--accent-color),
|
||||
\\ transparent 50%
|
||||
\\ );
|
||||
\\}
|
||||
\\
|
||||
\\/*
|
||||
\\ * Splits
|
||||
\\ */
|
||||
\\
|
||||
\\.window .split paned > separator {
|
||||
\\ background-color: color-mix(
|
||||
\\ in srgb,
|
||||
\\ var(--window-bg-color),
|
||||
\\ transparent 0%
|
||||
\\ );
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
|
||||
switch (window_theme) {
|
||||
.ghostty => try writer.print(
|
||||
\\:root {{
|
||||
|
|
@ -794,15 +931,6 @@ pub const Application = extern struct {
|
|||
}),
|
||||
else => {},
|
||||
}
|
||||
|
||||
const data = try alloc.dupeZ(u8, buf.items);
|
||||
defer alloc.free(data);
|
||||
|
||||
// Clears any previously loaded CSS from this provider
|
||||
loadCssProviderFromData(
|
||||
self.private().css_provider,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
fn loadCustomCss(self: *Self) !void {
|
||||
|
|
@ -872,7 +1000,8 @@ pub const Application = extern struct {
|
|||
self.syncActionAccelerator("win.close", .{ .close_window = {} });
|
||||
self.syncActionAccelerator("win.new-window", .{ .new_window = {} });
|
||||
self.syncActionAccelerator("win.new-tab", .{ .new_tab = {} });
|
||||
self.syncActionAccelerator("win.close-tab", .{ .close_tab = {} });
|
||||
self.syncActionAccelerator("win.close-tab::this", .{ .close_tab = .this });
|
||||
self.syncActionAccelerator("tab.close::this", .{ .close_tab = .this });
|
||||
self.syncActionAccelerator("win.split-right", .{ .new_split = .right });
|
||||
self.syncActionAccelerator("win.split-down", .{ .new_split = .down });
|
||||
self.syncActionAccelerator("win.split-left", .{ .new_split = .left });
|
||||
|
|
@ -1576,12 +1705,16 @@ pub const Application = extern struct {
|
|||
|
||||
/// All apprt action handlers
|
||||
const Action = struct {
|
||||
pub fn closeTab(target: apprt.Target) bool {
|
||||
pub fn closeTab(target: apprt.Target, value: apprt.Action.Value(.close_tab)) bool {
|
||||
switch (target) {
|
||||
.app => return false,
|
||||
.surface => |core| {
|
||||
const surface = core.rt_surface.surface;
|
||||
return surface.as(gtk.Widget).activateAction("tab.close", null) != 0;
|
||||
return surface.as(gtk.Widget).activateAction(
|
||||
"tab.close",
|
||||
glib.ext.VariantType.stringFor([:0]const u8),
|
||||
@as([*:0]const u8, @tagName(value)),
|
||||
) != 0;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1853,6 +1986,13 @@ const Action = struct {
|
|||
self: *Application,
|
||||
parent: ?*CoreSurface,
|
||||
) !void {
|
||||
// Note that we've requested a window at least once. This is used
|
||||
// to trigger quit on no windows. Note I'm not sure if this is REALLY
|
||||
// necessary, but I don't want to risk a bug where on a slow machine
|
||||
// or something we quit immediately after starting up because there
|
||||
// was a delay in the event loop before we created a Window.
|
||||
self.private().requested_window = true;
|
||||
|
||||
const win = Window.new(self);
|
||||
initAndShowWindow(self, win, parent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const Common = @import("../class.zig").Common;
|
|||
const Config = @import("config.zig").Config;
|
||||
const Dialog = @import("dialog.zig").Dialog;
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_config_errors_dialog);
|
||||
const log = std.log.scoped(.gtk_ghostty_close_confirmation_dialog);
|
||||
|
||||
pub const CloseConfirmationDialog = extern struct {
|
||||
const Self = @This();
|
||||
|
|
|
|||
|
|
@ -105,6 +105,24 @@ pub const Surface = extern struct {
|
|||
);
|
||||
};
|
||||
|
||||
pub const @"error" = struct {
|
||||
pub const name = "error";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
bool,
|
||||
.{
|
||||
.default = false,
|
||||
.accessor = gobject.ext.privateFieldAccessor(
|
||||
Self,
|
||||
Private,
|
||||
&Private.offset,
|
||||
"error",
|
||||
),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"font-size-request" = struct {
|
||||
pub const name = "font-size-request";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
|
|
@ -472,6 +490,12 @@ pub const Surface = extern struct {
|
|||
// false by a parent widget.
|
||||
bell_ringing: bool = false,
|
||||
|
||||
/// True if this surface is in an error state. This is currently
|
||||
/// a simple boolean with no additional information on WHAT the
|
||||
/// error state is, because we don't yet need it or use it. For now,
|
||||
/// if this is true, then it means the terminal is non-functional.
|
||||
@"error": bool = false,
|
||||
|
||||
/// A weak reference to an inspector window.
|
||||
inspector: ?*InspectorWindow = null,
|
||||
|
||||
|
|
@ -571,6 +595,17 @@ pub const Surface = extern struct {
|
|||
return @intFromBool(config.@"bell-features".border);
|
||||
}
|
||||
|
||||
fn closureStackChildName(
|
||||
_: *Self,
|
||||
error_: c_int,
|
||||
) callconv(.c) ?[*:0]const u8 {
|
||||
const err = error_ != 0;
|
||||
return if (err)
|
||||
glib.ext.dupeZ(u8, "error")
|
||||
else
|
||||
glib.ext.dupeZ(u8, "terminal");
|
||||
}
|
||||
|
||||
pub fn toggleFullscreen(self: *Self) void {
|
||||
signals.@"toggle-fullscreen".impl.emit(
|
||||
self,
|
||||
|
|
@ -1540,6 +1575,12 @@ pub const Surface = extern struct {
|
|||
self.as(gobject.Object).notifyByPspec(properties.@"bell-ringing".impl.param_spec);
|
||||
}
|
||||
|
||||
pub fn setError(self: *Self, v: bool) void {
|
||||
const priv = self.private();
|
||||
priv.@"error" = v;
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"error".impl.param_spec);
|
||||
}
|
||||
|
||||
fn propConfig(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
|
|
@ -1592,6 +1633,28 @@ pub const Surface = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn propError(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
if (priv.@"error") {
|
||||
// Ensure we have an opaque background. The window will NOT set
|
||||
// this if we have transparency set and we need an opaque
|
||||
// background for the error message to be readable.
|
||||
self.as(gtk.Widget).addCssClass("background");
|
||||
} else {
|
||||
// Regardless of transparency setting, we remove the background
|
||||
// CSS class from this widget. Parent widgets will set it
|
||||
// appropriately (see window.zig for example).
|
||||
self.as(gtk.Widget).removeCssClass("background");
|
||||
}
|
||||
|
||||
// Note above: in both cases setting our error view is handled by
|
||||
// a Gtk.Stack visible-child-name binding.
|
||||
}
|
||||
|
||||
fn propMouseHoverUrl(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
|
|
@ -1942,8 +2005,11 @@ pub const Surface = extern struct {
|
|||
// Bell stops ringing if any mouse button is pressed.
|
||||
self.setBellRinging(false);
|
||||
|
||||
// If we don't have focus, grab it.
|
||||
// Get our surface. If we don't have one, ignore this.
|
||||
const priv = self.private();
|
||||
const core_surface = priv.core_surface orelse return;
|
||||
|
||||
// If we don't have focus, grab it.
|
||||
const gl_area_widget = priv.gl_area.as(gtk.Widget);
|
||||
if (gl_area_widget.hasFocus() == 0) {
|
||||
_ = gl_area_widget.grabFocus();
|
||||
|
|
@ -1951,10 +2017,10 @@ pub const Surface = extern struct {
|
|||
|
||||
// Report the event
|
||||
const button = translateMouseButton(gesture.as(gtk.GestureSingle).getCurrentButton());
|
||||
const consumed = if (priv.core_surface) |surface| consumed: {
|
||||
const consumed = consumed: {
|
||||
const gtk_mods = event.getModifierState();
|
||||
const mods = gtk_key.translateMods(gtk_mods);
|
||||
break :consumed surface.mouseButtonCallback(
|
||||
break :consumed core_surface.mouseButtonCallback(
|
||||
.press,
|
||||
button,
|
||||
mods,
|
||||
|
|
@ -1962,7 +2028,7 @@ pub const Surface = extern struct {
|
|||
log.warn("error in key callback err={}", .{err});
|
||||
break :err false;
|
||||
};
|
||||
} else false;
|
||||
};
|
||||
|
||||
// If a right click isn't consumed, mouseButtonCallback selects the hovered
|
||||
// word and returns false. We can use this to handle the context menu
|
||||
|
|
@ -2303,21 +2369,23 @@ pub const Surface = extern struct {
|
|||
) callconv(.c) void {
|
||||
log.debug("realize", .{});
|
||||
|
||||
// Make the GL area current so we can detect any OpenGL errors. If
|
||||
// we have errors here we can't render and we switch to the error
|
||||
// state.
|
||||
const priv = self.private();
|
||||
priv.gl_area.makeCurrent();
|
||||
if (priv.gl_area.getError()) |err| {
|
||||
log.warn("failed to make GL context current: {s}", .{err.f_message orelse "(no message)"});
|
||||
log.warn("this error is almost always due to a library, driver, or GTK issue", .{});
|
||||
log.warn("this is a common cause of this issue: https://ghostty.org/docs/help/gtk-opengl-context", .{});
|
||||
self.setError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we already have an initialized surface then we notify it.
|
||||
// If we don't, we'll initialize it on the first resize so we have
|
||||
// our proper initial dimensions.
|
||||
const priv = self.private();
|
||||
if (priv.core_surface) |v| realize: {
|
||||
// We need to make the context current so we can call GL functions.
|
||||
// This is required for all surface operations.
|
||||
priv.gl_area.makeCurrent();
|
||||
if (priv.gl_area.getError()) |err| {
|
||||
log.warn("failed to make GL context current: {s}", .{err.f_message orelse "(no message)"});
|
||||
log.warn("this error is usually due to a driver or gtk bug", .{});
|
||||
log.warn("this is a common cause of this issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/4950", .{});
|
||||
break :realize;
|
||||
}
|
||||
|
||||
v.renderer.displayRealized() catch |err| {
|
||||
log.warn("core displayRealized failed err={}", .{err});
|
||||
break :realize;
|
||||
|
|
@ -2662,11 +2730,13 @@ pub const Surface = extern struct {
|
|||
class.bindTemplateCallback("child_exited_close", &childExitedClose);
|
||||
class.bindTemplateCallback("context_menu_closed", &contextMenuClosed);
|
||||
class.bindTemplateCallback("notify_config", &propConfig);
|
||||
class.bindTemplateCallback("notify_error", &propError);
|
||||
class.bindTemplateCallback("notify_mouse_hover_url", &propMouseHoverUrl);
|
||||
class.bindTemplateCallback("notify_mouse_hidden", &propMouseHidden);
|
||||
class.bindTemplateCallback("notify_mouse_shape", &propMouseShape);
|
||||
class.bindTemplateCallback("notify_bell_ringing", &propBellRinging);
|
||||
class.bindTemplateCallback("should_border_be_shown", &closureShouldBorderBeShown);
|
||||
class.bindTemplateCallback("stack_child_name", &closureStackChildName);
|
||||
|
||||
// Properties
|
||||
gobject.ext.registerProperties(class, &.{
|
||||
|
|
@ -2674,6 +2744,7 @@ pub const Surface = extern struct {
|
|||
properties.config.impl,
|
||||
properties.@"child-exited".impl,
|
||||
properties.@"default-size".impl,
|
||||
properties.@"error".impl,
|
||||
properties.@"font-size-request".impl,
|
||||
properties.focused.impl,
|
||||
properties.@"min-size".impl,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ const gresource = @import("../build/gresource.zig");
|
|||
const Common = @import("../class.zig").Common;
|
||||
const Config = @import("config.zig").Config;
|
||||
const Application = @import("application.zig").Application;
|
||||
const CloseConfirmationDialog = @import("close_confirmation_dialog.zig").CloseConfirmationDialog;
|
||||
const SplitTree = @import("split_tree.zig").SplitTree;
|
||||
const Surface = @import("surface.zig").Surface;
|
||||
|
||||
|
|
@ -199,8 +198,11 @@ pub const Tab = extern struct {
|
|||
}
|
||||
|
||||
fn initActionMap(self: *Self) void {
|
||||
const s_param_type = glib.ext.VariantType.newFor([:0]const u8);
|
||||
defer s_param_type.free();
|
||||
|
||||
const actions = [_]ext.actions.Action(Self){
|
||||
.init("close", actionClose, null),
|
||||
.init("close", actionClose, s_param_type),
|
||||
.init("ring-bell", actionRingBell, null),
|
||||
};
|
||||
|
||||
|
|
@ -314,18 +316,44 @@ pub const Tab = extern struct {
|
|||
|
||||
fn actionClose(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
param_: ?*glib.Variant,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
const param = param_ orelse {
|
||||
log.warn("tab.close-tab called without a parameter", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
var str: ?[*:0]const u8 = null;
|
||||
param.get("&s", &str);
|
||||
|
||||
const tab_view = ext.getAncestor(
|
||||
adw.TabView,
|
||||
self.as(gtk.Widget),
|
||||
) orelse return;
|
||||
|
||||
const page = tab_view.getPage(self.as(gtk.Widget));
|
||||
|
||||
const mode = std.meta.stringToEnum(
|
||||
apprt.action.CloseTabMode,
|
||||
std.mem.span(
|
||||
str orelse {
|
||||
log.warn("invalid mode provided to tab.close-tab", .{});
|
||||
return;
|
||||
},
|
||||
),
|
||||
) orelse {
|
||||
// Need to be defensive here since actions can be triggered externally.
|
||||
log.warn("invalid mode provided to tab.close-tab: {s}", .{str.?});
|
||||
return;
|
||||
};
|
||||
|
||||
// Delegate to our parent to handle this, since this will emit
|
||||
// a close-page signal that the parent can intercept.
|
||||
tab_view.closePage(page);
|
||||
switch (mode) {
|
||||
.this => tab_view.closePage(page),
|
||||
.other => tab_view.closeOtherPages(page),
|
||||
}
|
||||
}
|
||||
|
||||
fn actionRingBell(
|
||||
|
|
|
|||
|
|
@ -320,10 +320,13 @@ pub const Window = extern struct {
|
|||
|
||||
/// Setup our action map.
|
||||
fn initActionMap(self: *Self) void {
|
||||
const s_variant_type = glib.ext.VariantType.newFor([:0]const u8);
|
||||
defer s_variant_type.free();
|
||||
|
||||
const actions = [_]ext.actions.Action(Self){
|
||||
.init("about", actionAbout, null),
|
||||
.init("close", actionClose, null),
|
||||
.init("close-tab", actionCloseTab, null),
|
||||
.init("close-tab", actionCloseTab, s_variant_type),
|
||||
.init("new-tab", actionNewTab, null),
|
||||
.init("new-window", actionNewWindow, null),
|
||||
.init("ring-bell", actionRingBell, null),
|
||||
|
|
@ -961,7 +964,14 @@ pub const Window = extern struct {
|
|||
_: *gobject.ParamSpec,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
self.addToast(i18n._("Reloaded the configuration"));
|
||||
const priv = self.private();
|
||||
if (priv.config) |config_obj| {
|
||||
const config = config_obj.get();
|
||||
if (config.@"app-notifications".@"config-reload") {
|
||||
self.addToast(i18n._("Reloaded the configuration"));
|
||||
}
|
||||
}
|
||||
|
||||
self.syncAppearance();
|
||||
}
|
||||
|
||||
|
|
@ -980,6 +990,22 @@ pub const Window = extern struct {
|
|||
};
|
||||
}
|
||||
|
||||
fn propIsActive(
|
||||
_: *gtk.Window,
|
||||
_: *gobject.ParamSpec,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
// Don't change urgency if we're not the active window.
|
||||
if (self.as(gtk.Window).isActive() == 0) return;
|
||||
|
||||
self.winproto().setUrgent(false) catch |err| {
|
||||
log.warn(
|
||||
"winproto failed to reset urgency={}",
|
||||
.{err},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
fn propGdkSurfaceWidth(
|
||||
_: *gdk.Surface,
|
||||
_: *gobject.ParamSpec,
|
||||
|
|
@ -1656,10 +1682,31 @@ pub const Window = extern struct {
|
|||
|
||||
fn actionCloseTab(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
param_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.close_tab);
|
||||
const param = param_ orelse {
|
||||
log.warn("win.close-tab called without a parameter", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
var str: ?[*:0]const u8 = null;
|
||||
param.get("&s", &str);
|
||||
|
||||
const mode = std.meta.stringToEnum(
|
||||
input.Binding.Action.CloseTabMode,
|
||||
std.mem.span(
|
||||
str orelse {
|
||||
log.warn("invalid mode provided to win.close-tab", .{});
|
||||
return;
|
||||
},
|
||||
),
|
||||
) orelse {
|
||||
log.warn("invalid mode provided to win.close-tab: {s}", .{str.?});
|
||||
return;
|
||||
};
|
||||
|
||||
self.performBindingAction(.{ .close_tab = mode });
|
||||
}
|
||||
|
||||
fn actionNewWindow(
|
||||
|
|
@ -1758,10 +1805,13 @@ pub const Window = extern struct {
|
|||
native.beep();
|
||||
}
|
||||
|
||||
if (config.@"bell-features".attention) {
|
||||
if (config.@"bell-features".attention) attention: {
|
||||
// Dont set urgency if the window is already active.
|
||||
if (self.as(gtk.Window).isActive() != 0) break :attention;
|
||||
|
||||
// Request user attention
|
||||
self.winproto().setUrgent(true) catch |err| {
|
||||
log.warn("failed to request user attention={}", .{err});
|
||||
log.warn("winproto failed to set urgency={}", .{err});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1905,6 +1955,7 @@ pub const Window = extern struct {
|
|||
class.bindTemplateCallback("notify_selected_page", &tabViewSelectedPage);
|
||||
class.bindTemplateCallback("notify_config", &propConfig);
|
||||
class.bindTemplateCallback("notify_fullscreened", &propFullscreened);
|
||||
class.bindTemplateCallback("notify_is_active", &propIsActive);
|
||||
class.bindTemplateCallback("notify_maximized", &propMaximized);
|
||||
class.bindTemplateCallback("notify_menu_active", &propMenuActive);
|
||||
class.bindTemplateCallback("notify_quick_terminal", &propQuickTerminal);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ window.ssd.no-border-radius {
|
|||
border-radius: 0 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* GhosttySurface URL overlay
|
||||
*/
|
||||
label.url-overlay {
|
||||
|
|
@ -83,13 +83,13 @@ label.resize-overlay {
|
|||
*/
|
||||
.child-exited.normal revealer widget {
|
||||
background-color: rgba(38, 162, 105, 0.5);
|
||||
/* after GTK 4.16 is a requirement, switch to the following:
|
||||
/* after GTK 4.16 is a requirement, switch to the following: */
|
||||
/* background-color: color-mix(in srgb, var(--success-bg-color), transparent 50%); */
|
||||
}
|
||||
|
||||
.child-exited.abnormal revealer widget {
|
||||
background-color: rgba(192, 28, 40, 0.5);
|
||||
/* after GTK 4.16 is a requirement, switch to the following:
|
||||
/* after GTK 4.16 is a requirement, switch to the following: */
|
||||
/* background-color: color-mix(in srgb, var(--error-bg-color), transparent 50%); */
|
||||
}
|
||||
|
||||
|
|
@ -97,13 +97,15 @@ label.resize-overlay {
|
|||
* Surface
|
||||
*/
|
||||
.surface progressbar.error trough progress {
|
||||
background-color: rgb(192, 28, 40);
|
||||
background-color: rgba(192, 28, 40, 0.5);
|
||||
/* after GTK 4.16 is a requirement, switch to the following: */
|
||||
/* background-color: color-mix(in srgb, var(--error-bg-color), transparent); */
|
||||
/* background-color: color-mix(in srgb, var(--error-bg-color), transparent 50%); */
|
||||
}
|
||||
|
||||
.surface .bell-overlay {
|
||||
border-color: color-mix(in srgb, var(--accent-color), transparent 50%);
|
||||
border-color: rgba(58, 148, 74, 0.5);
|
||||
/* after GTK 4.16 is a requirement, switch to the following: */
|
||||
/* background-color: color-mix(in srgb, var(--accent-color), transparent 50%); */
|
||||
border-width: 3px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
|
@ -127,6 +129,8 @@ label.resize-overlay {
|
|||
|
||||
.window .split paned > separator {
|
||||
background-color: rgba(250, 250, 250, 1);
|
||||
/* after GTK 4.16 is a requirement, switch to the following: */
|
||||
/* background-color: color-mix(in srgb, var(--window-bg-color), transparent 0%); */
|
||||
background-clip: content-box;
|
||||
|
||||
/* This works around the oversized drag area for the right side of GtkPaned.
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ template $GhosttyCloseConfirmationDialog: $GhosttyDialog {
|
|||
cancel: _("Cancel"),
|
||||
close: _("Close") destructive,
|
||||
]
|
||||
|
||||
close-response: "cancel";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,146 +8,172 @@ template $GhosttySurface: Adw.Bin {
|
|||
|
||||
notify::bell-ringing => $notify_bell_ringing();
|
||||
notify::config => $notify_config();
|
||||
notify::error => $notify_error();
|
||||
notify::mouse-hover-url => $notify_mouse_hover_url();
|
||||
notify::mouse-hidden => $notify_mouse_hidden();
|
||||
notify::mouse-shape => $notify_mouse_shape();
|
||||
|
||||
Overlay {
|
||||
focusable: false;
|
||||
focus-on-click: false;
|
||||
Stack {
|
||||
StackPage {
|
||||
name: "terminal";
|
||||
|
||||
child: Box {
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
child: Overlay {
|
||||
focusable: false;
|
||||
focus-on-click: false;
|
||||
|
||||
GLArea gl_area {
|
||||
realize => $gl_realize();
|
||||
unrealize => $gl_unrealize();
|
||||
render => $gl_render();
|
||||
resize => $gl_resize();
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
focusable: true;
|
||||
focus-on-click: true;
|
||||
has-stencil-buffer: false;
|
||||
has-depth-buffer: false;
|
||||
allowed-apis: gl;
|
||||
}
|
||||
child: Box {
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
|
||||
PopoverMenu context_menu {
|
||||
closed => $context_menu_closed();
|
||||
menu-model: context_menu_model;
|
||||
flags: nested;
|
||||
halign: start;
|
||||
has-arrow: false;
|
||||
}
|
||||
};
|
||||
GLArea gl_area {
|
||||
realize => $gl_realize();
|
||||
unrealize => $gl_unrealize();
|
||||
render => $gl_render();
|
||||
resize => $gl_resize();
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
focusable: true;
|
||||
focus-on-click: true;
|
||||
has-stencil-buffer: false;
|
||||
has-depth-buffer: false;
|
||||
allowed-apis: gl;
|
||||
}
|
||||
|
||||
[overlay]
|
||||
ProgressBar progress_bar_overlay {
|
||||
styles [
|
||||
"osd",
|
||||
]
|
||||
PopoverMenu context_menu {
|
||||
closed => $context_menu_closed();
|
||||
menu-model: context_menu_model;
|
||||
flags: nested;
|
||||
halign: start;
|
||||
has-arrow: false;
|
||||
}
|
||||
};
|
||||
|
||||
visible: false;
|
||||
halign: fill;
|
||||
valign: start;
|
||||
[overlay]
|
||||
ProgressBar progress_bar_overlay {
|
||||
styles [
|
||||
"osd",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: fill;
|
||||
valign: start;
|
||||
}
|
||||
|
||||
[overlay]
|
||||
// The "border" bell feature is implemented here as an overlay rather than
|
||||
// just adding a border to the GLArea or other widget for two reasons.
|
||||
// First, adding a border to an existing widget causes a resize of the
|
||||
// widget which undesirable side effects. Second, we can make it reactive
|
||||
// here in the blueprint with relatively little code.
|
||||
Revealer {
|
||||
reveal-child: bind $should_border_be_shown(template.config, template.bell-ringing) as <bool>;
|
||||
transition-type: crossfade;
|
||||
transition-duration: 500;
|
||||
|
||||
Box bell_overlay {
|
||||
styles [
|
||||
"bell-overlay",
|
||||
]
|
||||
|
||||
halign: fill;
|
||||
valign: fill;
|
||||
}
|
||||
}
|
||||
|
||||
[overlay]
|
||||
$GhosttySurfaceChildExited child_exited_overlay {
|
||||
visible: bind template.child-exited;
|
||||
close-request => $child_exited_close();
|
||||
}
|
||||
|
||||
[overlay]
|
||||
$GhosttyResizeOverlay resize_overlay {}
|
||||
|
||||
[overlay]
|
||||
Label url_left {
|
||||
styles [
|
||||
"background",
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: start;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
|
||||
EventControllerMotion url_ec_motion {
|
||||
enter => $url_mouse_enter();
|
||||
leave => $url_mouse_leave();
|
||||
}
|
||||
}
|
||||
|
||||
[overlay]
|
||||
Label url_right {
|
||||
styles [
|
||||
"background",
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
}
|
||||
|
||||
// Event controllers for interactivity
|
||||
EventControllerFocus {
|
||||
enter => $focus_enter();
|
||||
leave => $focus_leave();
|
||||
}
|
||||
|
||||
EventControllerKey {
|
||||
key-pressed => $key_pressed();
|
||||
key-released => $key_released();
|
||||
}
|
||||
|
||||
EventControllerMotion {
|
||||
motion => $mouse_motion();
|
||||
leave => $mouse_leave();
|
||||
}
|
||||
|
||||
EventControllerScroll {
|
||||
scroll => $scroll();
|
||||
scroll-begin => $scroll_begin();
|
||||
scroll-end => $scroll_end();
|
||||
flags: both_axes;
|
||||
}
|
||||
|
||||
GestureClick {
|
||||
pressed => $mouse_down();
|
||||
released => $mouse_up();
|
||||
button: 0;
|
||||
}
|
||||
|
||||
DropTarget drop_target {
|
||||
drop => $drop();
|
||||
actions: copy;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[overlay]
|
||||
// The "border" bell feature is implemented here as an overlay rather than
|
||||
// just adding a border to the GLArea or other widget for two reasons.
|
||||
// First, adding a border to an existing widget causes a resize of the
|
||||
// widget which undesirable side effects. Second, we can make it reactive
|
||||
// here in the blueprint with relatively little code.
|
||||
Revealer {
|
||||
reveal-child: bind $should_border_be_shown(template.config, template.bell-ringing) as <bool>;
|
||||
transition-type: crossfade;
|
||||
transition-duration: 500;
|
||||
StackPage {
|
||||
name: "error";
|
||||
|
||||
Box bell_overlay {
|
||||
styles [
|
||||
"bell-overlay",
|
||||
]
|
||||
child: Adw.StatusPage {
|
||||
icon-name: "computer-fail-symbolic";
|
||||
title: _("Oh, no.");
|
||||
description: _("Unable to acquire an OpenGL context for rendering.");
|
||||
|
||||
halign: fill;
|
||||
valign: fill;
|
||||
}
|
||||
child: LinkButton {
|
||||
label: "https://ghostty.org/docs/help/gtk-opengl-context";
|
||||
uri: "https://ghostty.org/docs/help/gtk-opengl-context";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
[overlay]
|
||||
$GhosttySurfaceChildExited child_exited_overlay {
|
||||
visible: bind template.child-exited;
|
||||
close-request => $child_exited_close();
|
||||
}
|
||||
|
||||
[overlay]
|
||||
$GhosttyResizeOverlay resize_overlay {}
|
||||
|
||||
[overlay]
|
||||
Label url_left {
|
||||
styles [
|
||||
"background",
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: start;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
|
||||
EventControllerMotion url_ec_motion {
|
||||
enter => $url_mouse_enter();
|
||||
leave => $url_mouse_leave();
|
||||
}
|
||||
}
|
||||
|
||||
[overlay]
|
||||
Label url_right {
|
||||
styles [
|
||||
"background",
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
}
|
||||
}
|
||||
|
||||
// Event controllers for interactivity
|
||||
EventControllerFocus {
|
||||
enter => $focus_enter();
|
||||
leave => $focus_leave();
|
||||
}
|
||||
|
||||
EventControllerKey {
|
||||
key-pressed => $key_pressed();
|
||||
key-released => $key_released();
|
||||
}
|
||||
|
||||
EventControllerMotion {
|
||||
motion => $mouse_motion();
|
||||
leave => $mouse_leave();
|
||||
}
|
||||
|
||||
EventControllerScroll {
|
||||
scroll => $scroll();
|
||||
scroll-begin => $scroll_begin();
|
||||
scroll-end => $scroll_end();
|
||||
flags: both_axes;
|
||||
}
|
||||
|
||||
GestureClick {
|
||||
pressed => $mouse_down();
|
||||
released => $mouse_up();
|
||||
button: 0;
|
||||
}
|
||||
|
||||
DropTarget drop_target {
|
||||
drop => $drop();
|
||||
actions: copy;
|
||||
// The order matters here: we can only set this after the stack
|
||||
// pages above have been created.
|
||||
visible-child-name: bind $stack_child_name(template.error) as <string>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +254,8 @@ menu context_menu_model {
|
|||
|
||||
item {
|
||||
label: _("Close Tab");
|
||||
action: "win.close-tab";
|
||||
action: "tab.close";
|
||||
target: "this";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ template $GhosttyWindow: Adw.ApplicationWindow {
|
|||
realize => $realize();
|
||||
notify::config => $notify_config();
|
||||
notify::fullscreened => $notify_fullscreened();
|
||||
notify::is-active => $notify_is_active();
|
||||
notify::maximized => $notify_maximized();
|
||||
notify::quick-terminal => $notify_quick_terminal();
|
||||
notify::scale-factor => $notify_scale_factor();
|
||||
|
|
@ -225,6 +226,7 @@ menu main_menu {
|
|||
item {
|
||||
label: _("Close Tab");
|
||||
action: "win.close-tab";
|
||||
target: "this";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -491,7 +491,7 @@ pub fn performAction(
|
|||
.toggle_maximize => self.toggleMaximize(target),
|
||||
.toggle_fullscreen => self.toggleFullscreen(target, value),
|
||||
.new_tab => try self.newTab(target),
|
||||
.close_tab => return try self.closeTab(target),
|
||||
.close_tab => return try self.closeTab(target, value),
|
||||
.goto_tab => return self.gotoTab(target, value),
|
||||
.move_tab => self.moveTab(target, value),
|
||||
.new_split => try self.newSplit(target, value),
|
||||
|
|
@ -585,7 +585,7 @@ fn newTab(_: *App, target: apprt.Target) !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn closeTab(_: *App, target: apprt.Target) !bool {
|
||||
fn closeTab(_: *App, target: apprt.Target, value: apprt.Action.Value(.close_tab)) !bool {
|
||||
switch (target) {
|
||||
.app => return false,
|
||||
.surface => |v| {
|
||||
|
|
@ -597,8 +597,16 @@ fn closeTab(_: *App, target: apprt.Target) !bool {
|
|||
return false;
|
||||
};
|
||||
|
||||
tab.closeWithConfirmation();
|
||||
return true;
|
||||
switch (value) {
|
||||
.this => {
|
||||
tab.closeWithConfirmation();
|
||||
return true;
|
||||
},
|
||||
.other => {
|
||||
log.warn("close-tab:other is not implemented", .{});
|
||||
return false;
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1145,7 +1153,7 @@ fn syncActionAccelerators(self: *App) !void {
|
|||
try self.syncActionAccelerator("win.close", .{ .close_window = {} });
|
||||
try self.syncActionAccelerator("win.new-window", .{ .new_window = {} });
|
||||
try self.syncActionAccelerator("win.new-tab", .{ .new_tab = {} });
|
||||
try self.syncActionAccelerator("win.close-tab", .{ .close_tab = {} });
|
||||
try self.syncActionAccelerator("win.close-tab", .{ .close_tab = .this });
|
||||
try self.syncActionAccelerator("win.split-right", .{ .new_split = .right });
|
||||
try self.syncActionAccelerator("win.split-down", .{ .new_split = .down });
|
||||
try self.syncActionAccelerator("win.split-left", .{ .new_split = .left });
|
||||
|
|
|
|||
|
|
@ -772,7 +772,9 @@ pub fn focusCurrentTab(self: *Window) void {
|
|||
}
|
||||
|
||||
pub fn onConfigReloaded(self: *Window) void {
|
||||
self.sendToast(i18n._("Reloaded the configuration"));
|
||||
if (self.app.config.@"app-notifications".@"config-reload") {
|
||||
self.sendToast(i18n._("Reloaded the configuration"));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sendToast(self: *Window, title: [*:0]const u8) void {
|
||||
|
|
@ -1074,7 +1076,7 @@ fn gtkActionCloseTab(
|
|||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .close_tab = {} });
|
||||
self.performBindingAction(.{ .close_tab = .this });
|
||||
}
|
||||
|
||||
fn gtkActionSplitRight(
|
||||
|
|
|
|||
|
|
@ -131,6 +131,13 @@ pub const VTable = struct {
|
|||
};
|
||||
|
||||
test Benchmark {
|
||||
// This test fails on FreeBSD so skip:
|
||||
//
|
||||
// /home/runner/work/ghostty/ghostty/src/benchmark/Benchmark.zig:165:5: 0x3cd2de1 in decltest.Benchmark (ghostty-test)
|
||||
// try testing.expect(result.duration > 0);
|
||||
// ^
|
||||
if (builtin.os.tag == .freebsd) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
const Simple = struct {
|
||||
const Self = @This();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const args = @import("args.zig");
|
||||
const x11_color = @import("../terminal/main.zig").x11_color;
|
||||
const vaxis = @import("vaxis");
|
||||
const tui = @import("tui.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
pub fn deinit(self: Options) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
/// If `true`, print without formatting even if printing to a tty
|
||||
plain: bool = false,
|
||||
|
||||
/// Enables "-h" and "--help" to work.
|
||||
pub fn help(self: Options) !void {
|
||||
_ = self;
|
||||
|
|
@ -17,7 +24,12 @@ pub const Options = struct {
|
|||
|
||||
/// The `list-colors` command is used to list all the named RGB colors in
|
||||
/// Ghostty.
|
||||
pub fn run(alloc: std.mem.Allocator) !u8 {
|
||||
///
|
||||
/// Flags:
|
||||
///
|
||||
/// * `--plain`: will disable formatting and make the output more
|
||||
/// friendly for Unix tooling. This is default when not printing to a tty.
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var opts: Options = .{};
|
||||
defer opts.deinit();
|
||||
|
||||
|
|
@ -27,7 +39,7 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
|
|||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stdout = std.io.getStdOut();
|
||||
|
||||
var keys = std.ArrayList([]const u8).init(alloc);
|
||||
defer keys.deinit();
|
||||
|
|
@ -39,15 +51,163 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
|
|||
}
|
||||
}.lessThan);
|
||||
|
||||
for (keys.items) |name| {
|
||||
const rgb = x11_color.map.get(name).?;
|
||||
try stdout.print("{s} = #{x:0>2}{x:0>2}{x:0>2}\n", .{
|
||||
name,
|
||||
rgb.r,
|
||||
rgb.g,
|
||||
rgb.b,
|
||||
});
|
||||
// Despite being under the posix namespace, this also works on Windows as of zig 0.13.0
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) {
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
return prettyPrint(arena.allocator(), keys.items);
|
||||
} else {
|
||||
const writer = stdout.writer();
|
||||
for (keys.items) |name| {
|
||||
const rgb = x11_color.map.get(name).?;
|
||||
try writer.print("{s} = #{x:0>2}{x:0>2}{x:0>2}\n", .{
|
||||
name,
|
||||
rgb.r,
|
||||
rgb.g,
|
||||
rgb.b,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
||||
// Set up vaxis
|
||||
var tty = try vaxis.Tty.init();
|
||||
defer tty.deinit();
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
defer vx.deinit(alloc, tty.anyWriter());
|
||||
|
||||
// We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an
|
||||
// event loop to auto-enable it.
|
||||
vx.caps.unicode = .unicode;
|
||||
try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
var buf_writer = tty.bufferedWriter();
|
||||
const writer = buf_writer.writer().any();
|
||||
|
||||
const winsize: vaxis.Winsize = switch (builtin.os.tag) {
|
||||
// We use some default, it doesn't really matter for what
|
||||
// we're doing because we don't do any wrapping.
|
||||
.windows => .{
|
||||
.rows = 24,
|
||||
.cols = 120,
|
||||
.x_pixel = 1024,
|
||||
.y_pixel = 768,
|
||||
},
|
||||
|
||||
else => try vaxis.Tty.getWinsize(tty.fd),
|
||||
};
|
||||
try vx.resize(alloc, tty.anyWriter(), winsize);
|
||||
|
||||
const win = vx.window();
|
||||
|
||||
var max_name_len: usize = 0;
|
||||
for (keys) |name| {
|
||||
if (name.len > max_name_len) max_name_len = name.len;
|
||||
}
|
||||
|
||||
// max name length plus " = #RRGGBB XX" plus " " gutter between columns
|
||||
const column_size = max_name_len + 15;
|
||||
// add two to take into account lack of gutter after last column
|
||||
const columns: usize = @divFloor(win.width + 2, column_size);
|
||||
|
||||
var i: usize = 0;
|
||||
const step = @divFloor(keys.len, columns) + 1;
|
||||
while (i < step) : (i += 1) {
|
||||
win.clear();
|
||||
|
||||
var result: vaxis.Window.PrintResult = .{ .col = 0, .row = 0, .overflow = false };
|
||||
|
||||
for (0..columns) |j| {
|
||||
const k = i + (step * j);
|
||||
if (k >= keys.len) continue;
|
||||
|
||||
const name = keys[k];
|
||||
const rgb = x11_color.map.get(name).?;
|
||||
|
||||
const style1: vaxis.Style = .{
|
||||
.fg = .{
|
||||
.rgb = .{ rgb.r, rgb.g, rgb.b },
|
||||
},
|
||||
};
|
||||
const style2: vaxis.Style = .{
|
||||
.fg = .{
|
||||
.rgb = .{ rgb.r, rgb.g, rgb.b },
|
||||
},
|
||||
.bg = .{
|
||||
.rgb = .{ rgb.r, rgb.g, rgb.b },
|
||||
},
|
||||
};
|
||||
|
||||
// name of the color
|
||||
result = win.printSegment(
|
||||
.{ .text = name },
|
||||
.{ .col_offset = result.col },
|
||||
);
|
||||
// push the color data to the end of the column
|
||||
for (0..max_name_len - name.len) |_| {
|
||||
result = win.printSegment(
|
||||
.{ .text = " " },
|
||||
.{ .col_offset = result.col },
|
||||
);
|
||||
}
|
||||
result = win.printSegment(
|
||||
.{ .text = " = " },
|
||||
.{ .col_offset = result.col },
|
||||
);
|
||||
// rgb triple
|
||||
result = win.printSegment(.{
|
||||
.text = try std.fmt.allocPrint(
|
||||
alloc,
|
||||
"#{x:0>2}{x:0>2}{x:0>2}",
|
||||
.{
|
||||
rgb.r, rgb.g, rgb.b,
|
||||
},
|
||||
),
|
||||
.style = style1,
|
||||
}, .{ .col_offset = result.col });
|
||||
result = win.printSegment(
|
||||
.{ .text = " " },
|
||||
.{ .col_offset = result.col },
|
||||
);
|
||||
// colored block
|
||||
result = win.printSegment(
|
||||
.{
|
||||
.text = " ",
|
||||
.style = style2,
|
||||
},
|
||||
.{ .col_offset = result.col },
|
||||
);
|
||||
// add the gutter if needed
|
||||
if (j + 1 < columns) {
|
||||
result = win.printSegment(
|
||||
.{
|
||||
.text = " ",
|
||||
},
|
||||
.{ .col_offset = result.col },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the rest of the line
|
||||
while (result.col != 0) {
|
||||
result = win.printSegment(
|
||||
.{
|
||||
.text = " ",
|
||||
},
|
||||
.{ .col_offset = result.col },
|
||||
);
|
||||
}
|
||||
|
||||
// output the data
|
||||
try vx.prettyPrint(writer);
|
||||
}
|
||||
|
||||
// be sure to flush!
|
||||
try buf_writer.flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -767,6 +767,22 @@ palette: Palette = .{},
|
|||
/// the mouse is shown again when a new window, tab, or split is created.
|
||||
@"mouse-hide-while-typing": bool = false,
|
||||
|
||||
/// When to scroll the surface to the bottom. The format of this is a list of
|
||||
/// options to enable separated by commas. If you prefix an option with `no-`
|
||||
/// then it is disabled. If you omit an option, its default value is used.
|
||||
///
|
||||
/// Available options:
|
||||
///
|
||||
/// - `keystroke` If set, scroll the surface to the bottom when the user
|
||||
/// presses a key that results in data being sent to the PTY (basically
|
||||
/// anything but modifiers or keybinds that are processed by Ghostty).
|
||||
///
|
||||
/// - `output` If set, scroll the surface to the bottom if there is new data
|
||||
/// to display. (Currently unimplemented.)
|
||||
///
|
||||
/// The default is `keystroke, no-output`.
|
||||
@"scroll-to-bottom": ScrollToBottom = .default,
|
||||
|
||||
/// Determines whether running programs can detect the shift key pressed with a
|
||||
/// mouse click. Typically, the shift key is used to extend mouse selection.
|
||||
///
|
||||
|
|
@ -2499,6 +2515,8 @@ keybind: Keybinds = .{},
|
|||
///
|
||||
/// - `clipboard-copy` (default: true) - Show a notification when text is copied
|
||||
/// to the clipboard.
|
||||
/// - `config-reload` (default: true) - Show a notification when
|
||||
/// the configuration is reloaded.
|
||||
///
|
||||
/// To specify a notification to enable, specify the name of the notification.
|
||||
/// To specify a notification to disable, prefix the name with `no-`. For
|
||||
|
|
@ -3017,6 +3035,13 @@ else
|
|||
/// Available since Ghostty 1.2.0.
|
||||
@"bold-color": ?BoldColor = null,
|
||||
|
||||
/// The opacity level (opposite of transparency) of the faint text. A value of
|
||||
/// 1 is fully opaque and a value of 0 is fully transparent. A value less than 0
|
||||
/// or greater than 1 will be clamped to the nearest valid value.
|
||||
///
|
||||
/// Available since Ghostty 1.2.0.
|
||||
@"faint-opacity": f64 = 0.5,
|
||||
|
||||
/// This will be used to set the `TERM` environment variable.
|
||||
/// HACK: We set this with an `xterm` prefix because vim uses that to enable key
|
||||
/// protocols (specifically this will enable `modifyOtherKeys`), among other
|
||||
|
|
@ -3999,6 +4024,8 @@ pub fn finalize(self: *Config) !void {
|
|||
if (self.@"auto-update-channel" == null) {
|
||||
self.@"auto-update-channel" = build_config.release_channel;
|
||||
}
|
||||
|
||||
self.@"faint-opacity" = std.math.clamp(self.@"faint-opacity", 0.0, 1.0);
|
||||
}
|
||||
|
||||
/// Callback for src/cli/args.zig to allow us to handle special cases
|
||||
|
|
@ -5596,7 +5623,7 @@ pub const Keybinds = struct {
|
|||
try self.set.put(
|
||||
alloc,
|
||||
.{ .key = .{ .unicode = 'w' }, .mods = .{ .ctrl = true, .shift = true } },
|
||||
.{ .close_tab = {} },
|
||||
.{ .close_tab = .this },
|
||||
);
|
||||
try self.set.putFlags(
|
||||
alloc,
|
||||
|
|
@ -5902,7 +5929,7 @@ pub const Keybinds = struct {
|
|||
try self.set.put(
|
||||
alloc,
|
||||
.{ .key = .{ .unicode = 'w' }, .mods = .{ .super = true, .alt = true } },
|
||||
.{ .close_tab = {} },
|
||||
.{ .close_tab = .this },
|
||||
);
|
||||
try self.set.put(
|
||||
alloc,
|
||||
|
|
@ -7058,6 +7085,7 @@ pub const GtkTitlebarStyle = enum(c_int) {
|
|||
/// See app-notifications
|
||||
pub const AppNotifications = packed struct {
|
||||
@"clipboard-copy": bool = true,
|
||||
@"config-reload": bool = true,
|
||||
};
|
||||
|
||||
/// See bell-features
|
||||
|
|
@ -7195,6 +7223,53 @@ pub const QuickTerminalSize = struct {
|
|||
height: u32,
|
||||
};
|
||||
|
||||
/// C API structure for QuickTerminalSize
|
||||
pub const C = extern struct {
|
||||
primary: C.Size,
|
||||
secondary: C.Size,
|
||||
|
||||
pub const Size = extern struct {
|
||||
tag: Tag,
|
||||
value: Value,
|
||||
|
||||
pub const Tag = enum(u8) { none, percentage, pixels };
|
||||
|
||||
pub const Value = extern union {
|
||||
percentage: f32,
|
||||
pixels: u32,
|
||||
};
|
||||
|
||||
pub const none: C.Size = .{ .tag = .none, .value = undefined };
|
||||
|
||||
pub fn percentage(v: f32) C.Size {
|
||||
return .{
|
||||
.tag = .percentage,
|
||||
.value = .{ .percentage = v },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn pixels(v: u32) C.Size {
|
||||
return .{
|
||||
.tag = .pixels,
|
||||
.value = .{ .pixels = v },
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub fn cval(self: QuickTerminalSize) C {
|
||||
return .{
|
||||
.primary = if (self.primary) |p| switch (p) {
|
||||
.percentage => |v| .percentage(v),
|
||||
.pixels => |v| .pixels(v),
|
||||
} else .none,
|
||||
.secondary = if (self.secondary) |s| switch (s) {
|
||||
.percentage => |v| .percentage(v),
|
||||
.pixels => |v| .pixels(v),
|
||||
} else .none,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn calculate(
|
||||
self: QuickTerminalSize,
|
||||
position: QuickTerminalPosition,
|
||||
|
|
@ -7268,6 +7343,7 @@ pub const QuickTerminalSize = struct {
|
|||
|
||||
try formatter.formatEntry([]const u8, fbs.getWritten());
|
||||
}
|
||||
|
||||
test "parse QuickTerminalSize" {
|
||||
const testing = std.testing;
|
||||
var v: QuickTerminalSize = undefined;
|
||||
|
|
@ -7980,6 +8056,14 @@ pub const WindowPadding = struct {
|
|||
}
|
||||
};
|
||||
|
||||
/// See scroll-to-bottom
|
||||
pub const ScrollToBottom = packed struct {
|
||||
keystroke: bool = true,
|
||||
output: bool = false,
|
||||
|
||||
pub const default: ScrollToBottom = .{};
|
||||
};
|
||||
|
||||
test "parse duration" {
|
||||
inline for (Duration.units) |unit| {
|
||||
var buf: [16]u8 = undefined;
|
||||
|
|
|
|||
|
|
@ -552,11 +552,15 @@ pub const Action = union(enum) {
|
|||
/// of the `confirm-close-surface` configuration setting.
|
||||
close_surface,
|
||||
|
||||
/// Close the current tab and all splits therein.
|
||||
/// Close the current tab and all splits therein _or_ close all tabs and
|
||||
/// splits thein of tabs _other_ than the current tab, depending on the
|
||||
/// mode.
|
||||
///
|
||||
/// If the mode is not specified, defaults to closing the current tab.
|
||||
///
|
||||
/// This might trigger a close confirmation popup, depending on the value
|
||||
/// of the `confirm-close-surface` configuration setting.
|
||||
close_tab,
|
||||
close_tab: CloseTabMode,
|
||||
|
||||
/// Close the current window and all tabs and splits therein.
|
||||
///
|
||||
|
|
@ -858,6 +862,13 @@ pub const Action = union(enum) {
|
|||
hide,
|
||||
};
|
||||
|
||||
pub const CloseTabMode = enum {
|
||||
this,
|
||||
other,
|
||||
|
||||
pub const default: CloseTabMode = .this;
|
||||
};
|
||||
|
||||
fn parseEnum(comptime T: type, value: []const u8) !T {
|
||||
return std.meta.stringToEnum(T, value) orelse return Error.InvalidFormat;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -393,11 +393,18 @@ fn actionCommands(action: Action.Key) []const Command {
|
|||
.description = "Close the current terminal.",
|
||||
}},
|
||||
|
||||
.close_tab => comptime &.{.{
|
||||
.action = .close_tab,
|
||||
.title = "Close Tab",
|
||||
.description = "Close the current tab.",
|
||||
}},
|
||||
.close_tab => comptime &.{
|
||||
.{
|
||||
.action = .{ .close_tab = .this },
|
||||
.title = "Close Tab",
|
||||
.description = "Close the current tab.",
|
||||
},
|
||||
.{
|
||||
.action = .{ .close_tab = .other },
|
||||
.title = "Close Other Tabs",
|
||||
.description = "Close all tabs in this window except the current one.",
|
||||
},
|
||||
},
|
||||
|
||||
.close_window => comptime &.{.{
|
||||
.action = .close_window,
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ pub const locales = [_][:0]const u8{
|
|||
"ca_ES.UTF-8",
|
||||
"bg_BG.UTF-8",
|
||||
"ga_IE.UTF-8",
|
||||
"hu_HU.UTF-8",
|
||||
"he_IL.UTF-8",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -522,6 +522,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
selection_background: ?configpkg.Config.TerminalColor,
|
||||
selection_foreground: ?configpkg.Config.TerminalColor,
|
||||
bold_color: ?configpkg.BoldColor,
|
||||
faint_opacity: u8,
|
||||
min_contrast: f32,
|
||||
padding_color: configpkg.WindowPaddingColor,
|
||||
custom_shaders: configpkg.RepeatablePath,
|
||||
|
|
@ -584,6 +585,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
.background = config.background.toTerminalRGB(),
|
||||
.foreground = config.foreground.toTerminalRGB(),
|
||||
.bold_color = config.@"bold-color",
|
||||
.faint_opacity = @intFromFloat(@ceil(config.@"faint-opacity" * 255)),
|
||||
|
||||
.min_contrast = @floatCast(config.@"minimum-contrast"),
|
||||
.padding_color = config.@"window-padding-color",
|
||||
|
|
@ -2225,23 +2227,44 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
const cursor_width: f32 = @floatFromInt(cursor.glyph_size[0]);
|
||||
const cursor_height: f32 = @floatFromInt(cursor.glyph_size[1]);
|
||||
|
||||
// Left edge of the cell the cursor is in.
|
||||
var pixel_x: f32 = @floatFromInt(
|
||||
cursor.grid_pos[0] * cell.width + padding.left,
|
||||
);
|
||||
// Top edge, relative to the top of the
|
||||
// screen, of the cell the cursor is in.
|
||||
var pixel_y: f32 = @floatFromInt(
|
||||
cursor.grid_pos[1] * cell.height + padding.top,
|
||||
);
|
||||
|
||||
pixel_x += @floatFromInt(cursor.bearings[0]);
|
||||
pixel_y += @floatFromInt(cursor.bearings[1]);
|
||||
|
||||
// If +Y is up in our shaders, we need to flip the coordinate.
|
||||
// If +Y is up in our shaders, we need to flip the coordinate
|
||||
// so that it's instead the top edge of the cell relative to
|
||||
// the *bottom* of the screen.
|
||||
if (!GraphicsAPI.custom_shader_y_is_down) {
|
||||
pixel_y = @as(f32, @floatFromInt(screen.height)) - pixel_y;
|
||||
// We need to add the cursor height because we need the +Y
|
||||
// edge for the Y coordinate, and flipping means that it's
|
||||
// the -Y edge now.
|
||||
pixel_y += cursor_height;
|
||||
}
|
||||
|
||||
// Add the X bearing to get the -X (left) edge of the cursor.
|
||||
pixel_x += @floatFromInt(cursor.bearings[0]);
|
||||
|
||||
// How we deal with the Y bearing depends on which direction
|
||||
// is "up", since we want our final `pixel_y` value to be the
|
||||
// +Y edge of the cursor.
|
||||
if (GraphicsAPI.custom_shader_y_is_down) {
|
||||
// As a reminder, the Y bearing is the distance from the
|
||||
// bottom of the cell to the top of the glyph, so to get
|
||||
// the +Y edge we need to add the cell height, subtract
|
||||
// the Y bearing, and add the glyph height to get the +Y
|
||||
// (bottom) edge of the cursor.
|
||||
pixel_y += @floatFromInt(cell.height);
|
||||
pixel_y -= @floatFromInt(cursor.bearings[1]);
|
||||
pixel_y += @floatFromInt(cursor.glyph_size[1]);
|
||||
} else {
|
||||
// If the Y direction is reversed though, we instead want
|
||||
// the *top* edge of the cursor, which means we just need
|
||||
// to subtract the cell height and add the Y bearing.
|
||||
pixel_y -= @floatFromInt(cell.height);
|
||||
pixel_y += @floatFromInt(cursor.bearings[1]);
|
||||
}
|
||||
|
||||
const new_cursor: [4]f32 = .{
|
||||
|
|
@ -2612,7 +2635,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
};
|
||||
|
||||
// Foreground alpha for this cell.
|
||||
const alpha: u8 = if (style.flags.faint) 175 else 255;
|
||||
const alpha: u8 = if (style.flags.faint) self.config.faint_opacity else 255;
|
||||
|
||||
// Set the cell's background color.
|
||||
{
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ pub const Action = union(enum) {
|
|||
/// Maximum number of intermediate characters during parsing. This is
|
||||
/// 4 because we also use the intermediates array for UTF8 decoding which
|
||||
/// can be at most 4 bytes.
|
||||
const MAX_INTERMEDIATE = 4;
|
||||
pub const MAX_INTERMEDIATE = 4;
|
||||
|
||||
/// Maximum number of CSI parameters. This is arbitrary. Practically, the
|
||||
/// only CSI command that uses more than 3 parameters is the SGR command
|
||||
|
|
@ -206,7 +206,7 @@ const MAX_INTERMEDIATE = 4;
|
|||
/// number. I implore TUI authors to not use more than this number of CSI
|
||||
/// params, but I suspect we'll introduce a slow path with heap allocation
|
||||
/// one day.
|
||||
const MAX_PARAMS = 24;
|
||||
pub const MAX_PARAMS = 24;
|
||||
|
||||
/// Current state of the state machine
|
||||
state: State,
|
||||
|
|
@ -949,6 +949,55 @@ test "csi: too many params" {
|
|||
}
|
||||
}
|
||||
|
||||
test "csi: sgr with up to our max parameters" {
|
||||
for (1..MAX_PARAMS + 1) |max| {
|
||||
var p = init();
|
||||
_ = p.next(0x1B);
|
||||
_ = p.next('[');
|
||||
|
||||
for (0..max - 1) |_| {
|
||||
_ = p.next('1');
|
||||
_ = p.next(';');
|
||||
}
|
||||
_ = p.next('2');
|
||||
|
||||
{
|
||||
const a = p.next('H');
|
||||
try testing.expect(p.state == .ground);
|
||||
try testing.expect(a[0] == null);
|
||||
try testing.expect(a[1].? == .csi_dispatch);
|
||||
try testing.expect(a[2] == null);
|
||||
|
||||
const csi = a[1].?.csi_dispatch;
|
||||
try testing.expectEqual(@as(usize, max), csi.params.len);
|
||||
try testing.expectEqual(@as(u16, 2), csi.params[max - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "csi: sgr beyond our max drops it" {
|
||||
// Has to be +2 for the loops below
|
||||
const max = MAX_PARAMS + 2;
|
||||
|
||||
var p = init();
|
||||
_ = p.next(0x1B);
|
||||
_ = p.next('[');
|
||||
|
||||
for (0..max - 1) |_| {
|
||||
_ = p.next('1');
|
||||
_ = p.next(';');
|
||||
}
|
||||
_ = p.next('2');
|
||||
|
||||
{
|
||||
const a = p.next('H');
|
||||
try testing.expect(p.state == .ground);
|
||||
try testing.expect(a[0] == null);
|
||||
try testing.expect(a[1] == null);
|
||||
try testing.expect(a[2] == null);
|
||||
}
|
||||
}
|
||||
|
||||
test "dcs: XTGETTCAP" {
|
||||
var p = init();
|
||||
_ = p.next(0x1B);
|
||||
|
|
|
|||
|
|
@ -147,25 +147,28 @@ pub const Command = union(enum) {
|
|||
/// End a hyperlink (OSC 8)
|
||||
hyperlink_end: void,
|
||||
|
||||
/// Sleep (OSC 9;1)
|
||||
sleep: struct {
|
||||
/// ConEmu sleep (OSC 9;1)
|
||||
conemu_sleep: struct {
|
||||
duration_ms: u16,
|
||||
},
|
||||
|
||||
/// Show GUI message Box (OSC 9;2)
|
||||
show_message_box: []const u8,
|
||||
/// ConEmu show GUI message box (OSC 9;2)
|
||||
conemu_show_message_box: []const u8,
|
||||
|
||||
/// Change ConEmu tab (OSC 9;3)
|
||||
change_conemu_tab_title: union(enum) {
|
||||
reset: void,
|
||||
/// ConEmu change tab title (OSC 9;3)
|
||||
conemu_change_tab_title: union(enum) {
|
||||
reset,
|
||||
value: []const u8,
|
||||
},
|
||||
|
||||
/// Set progress state (OSC 9;4)
|
||||
progress_report: ProgressReport,
|
||||
/// ConEmu progress report (OSC 9;4)
|
||||
conemu_progress_report: ProgressReport,
|
||||
|
||||
/// Wait input (OSC 9;5)
|
||||
wait_input: void,
|
||||
/// ConEmu wait input (OSC 9;5)
|
||||
conemu_wait_input,
|
||||
|
||||
/// ConEmu GUI macro (OSC 9;6)
|
||||
conemu_guimacro: []const u8,
|
||||
|
||||
pub const ColorOperation = union(enum) {
|
||||
pub const Source = enum(u16) {
|
||||
|
|
@ -208,7 +211,6 @@ pub const Command = union(enum) {
|
|||
};
|
||||
|
||||
pub const ProgressReport = struct {
|
||||
// sync with ghostty_terminal_osc_command_progressreport_state_e in include/ghostty.h
|
||||
pub const State = enum(c_int) {
|
||||
remove,
|
||||
set,
|
||||
|
|
@ -220,7 +222,7 @@ pub const Command = union(enum) {
|
|||
state: State,
|
||||
progress: ?u8 = null,
|
||||
|
||||
// sync with ghostty_terminal_osc_command_progressreport_s in include/ghostty.h
|
||||
// sync with ghostty_action_progress_report_s
|
||||
pub const C = extern struct {
|
||||
state: c_int,
|
||||
progress: i8,
|
||||
|
|
@ -229,7 +231,11 @@ pub const Command = union(enum) {
|
|||
pub fn cval(self: ProgressReport) C {
|
||||
return .{
|
||||
.state = @intFromEnum(self.state),
|
||||
.progress = if (self.progress) |progress| @intCast(std.math.clamp(progress, 0, 100)) else -1,
|
||||
.progress = if (self.progress) |progress| @intCast(std.math.clamp(
|
||||
progress,
|
||||
0,
|
||||
100,
|
||||
)) else -1,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -431,6 +437,7 @@ pub const Parser = struct {
|
|||
conemu_progress_state,
|
||||
conemu_progress_prevalue,
|
||||
conemu_progress_value,
|
||||
conemu_guimacro,
|
||||
};
|
||||
|
||||
pub fn init() Parser {
|
||||
|
|
@ -957,107 +964,147 @@ pub const Parser = struct {
|
|||
.osc_9 => switch (c) {
|
||||
'1' => {
|
||||
self.state = .conemu_sleep;
|
||||
// This will end up being either a ConEmu sleep OSC 9;1,
|
||||
// or a desktop notification OSC 9 that begins with '1', so
|
||||
// mark as complete.
|
||||
self.complete = true;
|
||||
},
|
||||
'2' => {
|
||||
self.state = .conemu_message_box;
|
||||
// This will end up being either a ConEmu message box OSC 9;2,
|
||||
// or a desktop notification OSC 9 that begins with '2', so
|
||||
// mark as complete.
|
||||
self.complete = true;
|
||||
},
|
||||
'3' => {
|
||||
self.state = .conemu_tab;
|
||||
// This will end up being either a ConEmu message box OSC 9;3,
|
||||
// or a desktop notification OSC 9 that begins with '3', so
|
||||
// mark as complete.
|
||||
self.complete = true;
|
||||
},
|
||||
'4' => {
|
||||
self.state = .conemu_progress_prestate;
|
||||
// This will end up being either a ConEmu progress report
|
||||
// OSC 9;4, or a desktop notification OSC 9 that begins with
|
||||
// '4', so mark as complete.
|
||||
self.complete = true;
|
||||
},
|
||||
'5' => {
|
||||
// Note that sending an OSC 9 desktop notification that
|
||||
// starts with 5 is impossible due to this.
|
||||
self.state = .swallow;
|
||||
self.command = .{ .wait_input = {} };
|
||||
self.command = .conemu_wait_input;
|
||||
self.complete = true;
|
||||
},
|
||||
'6' => {
|
||||
self.state = .conemu_guimacro;
|
||||
// This will end up being either a ConEmu GUI macro OSC 9;6,
|
||||
// or a desktop notification OSC 9 that begins with '6', so
|
||||
// mark as complete.
|
||||
self.complete = true;
|
||||
},
|
||||
|
||||
// Todo: parse out other ConEmu operating system commands.
|
||||
// Even if we don't support them we probably don't want
|
||||
// them showing up as desktop notifications.
|
||||
// Todo: parse out other ConEmu operating system commands. Even
|
||||
// if we don't support them we probably don't want them showing
|
||||
// up as desktop notifications.
|
||||
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
.conemu_sleep => switch (c) {
|
||||
';' => {
|
||||
self.command = .{ .sleep = .{ .duration_ms = 100 } };
|
||||
self.command = .{ .conemu_sleep = .{ .duration_ms = 100 } };
|
||||
self.buf_start = self.buf_idx;
|
||||
self.complete = true;
|
||||
self.state = .conemu_sleep_value;
|
||||
},
|
||||
else => self.state = .invalid,
|
||||
},
|
||||
|
||||
.conemu_message_box => switch (c) {
|
||||
';' => {
|
||||
self.command = .{ .show_message_box = undefined };
|
||||
self.temp_state = .{ .str = &self.command.show_message_box };
|
||||
self.buf_start = self.buf_idx;
|
||||
self.complete = true;
|
||||
self.prepAllocableString();
|
||||
},
|
||||
else => self.state = .invalid,
|
||||
// OSC 9;1 <something other than semicolon> is a desktop
|
||||
// notification.
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
.conemu_sleep_value => switch (c) {
|
||||
else => self.complete = true,
|
||||
},
|
||||
|
||||
.conemu_message_box => switch (c) {
|
||||
';' => {
|
||||
self.command = .{ .conemu_show_message_box = undefined };
|
||||
self.temp_state = .{ .str = &self.command.conemu_show_message_box };
|
||||
self.buf_start = self.buf_idx;
|
||||
self.complete = true;
|
||||
self.prepAllocableString();
|
||||
},
|
||||
|
||||
// OSC 9;2 <something other than semicolon> is a desktop
|
||||
// notification.
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
.conemu_tab => switch (c) {
|
||||
';' => {
|
||||
self.state = .conemu_tab_txt;
|
||||
self.command = .{ .change_conemu_tab_title = .{ .reset = {} } };
|
||||
self.command = .{ .conemu_change_tab_title = .reset };
|
||||
self.buf_start = self.buf_idx;
|
||||
self.complete = true;
|
||||
},
|
||||
else => self.state = .invalid,
|
||||
|
||||
// OSC 9;3 <something other than semicolon> is a desktop
|
||||
// notification.
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
.conemu_tab_txt => {
|
||||
self.command = .{ .change_conemu_tab_title = .{ .value = undefined } };
|
||||
self.temp_state = .{ .str = &self.command.change_conemu_tab_title.value };
|
||||
self.command = .{ .conemu_change_tab_title = .{ .value = undefined } };
|
||||
self.temp_state = .{ .str = &self.command.conemu_change_tab_title.value };
|
||||
self.complete = true;
|
||||
self.prepAllocableString();
|
||||
},
|
||||
|
||||
.conemu_progress_prestate => switch (c) {
|
||||
';' => {
|
||||
self.command = .{ .progress_report = .{
|
||||
self.command = .{ .conemu_progress_report = .{
|
||||
.state = undefined,
|
||||
} };
|
||||
self.state = .conemu_progress_state;
|
||||
},
|
||||
|
||||
// OSC 9;4 <something other than semicolon> is a desktop
|
||||
// notification.
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
.conemu_progress_state => switch (c) {
|
||||
'0' => {
|
||||
self.command.progress_report.state = .remove;
|
||||
self.command.conemu_progress_report.state = .remove;
|
||||
self.state = .swallow;
|
||||
self.complete = true;
|
||||
},
|
||||
'1' => {
|
||||
self.command.progress_report.state = .set;
|
||||
self.command.progress_report.progress = 0;
|
||||
self.command.conemu_progress_report.state = .set;
|
||||
self.command.conemu_progress_report.progress = 0;
|
||||
self.state = .conemu_progress_prevalue;
|
||||
},
|
||||
'2' => {
|
||||
self.command.progress_report.state = .@"error";
|
||||
self.command.conemu_progress_report.state = .@"error";
|
||||
self.complete = true;
|
||||
self.state = .conemu_progress_prevalue;
|
||||
},
|
||||
'3' => {
|
||||
self.command.progress_report.state = .indeterminate;
|
||||
self.command.conemu_progress_report.state = .indeterminate;
|
||||
self.complete = true;
|
||||
self.state = .swallow;
|
||||
},
|
||||
'4' => {
|
||||
self.command.progress_report.state = .pause;
|
||||
self.command.conemu_progress_report.state = .pause;
|
||||
self.complete = true;
|
||||
self.state = .conemu_progress_prevalue;
|
||||
},
|
||||
|
||||
// OSC 9;4; <something other than 0-4> is a desktop
|
||||
// notification.
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
|
|
@ -1066,6 +1113,8 @@ pub const Parser = struct {
|
|||
self.state = .conemu_progress_value;
|
||||
},
|
||||
|
||||
// OSC 9;4;<0-4> <something other than semicolon> is a desktop
|
||||
// notification.
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
|
|
@ -1077,8 +1126,16 @@ pub const Parser = struct {
|
|||
|
||||
// If we aren't a set substate, then we don't care
|
||||
// about the value.
|
||||
const p = &self.command.progress_report;
|
||||
if (p.state != .set and p.state != .@"error" and p.state != .pause) break :value;
|
||||
const p = &self.command.conemu_progress_report;
|
||||
switch (p.state) {
|
||||
.remove,
|
||||
.indeterminate,
|
||||
=> break :value,
|
||||
.set,
|
||||
.@"error",
|
||||
.pause,
|
||||
=> {},
|
||||
}
|
||||
|
||||
if (p.state == .set)
|
||||
assert(p.progress != null)
|
||||
|
|
@ -1104,6 +1161,20 @@ pub const Parser = struct {
|
|||
},
|
||||
},
|
||||
|
||||
.conemu_guimacro => switch (c) {
|
||||
';' => {
|
||||
self.command = .{ .conemu_guimacro = undefined };
|
||||
self.temp_state = .{ .str = &self.command.conemu_guimacro };
|
||||
self.buf_start = self.buf_idx;
|
||||
self.state = .string;
|
||||
self.complete = true;
|
||||
},
|
||||
|
||||
// OSC 9;6 <something other than semicolon> is a desktop
|
||||
// notification.
|
||||
else => self.showDesktopNotification(),
|
||||
},
|
||||
|
||||
.semantic_prompt => switch (c) {
|
||||
'A' => {
|
||||
self.state = .semantic_option_start;
|
||||
|
|
@ -1212,6 +1283,11 @@ pub const Parser = struct {
|
|||
|
||||
self.temp_state = .{ .str = &self.command.show_desktop_notification.body };
|
||||
self.state = .string;
|
||||
// Set as complete as we've already seen one character that should be
|
||||
// part of the notification. If we wait for another character to set
|
||||
// `complete` when the state is `.string` we won't be able to send any
|
||||
// single character notifications.
|
||||
self.complete = true;
|
||||
}
|
||||
|
||||
fn prepAllocableString(self: *Parser) void {
|
||||
|
|
@ -1332,7 +1408,7 @@ pub const Parser = struct {
|
|||
|
||||
fn endConEmuSleepValue(self: *Parser) void {
|
||||
switch (self.command) {
|
||||
.sleep => |*v| v.duration_ms = value: {
|
||||
.conemu_sleep => |*v| v.duration_ms = value: {
|
||||
const str = self.buf[self.buf_start..self.buf_idx];
|
||||
if (str.len == 0) break :value 100;
|
||||
|
||||
|
|
@ -1595,6 +1671,26 @@ pub const Parser = struct {
|
|||
.hyperlink_uri => self.endHyperlink(),
|
||||
.string => self.endString(),
|
||||
.conemu_sleep_value => self.endConEmuSleepValue(),
|
||||
|
||||
// We received OSC 9;X ST, but nothing else, finish off as a
|
||||
// desktop notification with "X" as the body.
|
||||
.conemu_sleep,
|
||||
.conemu_message_box,
|
||||
.conemu_tab,
|
||||
.conemu_progress_prestate,
|
||||
.conemu_progress_state,
|
||||
.conemu_guimacro,
|
||||
=> {
|
||||
self.showDesktopNotification();
|
||||
self.endString();
|
||||
},
|
||||
|
||||
// A ConEmu progress report that has reached these states is
|
||||
// complete, don't do anything to them.
|
||||
.conemu_progress_prevalue,
|
||||
.conemu_progress_value,
|
||||
=> {},
|
||||
|
||||
.allocable_string => self.endAllocableString(),
|
||||
.kitty_color_protocol_key => self.endKittyColorProtocolOption(.key_only, true),
|
||||
.kitty_color_protocol_value => self.endKittyColorProtocolOption(.key_and_value, true),
|
||||
|
|
@ -2770,7 +2866,7 @@ test "OSC: OSC104: empty palette index" {
|
|||
try std.testing.expect(it.next() == null);
|
||||
}
|
||||
|
||||
test "OSC: conemu sleep" {
|
||||
test "OSC: OSC 9;1 ConEmu sleep" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2780,11 +2876,11 @@ test "OSC: conemu sleep" {
|
|||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .sleep);
|
||||
try testing.expectEqual(420, cmd.sleep.duration_ms);
|
||||
try testing.expect(cmd == .conemu_sleep);
|
||||
try testing.expectEqual(420, cmd.conemu_sleep.duration_ms);
|
||||
}
|
||||
|
||||
test "OSC: conemu sleep with no value default to 100ms" {
|
||||
test "OSC: OSC 9;1 ConEmu sleep with no value default to 100ms" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2794,11 +2890,11 @@ test "OSC: conemu sleep with no value default to 100ms" {
|
|||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .sleep);
|
||||
try testing.expectEqual(100, cmd.sleep.duration_ms);
|
||||
try testing.expect(cmd == .conemu_sleep);
|
||||
try testing.expectEqual(100, cmd.conemu_sleep.duration_ms);
|
||||
}
|
||||
|
||||
test "OSC: conemu sleep cannot exceed 10000ms" {
|
||||
test "OSC: OSC 9;1 conemu sleep cannot exceed 10000ms" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2808,11 +2904,11 @@ test "OSC: conemu sleep cannot exceed 10000ms" {
|
|||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .sleep);
|
||||
try testing.expectEqual(10000, cmd.sleep.duration_ms);
|
||||
try testing.expect(cmd == .conemu_sleep);
|
||||
try testing.expectEqual(10000, cmd.conemu_sleep.duration_ms);
|
||||
}
|
||||
|
||||
test "OSC: conemu sleep invalid input" {
|
||||
test "OSC: OSC 9;1 conemu sleep invalid input" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2822,11 +2918,39 @@ test "OSC: conemu sleep invalid input" {
|
|||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .sleep);
|
||||
try testing.expectEqual(100, cmd.sleep.duration_ms);
|
||||
try testing.expect(cmd == .conemu_sleep);
|
||||
try testing.expectEqual(100, cmd.conemu_sleep.duration_ms);
|
||||
}
|
||||
|
||||
test "OSC: show desktop notification" {
|
||||
test "OSC: OSC 9;1 conemu sleep -> desktop notification 1" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;1";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("1", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9;1 conemu sleep -> desktop notification 2" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;1a";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("1a", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9 show desktop notification" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2836,11 +2960,25 @@ test "OSC: show desktop notification" {
|
|||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings(cmd.show_desktop_notification.title, "");
|
||||
try testing.expectEqualStrings(cmd.show_desktop_notification.body, "Hello world");
|
||||
try testing.expectEqualStrings("", cmd.show_desktop_notification.title);
|
||||
try testing.expectEqualStrings("Hello world", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: show desktop notification with title" {
|
||||
test "OSC: OSC 9 show single character desktop notification" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;H";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("", cmd.show_desktop_notification.title);
|
||||
try testing.expectEqualStrings("H", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 777 show desktop notification with title" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2854,7 +2992,7 @@ test "OSC: show desktop notification with title" {
|
|||
try testing.expectEqualStrings(cmd.show_desktop_notification.body, "Body");
|
||||
}
|
||||
|
||||
test "OSC: conemu message box" {
|
||||
test "OSC: OSC 9;2 ConEmu message box" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2863,11 +3001,11 @@ test "OSC: conemu message box" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .show_message_box);
|
||||
try testing.expectEqualStrings("hello world", cmd.show_message_box);
|
||||
try testing.expect(cmd == .conemu_show_message_box);
|
||||
try testing.expectEqualStrings("hello world", cmd.conemu_show_message_box);
|
||||
}
|
||||
|
||||
test "OSC: conemu message box invalid input" {
|
||||
test "OSC: 9;2 ConEmu message box invalid input" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2875,11 +3013,12 @@ test "OSC: conemu message box invalid input" {
|
|||
const input = "9;2";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b');
|
||||
try testing.expect(cmd == null);
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("2", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: conemu message box empty message" {
|
||||
test "OSC: 9;2 ConEmu message box empty message" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2888,11 +3027,11 @@ test "OSC: conemu message box empty message" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .show_message_box);
|
||||
try testing.expectEqualStrings("", cmd.show_message_box);
|
||||
try testing.expect(cmd == .conemu_show_message_box);
|
||||
try testing.expectEqualStrings("", cmd.conemu_show_message_box);
|
||||
}
|
||||
|
||||
test "OSC: conemu message box spaces only message" {
|
||||
test "OSC: 9;2 ConEmu message box spaces only message" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2901,11 +3040,39 @@ test "OSC: conemu message box spaces only message" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .show_message_box);
|
||||
try testing.expectEqualStrings(" ", cmd.show_message_box);
|
||||
try testing.expect(cmd == .conemu_show_message_box);
|
||||
try testing.expectEqualStrings(" ", cmd.conemu_show_message_box);
|
||||
}
|
||||
|
||||
test "OSC: conemu change tab title" {
|
||||
test "OSC: OSC 9;2 message box -> desktop notification 1" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;2";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("2", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9;2 message box -> desktop notification 2" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;2a";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("2a", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: 9;3 ConEmu change tab title" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2914,11 +3081,11 @@ test "OSC: conemu change tab title" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .change_conemu_tab_title);
|
||||
try testing.expectEqualStrings("foo bar", cmd.change_conemu_tab_title.value);
|
||||
try testing.expect(cmd == .conemu_change_tab_title);
|
||||
try testing.expectEqualStrings("foo bar", cmd.conemu_change_tab_title.value);
|
||||
}
|
||||
|
||||
test "OSC: conemu change tab reset title" {
|
||||
test "OSC: 9;3 ConEmu change tab title reset" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2928,11 +3095,11 @@ test "OSC: conemu change tab reset title" {
|
|||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
const expected_command: Command = .{ .change_conemu_tab_title = .{ .reset = {} } };
|
||||
const expected_command: Command = .{ .conemu_change_tab_title = .reset };
|
||||
try testing.expectEqual(expected_command, cmd);
|
||||
}
|
||||
|
||||
test "OSC: conemu change tab spaces only title" {
|
||||
test "OSC: 9;3 ConEmu change tab title spaces only" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2942,11 +3109,11 @@ test "OSC: conemu change tab spaces only title" {
|
|||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .change_conemu_tab_title);
|
||||
try testing.expectEqualStrings(" ", cmd.change_conemu_tab_title.value);
|
||||
try testing.expect(cmd == .conemu_change_tab_title);
|
||||
try testing.expectEqualStrings(" ", cmd.conemu_change_tab_title.value);
|
||||
}
|
||||
|
||||
test "OSC: conemu change tab invalid input" {
|
||||
test "OSC: OSC 9;3 change tab title -> desktop notification 1" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2954,11 +3121,27 @@ test "OSC: conemu change tab invalid input" {
|
|||
const input = "9;3";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b');
|
||||
try testing.expect(cmd == null);
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("3", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress set" {
|
||||
test "OSC: OSC 9;3 message box -> desktop notification 2" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;3a";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("3a", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9;4 ConEmu progress set" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2967,12 +3150,12 @@ test "OSC: OSC9 progress set" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .set);
|
||||
try testing.expect(cmd.progress_report.progress == 100);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .set);
|
||||
try testing.expect(cmd.conemu_progress_report.progress == 100);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress set overflow" {
|
||||
test "OSC: OSC 9;4 ConEmu progress set overflow" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2981,12 +3164,12 @@ test "OSC: OSC9 progress set overflow" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .set);
|
||||
try testing.expect(cmd.progress_report.progress == 100);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .set);
|
||||
try testing.expectEqual(100, cmd.conemu_progress_report.progress);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress set single digit" {
|
||||
test "OSC: OSC 9;4 ConEmu progress set single digit" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -2995,12 +3178,12 @@ test "OSC: OSC9 progress set single digit" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .set);
|
||||
try testing.expect(cmd.progress_report.progress == 9);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .set);
|
||||
try testing.expect(cmd.conemu_progress_report.progress == 9);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress set double digit" {
|
||||
test "OSC: OSC 9;4 ConEmu progress set double digit" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3009,12 +3192,12 @@ test "OSC: OSC9 progress set double digit" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .set);
|
||||
try testing.expect(cmd.progress_report.progress == 94);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .set);
|
||||
try testing.expectEqual(94, cmd.conemu_progress_report.progress);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress set extra semicolon ignored" {
|
||||
test "OSC: OSC 9;4 ConEmu progress set extra semicolon ignored" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3023,12 +3206,12 @@ test "OSC: OSC9 progress set extra semicolon ignored" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .set);
|
||||
try testing.expect(cmd.progress_report.progress == 100);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .set);
|
||||
try testing.expectEqual(100, cmd.conemu_progress_report.progress);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress remove with no progress" {
|
||||
test "OSC: OSC 9;4 ConEmu progress remove with no progress" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3037,12 +3220,12 @@ test "OSC: OSC9 progress remove with no progress" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .remove);
|
||||
try testing.expect(cmd.progress_report.progress == null);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .remove);
|
||||
try testing.expect(cmd.conemu_progress_report.progress == null);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress remove with double semicolon" {
|
||||
test "OSC: OSC 9;4 ConEmu progress remove with double semicolon" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3051,12 +3234,12 @@ test "OSC: OSC9 progress remove with double semicolon" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .remove);
|
||||
try testing.expect(cmd.progress_report.progress == null);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .remove);
|
||||
try testing.expect(cmd.conemu_progress_report.progress == null);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress remove ignores progress" {
|
||||
test "OSC: OSC 9;4 ConEmu progress remove ignores progress" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3065,12 +3248,12 @@ test "OSC: OSC9 progress remove ignores progress" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .remove);
|
||||
try testing.expect(cmd.progress_report.progress == null);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .remove);
|
||||
try testing.expect(cmd.conemu_progress_report.progress == null);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress remove extra semicolon" {
|
||||
test "OSC: OSC 9;4 ConEmu progress remove extra semicolon" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3079,11 +3262,11 @@ test "OSC: OSC9 progress remove extra semicolon" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .remove);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .remove);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress error" {
|
||||
test "OSC: OSC 9;4 ConEmu progress error" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3092,12 +3275,12 @@ test "OSC: OSC9 progress error" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .@"error");
|
||||
try testing.expect(cmd.progress_report.progress == null);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .@"error");
|
||||
try testing.expect(cmd.conemu_progress_report.progress == null);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress error with progress" {
|
||||
test "OSC: OSC 9;4 ConEmu progress error with progress" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3106,12 +3289,12 @@ test "OSC: OSC9 progress error with progress" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .@"error");
|
||||
try testing.expect(cmd.progress_report.progress == 100);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .@"error");
|
||||
try testing.expect(cmd.conemu_progress_report.progress == 100);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress pause" {
|
||||
test "OSC: OSC 9;4 progress pause" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3120,12 +3303,12 @@ test "OSC: OSC9 progress pause" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .pause);
|
||||
try testing.expect(cmd.progress_report.progress == null);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .pause);
|
||||
try testing.expect(cmd.conemu_progress_report.progress == null);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 progress pause with progress" {
|
||||
test "OSC: OSC 9;4 ConEmu progress pause with progress" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3134,12 +3317,68 @@ test "OSC: OSC9 progress pause with progress" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .progress_report);
|
||||
try testing.expect(cmd.progress_report.state == .pause);
|
||||
try testing.expect(cmd.progress_report.progress == 100);
|
||||
try testing.expect(cmd == .conemu_progress_report);
|
||||
try testing.expect(cmd.conemu_progress_report.state == .pause);
|
||||
try testing.expect(cmd.conemu_progress_report.progress == 100);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 conemu wait input" {
|
||||
test "OSC: OSC 9;4 progress -> desktop notification 1" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;4";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("4", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9;4 progress -> desktop notification 2" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;4;";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("4;", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9;4 progress -> desktop notification 3" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;4;5";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("4;5", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9;4 progress -> desktop notification 4" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
||||
const input = "9;4;5a";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("4;5a", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
||||
test "OSC: OSC 9;5 ConEmu wait input" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3148,10 +3387,10 @@ test "OSC: OSC9 conemu wait input" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .wait_input);
|
||||
try testing.expect(cmd == .conemu_wait_input);
|
||||
}
|
||||
|
||||
test "OSC: OSC9 conemu wait ignores trailing characters" {
|
||||
test "OSC: OSC 9;5 ConEmu wait ignores trailing characters" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init();
|
||||
|
|
@ -3160,7 +3399,7 @@ test "OSC: OSC9 conemu wait ignores trailing characters" {
|
|||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .wait_input);
|
||||
try testing.expect(cmd == .conemu_wait_input);
|
||||
}
|
||||
|
||||
test "OSC: empty param" {
|
||||
|
|
@ -3415,3 +3654,45 @@ test "OSC: kitty color protocol no key" {
|
|||
try testing.expect(cmd == .kitty_color_protocol);
|
||||
try testing.expectEqual(0, cmd.kitty_color_protocol.list.items.len);
|
||||
}
|
||||
|
||||
test "OSC: 9;6: ConEmu guimacro 1" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .initAlloc(testing.allocator);
|
||||
defer p.deinit();
|
||||
|
||||
const input = "9;6;a";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .conemu_guimacro);
|
||||
try testing.expectEqualStrings("a", cmd.conemu_guimacro);
|
||||
}
|
||||
|
||||
test "OSC: 9;6: ConEmu guimacro 2" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .initAlloc(testing.allocator);
|
||||
defer p.deinit();
|
||||
|
||||
const input = "9;6;ab";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .conemu_guimacro);
|
||||
try testing.expectEqualStrings("ab", cmd.conemu_guimacro);
|
||||
}
|
||||
|
||||
test "OSC: 9;6: ConEmu guimacro 3 incomplete -> desktop notification" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .initAlloc(testing.allocator);
|
||||
defer p.deinit();
|
||||
|
||||
const input = "9;6";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .show_desktop_notification);
|
||||
try testing.expectEqualStrings("6", cmd.show_desktop_notification.body);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ pub const Parser = struct {
|
|||
self.idx += 1;
|
||||
return .{ .unknown = .{
|
||||
.full = self.params,
|
||||
.partial = slice[0 .. self.idx - start + 1],
|
||||
.partial = slice[0..@min(self.idx - start + 1, slice.len)],
|
||||
} };
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ pub fn Stream(comptime Handler: type) type {
|
|||
// the parser state to ground.
|
||||
0x18, 0x1A => self.parser.state = .ground,
|
||||
// A parameter digit:
|
||||
'0'...'9' => if (self.parser.params_idx < 16) {
|
||||
'0'...'9' => if (self.parser.params_idx < Parser.MAX_PARAMS) {
|
||||
self.parser.param_acc *|= 10;
|
||||
self.parser.param_acc +|= c - '0';
|
||||
// The parser's CSI param action uses param_acc_idx
|
||||
|
|
@ -259,7 +259,7 @@ pub fn Stream(comptime Handler: type) type {
|
|||
self.parser.param_acc_idx |= 1;
|
||||
},
|
||||
// A parameter separator:
|
||||
':', ';' => if (self.parser.params_idx < 16) {
|
||||
':', ';' => if (self.parser.params_idx < Parser.MAX_PARAMS) {
|
||||
self.parser.params[self.parser.params_idx] = self.parser.param_acc;
|
||||
if (c == ':') self.parser.params_sep.set(self.parser.params_idx);
|
||||
self.parser.params_idx += 1;
|
||||
|
|
@ -1598,14 +1598,19 @@ pub fn Stream(comptime Handler: type) type {
|
|||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
||||
.progress_report => |v| {
|
||||
.conemu_progress_report => |v| {
|
||||
if (@hasDecl(T, "handleProgressReport")) {
|
||||
try self.handler.handleProgressReport(v);
|
||||
return;
|
||||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
||||
.sleep, .show_message_box, .change_conemu_tab_title, .wait_input => {
|
||||
.conemu_sleep,
|
||||
.conemu_show_message_box,
|
||||
.conemu_change_tab_title,
|
||||
.conemu_wait_input,
|
||||
.conemu_guimacro,
|
||||
=> {
|
||||
log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
||||
|
|
@ -2596,3 +2601,22 @@ test "stream CSI ? W reset tab stops" {
|
|||
try s.nextSlice("\x1b[?1;2;3W");
|
||||
try testing.expect(s.handler.reset);
|
||||
}
|
||||
|
||||
test "stream: SGR with 17+ parameters for underline color" {
|
||||
const H = struct {
|
||||
attrs: ?sgr.Attribute = null,
|
||||
called: bool = false,
|
||||
|
||||
pub fn setAttribute(self: *@This(), attr: sgr.Attribute) !void {
|
||||
self.attrs = attr;
|
||||
self.called = true;
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
// Kakoune-style SGR with underline color as 17th parameter
|
||||
// This tests the fix where param 17 was being dropped
|
||||
try s.nextSlice("\x1b[4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136;0m");
|
||||
try testing.expect(s.handler.called);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1513,7 +1513,8 @@ fn execCommand(
|
|||
}
|
||||
|
||||
return switch (command) {
|
||||
.direct => |v| v,
|
||||
// We need to clone the command since there's no guarantee the config remains valid.
|
||||
.direct => |_| (try command.clone(alloc)).direct,
|
||||
|
||||
.shell => |v| shell: {
|
||||
var args: std.ArrayList([:0]const u8) = try .initCapacity(alloc, 4);
|
||||
|
|
@ -1688,3 +1689,35 @@ test "execCommand: direct command, error passwd" {
|
|||
try testing.expectEqualStrings(result[0], "foo");
|
||||
try testing.expectEqualStrings(result[1], "bar baz");
|
||||
}
|
||||
|
||||
test "execCommand: direct command, config freed" {
|
||||
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var command_arena = ArenaAllocator.init(alloc);
|
||||
const command_alloc = command_arena.allocator();
|
||||
const command = try (configpkg.Command{
|
||||
.direct = &.{
|
||||
"foo",
|
||||
"bar baz",
|
||||
},
|
||||
}).clone(command_alloc);
|
||||
|
||||
const result = try execCommand(alloc, command, struct {
|
||||
fn get(_: Allocator) !PasswdEntry {
|
||||
// Failed passwd entry means we can't construct a macOS
|
||||
// login command and falls back to POSIX behavior.
|
||||
return error.Fail;
|
||||
}
|
||||
});
|
||||
|
||||
command_arena.deinit();
|
||||
|
||||
try testing.expectEqual(2, result.len);
|
||||
try testing.expectEqualStrings(result[0], "foo");
|
||||
try testing.expectEqualStrings(result[1], "bar baz");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue