@wangyaoshen/remux 0.3.8-dev.a8ceb0c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +47 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +38 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  4. package/.github/dependabot.yml +33 -0
  5. package/.github/workflows/ci.yml +65 -0
  6. package/.github/workflows/deploy.yml +65 -0
  7. package/.github/workflows/publish.yml +312 -0
  8. package/.github/workflows/release-please.yml +21 -0
  9. package/.gitmodules +3 -0
  10. package/.nvmrc +1 -0
  11. package/.release-please-manifest.json +3 -0
  12. package/CLAUDE.md +104 -0
  13. package/Dockerfile +23 -0
  14. package/LICENSE +21 -0
  15. package/README.md +120 -0
  16. package/apps/ios/Config/signing.xcconfig +4 -0
  17. package/apps/ios/Package.swift +26 -0
  18. package/apps/ios/Remux.xcodeproj/project.pbxproj +477 -0
  19. package/apps/ios/Remux.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  20. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/Contents.json +23 -0
  21. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png +0 -0
  22. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_120x120.png +0 -0
  23. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_152x152.png +0 -0
  24. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_167x167.png +0 -0
  25. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_180x180.png +0 -0
  26. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_20x20.png +0 -0
  27. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_29x29.png +0 -0
  28. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_40x40.png +0 -0
  29. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_58x58.png +0 -0
  30. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_60x60.png +0 -0
  31. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_76x76.png +0 -0
  32. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_80x80.png +0 -0
  33. package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_87x87.png +0 -0
  34. package/apps/ios/Sources/Remux/Assets.xcassets/Contents.json +6 -0
  35. package/apps/ios/Sources/Remux/Extensions/FaceIDManager.swift +29 -0
  36. package/apps/ios/Sources/Remux/Extensions/InspectCache.swift +66 -0
  37. package/apps/ios/Sources/Remux/MainTabView.swift +32 -0
  38. package/apps/ios/Sources/Remux/Remux.entitlements +8 -0
  39. package/apps/ios/Sources/Remux/RemuxiOSApp.swift +14 -0
  40. package/apps/ios/Sources/Remux/RootView.swift +130 -0
  41. package/apps/ios/Sources/Remux/Views/Control/ControlView.swift +102 -0
  42. package/apps/ios/Sources/Remux/Views/Inspect/InspectView.swift +98 -0
  43. package/apps/ios/Sources/Remux/Views/Live/LiveTerminalView.swift +132 -0
  44. package/apps/ios/Sources/Remux/Views/Now/NowView.swift +173 -0
  45. package/apps/ios/Sources/Remux/Views/Onboarding/ManualConnectView.swift +55 -0
  46. package/apps/ios/Sources/Remux/Views/Onboarding/OnboardingView.swift +70 -0
  47. package/apps/ios/Sources/Remux/Views/Onboarding/QRScannerView.swift +92 -0
  48. package/apps/ios/Sources/Remux/Views/Settings/MeView.swift +136 -0
  49. package/apps/macos/Package.swift +37 -0
  50. package/apps/macos/Resources/shell-integration/bash/bash-preexec.sh +382 -0
  51. package/apps/macos/Resources/shell-integration/bash/ghostty.bash +315 -0
  52. package/apps/macos/Resources/shell-integration/elvish/lib/ghostty-integration.elv +191 -0
  53. package/apps/macos/Resources/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +246 -0
  54. package/apps/macos/Resources/shell-integration/nushell/vendor/autoload/ghostty.nu +110 -0
  55. package/apps/macos/Resources/shell-integration/zsh/.zshenv +61 -0
  56. package/apps/macos/Resources/shell-integration/zsh/ghostty-integration +458 -0
  57. package/apps/macos/Resources/terminfo/67/ghostty +0 -0
  58. package/apps/macos/Resources/terminfo/78/xterm-ghostty +0 -0
  59. package/apps/macos/Sources/Remux/AppDelegate.swift +257 -0
  60. package/apps/macos/Sources/Remux/CrashReporter.swift +210 -0
  61. package/apps/macos/Sources/Remux/FinderIntegration.swift +117 -0
  62. package/apps/macos/Sources/Remux/GhosttyConfig.swift +311 -0
  63. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutAction.swift +115 -0
  64. package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutSettingsView.swift +271 -0
  65. package/apps/macos/Sources/Remux/KeyboardShortcuts/StoredShortcut.swift +149 -0
  66. package/apps/macos/Sources/Remux/MainContentView.swift +308 -0
  67. package/apps/macos/Sources/Remux/MenuBarManager.swift +275 -0
  68. package/apps/macos/Sources/Remux/NotificationManager.swift +145 -0
  69. package/apps/macos/Sources/Remux/PortScanner.swift +152 -0
  70. package/apps/macos/Sources/Remux/RemuxApp.swift +13 -0
  71. package/apps/macos/Sources/Remux/SSHDetector.swift +151 -0
  72. package/apps/macos/Sources/Remux/SessionPersistence.swift +226 -0
  73. package/apps/macos/Sources/Remux/SocketController.swift +258 -0
  74. package/apps/macos/Sources/Remux/UpdateChecker.swift +152 -0
  75. package/apps/macos/Sources/Remux/Views/CommandPalette.swift +198 -0
  76. package/apps/macos/Sources/Remux/Views/ConnectionView.swift +84 -0
  77. package/apps/macos/Sources/Remux/Views/InspectView.swift +127 -0
  78. package/apps/macos/Sources/Remux/Views/SettingsView.swift +77 -0
  79. package/apps/macos/Sources/Remux/Views/Sidebar/SidebarView.swift +410 -0
  80. package/apps/macos/Sources/Remux/Views/SplitTree/BrowserPanel.swift +193 -0
  81. package/apps/macos/Sources/Remux/Views/SplitTree/MarkdownPanel.swift +277 -0
  82. package/apps/macos/Sources/Remux/Views/SplitTree/PanelProtocol.swift +14 -0
  83. package/apps/macos/Sources/Remux/Views/SplitTree/SplitNode.swift +149 -0
  84. package/apps/macos/Sources/Remux/Views/SplitTree/SplitView.swift +234 -0
  85. package/apps/macos/Sources/Remux/Views/SplitTree/TerminalPanel.swift +26 -0
  86. package/apps/macos/Sources/Remux/Views/TabBarView.swift +94 -0
  87. package/apps/macos/Sources/Remux/Views/Terminal/ClipboardHelper.swift +101 -0
  88. package/apps/macos/Sources/Remux/Views/Terminal/CopyModeOverlay.swift +325 -0
  89. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeTerminalView.swift +39 -0
  90. package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeView.swift +559 -0
  91. package/apps/macos/Sources/Remux/Views/Terminal/SurfaceSearchOverlay.swift +109 -0
  92. package/apps/macos/Sources/Remux/Views/Terminal/TerminalContainerView.swift +95 -0
  93. package/apps/macos/Sources/Remux/Views/Terminal/TerminalRelay.swift +117 -0
  94. package/build.mjs +33 -0
  95. package/native/android/DecodeGoldenPayloads.kt +487 -0
  96. package/native/android/ProtocolModels.kt +188 -0
  97. package/native/ios/DecodeGoldenPayloads.swift +711 -0
  98. package/native/ios/ProtocolModels.swift +200 -0
  99. package/package.json +45 -0
  100. package/packages/RemuxKit/Package.swift +27 -0
  101. package/packages/RemuxKit/Sources/RemuxKit/Device/DeviceManager.swift +27 -0
  102. package/packages/RemuxKit/Sources/RemuxKit/Models/ProtocolModels.swift +206 -0
  103. package/packages/RemuxKit/Sources/RemuxKit/Networking/MessageRouter.swift +108 -0
  104. package/packages/RemuxKit/Sources/RemuxKit/Networking/RemuxConnection.swift +395 -0
  105. package/packages/RemuxKit/Sources/RemuxKit/State/RemuxState.swift +188 -0
  106. package/packages/RemuxKit/Sources/RemuxKit/Storage/KeychainStore.swift +142 -0
  107. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyBridge.swift +145 -0
  108. package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyTerminalView.swift +35 -0
  109. package/packages/RemuxKit/Sources/RemuxKit/Terminal/Resources/ghostty-terminal.html +91 -0
  110. package/packages/RemuxKit/Tests/RemuxKitTests/ConnectionIntegrationTest.swift +74 -0
  111. package/packages/RemuxKit/Tests/RemuxKitTests/KeychainStoreTests.swift +81 -0
  112. package/packages/RemuxKit/Tests/RemuxKitTests/ProtocolModelsTests.swift +179 -0
  113. package/packages/RemuxKit/Tests/RemuxKitTests/RemuxStateTests.swift +62 -0
  114. package/playwright.config.ts +17 -0
  115. package/pnpm-lock.yaml +1588 -0
  116. package/pty-daemon.js +303 -0
  117. package/release-please-config.json +14 -0
  118. package/scripts/auto-deploy.sh +46 -0
  119. package/scripts/build-dmg.sh +121 -0
  120. package/scripts/build-ghostty-kit.sh +43 -0
  121. package/scripts/check-active-terminology.mjs +132 -0
  122. package/scripts/setup-ci-secrets.sh +80 -0
  123. package/scripts/sync-ghostty-web.sh +28 -0
  124. package/scripts/upload-testflight.sh +100 -0
  125. package/server.js +7074 -0
  126. package/src/adapters/agent-events.ts +246 -0
  127. package/src/adapters/claude-code.ts +158 -0
  128. package/src/adapters/codex.ts +210 -0
  129. package/src/adapters/generic-shell.ts +58 -0
  130. package/src/adapters/index.ts +15 -0
  131. package/src/adapters/registry.ts +99 -0
  132. package/src/adapters/types.ts +41 -0
  133. package/src/auth.ts +174 -0
  134. package/src/e2ee.ts +236 -0
  135. package/src/git-service.ts +168 -0
  136. package/src/message-buffer.ts +137 -0
  137. package/src/pty-daemon.ts +357 -0
  138. package/src/push.ts +127 -0
  139. package/src/renderers.ts +455 -0
  140. package/src/server.ts +2407 -0
  141. package/src/service.ts +226 -0
  142. package/src/session.ts +978 -0
  143. package/src/store.ts +1422 -0
  144. package/src/team.ts +123 -0
  145. package/src/tunnel.ts +126 -0
  146. package/src/types.d.ts +50 -0
  147. package/src/vt-tracker.ts +188 -0
  148. package/src/workspace-head.ts +144 -0
  149. package/src/workspace.ts +153 -0
  150. package/src/ws-handler.ts +1526 -0
  151. package/start.ps1 +83 -0
  152. package/tests/adapters.test.js +171 -0
  153. package/tests/auth.test.js +243 -0
  154. package/tests/codex-adapter.test.js +535 -0
  155. package/tests/durable-stream.test.js +153 -0
  156. package/tests/e2e/app.spec.js +530 -0
  157. package/tests/e2ee.test.js +325 -0
  158. package/tests/message-buffer.test.js +245 -0
  159. package/tests/message-routing.test.js +305 -0
  160. package/tests/pty-daemon.test.js +346 -0
  161. package/tests/push.test.js +281 -0
  162. package/tests/renderers.test.js +391 -0
  163. package/tests/search-shell.test.js +499 -0
  164. package/tests/server.test.js +882 -0
  165. package/tests/service.test.js +267 -0
  166. package/tests/store.test.js +369 -0
  167. package/tests/tunnel.test.js +67 -0
  168. package/tests/workspace-head.test.js +116 -0
  169. package/tests/workspace.test.js +417 -0
  170. package/tsconfig.backend.json +11 -0
  171. package/tsconfig.json +15 -0
  172. package/tui/client/client_test.go +125 -0
  173. package/tui/client/connection.go +342 -0
  174. package/tui/client/host_manager.go +141 -0
  175. package/tui/config/cache.go +81 -0
  176. package/tui/config/config.go +53 -0
  177. package/tui/config/config_test.go +89 -0
  178. package/tui/go.mod +32 -0
  179. package/tui/go.sum +50 -0
  180. package/tui/main.go +261 -0
  181. package/tui/tests/integration_test.go +283 -0
  182. package/tui/ui/model.go +310 -0
  183. package/vitest.config.js +10 -0
@@ -0,0 +1,458 @@
1
+ # vim:ft=zsh
2
+ #
3
+ # Based on (started as) a copy of Kitty's zsh integration. Kitty is
4
+ # distributed under GPLv3, so this file is also distributed under GPLv3.
5
+ # The license header is reproduced below:
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ #
21
+ # Enables integration between zsh and ghostty.
22
+ #
23
+ # This is an autoloadable function. It's invoked automatically in shells
24
+ # directly spawned by Ghostty but not in any other shells. For example, running
25
+ # `exec zsh`, `sudo -E zsh`, `tmux`, or plain `zsh` will create a shell where
26
+ # ghostty-integration won't automatically run. Zsh users who want integration with
27
+ # Ghostty in all shells should add the following lines to their .zshrc:
28
+ #
29
+ # if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
30
+ # source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration
31
+ # fi
32
+ #
33
+ # Implementation note: We can assume that alias expansion is disabled in this
34
+ # file, so no need to quote defensively. We still have to defensively prefix all
35
+ # builtins with `builtin` to avoid accidentally invoking user-defined functions.
36
+ # We avoid `function` reserved word as an additional defensive measure.
37
+
38
+ # Note that updating options with `builtin emulate -L zsh` affects the global options
39
+ # if it's called outside of a function. So nearly all code has to be in functions.
40
+ _entrypoint() {
41
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
42
+
43
+ [[ -o interactive ]] || builtin return 0 # non-interactive shell
44
+ (( ! $+_ghostty_state )) || builtin return 0 # already initialized
45
+
46
+ # We require zsh 5.1+ (released Sept 2015) for features like functions_source,
47
+ # introspection arrays, and array pattern substitution.
48
+ if ! { builtin autoload -- is-at-least 2>/dev/null && is-at-least 5.1; }; then
49
+ builtin echo "Zsh ${ZSH_VERSION} is too old for ghostty shell integration (5.1+ required)" >&2
50
+ builtin return 1
51
+ fi
52
+
53
+ # 0: no OSC 133 [AC] marks have been written yet.
54
+ # 1: the last written OSC 133 C has not been closed with D yet.
55
+ # 2: none of the above.
56
+ builtin typeset -gi _ghostty_state
57
+
58
+ # Attempt to create a writable file descriptor to the TTY so that we can print
59
+ # to the TTY later even when STDOUT is redirected. This code is fairly subtle.
60
+ #
61
+ # - It's tempting to do `[[ -t 1 ]] && exec {_ghostty_state}>&1` but we cannot do this
62
+ # because it'll create a file descriptor >= 10 without O_CLOEXEC. This file
63
+ # descriptor will leak to child processes.
64
+ # - If we do `exec {3}>&1`, the file descriptor won't leak to the child processes
65
+ # but it'll still leak if the current process is replaced with another. In
66
+ # addition, it'll break user code that relies on fd 3 being available.
67
+ # - Zsh doesn't expose dup3, which would have allowed us to copy STDOUT with
68
+ # O_CLOEXEC. The only way to create a file descriptor with O_CLOEXEC is via
69
+ # sysopen.
70
+ # - `zmodload zsh/system` and `sysopen -o cloexec -wu _ghostty_fd -- /dev/tty` can
71
+ # fail with an error message to STDERR (the latter can happen even if /dev/tty
72
+ # is writable), hence the redirection of STDERR. We do it for the whole block
73
+ # for performance reasons (redirections are slow).
74
+ # - We must open the file descriptor right here rather than in _ghostty_deferred_init
75
+ # because there are broken zsh plugins out there that run `exec {fd}< <(cmd)`
76
+ # and then close the file descriptor more than once while suppressing errors.
77
+ # This could end up closing our file descriptor if we opened it in
78
+ # _ghostty_deferred_init.
79
+ typeset -gi _ghostty_fd
80
+ {
81
+ builtin zmodload zsh/system && (( $+builtins[sysopen] )) && {
82
+ { [[ -w $TTY ]] && builtin sysopen -o cloexec -wu _ghostty_fd -- $TTY } ||
83
+ { [[ -w /dev/tty ]] && builtin sysopen -o cloexec -wu _ghostty_fd -- /dev/tty }
84
+ }
85
+ } 2>/dev/null || (( _ghostty_fd = 1 ))
86
+
87
+ # Defer initialization so that other zsh init files can be configure
88
+ # the integration.
89
+ builtin typeset -ag precmd_functions
90
+ precmd_functions+=(_ghostty_deferred_init)
91
+ }
92
+
93
+ _ghostty_deferred_init() {
94
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
95
+
96
+ # Enable semantic markup with OSC 133.
97
+ _ghostty_precmd() {
98
+ builtin local -i cmd_status=$?
99
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
100
+
101
+ # Don't write OSC 133 D when our precmd handler is invoked from zle.
102
+ # Some plugins do that to update prompt on cd.
103
+ if ! builtin zle; then
104
+ # This code works incorrectly in the presence of a precmd or chpwd
105
+ # hook that prints. For example, sindresorhus/pure prints an empty
106
+ # line on precmd and marlonrichert/zsh-snap prints $PWD on chpwd.
107
+ # We'll end up writing our OSC 133 D mark too late.
108
+ #
109
+ # Another failure mode is when the output of a command doesn't end
110
+ # with LF and prompst_sp is set (it is by default). In this case
111
+ # we'll incorrectly state that '%' from prompt_sp is a part of the
112
+ # command's output.
113
+ if (( _ghostty_state == 1 )); then
114
+ # The last written OSC 133 C has not been closed with D yet.
115
+ # Close it and supply status.
116
+ builtin print -nu $_ghostty_fd '\e]133;D;'$cmd_status'\a'
117
+ (( _ghostty_state = 2 ))
118
+ elif (( _ghostty_state == 2 )); then
119
+ # There might be an unclosed OSC 133 C. Close that.
120
+ builtin print -nu $_ghostty_fd '\e]133;D\a'
121
+ fi
122
+ fi
123
+
124
+ builtin local mark1=$'%{\e]133;A;cl=line\a%}'
125
+ if [[ -o prompt_percent ]]; then
126
+ builtin typeset -g precmd_functions
127
+ if [[ ${precmd_functions[-1]} == _ghostty_precmd ]]; then
128
+ # This is the best case for us: we can add our marks to PS1 and
129
+ # PS2. This way our marks will be printed whenever zsh
130
+ # redisplays prompt: on reset-prompt, on SIGWINCH, and on
131
+ # SIGCHLD if notify is set. Themes that update prompt
132
+ # asynchronously from a `zle -F` handler might still remove our
133
+ # marks. Oh well.
134
+
135
+ # Restore PS1/PS2 to their pre-mark state if nothing else has
136
+ # modified them since we last added marks. This avoids exposing
137
+ # PS1 with our marks to other hooks (which can break themes like
138
+ # Pure that use pattern matching to strip/rebuild the prompt).
139
+ # If PS1 was modified (by a theme, async update, etc.), we
140
+ # keep the modified version, prioritizing the theme's changes.
141
+ builtin local ps1_changed=0
142
+ if [[ -n ${_ghostty_saved_ps1+x} ]]; then
143
+ if [[ $PS1 == $_ghostty_marked_ps1 ]]; then
144
+ PS1=$_ghostty_saved_ps1
145
+ PS2=$_ghostty_saved_ps2
146
+ elif [[ $PS1 != $_ghostty_saved_ps1 ]]; then
147
+ ps1_changed=1
148
+ fi
149
+ fi
150
+
151
+ # Save the clean PS1/PS2 before we add marks.
152
+ _ghostty_saved_ps1=$PS1
153
+ _ghostty_saved_ps2=$PS2
154
+
155
+ # Add our marks. Since we always start from a clean PS1
156
+ # (either restored above or freshly set by a theme), we can
157
+ # unconditionally add mark1 and markB.
158
+ builtin local mark2=$'%{\e]133;A;k=s\a%}'
159
+ builtin local markB=$'%{\e]133;B\a%}'
160
+ PS1=${mark1}${PS1}${markB}
161
+
162
+ # Handle multiline prompts by marking newline-separated
163
+ # continuation lines with k=s (mark2). We skip the newline
164
+ # immediately after mark1 to avoid introducing a double
165
+ # newline due to OSC 133;A's fresh-line behavior.
166
+ #
167
+ # We skip this when PS1 changed because injecting marks into
168
+ # newlines can break pattern matching in themes that
169
+ # strip/rebuild the prompt dynamically (e.g., Pure).
170
+ if (( ! ps1_changed )) && [[ $PS1 == *$'\n'* ]]; then
171
+ if [[ $PS1 == ${mark1}$'\n'* ]]; then
172
+ builtin local rest=${PS1#${mark1}$'\n'}
173
+ if [[ $rest == *$'\n'* ]]; then
174
+ PS1=${mark1}$'\n'${rest//$'\n'/$'\n'${mark2}}
175
+ fi
176
+ else
177
+ PS1=${PS1//$'\n'/$'\n'${mark2}}
178
+ fi
179
+ fi
180
+
181
+ # PS2 mark is needed when clearing the prompt on resize
182
+ PS2=${mark2}${PS2}${markB}
183
+
184
+ # Save the marked PS1 so we can detect modifications
185
+ # by other hooks in the next cycle.
186
+ _ghostty_marked_ps1=$PS1
187
+ (( _ghostty_state = 2 ))
188
+ else
189
+ # If our precmd hook is not the last, we cannot rely on prompt
190
+ # changes to stick, so we don't even try. At least we can move
191
+ # our hook to the end to have better luck next time. If there is
192
+ # another piece of code that wants to take this privileged
193
+ # position, this won't work well. We'll break them as much as
194
+ # they are breaking us.
195
+ precmd_functions=(${precmd_functions:#_ghostty_precmd} _ghostty_precmd)
196
+ # Plugins that invoke precmd hooks from zle do that before zle
197
+ # is trashed. This means that the cursor is in the middle of
198
+ # BUFFER and we cannot print our mark there. Prompt might
199
+ # already have a mark, so the following reset-prompt will write
200
+ # it. If it doesn't, there is nothing we can do.
201
+ if ! builtin zle; then
202
+ builtin print -rnu $_ghostty_fd -- $mark1[3,-3]
203
+ (( _ghostty_state = 2 ))
204
+ fi
205
+ fi
206
+ elif ! builtin zle; then
207
+ # Without prompt_percent we cannot patch prompt. Just print the
208
+ # mark, except when we are invoked from zle. In the latter case we
209
+ # cannot do anything.
210
+ builtin print -rnu $_ghostty_fd -- $mark1[3,-3]
211
+ (( _ghostty_state = 2 ))
212
+ fi
213
+ }
214
+
215
+ _ghostty_preexec() {
216
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
217
+
218
+ # Restore the original PS1/PS2 if nothing else has modified them
219
+ # since our precmd added marks. This ensures other preexec hooks
220
+ # see a clean PS1 without our marks. If PS1 was modified (e.g.,
221
+ # by an async theme update), we leave it alone.
222
+ if [[ -n ${_ghostty_saved_ps1+x} && $PS1 == $_ghostty_marked_ps1 ]]; then
223
+ PS1=$_ghostty_saved_ps1
224
+ PS2=$_ghostty_saved_ps2
225
+ fi
226
+
227
+ # This will work incorrectly in the presence of a preexec hook that
228
+ # prints. For example, if MichaelAquilina/zsh-you-should-use installs
229
+ # its preexec hook before us, we'll incorrectly mark its output as
230
+ # belonging to the command (as if the user typed it into zle) rather
231
+ # than command output.
232
+ builtin print -nu $_ghostty_fd '\e]133;C\a'
233
+ (( _ghostty_state = 1 ))
234
+ }
235
+
236
+ # Enable reporting current working dir to terminal. Ghostty supports
237
+ # the kitty-shell-cwd format.
238
+ _ghostty_report_pwd() { builtin print -nu $_ghostty_fd '\e]7;kitty-shell-cwd://'"$HOST""$PWD"'\a'; }
239
+ chpwd_functions=(${chpwd_functions[@]} "_ghostty_report_pwd")
240
+ # An executed program could change cwd and report the changed cwd, so also report cwd at each new prompt
241
+ # as in this case chpwd_functions is insufficient. chpwd_functions is still needed for things like: cd x && something
242
+ functions[_ghostty_precmd]+="
243
+ _ghostty_report_pwd"
244
+ _ghostty_report_pwd
245
+
246
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
247
+ # Enable terminal title changes, formatted for user-friendly display.
248
+ functions[_ghostty_precmd]+="
249
+ builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
250
+ functions[_ghostty_preexec]+="
251
+ builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${1//[[:cntrl:]]}\"\$'\\a'"
252
+ fi
253
+
254
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
255
+ # Enable cursor shape changes depending on the current keymap.
256
+ # This implementation leaks blinking block cursor into external commands
257
+ # executed from zle. For example, users of fzf-based widgets may find
258
+ # themselves with a blinking block cursor within fzf.
259
+ _ghostty_zle_line_init _ghostty_zle_line_finish _ghostty_zle_keymap_select() {
260
+ builtin local steady=0
261
+ [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor:steady"* ]] && steady=1
262
+ case ${KEYMAP-} in
263
+ vicmd|visual) builtin print -nu "$_ghostty_fd" "\e[$(( 1 + steady )) q" ;; # block
264
+ *) builtin print -nu "$_ghostty_fd" "\e[$(( 5 + steady )) q" ;; # bar
265
+ esac
266
+ }
267
+ # Restore the default shape before executing an external command
268
+ functions[_ghostty_preexec]+="
269
+ builtin print -rnu $_ghostty_fd \$'\\e[0 q'"
270
+ fi
271
+
272
+ # Emit semantic prompt markers at line-init if PS1 doesn't contain our
273
+ # marks. This ensures the terminal sees prompt markers even if another
274
+ # plugin (like zinit or oh-my-posh) regenerated PS1 after our precmd ran.
275
+ # We use 133;P instead of 133;A to avoid fresh-line behavior which would
276
+ # disrupt the display since the prompt has already been drawn. We also
277
+ # emit 133;B to mark the input area, which is needed for click-to-move.
278
+ (( $+functions[_ghostty_zle_line_init] )) || _ghostty_zle_line_init() { builtin true; }
279
+ functions[_ghostty_zle_line_init]="
280
+ if [[ \$PS1 != *$'%{\\e]133;A'* ]]; then
281
+ builtin print -nu \$_ghostty_fd '\\e]133;P;k=i\\a\\e]133;B\\a'
282
+ fi
283
+ "${functions[_ghostty_zle_line_init]}
284
+
285
+ # Add Ghostty binary to PATH if the path feature is enabled
286
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* ]] && [[ -n "$GHOSTTY_BIN_DIR" ]]; then
287
+ if [[ ":$PATH:" != *":$GHOSTTY_BIN_DIR:"* ]]; then
288
+ builtin export PATH="$PATH:$GHOSTTY_BIN_DIR"
289
+ fi
290
+ fi
291
+
292
+ # Sudo
293
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* ]] && [[ -n "$TERMINFO" ]]; then
294
+ # Wrap `sudo` command to ensure Ghostty terminfo is preserved
295
+ function sudo() {
296
+ builtin local sudo_has_sudoedit_flags="no"
297
+ for arg in "$@"; do
298
+ # Check if argument is '-e' or '--edit' (sudoedit flags)
299
+ if [[ "$arg" == "-e" || $arg == "--edit" ]]; then
300
+ sudo_has_sudoedit_flags="yes"
301
+ builtin break
302
+ fi
303
+ # Check if argument is neither an option nor a key-value pair
304
+ if [[ "$arg" != -* && "$arg" != *=* ]]; then
305
+ builtin break
306
+ fi
307
+ done
308
+ if [[ "$sudo_has_sudoedit_flags" == "yes" ]]; then
309
+ builtin command sudo "$@";
310
+ else
311
+ builtin command sudo --preserve-env=TERMINFO "$@";
312
+ fi
313
+ }
314
+ fi
315
+
316
+ # SSH Integration
317
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-* ]]; then
318
+ function ssh() {
319
+ emulate -L zsh
320
+ setopt local_options no_glob_subst
321
+
322
+ local ssh_term ssh_opts
323
+ ssh_term="xterm-256color"
324
+ ssh_opts=()
325
+
326
+ # Configure environment variables for remote session
327
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-env* ]]; then
328
+ ssh_opts+=(-o "SetEnv COLORTERM=truecolor")
329
+ ssh_opts+=(-o "SendEnv TERM_PROGRAM TERM_PROGRAM_VERSION")
330
+ fi
331
+
332
+ # Install terminfo on remote host if needed
333
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-terminfo* ]]; then
334
+ local ssh_user ssh_hostname
335
+
336
+ while IFS=' ' read -r ssh_key ssh_value; do
337
+ case "$ssh_key" in
338
+ user) ssh_user="$ssh_value" ;;
339
+ hostname) ssh_hostname="$ssh_value" ;;
340
+ esac
341
+ [[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
342
+ done < <(command ssh -G "$@" 2>/dev/null)
343
+
344
+ if [[ -n "$ssh_hostname" ]]; then
345
+ local ssh_target="${ssh_user}@${ssh_hostname}"
346
+
347
+ # Check if terminfo is already cached
348
+ if "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --host="$ssh_target" >/dev/null 2>&1; then
349
+ ssh_term="xterm-ghostty"
350
+ elif (( $+commands[infocmp] )); then
351
+ local ssh_terminfo ssh_cpath_dir ssh_cpath
352
+
353
+ ssh_terminfo=$(infocmp -0 -x xterm-ghostty 2>/dev/null)
354
+
355
+ if [[ -n "$ssh_terminfo" ]]; then
356
+ print "Setting up xterm-ghostty terminfo on $ssh_hostname..." >&2
357
+
358
+ ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
359
+ ssh_cpath="$ssh_cpath_dir/socket"
360
+
361
+ if builtin print -r "$ssh_terminfo" | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
362
+ infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
363
+ command -v tic >/dev/null 2>&1 || exit 1
364
+ mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
365
+ exit 1
366
+ ' 2>/dev/null; then
367
+ ssh_term="xterm-ghostty"
368
+ ssh_opts+=(-o "ControlPath=$ssh_cpath")
369
+
370
+ # Cache successful installation
371
+ "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
372
+ else
373
+ print "Warning: Failed to install terminfo." >&2
374
+ fi
375
+ else
376
+ print "Warning: Could not generate terminfo data." >&2
377
+ fi
378
+ else
379
+ print "Warning: ghostty command not available for cache management." >&2
380
+ fi
381
+ fi
382
+ fi
383
+
384
+ # Execute SSH with TERM environment variable
385
+ TERM="$ssh_term" command ssh "${ssh_opts[@]}" "$@"
386
+ }
387
+ fi
388
+
389
+ # Some zsh users manually run `source ~/.zshrc` in order to apply rc file
390
+ # changes to the current shell. This is a terrible practice that breaks many
391
+ # things, including our shell integration. For example, Oh My Zsh and Prezto
392
+ # (both very popular among zsh users) will remove zle-line-init and
393
+ # zle-line-finish hooks if .zshrc is manually sourced. Prezto will also remove
394
+ # zle-keymap-select.
395
+ #
396
+ # Another common (and much more robust) way to apply rc file changes to the
397
+ # current shell is `exec zsh`. This will remove our integration from the shell
398
+ # unless it's explicitly invoked from .zshrc. This is not an issue with
399
+ # `exec zsh` but rather with our implementation of automatic shell integration.
400
+
401
+ # In the ideal world we would use add-zle-hook-widget to hook zle-line-init
402
+ # and similar widget. This breaks user configs though, so we have do this
403
+ # horrible thing instead.
404
+ builtin local hook func widget orig_widget flag
405
+ for hook in line-init line-finish keymap-select; do
406
+ func=_ghostty_zle_${hook/-/_}
407
+ (( $+functions[$func] )) || builtin continue
408
+ widget=zle-$hook
409
+ if [[ $widgets[$widget] == user:azhw:* &&
410
+ $+functions[add-zle-hook-widget] -eq 1 ]]; then
411
+ # If the widget is already hooked by add-zle-hook-widget at the top
412
+ # level, add our hook at the end. We MUST do it this way. We cannot
413
+ # just wrap the widget ourselves in this case because it would
414
+ # trigger bugs in add-zle-hook-widget.
415
+ add-zle-hook-widget $hook $func
416
+ else
417
+ if (( $+widgets[$widget] )); then
418
+ # There is a widget but it's not from add-zle-hook-widget. We
419
+ # can rename the original widget, install our own and invoke
420
+ # the original when we are called.
421
+ #
422
+ # Note: The leading dot is to work around bugs in
423
+ # zsh-syntax-highlighting.
424
+ orig_widget=._ghostty_orig_$widget
425
+ builtin zle -A $widget $orig_widget
426
+ if [[ $widgets[$widget] == user:* ]]; then
427
+ # No -w here to preserve $WIDGET within the original widget.
428
+ flag=
429
+ else
430
+ flag=w
431
+ fi
432
+ functions[$func]+="
433
+ builtin zle $orig_widget -N$flag -- \"\$@\""
434
+ fi
435
+ builtin zle -N $widget $func
436
+ fi
437
+ done
438
+
439
+ if (( $+functions[_ghostty_preexec] )); then
440
+ builtin typeset -ag preexec_functions
441
+ preexec_functions+=(_ghostty_preexec)
442
+ fi
443
+
444
+ builtin typeset -ag precmd_functions
445
+ if (( $+functions[_ghostty_precmd] )); then
446
+ precmd_functions=(${precmd_functions:#_ghostty_deferred_init} _ghostty_precmd)
447
+ _ghostty_precmd
448
+ else
449
+ precmd_functions=(${precmd_functions:#_ghostty_deferred_init})
450
+ fi
451
+
452
+ # Unfunction _ghostty_deferred_init to save memory. Don't unfunction
453
+ # ghostty-integration though because decent public functions aren't supposed to
454
+ # to unfunction themselves when invoked. Unfunctioning is done by calling code.
455
+ builtin unfunction _ghostty_deferred_init
456
+ }
457
+
458
+ _entrypoint