Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.gitattributes text eol=lf
*.patch text eol=lf
12 changes: 10 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,17 @@ jobs:
id: ghostty-cache
with:
path: ghostty-src
key: ghostty-src-${{ steps.ghostty.outputs.full }}
key: ghostty-src-${{ steps.ghostty.outputs.full }}-${{ hashFiles('packages/libghostty/patches/*.patch') }}
- name: download ghostty source
if: steps.ghostty-cache.outputs.cache-hit != 'true'
run: |
curl -fsSL "https://github.com/ghostty-org/ghostty/archive/${{ steps.ghostty.outputs.full }}.tar.gz" -o ghostty.tar.gz
mkdir -p ghostty-src
tar xzf ghostty.tar.gz -C ghostty-src --strip-components=1
git -C ghostty-src init --quiet
git -C ghostty-src apply --unidiff-zero "$GITHUB_WORKSPACE/packages/libghostty/patches/osc52-clipboard-write.patch"
rm -rf ghostty-src/.git
touch ghostty-src/.git
rm ghostty.tar.gz
- name: compile
working-directory: ghostty-src
Expand Down Expand Up @@ -164,13 +168,17 @@ jobs:
id: ghostty-cache
with:
path: ghostty-src
key: ghostty-src-${{ steps.ghostty.outputs.full }}
key: ghostty-src-${{ steps.ghostty.outputs.full }}-${{ hashFiles('packages/libghostty/patches/*.patch') }}
- name: download ghostty source
if: steps.ghostty-cache.outputs.cache-hit != 'true'
run: |
curl -fsSL "https://github.com/ghostty-org/ghostty/archive/${{ steps.ghostty.outputs.full }}.tar.gz" -o ghostty.tar.gz
mkdir -p ghostty-src
tar xzf ghostty.tar.gz -C ghostty-src --strip-components=1
git -C ghostty-src init --quiet
git -C ghostty-src apply --unidiff-zero "$GITHUB_WORKSPACE/packages/libghostty/patches/osc52-clipboard-write.patch"
rm -rf ghostty-src/.git
touch ghostty-src/.git
rm ghostty.tar.gz
- name: compile wasm
working-directory: ghostty-src
Expand Down
80 changes: 77 additions & 3 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,17 @@ jobs:
id: ghostty-cache
with:
path: ghostty
key: ghostty-src-${{ steps.ghostty.outputs.commit }}
key: ghostty-src-${{ steps.ghostty.outputs.commit }}-${{ hashFiles('packages/libghostty/patches/*.patch') }}
- name: download ghostty source
if: steps.ghostty-cache.outputs.cache-hit != 'true'
shell: bash
run: |
curl -fsSL "https://github.com/ghostty-org/ghostty/archive/${{ steps.ghostty.outputs.commit }}.tar.gz" -o ghostty.tar.gz
mkdir -p ghostty
tar xzf ghostty.tar.gz -C ghostty --strip-components=1
git -C ghostty init --quiet
git -C ghostty apply --unidiff-zero "$GITHUB_WORKSPACE/packages/libghostty/patches/osc52-clipboard-write.patch"
rm -rf ghostty/.git
rm ghostty.tar.gz
- run: flutter pub get
- run: dart pub global activate very_good_cli
Expand Down Expand Up @@ -229,13 +232,16 @@ jobs:
id: ghostty-cache
with:
path: ghostty
key: ghostty-src-${{ steps.ghostty.outputs.commit }}
key: ghostty-src-${{ steps.ghostty.outputs.commit }}-${{ hashFiles('packages/libghostty/patches/*.patch') }}
- name: download ghostty source
if: steps.ghostty-cache.outputs.cache-hit != 'true'
run: |
curl -fsSL "https://github.com/ghostty-org/ghostty/archive/${{ steps.ghostty.outputs.commit }}.tar.gz" -o ghostty.tar.gz
mkdir -p ghostty
tar xzf ghostty.tar.gz -C ghostty --strip-components=1
git -C ghostty init --quiet
git -C ghostty apply --unidiff-zero "$GITHUB_WORKSPACE/packages/libghostty/patches/osc52-clipboard-write.patch"
rm -rf ghostty/.git
rm ghostty.tar.gz
- name: isolate from repo git
run: touch ghostty/.git
Expand Down Expand Up @@ -282,14 +288,17 @@ jobs:
id: ghostty-cache
with:
path: ghostty
key: ghostty-src-${{ steps.ghostty.outputs.commit }}
key: ghostty-src-${{ steps.ghostty.outputs.commit }}-${{ hashFiles('packages/libghostty/patches/*.patch') }}
- name: download ghostty source
if: steps.ghostty-cache.outputs.cache-hit != 'true'
shell: bash
run: |
curl -fsSL "https://github.com/ghostty-org/ghostty/archive/${{ steps.ghostty.outputs.commit }}.tar.gz" -o ghostty.tar.gz
mkdir -p ghostty
tar xzf ghostty.tar.gz -C ghostty --strip-components=1
git -C ghostty init --quiet
git -C ghostty apply --unidiff-zero "$GITHUB_WORKSPACE/packages/libghostty/patches/osc52-clipboard-write.patch"
rm -rf ghostty/.git
rm ghostty.tar.gz
- run: flutter pub get
- name: install very_good_cli
Expand All @@ -300,6 +309,70 @@ jobs:
working-directory: packages/flterm
run: very_good test

build-flterm-desktop:
name: build-flterm-${{ matrix.platform }}
needs: [analyze-flterm, changes]
if: needs.changes.outputs.flterm == 'true'
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: true
matrix:
include:
- platform: linux
runner: ubuntu-latest
- platform: macos
runner: macos-15
- platform: windows
runner: windows-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-tags: false
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: stable
cache: true
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: ${{ env.ZIG_VERSION }}
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.pub-cache
key: pub-flterm-desktop-${{ runner.os }}-${{ hashFiles('**/pubspec.lock') }}
restore-keys: pub-flterm-desktop-${{ runner.os }}-
- name: install Linux build dependencies
if: matrix.platform == 'linux'
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev ninja-build
- name: resolve ghostty source
id: ghostty
run: |
COMMIT=$(cat packages/libghostty/ghostty.version)
echo "commit=$COMMIT" >> "$GITHUB_OUTPUT"
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
id: ghostty-cache
with:
path: ghostty
key: ghostty-src-${{ steps.ghostty.outputs.commit }}-${{ hashFiles('packages/libghostty/patches/*.patch') }}
- name: download ghostty source
if: steps.ghostty-cache.outputs.cache-hit != 'true'
run: |
curl -fsSL "https://github.com/ghostty-org/ghostty/archive/${{ steps.ghostty.outputs.commit }}.tar.gz" -o ghostty.tar.gz
mkdir -p ghostty
tar xzf ghostty.tar.gz -C ghostty --strip-components=1
git -C ghostty init --quiet
git -C ghostty apply --unidiff-zero "$GITHUB_WORKSPACE/packages/libghostty/patches/osc52-clipboard-write.patch"
rm -rf ghostty/.git ghostty.tar.gz
- name: build desktop smoke app
run: |
SMOKE_DIR="$RUNNER_TEMP/flterm-smoke"
flutter create --platforms=${{ matrix.platform }} --project-name=flterm_smoke "$SMOKE_DIR"
cd "$SMOKE_DIR"
flutter pub add flterm --path "$GITHUB_WORKSPACE/packages/flterm"
flutter build ${{ matrix.platform }} --debug

test-ptyx-dart:
name: test-ptyx-dart-${{ matrix.os }}
needs: [analyze-ptyx, changes]
Expand Down Expand Up @@ -428,6 +501,7 @@ jobs:
- test-libghostty-native
- test-libghostty-wasm
- test-flterm
- build-flterm-desktop
- test-ptyx-dart
- test-ptyx-rust
runs-on: ubuntu-latest
Expand Down
30 changes: 30 additions & 0 deletions packages/flterm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## Unreleased

### Added

- **OSC 52 clipboard writes**: `TerminalController.onClipboardWrite` forwards
write-only clipboard requests from libghostty to terminal embedders.
- **Tracked touch policy**: `TerminalGestureSettings.touchMouseTracking` can
preserve terminal taps while routing touch and stylus drags to scrolling.
- **Layout-aware keyboard input**: desktop companions provide active-layout
unshifted characters, consumed modifiers, lock/side state, and dead-key
detection. `TerminalKeyEventNormalizer` supports custom runners, and
`TerminalConfig.optionAsAlt` configures macOS Option behavior.

### Fixed

- **macOS keyboard metadata**: nullable Option-free text is encoded with an
explicitly typed standard-codec null value so release builds compile.

## 0.0.4

### Breaking
Expand All @@ -13,6 +31,9 @@

- **Terminal links**: `TerminalView.linkSettings` detects OSC 8 links,
text URLs, file paths, and custom regex links.
- **Accessibility semantics**: `TerminalView` exposes visible, non-concealed
terminal text and terminal-focus actions to assistive technologies, with
customizable labels and hints.
- **Controller APIs**: `selectRange`, `hasSelection`, `pwd`, and
`onPwdChanged` expose selection and working-directory state.
- **Glyph Protocol**: `TerminalConfig.glyphProtocol` toggles Glyph Protocol
Expand All @@ -23,6 +44,15 @@
- **Rendering pipeline**: selection, cursor viewport state, and cell metadata
use refreshed libghostty render snapshots.

### Fixed

- **Tracked pointer input**: mouse buttons, hover, wheel coordinates, and
touch/stylus scroll positions are preserved in terminal mouse reports.
- **Rendering invalidation**: size reports no longer consume renderer dirty
state, and Kitty image replacements stay drawable while uploads complete.
- **Accessibility rendering**: terminal semantics use the renderer snapshot,
coalesce repeated updates, and follow the visible scrollback viewport.

## 0.0.3

### Breaking
Expand Down
42 changes: 42 additions & 0 deletions packages/flterm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ libghostty-vt engine.
hyperlinks; fonts. Immutable and `lerp`-able.
- Links for OSC 8 metadata, text URLs, file paths, and custom regex
rules with activation callbacks.
- Screen-reader semantics expose the visible viewport as text and provide an
action for focusing terminal input without announcing every output update as
a live region.

## Getting started

Expand Down Expand Up @@ -89,6 +92,45 @@ controller.paste('hello');
controller.clear();
```

## Keyboard input

flterm keeps platform key translation separate from terminal protocol
encoding. On Linux, macOS, and Windows, a native companion enriches Flutter's
physical key event with the active layout's unshifted codepoint, consumed
modifiers, lock and modifier-side state, and dead-key information. Dead keys
and IME preedit stay on Flutter's text-input path; committed input is sent to
the terminal separately.

On mobile and web, Flutter does not expose all of that native metadata. flterm
uses the produced character, pressed and locked keys, previously observed
unmodified characters, and a physical-key fallback. Basic input and terminal
shortcuts still work, but consumed modifiers and layout translation can be
less precise than on desktop.

Custom runners can replace or enrich the normalized event at the controller
boundary:

```dart
final controller = TerminalController(
keyEventNormalizer: (flutterEvent, fallback) {
return fallback.copyWith(
// Metadata captured before the runner normalizes its native event.
unshiftedCodepoint: nativeUnshiftedCodepoint(flutterEvent),
consumedMods: nativeConsumedMods(flutterEvent),
);
},
);
```

On macOS, Option participates in keyboard layout translation by default. It
can instead act as terminal Alt on either or both sides:

```dart
final controller = TerminalController(
config: const TerminalConfig(optionAsAlt: OptionAsAlt.true$),
);
```

Links are configured on the view. Built-in detection covers OSC 8
metadata, text URLs, and file paths.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import FlutterMacOS
import Foundation

import flterm

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FltermPlugin.register(with: registry.registrar(forPlugin: "FltermPlugin"))
}
2 changes: 1 addition & 1 deletion packages/flterm/example/macos/Podfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
platform :osx, '10.15'
platform :osx, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
Expand Down Expand Up @@ -621,7 +621,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
Expand Down Expand Up @@ -671,7 +671,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
Expand Down
7 changes: 6 additions & 1 deletion packages/flterm/lib/flterm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ export 'package:libghostty/libghostty.dart'
FormatterExtra,
FormatterFormat,
Key,
KeyAction,
Mods,
MouseTracking,
OptionAsAlt,
PointTag,
Position,
Scrollbar,
Expand All @@ -37,7 +39,10 @@ export 'src/foundation/terminal_gesture_settings.dart'
GestureModifier,
LineSelectMode,
TerminalGestureSettings,
TerminalSelectionShape;
TerminalSelectionShape,
TouchMouseTracking;
export 'src/foundation/terminal_keyboard_event.dart'
show TerminalKeyEventNormalizer, TerminalKeyboardEvent;
export 'src/foundation/terminal_theme.dart'
show
CursorTheme,
Expand Down
2 changes: 1 addition & 1 deletion packages/flterm/lib/src/foundation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export 'foundation/input_types.dart';
export 'foundation/platform_map.dart';
export 'foundation/terminal_config.dart';
export 'foundation/terminal_gesture_settings.dart';
export 'foundation/terminal_render_observer.dart';
export 'foundation/terminal_keyboard_event.dart';
export 'foundation/terminal_theme.dart';
2 changes: 1 addition & 1 deletion packages/flterm/lib/src/foundation/callbacks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ typedef OnResize = void Function(int cols, int rows);
/// ```
typedef TerminalMouseEvent = ({
MouseAction action,
MouseButton button,
MouseButton? button,
double pixelX,
double pixelY,
});
Loading