diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index b8b62d1dc..5b7213eaa 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -46,11 +46,13 @@ alexfeijoo44 alexjuca alosarjos amadeus +and-rs andrejdaskalov anhthang anmitalidev anthonyzhoon aprilnea +arneshbanerjee arvin7liu athaapa atomk @@ -60,6 +62,7 @@ b0uks b1nar10 balazs-szucs barutsrb +basteez bch bennettp123 benodiwal @@ -72,6 +75,7 @@ bo2themax bousii brentschroeter brianc442 +by-nelson c0x0o carldaws carlvillads @@ -97,9 +101,14 @@ deblasis dervedro devsunb diaaeddin +-didouougha slingshotting LLM output +diego-moment +diegoarmstrong dkinzler dmehala +dmunozv04 dobbylee +dolzenko doprz douglance douglas @@ -108,6 +117,7 @@ drepper dzhlobo ekaterinepapava elias8 +elitex45 -enkr1 enzowilliam ephemera @@ -147,9 +157,11 @@ hulet i999rri icodesign illiakrauchanka +j-c-m j0hnm4r5 jacobsandlund jake-stewart +jakeriksen -jamesarch Bad AI etiquette jamylak jarred-sumner @@ -164,6 +176,7 @@ josephmart jparise juniqlim justonia +jzillmann karesansui-u kataokatsuki kawarimidoll @@ -173,6 +186,7 @@ khipp kierancanter kirwiisp kjvdven +kleshzz kloneets knu -kody-w @@ -201,6 +215,7 @@ marrocco-simone masterflitzer matkotiric mattn +mgsloan micaeljarniac michielvk miguelelgallo @@ -235,6 +250,7 @@ nolinmcfarland nouritsu nwehg ocean6954 +ollioddi oshdubh otomn paaloeye @@ -251,8 +267,10 @@ pluiedev pouwerkerk poweruser64 prakhar54-byte +preiter93 prihlop priyans-hu +pssalman puzza007 qappell quinnypig @@ -265,6 +283,7 @@ rgehan rhodes-b rightaditya rjwittams +rkoten rmengelbrecht rmunn rockorager @@ -280,23 +299,32 @@ sanjj1 secrus seruman seyoungjeong +sghng shorsher silveirapf simonbcn +slowdub slsrepo steven-tk sunshine-syz +svector-anu svmhdvn +tacherasasi +-tangivis tasselx tbrundige tdgroot tdslot thirstycrow +-thomas-trijindev No human in the loop thoutbeckers ticclick tnagatomi +tothedarktowercame trag1c tristan957 +tsacha +tuananh turbolent tweedbeetle uhojin @@ -304,6 +332,7 @@ unphased unsaltedscholar uzaaft vancluever +vasilmytsyk vaughanandrews -vectorpeak Stupid bot cosplaying as a maintainer vegerot @@ -320,4 +349,5 @@ ydah zenangst zenyr zeshi09 +zlitus zubb diff --git a/.github/workflows/clean-artifacts.yml b/.github/workflows/clean-artifacts.yml index 69cb74ae5..3290693f3 100644 --- a/.github/workflows/clean-artifacts.yml +++ b/.github/workflows/clean-artifacts.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 10 steps: - name: Remove old artifacts - uses: c-hive/gha-remove-artifacts@44fc7acaf1b3d0987da0e8d4707a989d80e9554b # v1.4.0 + uses: c-hive/gha-remove-artifacts@62c2fbea931baa7dd4a6b73ea5a799984a818f61 # v1.8.0 with: age: "1 week" skip-tags: true diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index da1e66530..b3cbc8e38 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -41,7 +41,7 @@ jobs: mkdir dist tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz - - uses: flatpak/flatpak-github-actions/flatpak-builder@401fe28a8384095fc1531b9d320b292f0ee45adb # v6.7 + - uses: flatpak/flatpak-github-actions/flatpak-builder@79327416609af08178ad73b352877e51450790b3 # v6.8 with: bundle: com.mitchellh.ghostty manifest-path: dist/flatpak/com.mitchellh.ghostty.yml diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml index 34e7652a7..725c66424 100644 --- a/.github/workflows/milestone.yml +++ b/.github/workflows/milestone.yml @@ -18,7 +18,7 @@ jobs: name: Milestone Update steps: - name: Set Milestone for PR - uses: hustcer/milestone-action@ebed8d5daafd855a600d7e665c1b130f06d24130 # v3.1 + uses: hustcer/milestone-action@2f38355153344ccaaa44b5b5fcff9f604dff1b45 # v3.2 if: github.event.pull_request.merged == true && !contains(github.event.pull_request.title, 'VOUCHED') && !startsWith(github.event.pull_request.title, 'ci:') with: action: bind-pr # `bind-pr` is the default action @@ -26,7 +26,7 @@ jobs: # Bind milestone to closed issue that has a merged PR fix - name: Set Milestone for Issue - uses: hustcer/milestone-action@ebed8d5daafd855a600d7e665c1b130f06d24130 # v3.1 + uses: hustcer/milestone-action@2f38355153344ccaaa44b5b5fcff9f604dff1b45 # v3.2 if: github.event.issue.state == 'closed' with: action: bind-issue diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index b7beaa607..3b481c295 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -47,10 +47,10 @@ jobs: /nix /zig - name: Setup Nix - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" diff --git a/.github/workflows/publish-tag.yml b/.github/workflows/publish-tag.yml index acb1ab1f1..f9e195733 100644 --- a/.github/workflows/publish-tag.yml +++ b/.github/workflows/publish-tag.yml @@ -64,7 +64,7 @@ jobs: mkdir blob mv appcast.xml blob/appcast.xml - name: Upload Appcast to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }} diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 1f800408f..e8fa8e454 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -89,11 +89,11 @@ jobs: /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -147,7 +147,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -160,7 +160,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -328,7 +328,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -400,7 +400,7 @@ jobs: mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml - name: Upload to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }} diff --git a/.github/workflows/release-tip.yml b/.github/workflows/release-tip.yml index dd1a23c03..5201a29c2 100644 --- a/.github/workflows/release-tip.yml +++ b/.github/workflows/release-tip.yml @@ -42,10 +42,10 @@ jobs: with: path: | /nix - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -219,10 +219,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -239,7 +239,7 @@ jobs: nix develop -c minisign -S -m ghostty-source.tar.gz -s minisign.key < minisign.password - name: Update Release - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: name: 'Ghostty Tip ("Nightly")' prerelease: true @@ -274,10 +274,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -294,7 +294,7 @@ jobs: nix develop -c minisign -S -m libghostty-vt-source.tar.gz -s minisign.key < minisign.password - name: Update Release - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: name: 'Ghostty Tip ("Nightly")' prerelease: true @@ -310,7 +310,7 @@ jobs: mkdir -p blob/${GHOSTTY_COMMIT_LONG} cp libghostty-vt-source.tar.gz blob/${GHOSTTY_COMMIT_LONG}/libghostty-vt-source.tar.gz - name: Upload to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} @@ -356,7 +356,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -379,7 +379,7 @@ jobs: nix develop -c minisign -S -m ghostty-vt.xcframework.zip -s minisign.key < minisign.password - name: Update Release - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: name: 'Ghostty Tip ("Nightly")' prerelease: true @@ -395,7 +395,7 @@ jobs: mkdir -p blob/${GHOSTTY_COMMIT_LONG} cp ghostty-vt.xcframework.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-vt.xcframework.zip - name: Upload to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} @@ -409,6 +409,135 @@ jobs: echo "Release URLs:" echo " XCFramework: https://tip.files.ghostty.org/${GHOSTTY_COMMIT_LONG}/ghostty-vt.xcframework.zip" + build-lib-vt-wasm: + needs: [setup] + if: | + needs.setup.outputs.should_skip != 'true' && + ( + github.event_name == 'workflow_dispatch' || + ( + github.repository_owner == 'ghostty-org' && + github.ref_name == 'main' + ) + ) + runs-on: namespace-profile-ghostty-sm + env: + GHOSTTY_COMMIT_LONG: ${{ needs.setup.outputs.commit_long }} + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1 + with: + path: | + /nix + /zig + + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Build ReleaseFast WASM + run: | + nix develop -c zig build \ + -Demit-lib-vt \ + -Dtarget=wasm32-freestanding \ + -Doptimize=ReleaseFast + + - name: Optimize ReleaseFast WASM + run: | + nix develop -c wasm-opt -O3 --strip-dwarf \ + --enable-simd \ + --enable-bulk-memory \ + --enable-sign-ext \ + --enable-nontrapping-float-to-int \ + --enable-multivalue \ + --enable-reference-types \ + zig-out/bin/ghostty-vt.wasm \ + -o ghostty-vt.wasm + + - name: Build ReleaseSmall WASM + run: | + nix develop -c zig build \ + -Demit-lib-vt \ + -Dtarget=wasm32-freestanding \ + -Doptimize=ReleaseSmall + + - name: Optimize ReleaseSmall WASM + run: | + nix develop -c wasm-opt -O3 \ + --enable-simd \ + --enable-bulk-memory \ + --enable-sign-ext \ + --enable-nontrapping-float-to-int \ + --enable-multivalue \ + --enable-reference-types \ + zig-out/bin/ghostty-vt.wasm \ + -o ghostty-vt-small.wasm + + - name: Verify WASM simd128 + run: | + # wasm-tools names the simd128 proposal "simd". The artifact must + # fail validation when that feature is disabled. + for artifact in ghostty-vt.wasm ghostty-vt-small.wasm; do + nix develop -c wasm-tools validate "${artifact}" + if nix develop -c wasm-tools validate \ + --features=-simd "${artifact}"; then + echo "${artifact} unexpectedly validates with simd128 disabled" >&2 + exit 1 + fi + echo "Verified ${artifact} requires simd128" + done + + - name: Sign WASM + run: | + echo -n "${{ secrets.MINISIGN_KEY }}" > minisign.key + echo -n "${{ secrets.MINISIGN_PASSWORD }}" > minisign.password + nix develop -c minisign -S -m ghostty-vt.wasm -s minisign.key < minisign.password + nix develop -c minisign -S -m ghostty-vt-small.wasm -s minisign.key < minisign.password + + - name: Update Release + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 + with: + name: 'Ghostty Tip ("Nightly")' + prerelease: true + tag_name: tip + target_commitish: ${{ github.sha }} + files: | + ghostty-vt.wasm + ghostty-vt.wasm.minisig + ghostty-vt-small.wasm + ghostty-vt-small.wasm.minisig + token: ${{ secrets.GH_RELEASE_TOKEN }} + + - name: Prep R2 Storage + run: | + mkdir -p blob/${GHOSTTY_COMMIT_LONG} + cp ghostty-vt.wasm blob/${GHOSTTY_COMMIT_LONG}/ghostty-vt.wasm + cp ghostty-vt-small.wasm blob/${GHOSTTY_COMMIT_LONG}/ghostty-vt-small.wasm + - name: Upload to R2 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 + with: + r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} + r2-secret-access-key: ${{ secrets.CF_R2_TIP_SECRET_KEY }} + r2-bucket: ghostty-tip + source-dir: blob + destination-dir: ./ + + - name: Echo Release URLs + run: | + echo "Release URLs:" + echo " WASM (ReleaseFast): https://tip.files.ghostty.org/${GHOSTTY_COMMIT_LONG}/ghostty-vt.wasm" + echo " WASM (ReleaseSmall): https://tip.files.ghostty.org/${GHOSTTY_COMMIT_LONG}/ghostty-vt-small.wasm" + build-macos: needs: [setup] if: | @@ -448,7 +577,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -462,7 +591,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -581,7 +710,7 @@ jobs: # Update Release - name: Release - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: name: 'Ghostty Tip ("Nightly")' prerelease: true @@ -616,7 +745,7 @@ jobs: cp Ghostty.dmg blob/${GHOSTTY_COMMIT_LONG}/Ghostty.dmg - name: Upload to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} @@ -640,7 +769,7 @@ jobs: if: | github.repository_owner == 'ghostty-org' && github.ref_name == 'main' - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} @@ -659,7 +788,7 @@ jobs: EOF - name: Upload Release URLs - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: release-urls-${{ inputs.pr || '0' }} path: release-urls.txt @@ -704,7 +833,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -718,7 +847,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -830,7 +959,7 @@ jobs: # Update Release - name: Release - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: name: 'Ghostty Tip ("Nightly")' prerelease: true @@ -847,7 +976,7 @@ jobs: cp ghostty-macos-universal-debug-slow.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow.zip cp ghostty-macos-universal-debug-slow-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow-dsym.zip - name: Upload to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} @@ -901,7 +1030,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -915,7 +1044,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -1027,7 +1156,7 @@ jobs: # Update Release - name: Release - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: name: 'Ghostty Tip ("Nightly")' prerelease: true @@ -1044,7 +1173,7 @@ jobs: cp ghostty-macos-universal-debug-fast.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast.zip cp ghostty-macos-universal-debug-fast-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast-dsym.zip - name: Upload to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ec8b8844e..45d2002e0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,6 +94,9 @@ jobs: - build-cmake - build-flatpak - build-libghostty-vt + - build-libghostty-vt-freestanding + - build-libghostty-vt-wasm + - build-libghostty-vt-features - build-libghostty-vt-android - build-libghostty-vt-macos - build-libghostty-vt-windows @@ -169,10 +172,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -227,10 +230,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -263,7 +266,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -295,10 +298,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -364,7 +367,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -401,10 +404,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -439,10 +442,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -556,10 +559,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -590,10 +593,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -616,7 +619,6 @@ jobs: x86_64-windows-gnu, # doesn't work yet, we need a way to find msvc libc/c++ headers # x86_64-windows-msvc - wasm32-freestanding, ] runs-on: namespace-profile-ghostty-sm needs: test @@ -635,10 +637,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -648,6 +650,180 @@ jobs: nix develop -c zig build -Demit-lib-vt \ -Dtarget=${{ matrix.target }} + build-libghostty-vt-freestanding: + strategy: + matrix: + include: + - target: riscv32-freestanding-eabi + cpu: baseline + - target: thumb-freestanding-eabi + cpu: cortex_m4 + runs-on: namespace-profile-ghostty-sm + needs: test + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1 + with: + path: | + /nix + /zig + + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Build + run: | + nix develop -c zig build -Demit-lib-vt \ + -Dtarget=${{ matrix.target }} \ + -Dcpu=${{ matrix.cpu }} \ + -Doptimize=ReleaseSafe + + build-libghostty-vt-wasm: + runs-on: namespace-profile-ghostty-sm + needs: test + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1 + with: + path: | + /nix + /zig + + # Install Nix and use that to run our tests so our environment matches exactly. + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Build ReleaseFast WASM + run: | + nix develop -c zig build \ + -Demit-lib-vt \ + -Dtarget=wasm32-freestanding \ + -Doptimize=ReleaseFast + + - name: Optimize ReleaseFast WASM + run: | + nix develop -c wasm-opt -O3 --strip-dwarf \ + --enable-simd \ + --enable-bulk-memory \ + --enable-sign-ext \ + --enable-nontrapping-float-to-int \ + --enable-multivalue \ + --enable-reference-types \ + zig-out/bin/ghostty-vt.wasm \ + -o ghostty-vt.wasm + + - name: Build ReleaseSmall WASM + run: | + nix develop -c zig build \ + -Demit-lib-vt \ + -Dtarget=wasm32-freestanding \ + -Doptimize=ReleaseSmall + + - name: Validate WASM ABI manifest + run: | + nix develop -c zig build test-lib-vt-schema \ + -Dtarget=wasm32-freestanding \ + -Doptimize=ReleaseSmall + + - name: Optimize ReleaseSmall WASM + run: | + nix develop -c wasm-opt -O3 \ + --enable-simd \ + --enable-bulk-memory \ + --enable-sign-ext \ + --enable-nontrapping-float-to-int \ + --enable-multivalue \ + --enable-reference-types \ + zig-out/bin/ghostty-vt.wasm \ + -o ghostty-vt-small.wasm + + - name: Verify WASM simd128 + run: | + # wasm-tools names the simd128 proposal "simd". The artifact must + # fail validation when that feature is disabled. + for artifact in ghostty-vt.wasm ghostty-vt-small.wasm; do + nix develop -c wasm-tools validate "${artifact}" + if nix develop -c wasm-tools validate \ + --features=-simd "${artifact}"; then + echo "${artifact} unexpectedly validates with simd128 disabled" >&2 + exit 1 + fi + echo "Verified ${artifact} requires simd128" + done + + - name: Test WASM allocator + run: | + nix develop -c node test/wasm-alloc.mjs ghostty-vt.wasm + nix develop -c node test/wasm-alloc.mjs ghostty-vt-small.wasm + + # Compile-only checks for the -Dvt-features flags so that future changes + # don't regress any feature combination. + build-libghostty-vt-features: + runs-on: namespace-profile-ghostty-sm + needs: test + env: + ZIG_LOCAL_CACHE_DIR: /zig/local-cache + ZIG_GLOBAL_CACHE_DIR: /zig/global-cache + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Cache + uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1 + with: + path: | + /nix + /zig + + # Install Nix and use that to run our tests so our environment matches exactly. + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Compile feature flag matrix + run: | + features="snapshot formatter selection render_state input_encode color grid_introspection glyph_protocol kitty_graphics" + configs="-all" + for f in $features; do configs="$configs -all,+$f"; done + + for cfg in $configs; do + echo "::group::-Dvt-features=$cfg" + # The wasm lib build compiles the C API export block; the + # test build compiles every module test without running any. + nix develop -c zig build -Demit-lib-vt \ + "-Dvt-features=$cfg" \ + -Dtarget=wasm32-freestanding + nix develop -c zig build test-lib-vt-build \ + "-Dvt-features=$cfg" + echo "::endgroup::" + done + # lib-vt requires macOS runner for macOS/iOS builds because it requires the `apple_sdk` path build-libghostty-vt-macos: strategy: @@ -690,7 +866,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -735,10 +911,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -816,10 +992,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -845,10 +1021,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -878,10 +1054,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -960,10 +1136,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1000,10 +1176,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1068,10 +1244,10 @@ jobs: # uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # # Install Nix and use that to run our tests so our environment matches exactly. - # - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + # - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 # with: # nix_path: nixpkgs=channel:nixos-unstable - # - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + # - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 # with: # name: ghostty # authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1134,7 +1310,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1186,7 +1362,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1226,7 +1402,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1277,10 +1453,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1334,10 +1510,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1345,6 +1521,9 @@ jobs: - name: Test run: nix develop -c zig build test-lib-vt + - name: Validate native ABI manifest + run: nix develop -c zig build test-lib-vt-schema + test-kaitai: if: github.repository == 'ghostty-org/ghostty' && needs.skip.outputs.skip != 'true' needs: skip @@ -1359,10 +1538,10 @@ jobs: path: /nix # Install Nix so the verifier uses the pinned compiler and runtimes. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1394,10 +1573,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1442,10 +1621,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1477,10 +1656,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1511,7 +1690,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1567,10 +1746,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1598,10 +1777,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1640,10 +1819,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1671,10 +1850,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1701,10 +1880,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1733,7 +1912,7 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main with: determinate: true - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1759,10 +1938,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1787,10 +1966,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1815,10 +1994,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1848,10 +2027,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1876,10 +2055,10 @@ jobs: path: | /nix /zig - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1913,10 +2092,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -1975,10 +2154,10 @@ jobs: /zig # Install Nix and use that to run our tests so our environment matches exactly. - - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" diff --git a/.github/workflows/update-colorschemes.yml b/.github/workflows/update-colorschemes.yml index 8426d4f69..dfdd58abc 100644 --- a/.github/workflows/update-colorschemes.yml +++ b/.github/workflows/update-colorschemes.yml @@ -29,10 +29,10 @@ jobs: /zig - name: Setup Nix - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 with: nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + - uses: cachix/cachix-action@38b082610b782e7e93e209c35fd730d399dee866 # v17 with: name: ghostty authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -52,7 +52,7 @@ jobs: echo "filename=$FILENAME" >> $GITHUB_OUTPUT - name: Upload to R2 - uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4 + uses: ryand56/r2-upload-action@33ebb7a494b3a8a3c5ed2ea6bbdc72e707090317 # v1.5 with: r2-account-id: ${{ secrets.CF_R2_DEPS_ACCOUNT_ID }} r2-access-key-id: ${{ secrets.CF_R2_DEPS_AWS_KEY }} diff --git a/CODEOWNERS b/CODEOWNERS index ed1217313..4fc08bc5a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -168,6 +168,7 @@ /po/be.po @ghostty-org/be_BY /po/bg.po @ghostty-org/bg_BG /po/ca.po @ghostty-org/ca_ES +/po/da.po @ghostty-org/da_DK /po/de.po @ghostty-org/de_DE /po/es_AR.po @ghostty-org/es_AR /po/es_BO.po @ghostty-org/es_BO @@ -191,6 +192,8 @@ /po/pl.po @ghostty-org/pl_PL /po/pt_BR.po @ghostty-org/pt_BR /po/ru.po @ghostty-org/ru_RU +/po/sr.po @ghostty-org/sr_RS +/po/sr@latin.po @ghostty-org/sr_RS /po/tr.po @ghostty-org/tr_TR /po/uk.po @ghostty-org/uk_UA /po/vi.po @ghostty-org/vi_VN diff --git a/Doxyfile b/Doxyfile index 1703e6fac..77eeb3d3c 100644 --- a/Doxyfile +++ b/Doxyfile @@ -57,7 +57,8 @@ GENERATE_HTML = YES HTML_OUTPUT = zig-out/share/ghostty/doc/libghostty HTML_EXTRA_STYLESHEET = dist/doxygen/ghostty.css HTML_EXTRA_FILES = dist/doxygen/favicon.png \ - dist/doxygen/mobile-nav.js + dist/doxygen/mobile-nav.js \ + src/terminal/c/types.schema.json HTML_COLORSTYLE = DARK HTML_CODE_FOLDING = NO HTML_HEADER = dist/doxygen/header.html diff --git a/PACKAGING.md b/PACKAGING.md index 1483b8591..8cb422517 100644 --- a/PACKAGING.md +++ b/PACKAGING.md @@ -122,3 +122,56 @@ relevant to package maintainers: often necessary for system packages to specify a specific minimum Linux version, glibc, etc. Run `zig targets` to a get a full list of available targets. + +## WebAssembly (libghostty-vt) + +libghostty-vt can be built for WebAssembly for use in browsers and other +wasm runtimes: + +```sh +zig build -Demit-lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall +``` + +This produces `zig-out/bin/ghostty-vt.wasm`. + +Some notes for packaging the wasm module: + +- The build enables the `simd128` feature by default. Every browser engine + has supported it for years (Chrome 91, Firefox 89, Safari 16.4) and it is + a large performance win for VT parsing. If you target an unusual runtime + without SIMD support, opt out with `-Dcpu=generic`. + +- Optional feature areas can be compiled out with `-Dvt-features` to + significantly reduce binary size. The flag takes comma-separated + modifications applied to the default all-enabled feature set, + `-Dcpu`-style: `+feature` (or bare `feature`) enables, `-feature` + disables, and the special name `all` refers to every feature. Hyphens + and underscores are interchangeable in feature names. For example, a + read-only terminal viewer only needs the render state API: + + ```sh + zig build -Demit-lib-vt -Dtarget=wasm32-freestanding \ + -Doptimize=ReleaseSmall -Dvt-features=-all,+render-state + ``` + + This roughly halves the compressed module size versus the default + build. An interactive terminal typically wants + `-Dvt-features=-all,+render-state,+input-encode,+selection,+color`. + Disabled features drop both their C API exports and any escape + sequence handling (the sequences are still consumed and safely + ignored). See the `Features` struct in `src/terminal/build_options.zig` + for the full list of features and what each one covers. + +- `ReleaseSmall` is the recommended optimization mode for the web. Running + the result through [Binaryen's](https://github.com/WebAssembly/binaryen) + `wasm-opt -O3` shrinks it by roughly a further 10% without hurting + performance. + +- `ReleaseFast` measures 10-20% faster than `ReleaseSmall` on escape-heavy + terminal workloads, but the artifact is dominated by DWARF debug info. + If you want the speed, strip it: `wasm-opt -O3 --strip-dwarf` reduces a + ReleaseFast build from over 5MB to roughly 1.1MB (versus roughly 0.8MB + for ReleaseSmall). When invoking `wasm-opt`, pass the feature flags for + what the module uses, e.g. `--enable-simd --enable-bulk-memory +--enable-sign-ext --enable-nontrapping-float-to-int --enable-multivalue +--enable-reference-types`. diff --git a/build.zig b/build.zig index acf19ac91..5204b3d87 100644 --- a/build.zig +++ b/build.zig @@ -69,6 +69,14 @@ pub fn build(b: *std.Build) !void { "test-lib-vt", "Run libghostty-vt tests", ); + const test_lib_vt_build_step = b.step( + "test-lib-vt-build", + "Build libghostty-vt tests without running them (compile check)", + ); + const test_lib_vt_schema_step = b.step( + "test-lib-vt-schema", + "Validate the libghostty-vt ABI type manifest", + ); const test_valgrind_step = b.step( "test-valgrind", "Run tests under valgrind", @@ -117,20 +125,36 @@ pub fn build(b: *std.Build) !void { } // libghostty-vt - const libghostty_vt_shared = shared: { + const native_freestanding = config.target.result.os.tag == .freestanding and + !config.target.result.cpu.arch.isWasm(); + const libghostty_vt_shared: ?buildpkg.GhosttyLibVt = shared: { if (config.target.result.cpu.arch.isWasm()) { break :shared try buildpkg.GhosttyLibVt.initWasm( b, &mod, ); } + if (native_freestanding) break :shared null; break :shared try buildpkg.GhosttyLibVt.initShared( b, &mod, ); }; - libghostty_vt_shared.install(b.getInstallStep()); + if (libghostty_vt_shared) |shared| { + shared.install(b.getInstallStep()); + + const type_schema_test = b.addSystemCommand(&.{"python3"}); + type_schema_test.addFileArg(b.path("src/terminal/c/types-schema-verify.py")); + type_schema_test.addFileArg(b.path("src/terminal/c/types.schema.json")); + type_schema_test.addFileArg(shared.output); + test_lib_vt_schema_step.dependOn(&type_schema_test.step); + } else { + try test_lib_vt_schema_step.addError( + "cannot execute the ABI manifest for a native freestanding target", + .{}, + ); + } // libghostty-vt static lib const libghostty_vt_static = try buildpkg.GhosttyLibVt.initStatic( @@ -153,6 +177,15 @@ pub fn build(b: *std.Build) !void { libghostty_vt_static.output, static_lib_name, ).step); + + if (native_freestanding) { + b.getInstallStep().dependOn(&b.addInstallDirectory(.{ + .source_dir = b.path("include/ghostty"), + .install_dir = .header, + .install_subdir = "ghostty", + .include_extensions = &.{".h"}, + }).step); + } } // libghostty-vt xcframework (Apple only, universal binary). @@ -312,6 +345,7 @@ pub fn build(b: *std.Build) !void { const run_cmd = b.addSystemCommand(&.{ "valgrind", "--leak-check=full", + "--error-exitcode=1", "--num-callers=50", b.fmt("--suppressions={s}", .{b.pathFromRoot("valgrind.supp")}), "--gen-suppressions=all", @@ -329,6 +363,7 @@ pub fn build(b: *std.Build) !void { }); const mod_vt_test_run = b.addRunArtifact(mod_vt_test); test_lib_vt_step.dependOn(&mod_vt_test_run.step); + test_lib_vt_build_step.dependOn(&mod_vt_test.step); const mod_vt_c_test = b.addTest(.{ .root_module = mod.vt_c, @@ -336,6 +371,7 @@ pub fn build(b: *std.Build) !void { }); const mod_vt_c_test_run = b.addRunArtifact(mod_vt_c_test); test_lib_vt_step.dependOn(&mod_vt_c_test_run.step); + test_lib_vt_build_step.dependOn(&mod_vt_c_test.step); } // Tests (skip when building libghostty-vt) @@ -376,6 +412,7 @@ pub fn build(b: *std.Build) !void { const valgrind_run = b.addSystemCommand(&.{ "valgrind", "--leak-check=full", + "--error-exitcode=1", "--num-callers=50", b.fmt("--suppressions={s}", .{b.pathFromRoot("valgrind.supp")}), "--gen-suppressions=all", diff --git a/build.zig.zon b/build.zig.zon index f97fddc23..1c299c4b3 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -11,8 +11,8 @@ // Aro/translate-c itself. .translate_c = .{ .lazy = true, - .url = "https://deps.files.ghostty.org/translate_c-80f8b6e4f45a303268717d8e5f4f91d7837138bb.tar.gz", - .hash = "translate_c-0.0.0-Q_BUWmU6BwB_9JKG2l2W7i_mhmYWeRseTGBEHi_YlV5f", + .url = "https://codeberg.org/vancluever/translate-c/archive/4e879eb8aba615de112eabd1231ea6e01920cead.tar.gz", + .hash = "translate_c-0.0.0-Q_BUWhVNBwDOEcIqub4VFPJPB6D9dgwzUMHTX5KWr8Xr", }, // Zig libs @@ -66,7 +66,7 @@ .gobject = .{ // https://github.com/ghostty-org/zig-gobject based on zig_gobject // Temporary until we generate them at build time automatically. - .url = "https://github.com/ghostty-org/zig-gobject/releases/download/0.10.0-2026-07-28-36-1/ghostty-gobject-0.10.0-2026-07-28-36-1.tar.zst", + .url = "https://deps.files.ghostty.org/gobject-2026-07-28-36-1.tar.zst", .hash = "gobject-0.3.2-Skun7F6HogCMynX2JqeSHS7xr-8pK4ob-qRFIcEasVi3", .lazy = true, }, @@ -125,8 +125,8 @@ .apple_sdk = .{ .path = "./pkg/apple-sdk" }, .android_ndk = .{ .path = "./pkg/android-ndk" }, .iterm2_themes = .{ - .url = "https://deps.files.ghostty.org/ghostty-themes-release-20260803-155300-875a82f.tgz", - .hash = "N-V-__8AAPpcBAD4_75xLBbLiYqdojOwQP74eoWmpL3jPrBl", + .url = "https://deps.files.ghostty.org/ghostty-themes-release-20260831-151010-752a9c0.tgz", + .hash = "N-V-__8AAEFmBABuDGOKxAI6VMg41b9euMZ-z7HS9EcUdaor", .lazy = true, }, }, diff --git a/build.zig.zon.json b/build.zig.zon.json index 9c6e316bf..4b206b2f9 100644 --- a/build.zig.zon.json +++ b/build.zig.zon.json @@ -1,8 +1,8 @@ { - "aro-0.0.0-JSD1Qk8rOQDnuVcD4jAwMpHitA6pADRKzQ7M7hKRwxvD": { + "aro-0.0.0-JSD1Qk6lNgDdcDV4Vh7Sfy-34m2TluIVOdPzMmj_0BjX": { "name": "aro", - "url": "https://github.com/vancluever/arocc/archive/ecbc5c799574e0da2758a961b12efa586007f03c.tar.gz", - "hash": "sha256-rjNfhWjmA/1WR/xuHo4ls4fnDCbsI1VZSvb8SFRvwso=" + "url": "https://github.com/vancluever/arocc/archive/f97cdfc3779aec4b242299e2fc9a1c828c3547c6.tar.gz", + "hash": "sha256-G/NNgk7KhJSLdy17ip1igIzpYhAUlzO7ef8r3/iCv/s=" }, "N-V-__8AANT61wB--nJ95Gj_ctmzAtcjloZ__hRqNw5lC1Kr": { "name": "bindings", @@ -14,10 +14,10 @@ "url": "https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz", "hash": "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk=" }, - "N-V-__8AAIrfdwARSa-zMmxWwFuwpXf1T3asIN7s5jqi9c1v": { + "N-V-__8AAOgqbADacob-q2_DMQlmgaG4xKHRuW-6PJ4oJzMZ": { "name": "fontconfig", - "url": "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz", - "hash": "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU=" + "url": "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.3/fontconfig-2.18.3.tar.xz", + "hash": "sha256-T3tVSjjN94wDP2ZsiHHzdJ4UoJT2Wgf2MMke0LQ9NeM=" }, "N-V-__8AAKLKpwC4H27Ps_0iL3bPkQb-z6ZVSrB-x_3EEkub": { "name": "freetype", @@ -36,7 +36,7 @@ }, "gobject-0.3.2-Skun7F6HogCMynX2JqeSHS7xr-8pK4ob-qRFIcEasVi3": { "name": "gobject", - "url": "https://github.com/ghostty-org/zig-gobject/releases/download/0.10.0-2026-07-28-36-1/ghostty-gobject-0.10.0-2026-07-28-36-1.tar.zst", + "url": "https://deps.files.ghostty.org/gobject-2026-07-28-36-1.tar.zst", "hash": "sha256-dyCfm2XjiAk30zccjD6AgKFBdE7IRsJuoqnscfvnWSQ=" }, "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr": { @@ -59,10 +59,10 @@ "url": "https://deps.files.ghostty.org/N-V-__8AAEbOfQBnvcFcCX2W5z7tDaN8vaNZGamEQtNOe0UI.tar.gz", "hash": "sha256-yBbCDox18+Fa6Gc1DnmSVQLRpqhZOLsac7iSfl8x+cs=" }, - "N-V-__8AAPpcBAD4_75xLBbLiYqdojOwQP74eoWmpL3jPrBl": { + "N-V-__8AAEFmBABuDGOKxAI6VMg41b9euMZ-z7HS9EcUdaor": { "name": "iterm2_themes", - "url": "https://deps.files.ghostty.org/ghostty-themes-release-20260803-155300-875a82f.tgz", - "hash": "sha256-/qxLa9S8bSg2NAbJzwEtZhGvOxaPTAVFNWnBCs/MFBs=" + "url": "https://deps.files.ghostty.org/ghostty-themes-release-20260831-151010-752a9c0.tgz", + "hash": "sha256-g2U+uDe661v8fVDJG31EdyeW4IwcJDE04Rxe32yNFcU=" }, "N-V-__8AAIC5lwAVPJJzxnCAahSvZTIlG-HhtOvnM1uh-66x": { "name": "jetbrains_mono", @@ -114,10 +114,10 @@ "url": "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz", "hash": "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M=" }, - "translate_c-0.0.0-Q_BUWmU6BwB_9JKG2l2W7i_mhmYWeRseTGBEHi_YlV5f": { + "translate_c-0.0.0-Q_BUWhVNBwDOEcIqub4VFPJPB6D9dgwzUMHTX5KWr8Xr": { "name": "translate_c", - "url": "https://deps.files.ghostty.org/translate_c-80f8b6e4f45a303268717d8e5f4f91d7837138bb.tar.gz", - "hash": "sha256-fB7OsZ2PIijMzVMYg8SzDBtTKX7IZHbEvPuBTdyGtWk=" + "url": "https://codeberg.org/vancluever/translate-c/archive/4e879eb8aba615de112eabd1231ea6e01920cead.tar.gz", + "hash": "sha256-/sT7W8Kp+O11xaFBgpb/kDiWzfQ0MuXk/d/TuGq1Am8=" }, "uucode-0.2.0-ZZjBPlK5VADj7fdoq7G8LIHzD5o6FSkcBXXrRWr4jnrA": { "name": "uucode", @@ -149,10 +149,10 @@ "url": "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz", "hash": "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=" }, - "N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs": { + "N-V-__8AAP5JWgCGP_AD0teWpa4krRvE9VPZzvviGdbmN4jI": { "name": "wuffs", - "url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz", - "hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=" + "url": "https://deps.files.ghostty.org/wuffs-7411f488fe2e2c205c3d3b3d28638b7356522930.tar.gz", + "hash": "sha256-F4d9NG95iGUdbGLkWy47BchoCaZELF40YSN0sqlxmhw=" }, "z2d-0.12.1-j5P_Hsw8EQAKyZTQICCQnAH2xYkLDW8k9uefbsYdfPZ-": { "name": "z2d", diff --git a/build.zig.zon.nix b/build.zig.zon.nix index 01bc76f67..ddfa5d28d 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -98,11 +98,11 @@ in linkFarm name [ { - name = "aro-0.0.0-JSD1Qk8rOQDnuVcD4jAwMpHitA6pADRKzQ7M7hKRwxvD"; + name = "aro-0.0.0-JSD1Qk6lNgDdcDV4Vh7Sfy-34m2TluIVOdPzMmj_0BjX"; path = fetchZigArtifact { name = "aro"; - url = "https://github.com/vancluever/arocc/archive/ecbc5c799574e0da2758a961b12efa586007f03c.tar.gz"; - hash = "sha256-rjNfhWjmA/1WR/xuHo4ls4fnDCbsI1VZSvb8SFRvwso="; + url = "https://github.com/vancluever/arocc/archive/f97cdfc3779aec4b242299e2fc9a1c828c3547c6.tar.gz"; + hash = "sha256-G/NNgk7KhJSLdy17ip1igIzpYhAUlzO7ef8r3/iCv/s="; unpack = true; }; } @@ -125,11 +125,11 @@ in }; } { - name = "N-V-__8AAIrfdwARSa-zMmxWwFuwpXf1T3asIN7s5jqi9c1v"; + name = "N-V-__8AAOgqbADacob-q2_DMQlmgaG4xKHRuW-6PJ4oJzMZ"; path = fetchZigArtifact { name = "fontconfig"; - url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz"; - hash = "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU="; + url = "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.3/fontconfig-2.18.3.tar.xz"; + hash = "sha256-T3tVSjjN94wDP2ZsiHHzdJ4UoJT2Wgf2MMke0LQ9NeM="; unpack = false; }; } @@ -164,7 +164,7 @@ in name = "gobject-0.3.2-Skun7F6HogCMynX2JqeSHS7xr-8pK4ob-qRFIcEasVi3"; path = fetchZigArtifact { name = "gobject"; - url = "https://github.com/ghostty-org/zig-gobject/releases/download/0.10.0-2026-07-28-36-1/ghostty-gobject-0.10.0-2026-07-28-36-1.tar.zst"; + url = "https://deps.files.ghostty.org/gobject-2026-07-28-36-1.tar.zst"; hash = "sha256-dyCfm2XjiAk30zccjD6AgKFBdE7IRsJuoqnscfvnWSQ="; unpack = true; }; @@ -206,11 +206,11 @@ in }; } { - name = "N-V-__8AAPpcBAD4_75xLBbLiYqdojOwQP74eoWmpL3jPrBl"; + name = "N-V-__8AAEFmBABuDGOKxAI6VMg41b9euMZ-z7HS9EcUdaor"; path = fetchZigArtifact { name = "iterm2_themes"; - url = "https://deps.files.ghostty.org/ghostty-themes-release-20260803-155300-875a82f.tgz"; - hash = "sha256-/qxLa9S8bSg2NAbJzwEtZhGvOxaPTAVFNWnBCs/MFBs="; + url = "https://deps.files.ghostty.org/ghostty-themes-release-20260831-151010-752a9c0.tgz"; + hash = "sha256-g2U+uDe661v8fVDJG31EdyeW4IwcJDE04Rxe32yNFcU="; unpack = false; }; } @@ -305,11 +305,11 @@ in }; } { - name = "translate_c-0.0.0-Q_BUWmU6BwB_9JKG2l2W7i_mhmYWeRseTGBEHi_YlV5f"; + name = "translate_c-0.0.0-Q_BUWhVNBwDOEcIqub4VFPJPB6D9dgwzUMHTX5KWr8Xr"; path = fetchZigArtifact { name = "translate_c"; - url = "https://deps.files.ghostty.org/translate_c-80f8b6e4f45a303268717d8e5f4f91d7837138bb.tar.gz"; - hash = "sha256-fB7OsZ2PIijMzVMYg8SzDBtTKX7IZHbEvPuBTdyGtWk="; + url = "https://codeberg.org/vancluever/translate-c/archive/4e879eb8aba615de112eabd1231ea6e01920cead.tar.gz"; + hash = "sha256-/sT7W8Kp+O11xaFBgpb/kDiWzfQ0MuXk/d/TuGq1Am8="; unpack = true; }; } @@ -368,11 +368,11 @@ in }; } { - name = "N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs"; + name = "N-V-__8AAP5JWgCGP_AD0teWpa4krRvE9VPZzvviGdbmN4jI"; path = fetchZigArtifact { name = "wuffs"; - url = "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz"; - hash = "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM="; + url = "https://deps.files.ghostty.org/wuffs-7411f488fe2e2c205c3d3b3d28638b7356522930.tar.gz"; + hash = "sha256-F4d9NG95iGUdbGLkWy47BchoCaZELF40YSN0sqlxmhw="; unpack = false; }; } diff --git a/build.zig.zon.txt b/build.zig.zon.txt index 9c726c1fc..e1dd4b64f 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -1,16 +1,17 @@ git+https://github.com/rockorager/libvaxis.git#c1e1f23be38951c425cdf31af455ba23ef178940 git+https://github.com/zigimg/zigimg#d695acd97c02e57bb151e8f659d1280f5cd6ca70 +https://codeberg.org/vancluever/translate-c/archive/4e879eb8aba615de112eabd1231ea6e01920cead.tar.gz https://deps.files.ghostty.org/DearBindings_v0.17_ImGui_v1.92.5-docking.tar.gz https://deps.files.ghostty.org/JetBrainsMono-2.304.tar.gz https://deps.files.ghostty.org/N-V-__8AAEbOfQBnvcFcCX2W5z7tDaN8vaNZGamEQtNOe0UI.tar.gz https://deps.files.ghostty.org/N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA.tar.gz https://deps.files.ghostty.org/NerdFontsSymbolsOnly-3.4.0.tar.gz https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz -https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz https://deps.files.ghostty.org/gettext-0.24.tar.gz -https://deps.files.ghostty.org/ghostty-themes-release-20260803-155300-875a82f.tgz +https://deps.files.ghostty.org/ghostty-themes-release-20260831-151010-752a9c0.tgz https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz +https://deps.files.ghostty.org/gobject-2026-07-28-36-1.tar.zst https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz @@ -22,17 +23,16 @@ https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d8 https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz -https://deps.files.ghostty.org/translate_c-80f8b6e4f45a303268717d8e5f4f91d7837138bb.tar.gz https://deps.files.ghostty.org/uucode-2826a37a4562284fdacd8fa029d49509cc9bffcd.tar.gz https://deps.files.ghostty.org/vaxis-1dbbe575dff4586fe51e3217aa5c3fecdcbb6089.tar.gz https://deps.files.ghostty.org/wayland-0.6.0-lQa1kqz8AQADQmdNJsNhLoNHcnEGEUjrOaPV-dtEnEmX.tar.gz https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz -https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz +https://deps.files.ghostty.org/wuffs-7411f488fe2e2c205c3d3b3d28638b7356522930.tar.gz https://deps.files.ghostty.org/z2d-7dbae85c81784dba9988320bf9543ed9a81350c8.tar.gz https://deps.files.ghostty.org/zf-c35c421f84895193246db06c40683c1a30e616ef.tar.gz https://deps.files.ghostty.org/zig_js-3c23860e47fdcdc5af805efb7fd0bdac5fd3e9bc.tar.gz https://deps.files.ghostty.org/zig_objc-c8de82ff80281215ad92900866dab7103a8efa8b.tar.gz https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz -https://github.com/ghostty-org/zig-gobject/releases/download/0.10.0-2026-07-28-36-1/ghostty-gobject-0.10.0-2026-07-28-36-1.tar.zst -https://github.com/vancluever/arocc/archive/ecbc5c799574e0da2758a961b12efa586007f03c.tar.gz +https://github.com/vancluever/arocc/archive/f97cdfc3779aec4b242299e2fc9a1c828c3547c6.tar.gz +https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.3/fontconfig-2.18.3.tar.xz diff --git a/example/c-vt-effects/src/main.c b/example/c-vt-effects/src/main.c index bc5ece06d..08cfa4705 100644 --- a/example/c-vt-effects/src/main.c +++ b/example/c-vt-effects/src/main.c @@ -40,13 +40,17 @@ void on_title_changed(GhosttyTerminal terminal, void* userdata) { //! [effects-title-changed] //! [effects-clipboard-write] -GhosttyClipboardWriteResult on_clipboard_write( +void on_clipboard_write( GhosttyTerminal terminal, void* userdata, const GhosttyClipboardWrite* write) { (void)terminal; (void)userdata; + // The write is synchronous: a real embedder would ask the user for + // permission here (unless write->granted) and the VT stream waits until + // this callback returns. The replied result is sent to the program + // (OSC 5522) through the write_pty callback. printf(" clipboard write (location=%d, contents=%zu)\n", (int)write->location, write->contents_len); if (write->contents_len == 0) { @@ -66,10 +70,55 @@ GhosttyClipboardWriteResult on_clipboard_write( printf("\n"); } - return GHOSTTY_CLIPBOARD_WRITE_RESULT_SUCCESS; + GhosttyClipboardWriteReply reply = { + .size = sizeof(reply), + .result = GHOSTTY_CLIPBOARD_WRITE_RESULT_SUCCESS, + .remember = false, + }; + write->reply(write, &reply); } //! [effects-clipboard-write] +//! [effects-clipboard-read] +void on_clipboard_read( + GhosttyTerminal terminal, + void* userdata, + const GhosttyClipboardRead* read) { + (void)terminal; + (void)userdata; + + // The read is synchronous: a real embedder would ask the user for + // permission here (unless read->granted) and the VT stream waits until + // this callback returns. The reply is sent to the program through the + // write_pty callback. + printf(" clipboard read (location=%d, mimes=%zu)\n", + (int)read->location, read->mimes_len); + for (size_t i = 0; i < read->mimes_len; i++) { + printf(" "); + fwrite(read->mimes[i].ptr, 1, read->mimes[i].len, stdout); + printf("\n"); + } + + // Reply with every requested representation we have. This example only + // has text. + const char* text = "Hello from the clipboard"; + GhosttyClipboardContent content = { + .mime = {.ptr = (const uint8_t*)"text/plain", .len = 10}, + .data = {.ptr = (const uint8_t*)text, .len = strlen(text)}, + }; + GhosttyClipboardReadReply reply = { + .size = sizeof(reply), + .result = GHOSTTY_CLIPBOARD_READ_RESULT_SUCCESS, + .contents = &content, + .contents_len = 1, + .available = NULL, + .available_len = 0, + .remember = false, + }; + read->reply(read, &reply); +} +//! [effects-clipboard-read] + //! [effects-unknown-sequence] void on_unknown_sequence( GhosttyTerminal terminal, @@ -118,6 +167,8 @@ int main() { (const void *)on_title_changed); ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_CLIPBOARD_WRITE, (const void *)on_clipboard_write); + ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_CLIPBOARD_READ, + (const void *)on_clipboard_read); ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_UNKNOWN_SEQUENCE, (const void *)on_unknown_sequence); @@ -154,13 +205,19 @@ int main() { ghostty_terminal_vt_write(terminal, (const uint8_t*)clipboard_seq, strlen(clipboard_seq)); - // 5. Unsupported APC sequence + // 5. Clipboard read (OSC 52 ; c ; ? ST) + printf("Sending clipboard read:\n"); + const char* clipboard_read_seq = "\x1B]52;c;?\x1B\\"; + ghostty_terminal_vt_write(terminal, (const uint8_t*)clipboard_read_seq, + strlen(clipboard_read_seq)); + + // 6. Unsupported APC sequence printf("Sending unknown APC:\n"); const char* unknown_apc = "\x1B_private-command;payload\x1B\\"; ghostty_terminal_vt_write(terminal, (const uint8_t*)unknown_apc, strlen(unknown_apc)); - // 6. Another bell to show the counter increments + // 7. Another bell to show the counter increments printf("Sending another BEL:\n"); ghostty_terminal_vt_write(terminal, &bel, 1); diff --git a/example/c-vt-formatter/src/main.c b/example/c-vt-formatter/src/main.c index 9e7d250b9..066d70146 100644 --- a/example/c-vt-formatter/src/main.c +++ b/example/c-vt-formatter/src/main.c @@ -4,6 +4,24 @@ #include #include +typedef struct { + FILE *file; + size_t written; +} OutputWriter; + +static bool write_output(void *userdata, const uint8_t *data, size_t len) { + OutputWriter *output = userdata; + size_t offset = 0; + while (offset < len) { + size_t written = fwrite(data + offset, 1, len - offset, output->file); + output->written += written; + offset += written; + if (written == 0) return false; + } + + return true; +} + int main() { // Create a terminal with a small grid GhosttyTerminal terminal; @@ -14,8 +32,8 @@ int main() { // cursor movement and styling sequences. const char *commands[] = { "Line 1: Hello World!\r\n", // Simple text on row 1 - "Line 2: \033[1mBold\033[0m and " // Bold text on row 2 - "\033[4mUnderline\033[0m\r\n", + ("Line 2: \033[1mBold\033[0m and " // Bold text on row 2 + "\033[4mUnderline\033[0m\r\n"), "Line 3: placeholder\r\n", // Will be overwritten below "\033[3;1H", // CUP: move cursor back to row 3, col 1 "\033[2K", // EL: erase the entire line @@ -39,19 +57,16 @@ int main() { result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts); assert(result == GHOSTTY_SUCCESS); - // Format into an allocated buffer - uint8_t *buf = NULL; - size_t len = 0; - result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len); + // Stream the formatted output directly to stdout. The writer retains the + // exact byte count and reports destination errors through its return value. + OutputWriter output = {.file = stdout}; + GhosttyWriter writer = {.write = write_output, .userdata = &output}; + printf("Formatted output:\n"); + result = ghostty_formatter_format(formatter, writer); assert(result == GHOSTTY_SUCCESS); - - // Print the formatted output - printf("Formatted output (%zu bytes):\n", len); - fwrite(buf, 1, len, stdout); - printf("\n"); + printf("\n(%zu bytes)\n", output.written); // Clean up - ghostty_free(NULL, buf, len); ghostty_formatter_free(formatter); ghostty_terminal_free(terminal); return 0; diff --git a/example/c-vt-paste/README.md b/example/c-vt-paste/README.md index 377cd3c3b..1e1e6fa14 100644 --- a/example/c-vt-paste/README.md +++ b/example/c-vt-paste/README.md @@ -1,7 +1,13 @@ -# Example: `ghostty-vt` Paste Utilities +# Example: `ghostty-vt` Paste -This contains a simple example of how to use the `ghostty-vt` paste -utilities to check if paste data is safe and encode it for terminal input. +This contains a simple example of how to paste into a `ghostty-vt` +terminal with `ghostty_terminal_paste`: plain and bracketed (mode 2004) +text pastes, the unsafe-paste confirmation flow, and Kitty clipboard +protocol paste events (mode 5522) including the program's follow-up +clipboard read. The clipboard's data is produced on demand through a +read callback, so only what is actually pasted is ever read, and the +result streams to the pty in chunks. It also shows the terminal-free +building blocks for checking paste safety and encoding paste data. This uses a `build.zig` and `Zig` to build the C program so that we can reuse a lot of our build logic and depend directly on our source diff --git a/example/c-vt-paste/src/main.c b/example/c-vt-paste/src/main.c index e6e4b3d61..c83020eed 100644 --- a/example/c-vt-paste/src/main.c +++ b/example/c-vt-paste/src/main.c @@ -1,7 +1,160 @@ +#include +#include +#include #include #include #include +#define GS(s) ((GhosttyString){.ptr = (const uint8_t*)(s), .len = sizeof(s) - 1}) + +// Print bytes destined for the pty with control characters made visible. +static void print_escaped(const uint8_t* data, size_t len) { + for (size_t i = 0; i < len; i++) { + switch (data[i]) { + case 0x1b: printf("ESC"); break; + case '\r': printf("\\r"); break; + case '\n': printf("\\n"); break; + default: putchar(data[i]); break; + } + } +} + +// The base64 password of the last paste event, captured from the OK +// packet so the example can play the program's side of the protocol. +static char event_pw[128]; + +// Everything the terminal writes to the running program: the pasted +// text, or the paste event packets when mode 5522 is enabled. +static void on_write_pty(GhosttyTerminal terminal, + void* userdata, + const uint8_t* data, + size_t len) { + (void)terminal; + (void)userdata; + printf(" -> pty (%zu bytes): ", len); + print_escaped(data, len); + printf("\n"); + + // A paste event's OK packet: OSC 5522 ; type=read:status=OK:pw= ST + const char* prefix = "\x1b]5522;type=read:status=OK:pw="; + size_t prefix_len = strlen(prefix); + if (len > prefix_len && memcmp(data, prefix, prefix_len) == 0) { + size_t end = prefix_len; + while (end < len && data[end] != 0x1b) end++; + size_t pw_len = end - prefix_len; + if (pw_len < sizeof(event_pw)) { + memcpy(event_pw, data + prefix_len, pw_len); + event_pw[pw_len] = 0; + } + } +} + +// Serves clipboard reads. After a paste event the program's read arrives +// with `granted` set, because it carries the event's one-time password, +// so the embedder skips its permission prompt. +static void on_clipboard_read(GhosttyTerminal terminal, + void* userdata, + const GhosttyClipboardRead* read) { + (void)terminal; + (void)userdata; + printf(" clipboard read: name=\""); + fwrite(read->name.ptr, 1, read->name.len, stdout); + printf("\" granted=%s\n", read->granted ? "yes (no prompt needed)" : "no"); + + const char* text = "hello from the clipboard"; + GhosttyClipboardContent content = { + .mime = GS("text/plain"), + .data = {.ptr = (const uint8_t*)text, .len = strlen(text)}, + }; + GhosttyClipboardReadReply reply = { + .size = sizeof(reply), + .result = GHOSTTY_CLIPBOARD_READ_RESULT_SUCCESS, + .contents = &content, + .contents_len = 1, + .available = NULL, + .available_len = 0, + .remember = false, + }; + read->reply(read, &reply); +} + +// A real embedder would show a dialog here. +static bool confirm_with_user(void) { + printf(" paste could inject commands; user confirmed\n"); + return true; +} + +//! [terminal-paste] +// What the clipboard holds. A real embedder would keep a handle to the +// pasteboard or its items here; the data is only produced on demand. +typedef struct { + const char* text; +} clipboard_t; + +// Produces the data of one representation when the terminal needs it. +// Only the text is ever read: the image is listed on a paste event +// but never requested, so a large image costs nothing to paste. +// Nothing written to the writer is retained, so the data can be +// streamed from anywhere in pieces of any size. +static bool read_clipboard(void* userdata, GhosttyString mime, GhosttyWriter writer) { + clipboard_t* clipboard = userdata; + if (mime.len == strlen("text/plain") && + memcmp(mime.ptr, "text/plain", mime.len) == 0) { + // Stream the text in small pieces just to show that it works. + const uint8_t* data = (const uint8_t*)clipboard->text; + size_t len = strlen(clipboard->text); + for (size_t offset = 0; offset < len; offset += 4) { + size_t n = len - offset < 4 ? len - offset : 4; + if (!writer.write(writer.userdata, data + offset, n)) return false; + } + return true; + } + printf(" image read requested, which never happens\n"); + return false; +} + +// Paste whatever the clipboard holds. The terminal applies its own +// state: bracketed paste framing (mode 2004) or a Kitty paste event +// (mode 5522) instead of the text. +static void paste_clipboard(GhosttyTerminal terminal, const char* text) { + clipboard_t clipboard = {.text = text}; + GhosttyString mimes[] = { + // The first text representation is what a text paste writes. + GS("text/plain"), + // Listed on a paste event, never read. + GS("image/png"), + }; + GhosttyPaste paste = { + .size = sizeof(paste), + .location = GHOSTTY_CLIPBOARD_LOCATION_STANDARD, + .source = GHOSTTY_PASTE_SOURCE_CLIPBOARD, + .mimes = mimes, + .mimes_len = sizeof(mimes) / sizeof(mimes[0]), + .reader = {.read = read_clipboard, .userdata = &clipboard}, + .allow_unsafe = false, + }; + + bool written = false; + GhosttyResult result = ghostty_terminal_paste(terminal, &paste, &written); + if (result == GHOSTTY_REJECTED) { + // The text could inject commands (e.g. a newline outside of a + // bracketed paste). Nothing was written; ask, then retry. + if (!confirm_with_user()) return; + paste.allow_unsafe = true; + result = ghostty_terminal_paste(terminal, &paste, &written); + } + if (result != GHOSTTY_SUCCESS) { + fprintf(stderr, "paste failed: %d\n", (int)result); + return; + } + + // Whether the pty got the text or a paste event depends on the + // terminal's modes; either way it went through write_pty above, in + // chunks as the text was read. + printf(" %s\n", written ? "written" : "nothing to paste"); +} +//! [terminal-paste] + //! [paste-safety] void safety_example() { const char* safe_data = "hello world"; @@ -29,27 +182,87 @@ void encode_example() { if (result == GHOSTTY_SUCCESS) { printf("Encoded %zu bytes: ", written); - fwrite(buf, 1, written, stdout); + print_escaped((const uint8_t*)buf, written); printf("\n"); } } //! [paste-encode] +static void vt_write(GhosttyTerminal terminal, const char* seq) { + ghostty_terminal_vt_write(terminal, (const uint8_t*)seq, strlen(seq)); +} + int main() { + GhosttyTerminal terminal = NULL; + if (ghostty_terminal_new(NULL, &terminal, 80, 24) != GHOSTTY_SUCCESS) { + fprintf(stderr, "Failed to create terminal\n"); + return 1; + } + + // Pasted bytes and paste events go to write_pty. Serving clipboard + // reads is what lets the terminal send paste events at all: without + // this callback the program could never read the clipboard, so pastes + // stay text even when mode 5522 is enabled. + ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_WRITE_PTY, + (const void*)on_write_pty); + ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_CLIPBOARD_READ, + (const void*)on_clipboard_read); + + printf("Plain paste:\n"); + paste_clipboard(terminal, "hello world"); + + printf("Paste with a newline (refused, then confirmed):\n"); + paste_clipboard(terminal, "echo hi\n"); + + // The program enables bracketed paste: newlines are safe inside the + // frame and are preserved. + printf("Bracketed paste (mode 2004):\n"); + vt_write(terminal, "\x1b[?2004h"); + paste_clipboard(terminal, "line one\nline two"); + + // The program enables paste events: the clipboard's MIME types are + // listed with a one-time password instead of writing the data. + printf("Paste event (mode 5522):\n"); + vt_write(terminal, "\x1b[?5522h"); + paste_clipboard(terminal, "hello world"); + + // Play the program's side: read the clipboard with the password from + // the event. The read arrives granted and the data is served through + // write_pty as base64 without any permission prompt. + if (event_pw[0] != 0) { + printf("Program reads with the event password:\n"); + char read_seq[256]; + snprintf(read_seq, sizeof(read_seq), + "\x1b]5522;type=read:pw=%s:name=UGFzdGUgZXZlbnQ=;dGV4dC9wbGFpbg==\x1b\\", + event_pw); + vt_write(terminal, read_seq); + } + + // Text inserted by other means (IME, drag and drop) is never an event. + printf("IME text with mode 5522 enabled:\n"); + { + clipboard_t clipboard = {.text = "committed"}; + GhosttyString mime = GS("text/plain"); + GhosttyPaste paste = { + .size = sizeof(paste), + .location = GHOSTTY_CLIPBOARD_LOCATION_STANDARD, + .source = GHOSTTY_PASTE_SOURCE_TEXT, + .mimes = &mime, + .mimes_len = 1, + .reader = {.read = read_clipboard, .userdata = &clipboard}, + .allow_unsafe = true, + }; + bool written = false; + if (ghostty_terminal_paste(terminal, &paste, &written) == GHOSTTY_SUCCESS && + written) { + printf(" written\n"); + } + } + + ghostty_terminal_free(terminal); + + printf("\nTerminal-free building blocks:\n"); safety_example(); - - // Test unsafe paste data with bracketed paste end sequence - const char *unsafe_escape = "evil\x1b[201~code"; - if (!ghostty_paste_is_safe(unsafe_escape, strlen(unsafe_escape))) { - printf("Data with escape sequence is UNSAFE\n"); - } - - // Test empty data - const char *empty_data = ""; - if (ghostty_paste_is_safe(empty_data, 0)) { - printf("Empty data is safe\n"); - } - encode_example(); return 0; diff --git a/example/c-vt-render/src/main.c b/example/c-vt-render/src/main.c index cb9e7e8c2..705a4a0e6 100644 --- a/example/c-vt-render/src/main.c +++ b/example/c-vt-render/src/main.c @@ -97,7 +97,8 @@ int main(void) { // state. These are needed to resolve palette-indexed cell colors. GhosttyRenderStateColors colors = GHOSTTY_INIT_SIZED(GhosttyRenderStateColors); - result = ghostty_render_state_colors_get(render_state, &colors); + result = ghostty_render_state_get( + render_state, GHOSTTY_RENDER_STATE_DATA_COLORS, &colors); assert(result == GHOSTTY_SUCCESS); printf("Background: #%02x%02x%02x\n", @@ -107,31 +108,16 @@ int main(void) { //! [render-colors] //! [render-cursor] - // Read cursor position and visual style from the render state. - bool cursor_visible = false; - ghostty_render_state_get( - render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VISIBLE, - &cursor_visible); - - bool cursor_in_viewport = false; - ghostty_render_state_get( - render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE, - &cursor_in_viewport); - - if (cursor_visible && cursor_in_viewport) { - uint16_t cx, cy; - ghostty_render_state_get( - render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_X, &cx); - ghostty_render_state_get( - render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_Y, &cy); - - GhosttyRenderStateCursorVisualStyle style; - ghostty_render_state_get( - render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VISUAL_STYLE, - &style); + // Read all cursor state in one call. + GhosttyRenderStateCursor cursor = + GHOSTTY_INIT_SIZED(GhosttyRenderStateCursor); + result = ghostty_render_state_get( + render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR, &cursor); + assert(result == GHOSTTY_SUCCESS); + if (cursor.visible && cursor.viewport_has_value) { const char* style_name = "unknown"; - switch (style) { + switch (cursor.visual_style) { case GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BAR: style_name = "bar"; break; @@ -145,7 +131,8 @@ int main(void) { style_name = "hollow"; break; } - printf("Cursor at (%u, %u), style: %s\n", cx, cy, style_name); + printf("Cursor at (%u, %u), style: %s\n", + cursor.viewport_x, cursor.viewport_y, style_name); } //! [render-cursor] @@ -165,15 +152,9 @@ int main(void) { result = ghostty_render_state_row_cells_new(NULL, &cells); assert(result == GHOSTTY_SUCCESS); - int row_index = 0; - while (ghostty_render_state_row_iterator_next(row_iter)) { - // Check per-row dirty state; a real renderer would skip clean rows. - bool row_dirty = false; - ghostty_render_state_row_get( - row_iter, GHOSTTY_RENDER_STATE_ROW_DATA_DIRTY, &row_dirty); - - printf("Row %2d [%s]: ", row_index, - row_dirty ? "dirty" : "clean"); + uint16_t row_y = 0; + while (ghostty_render_state_row_iterator_next_dirty(row_iter, &row_y)) { + printf("Row %2u [dirty]: ", row_y); // Query the row-local selection range. Rows without a selection return // GHOSTTY_NO_VALUE; selected rows return inclusive start/end columns. @@ -239,22 +220,13 @@ int main(void) { } printf("\n"); - - // Clear per-row dirty flag after "rendering" it. - bool clean = false; - ghostty_render_state_row_set( - row_iter, GHOSTTY_RENDER_STATE_ROW_OPTION_DIRTY, &clean); - - row_index++; } //! [render-row-iterate] //! [render-dirty-reset] - // After finishing the frame, reset the global dirty state so the next - // update can report changes accurately. - GhosttyRenderStateDirty clean_state = GHOSTTY_RENDER_STATE_DIRTY_FALSE; - result = ghostty_render_state_set( - render_state, GHOSTTY_RENDER_STATE_OPTION_DIRTY, &clean_state); + // After successfully rendering the complete frame, clear both the global + // and per-row dirty state in one call. + result = ghostty_render_state_clean(render_state); assert(result == GHOSTTY_SUCCESS); //! [render-dirty-reset] diff --git a/example/c-vt-search/README.md b/example/c-vt-search/README.md new file mode 100644 index 000000000..3d11a3153 --- /dev/null +++ b/example/c-vt-search/README.md @@ -0,0 +1,19 @@ +# Example: `ghostty-vt` Terminal Search + +This contains a simple example of how to use the `ghostty-vt` search +API from C. It writes content into a terminal, searches it for a +string, navigates between the matches like a find bar, and reads the +viewport matches an embedder would use to draw highlights. + +This uses a `build.zig` and `Zig` to build the C program so that we +can reuse a lot of our build logic and depend directly on our source +tree, but Ghostty emits a standard C library that can be used with any +C tooling. + +## Usage + +Run the program: + +```shell-session +zig build run +``` diff --git a/example/c-vt-search/build.zig b/example/c-vt-search/build.zig new file mode 100644 index 000000000..51ddac608 --- /dev/null +++ b/example/c-vt-search/build.zig @@ -0,0 +1,42 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const run_step = b.step("run", "Run the app"); + + const exe_mod = b.createModule(.{ + .target = target, + .optimize = optimize, + }); + exe_mod.addCSourceFiles(.{ + .root = b.path("src"), + .files = &.{"main.c"}, + }); + + // You'll want to use a lazy dependency here so that ghostty is only + // downloaded if you actually need it. + if (b.lazyDependency("ghostty", .{ + // Setting simd to false will force a pure static build that + // doesn't even require libc, but it has a significant performance + // penalty. If your embedding app requires libc anyway, you should + // always keep simd enabled. + // .simd = false, + })) |dep| { + exe_mod.linkLibrary(dep.artifact("ghostty-vt")); + } + + // Exe + const exe = b.addExecutable(.{ + .name = "c_vt_search", + .root_module = exe_mod, + }); + b.installArtifact(exe); + + // Run + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd.addArgs(args); + run_step.dependOn(&run_cmd.step); +} diff --git a/example/c-vt-search/build.zig.zon b/example/c-vt-search/build.zig.zon new file mode 100644 index 000000000..2882f49f3 --- /dev/null +++ b/example/c-vt-search/build.zig.zon @@ -0,0 +1,24 @@ +.{ + .name = .c_vt_search, + .version = "0.0.0", + .fingerprint = 0x91a25a7f6cd3ff58, + .minimum_zig_version = "0.15.1", + .dependencies = .{ + // Ghostty dependency. In reality, you'd probably use a URL-based + // dependency like the one showed (and commented out) below this one. + // We use a path dependency here for simplicity and to ensure our + // examples always test against the source they're bundled with. + .ghostty = .{ .path = "../../" }, + + // Example of what a URL-based dependency looks like: + // .ghostty = .{ + // .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz", + // .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s", + // }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/example/c-vt-search/src/main.c b/example/c-vt-search/src/main.c new file mode 100644 index 000000000..769946807 --- /dev/null +++ b/example/c-vt-search/src/main.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include + +//! [search-main] +int main() { + // Create a terminal and fill it with some content to search. + GhosttyTerminal terminal; + GhosttyResult result = ghostty_terminal_new(NULL, &terminal, 80, 24); + assert(result == GHOSTTY_SUCCESS); + + const char *lines[] = { + "$ make test\r\n", + "compiling module A... ok\r\n", + "compiling module B... error: missing semicolon\r\n", + "linking... error: undefined symbol\r\n", + "$ grep -n ERROR build.log\r\n", + }; + for (size_t i = 0; i < sizeof(lines) / sizeof(lines[0]); i++) { + ghostty_terminal_vt_write(terminal, (const uint8_t *)lines[i], + strlen(lines[i])); + } + + // The user opened the find bar, so create a search bound to the + // terminal. It starts idle until it has a needle. + GhosttySearch search; + result = ghostty_search_new(NULL, &search, terminal); + assert(result == GHOSTTY_SUCCESS); + + // The user typed a query. Matching is byte-exact except ASCII + // letters, which compare case-insensitively, so "error" also finds + // "ERROR". Retyping just sets the needle again: a changed needle + // restarts the search and an unchanged one keeps its results. + GhosttyString needle = { (const uint8_t *)"error", 5 }; + result = ghostty_search_set(search, GHOSTTY_SEARCH_OPT_NEEDLE, &needle); + assert(result == GHOSTTY_SUCCESS); + + // Drive the search. Interactive embedders interleave + // ghostty_search_tick() and ghostty_search_feed() with their event + // loop, but for a one-shot search we can just run it to completion. + result = ghostty_search_run(search); + assert(result == GHOSTTY_SUCCESS); + + // The total match count, for find bar text like "1 of 3". + size_t total = 0; + result = ghostty_search_get(search, GHOSTTY_SEARCH_DATA_TOTAL_MATCHES, + &total); + assert(result == GHOSTTY_SUCCESS); + printf("%zu matches for \"error\"\n", total); + + // The user pressed Enter, so select the next match. Selection starts + // at the newest match, moves toward older content, and wraps around. + // This scrolls the viewport to the match if it isn't visible, per + // the GHOSTTY_SEARCH_OPT_SELECT_SCROLL policy. + while (true) { + result = ghostty_search_set(search, GHOSTTY_SEARCH_OPT_SELECT_NEXT, NULL); + if (result != GHOSTTY_SUCCESS) break; + + // Read the selection state in one call. Index 0 is the newest + // match, so a "k of n" find bar renders index + 1. + size_t idx = 0; + GhosttySelection match = GHOSTTY_INIT_SIZED(GhosttySelection); + const GhosttySearchData keys[] = { + GHOSTTY_SEARCH_DATA_SELECTED_INDEX, + GHOSTTY_SEARCH_DATA_SELECTED_MATCH, + }; + void *values[] = { &idx, &match }; + result = ghostty_search_get_multi( + search, sizeof(keys) / sizeof(keys[0]), keys, values, NULL); + assert(result == GHOSTTY_SUCCESS); + printf("selected %zu of %zu\n", idx + 1, total); + + // Wrapped back around to the first match: stop. + if (idx + 1 == total) break; + } + + // Each frame while the find bar is open, feed to catch up with any + // terminal changes and then read the viewport matches to draw + // highlights. The list can include matches just past the viewport + // when they share a page with it, so convert each endpoint to + // viewport coordinates and skip matches outside the visible rows. + result = ghostty_search_feed(search); + assert(result == GHOSTTY_SUCCESS); + + GhosttySelection viewport_storage[64]; + GhosttySelectionBuffer viewport = { + .ptr = viewport_storage, + .cap = sizeof(viewport_storage) / sizeof(viewport_storage[0]), + }; + result = ghostty_search_get(search, GHOSTTY_SEARCH_DATA_VIEWPORT_MATCHES, + &viewport); + assert(result == GHOSTTY_SUCCESS); + for (size_t i = 0; i < viewport.len; i++) { + GhosttyPointCoordinate start, end; + if (ghostty_terminal_point_from_grid_ref( + terminal, &viewport_storage[i].start, GHOSTTY_POINT_TAG_VIEWPORT, + &start) != GHOSTTY_SUCCESS) continue; + if (ghostty_terminal_point_from_grid_ref( + terminal, &viewport_storage[i].end, GHOSTTY_POINT_TAG_VIEWPORT, + &end) != GHOSTTY_SUCCESS) continue; + if (start.y >= 24 || end.y >= 24) continue; + + // A real embedder draws a highlight rect from start to end here. + printf("highlight rows %u-%u, cols %u-%u\n", + (unsigned)start.y, (unsigned)end.y, + (unsigned)start.x, (unsigned)end.x); + } + + // Closing the find bar. The search borrows the terminal, but the + // two can be freed in either order. + ghostty_search_free(search); + ghostty_terminal_free(terminal); + return 0; +} +//! [search-main] diff --git a/example/wasm-key-encode/index.html b/example/wasm-key-encode/index.html index 9f4d8bebb..2578bdad7 100644 --- a/example/wasm-key-encode/index.html +++ b/example/wasm-key-encode/index.html @@ -163,6 +163,7 @@ let wasmMemory = null; let encoderPtr = null; let lastKeyEvent = null; + let typeLayout = null; async function loadWasm() { try { @@ -184,6 +185,12 @@ wasmInstance = wasmModule.instance; wasmMemory = wasmInstance.exports.memory; + + const jsonPtr = wasmInstance.exports.ghostty_type_json(); + const jsonStr = new TextDecoder().decode( + new Uint8Array(wasmMemory.buffer, jsonPtr, wasmMemory.buffer.byteLength - jsonPtr) + ).split('\0')[0]; + typeLayout = JSON.parse(jsonStr); return true; } catch (e) { @@ -199,6 +206,15 @@ return wasmMemory.buffer; } + function readUsize(ptr) { + const view = new DataView(getBuffer()); + switch (typeLayout.abi.usize_size) { + case 4: return view.getUint32(ptr, true); + case 8: return Number(view.getBigUint64(ptr, true)); + default: throw new Error('unsupported size_t width'); + } + } + function formatHex(bytes) { return Array.from(bytes) .map(b => b.toString(16).padStart(2, '0')) @@ -415,16 +431,28 @@ function encodeKeyEvent(event) { if (!encoderPtr) return null; + const usizeSize = typeLayout.abi.usize_size; + let eventPtrPtr = 0; + let eventPtr = 0; + let utf8Ptr = 0; + let utf8Length = 0; + let requiredPtr = 0; + let required = 0; + let bufPtr = 0; + let writtenPtr = 0; + try { // Create key event - const eventPtrPtr = wasmInstance.exports.ghostty_wasm_alloc_opaque(); + eventPtrPtr = wasmInstance.exports.ghostty_wasm_alloc_opaque(); const result = wasmInstance.exports.ghostty_key_event_new(0, eventPtrPtr); if (result !== 0) { throw new Error(`ghostty_key_event_new failed with result ${result}`); } - const eventPtr = new DataView(getBuffer()).getUint32(eventPtrPtr, true); + eventPtr = wasmInstance.exports.ghostty_wasm_take_opaque(eventPtrPtr); + wasmInstance.exports.ghostty_wasm_free_opaque(eventPtrPtr); + eventPtrPtr = 0; // Get action from radio buttons const actionRadio = document.querySelector('input[name="action"]:checked'); @@ -458,9 +486,10 @@ // Set UTF-8 text from the key event (the actual character produced) if (event.key.length === 1) { const utf8Bytes = new TextEncoder().encode(event.key); - const utf8Ptr = wasmInstance.exports.ghostty_wasm_alloc_u8_array(utf8Bytes.length); + utf8Length = utf8Bytes.length; + utf8Ptr = wasmInstance.exports.ghostty_wasm_alloc(utf8Length); new Uint8Array(getBuffer()).set(utf8Bytes, utf8Ptr); - wasmInstance.exports.ghostty_key_event_set_utf8(eventPtr, utf8Ptr, utf8Bytes.length); + wasmInstance.exports.ghostty_key_event_set_utf8(eventPtr, utf8Ptr, utf8Length); } // Set unshifted codepoint @@ -470,15 +499,15 @@ } // Encode the key event - const requiredPtr = wasmInstance.exports.ghostty_wasm_alloc_usize(); + requiredPtr = wasmInstance.exports.ghostty_wasm_alloc(usizeSize); wasmInstance.exports.ghostty_key_encoder_encode( encoderPtr, eventPtr, 0, 0, requiredPtr ); - const required = new DataView(getBuffer()).getUint32(requiredPtr, true); + required = readUsize(requiredPtr); - const bufPtr = wasmInstance.exports.ghostty_wasm_alloc_u8_array(required); - const writtenPtr = wasmInstance.exports.ghostty_wasm_alloc_usize(); + bufPtr = wasmInstance.exports.ghostty_wasm_alloc(required); + writtenPtr = wasmInstance.exports.ghostty_wasm_alloc(usizeSize); const encodeResult = wasmInstance.exports.ghostty_key_encoder_encode( encoderPtr, eventPtr, bufPtr, required, writtenPtr ); @@ -487,7 +516,7 @@ return null; // No encoding for this key } - const written = new DataView(getBuffer()).getUint32(writtenPtr, true); + const written = readUsize(writtenPtr); const encoded = new Uint8Array(getBuffer()).slice(bufPtr, bufPtr + written); return { @@ -498,6 +527,13 @@ } catch (e) { console.error('Encoding error:', e); return null; + } finally { + wasmInstance.exports.ghostty_wasm_free(writtenPtr, usizeSize); + wasmInstance.exports.ghostty_wasm_free(bufPtr, required); + wasmInstance.exports.ghostty_wasm_free(requiredPtr, usizeSize); + wasmInstance.exports.ghostty_wasm_free(utf8Ptr, utf8Length); + wasmInstance.exports.ghostty_key_event_free(eventPtr); + wasmInstance.exports.ghostty_wasm_free_opaque(eventPtrPtr); } } @@ -555,13 +591,14 @@ if (!encoderPtr) return; const flags = getKittyFlags(); - const flagsPtr = wasmInstance.exports.ghostty_wasm_alloc_u8(); + const flagsPtr = wasmInstance.exports.ghostty_wasm_alloc(1); new DataView(getBuffer()).setUint8(flagsPtr, flags); wasmInstance.exports.ghostty_key_encoder_setopt( encoderPtr, 5, // GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS flagsPtr ); + wasmInstance.exports.ghostty_wasm_free(flagsPtr, 1); // Re-encode last key with new flags reencodeLastKey(); @@ -649,7 +686,8 @@ throw new Error(`ghostty_key_encoder_new failed with result ${result}`); } - encoderPtr = new DataView(getBuffer()).getUint32(encoderPtrPtr, true); + encoderPtr = wasmInstance.exports.ghostty_wasm_take_opaque(encoderPtrPtr); + wasmInstance.exports.ghostty_wasm_free_opaque(encoderPtrPtr); // Set kitty flags based on checkboxes updateEncoderFlags(); diff --git a/example/wasm-sgr/index.html b/example/wasm-sgr/index.html index e62b26c7e..24f318b9b 100644 --- a/example/wasm-sgr/index.html +++ b/example/wasm-sgr/index.html @@ -106,6 +106,7 @@