Sfoglia il codice sorgente

allow custom mappings for all textarea actions via config

Sebastian Herrlinger 4 mesi fa
parent
commit
e271852bc3

+ 32 - 10
bun.lock

@@ -243,8 +243,8 @@
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/util": "workspace:*",
         "@opencode-ai/util": "workspace:*",
         "@openrouter/ai-sdk-provider": "1.5.2",
         "@openrouter/ai-sdk-provider": "1.5.2",
-        "@opentui/core": "0.1.60",
-        "@opentui/solid": "0.1.60",
+        "@opentui/core": "0.0.0-20251211-4403a69a",
+        "@opentui/solid": "0.0.0-20251211-4403a69a",
         "@parcel/watcher": "2.5.1",
         "@parcel/watcher": "2.5.1",
         "@pierre/precision-diffs": "catalog:",
         "@pierre/precision-diffs": "catalog:",
         "@solid-primitives/event-bus": "1.1.2",
         "@solid-primitives/event-bus": "1.1.2",
@@ -1148,21 +1148,21 @@
 
 
     "@opentelemetry/api": ["@opentelemetry/[email protected]", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
     "@opentelemetry/api": ["@opentelemetry/[email protected]", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
 
 
-    "@opentui/core": ["@opentui/core@0.1.60", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.60", "@opentui/core-darwin-x64": "0.1.60", "@opentui/core-linux-arm64": "0.1.60", "@opentui/core-linux-x64": "0.1.60", "@opentui/core-win32-arm64": "0.1.60", "@opentui/core-win32-x64": "0.1.60", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-28jphd0AJo48uvEuKXcT9pJhgAu8I2rEJhPt25cc5ipJ2iw/eDk1uoxrbID80MPDqgOEzN21vXmzXwCd6ao+hg=="],
+    "@opentui/core": ["@opentui/core@0.0.0-20251211-4403a69a", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20251211-4403a69a", "@opentui/core-darwin-x64": "0.0.0-20251211-4403a69a", "@opentui/core-linux-arm64": "0.0.0-20251211-4403a69a", "@opentui/core-linux-x64": "0.0.0-20251211-4403a69a", "@opentui/core-win32-arm64": "0.0.0-20251211-4403a69a", "@opentui/core-win32-x64": "0.0.0-20251211-4403a69a", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-wTZKcokyU9yiDqyC0Pvf9eRSdT73s4Ynerkit/z8Af++tynqrTlZHZCXK3o42Ff7itCSILmijcTU94n69aEypA=="],
 
 
-    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.60", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N4feqnOBDA4O4yocpat5vOiV06HqJVwJGx8rEZE9DiOtl1i+1cPQ1Lx6+zWdLhbrVBJ0ENhb7Azox8sXkm/+5Q=="],
+    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20251211-4403a69a", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VAYjTa+Eiauy8gETXadD8y0PE6ppnKasDK1X354VoexZiWFR3r7rkL+TfDfk7whhqXDYyT44JDT1QmCAhVXRzQ=="],
 
 
-    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.60", "", { "os": "darwin", "cpu": "x64" }, "sha512-+z3q4WaoIs7ANU8+eTFlvnfCjAS81rk81TOdZm4TJ53Ti3/B+yheWtnV/mLpLLhvZDz2VUVxxRmfDrGMnJb4fQ=="],
+    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20251211-4403a69a", "", { "os": "darwin", "cpu": "x64" }, "sha512-n9oVMpsojlILj1soORZzZ2Mjh8Zl73ZNcY7ot0iRmOjBDccrjDTsqKfxoGjKNd/xJSphLeu1LYGlcI5O5OczWQ=="],
 
 
-    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.60", "", { "os": "linux", "cpu": "arm64" }, "sha512-/Q65sjqVGB9ygJ6lStI8n1X6RyfmJZC8XofRGEuFiMLiWcWC/xoBtztdL8LAIvHQy42y2+pl9zIiW0fWSQ0wjw=="],
+    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20251211-4403a69a", "", { "os": "linux", "cpu": "arm64" }, "sha512-vf4eUjPMI4ANitK4MpTGenZFddKgQD/K21aN6cZjusnH3mTEJAoIR7GbNtMdz3qclU43ajpzTID9sAwhshwdVQ=="],
 
 
-    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.60", "", { "os": "linux", "cpu": "x64" }, "sha512-AegF+g7OguIpjZKN+PS55sc3ZFY6fj+fLwfETbSRGw6NqX+aiwpae0Y3gXX1s298Yq5yQEzMXnARTCJTGH4uzg=="],
+    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20251211-4403a69a", "", { "os": "linux", "cpu": "x64" }, "sha512-61635Up0YvVJ8gZ2eMiL1c8OfA+U6wAzT++LoaurNjbmsUAlKHws6MZdqTLw7aspJJVGsRFbA6d1Y+gXFxbDrQ=="],
 
 
-    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.60", "", { "os": "win32", "cpu": "arm64" }, "sha512-fbkq8MOZJgT3r9q3JWqsfVxRpQ1SlbmhmvB35BzukXnZBK8eA178wbSadGH6irMDrkSIYye9WYddHI/iXjmgVQ=="],
+    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20251211-4403a69a", "", { "os": "win32", "cpu": "arm64" }, "sha512-3lUddTJGKZ6uU388eU79MY//IEbgGENCITetDrrRp7v9L1AxMntE1ihf6HniziwBvKKJcsUfqLiJWcq0WPZw2w=="],
 
 
-    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.60", "", { "os": "win32", "cpu": "x64" }, "sha512-OebCL7f9+CKodBw0G+NvKIcc74bl6/sBEHfb73cACdJDJKh+T3C3Vt9H3kQQ0m1C8wRAqX6rh706OArk1pUb2A=="],
+    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20251211-4403a69a", "", { "os": "win32", "cpu": "x64" }, "sha512-Xwc1gqYsn8UZNTzNKkigZozAhBNBGbfX2B/I/aSbyqL0h8+XIInOodI0urzJWc0B6aEv/IDiT6Rm3coXFikLIg=="],
 
 
-    "@opentui/solid": ["@opentui/solid@0.1.60", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.60", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-pn91stzAHNGWaNL6h39q55bq3G1/DLqxKtT3wVsRAV68dHfPpwmqikX1nEJZK8OU84ZTPS9Ly9fz8po2Mot2uQ=="],
+    "@opentui/solid": ["@opentui/solid@0.0.0-20251211-4403a69a", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20251211-4403a69a", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-vuLppAdd1Qgaqhie3q2TuEr+8udjT4d8uVg5arvCe1AUDVs19I8kvadVCfzGUVmtXgFIOEakbiv6AxDq5v9Zig=="],
 
 
     "@oslojs/asn1": ["@oslojs/[email protected]", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
     "@oslojs/asn1": ["@oslojs/[email protected]", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
 
 
@@ -4278,6 +4278,10 @@
 
 
     "openid-client/jose": ["[email protected]", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
     "openid-client/jose": ["[email protected]", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
 
 
+    "opentui-spinner/@opentui/core": ["@opentui/[email protected]", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.60", "@opentui/core-darwin-x64": "0.1.60", "@opentui/core-linux-arm64": "0.1.60", "@opentui/core-linux-x64": "0.1.60", "@opentui/core-win32-arm64": "0.1.60", "@opentui/core-win32-x64": "0.1.60", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-28jphd0AJo48uvEuKXcT9pJhgAu8I2rEJhPt25cc5ipJ2iw/eDk1uoxrbID80MPDqgOEzN21vXmzXwCd6ao+hg=="],
+
+    "opentui-spinner/@opentui/solid": ["@opentui/[email protected]", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.60", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-pn91stzAHNGWaNL6h39q55bq3G1/DLqxKtT3wVsRAV68dHfPpwmqikX1nEJZK8OU84ZTPS9Ly9fz8po2Mot2uQ=="],
+
     "p-locate/p-limit": ["[email protected]", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
     "p-locate/p-limit": ["[email protected]", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
 
 
     "parse-bmfont-xml/xml2js": ["[email protected]", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
     "parse-bmfont-xml/xml2js": ["[email protected]", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
@@ -4854,6 +4858,22 @@
 
 
     "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["[email protected]", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="],
     "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["[email protected]", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="],
 
 
+    "opentui-spinner/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N4feqnOBDA4O4yocpat5vOiV06HqJVwJGx8rEZE9DiOtl1i+1cPQ1Lx6+zWdLhbrVBJ0ENhb7Azox8sXkm/+5Q=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-darwin-x64": ["@opentui/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-+z3q4WaoIs7ANU8+eTFlvnfCjAS81rk81TOdZm4TJ53Ti3/B+yheWtnV/mLpLLhvZDz2VUVxxRmfDrGMnJb4fQ=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-linux-arm64": ["@opentui/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-/Q65sjqVGB9ygJ6lStI8n1X6RyfmJZC8XofRGEuFiMLiWcWC/xoBtztdL8LAIvHQy42y2+pl9zIiW0fWSQ0wjw=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-linux-x64": ["@opentui/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-AegF+g7OguIpjZKN+PS55sc3ZFY6fj+fLwfETbSRGw6NqX+aiwpae0Y3gXX1s298Yq5yQEzMXnARTCJTGH4uzg=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-win32-arm64": ["@opentui/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-fbkq8MOZJgT3r9q3JWqsfVxRpQ1SlbmhmvB35BzukXnZBK8eA178wbSadGH6irMDrkSIYye9WYddHI/iXjmgVQ=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-win32-x64": ["@opentui/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-OebCL7f9+CKodBw0G+NvKIcc74bl6/sBEHfb73cACdJDJKh+T3C3Vt9H3kQQ0m1C8wRAqX6rh706OArk1pUb2A=="],
+
+    "opentui-spinner/@opentui/solid/@babel/core": ["@babel/[email protected]", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
+
+    "opentui-spinner/@opentui/solid/babel-preset-solid": ["[email protected]", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.1" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.8" }, "optionalPeers": ["solid-js"] }, "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw=="],
+
     "parse-bmfont-xml/xml2js/sax": ["[email protected]", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="],
     "parse-bmfont-xml/xml2js/sax": ["[email protected]", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="],
 
 
     "pkg-up/find-up/locate-path": ["[email protected]", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
     "pkg-up/find-up/locate-path": ["[email protected]", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
@@ -5032,6 +5052,8 @@
 
 
     "opencontrol/@modelcontextprotocol/sdk/raw-body/http-errors": ["[email protected]", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
     "opencontrol/@modelcontextprotocol/sdk/raw-body/http-errors": ["[email protected]", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
 
 
+    "opentui-spinner/@opentui/solid/@babel/core/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
     "pkg-up/find-up/locate-path/p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
     "pkg-up/find-up/locate-path/p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
 
 
     "pkg-up/find-up/locate-path/path-exists": ["[email protected]", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
     "pkg-up/find-up/locate-path/path-exists": ["[email protected]", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],

+ 2 - 2
packages/opencode/package.json

@@ -71,8 +71,8 @@
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/util": "workspace:*",
     "@opencode-ai/util": "workspace:*",
     "@openrouter/ai-sdk-provider": "1.5.2",
     "@openrouter/ai-sdk-provider": "1.5.2",
-    "@opentui/core": "0.1.60",
-    "@opentui/solid": "0.1.60",
+    "@opentui/core": "0.0.0-20251211-4403a69a",
+    "@opentui/solid": "0.0.0-20251211-4403a69a",
     "@parcel/watcher": "2.5.1",
     "@parcel/watcher": "2.5.1",
     "@pierre/precision-diffs": "catalog:",
     "@pierre/precision-diffs": "catalog:",
     "@solid-primitives/event-bus": "1.1.2",
     "@solid-primitives/event-bus": "1.1.2",

+ 56 - 16
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

@@ -46,6 +46,60 @@ export type PromptRef = {
 
 
 const PLACEHOLDERS = ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"]
 const PLACEHOLDERS = ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"]
 
 
+const TEXTAREA_ACTIONS = [
+  "submit",
+  "newline",
+  "move-left",
+  "move-right",
+  "move-up",
+  "move-down",
+  "select-left",
+  "select-right",
+  "select-up",
+  "select-down",
+  "line-home",
+  "line-end",
+  "select-line-home",
+  "select-line-end",
+  "visual-line-home",
+  "visual-line-end",
+  "select-visual-line-home",
+  "select-visual-line-end",
+  "buffer-home",
+  "buffer-end",
+  "select-buffer-home",
+  "select-buffer-end",
+  "delete-line",
+  "delete-to-line-end",
+  "delete-to-line-start",
+  "backspace",
+  "delete",
+  "undo",
+  "redo",
+  "word-forward",
+  "word-backward",
+  "select-word-forward",
+  "select-word-backward",
+  "delete-word-forward",
+  "delete-word-backward",
+] as const
+
+function mapTextareaKeybindings(
+  keybinds: Record<string, { ctrl: boolean; meta: boolean; shift: boolean; leader: boolean; name: string }[]>,
+  action: (typeof TEXTAREA_ACTIONS)[number],
+): KeyBinding[] {
+  const configKey = `input_${action.replace(/-/g, "_")}`
+  const bindings = keybinds[configKey]
+  if (!bindings) return []
+  return bindings.map((binding) => ({
+    name: binding.name,
+    ctrl: binding.ctrl || undefined,
+    meta: binding.meta || undefined,
+    shift: binding.shift || undefined,
+    action,
+  }))
+}
+
 export function Prompt(props: PromptProps) {
 export function Prompt(props: PromptProps) {
   let input: TextareaRenderable
   let input: TextareaRenderable
   let anchor: BoxRenderable
   let anchor: BoxRenderable
@@ -76,26 +130,12 @@ export function Prompt(props: PromptProps) {
   }
   }
 
 
   const textareaKeybindings = createMemo(() => {
   const textareaKeybindings = createMemo(() => {
-    const newlineBindings = keybind.all.input_newline || []
-    const submitBindings = keybind.all.input_submit || []
+    const keybinds = keybind.all
 
 
     return [
     return [
       { name: "return", action: "submit" },
       { name: "return", action: "submit" },
       { name: "return", meta: true, action: "newline" },
       { name: "return", meta: true, action: "newline" },
-      ...newlineBindings.map((binding) => ({
-        name: binding.name,
-        ctrl: binding.ctrl || undefined,
-        meta: binding.meta || undefined,
-        shift: binding.shift || undefined,
-        action: "newline" as const,
-      })),
-      ...submitBindings.map((binding) => ({
-        name: binding.name,
-        ctrl: binding.ctrl || undefined,
-        meta: binding.meta || undefined,
-        shift: binding.shift || undefined,
-        action: "submit" as const,
-      })),
+      ...TEXTAREA_ACTIONS.flatMap((action) => mapTextareaKeybindings(keybinds, action)),
     ] satisfies KeyBinding[]
     ] satisfies KeyBinding[]
   })
   })
 
 

+ 62 - 1
packages/opencode/src/config/config.ts

@@ -466,7 +466,68 @@ export namespace Config {
       input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
       input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
       input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
       input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
       input_submit: z.string().optional().default("return").describe("Submit input"),
       input_submit: z.string().optional().default("return").describe("Submit input"),
-      input_newline: z.string().optional().default("shift+return,ctrl+j").describe("Insert newline in input"),
+      input_newline: z
+        .string()
+        .optional()
+        .default("shift+return,ctrl+return,alt+return,ctrl+j")
+        .describe("Insert newline in input"),
+      input_move_left: z.string().optional().default("left,ctrl+b").describe("Move cursor left in input"),
+      input_move_right: z.string().optional().default("right,ctrl+f").describe("Move cursor right in input"),
+      input_move_up: z.string().optional().default("up").describe("Move cursor up in input"),
+      input_move_down: z.string().optional().default("down").describe("Move cursor down in input"),
+      input_select_left: z.string().optional().default("shift+left").describe("Select left in input"),
+      input_select_right: z.string().optional().default("shift+right").describe("Select right in input"),
+      input_select_up: z.string().optional().default("shift+up").describe("Select up in input"),
+      input_select_down: z.string().optional().default("shift+down").describe("Select down in input"),
+      input_line_home: z.string().optional().default("ctrl+a").describe("Move to start of line in input"),
+      input_line_end: z.string().optional().default("ctrl+e").describe("Move to end of line in input"),
+      input_select_line_home: z.string().optional().default("none").describe("Select to start of line in input"),
+      input_select_line_end: z.string().optional().default("none").describe("Select to end of line in input"),
+      input_visual_line_home: z.string().optional().default("none").describe("Move to start of visual line in input"),
+      input_visual_line_end: z.string().optional().default("none").describe("Move to end of visual line in input"),
+      input_select_visual_line_home: z
+        .string()
+        .optional()
+        .default("none")
+        .describe("Select to start of visual line in input"),
+      input_select_visual_line_end: z
+        .string()
+        .optional()
+        .default("none")
+        .describe("Select to end of visual line in input"),
+      input_buffer_home: z.string().optional().default("home").describe("Move to start of buffer in input"),
+      input_buffer_end: z.string().optional().default("end").describe("Move to end of buffer in input"),
+      input_select_buffer_home: z
+        .string()
+        .optional()
+        .default("shift+home")
+        .describe("Select to start of buffer in input"),
+      input_select_buffer_end: z.string().optional().default("shift+end").describe("Select to end of buffer in input"),
+      input_delete_line: z.string().optional().default("alt+d").describe("Delete line in input"),
+      input_delete_to_line_end: z.string().optional().default("ctrl+k").describe("Delete to end of line in input"),
+      input_delete_to_line_start: z.string().optional().default("ctrl+u").describe("Delete to start of line in input"),
+      input_backspace: z.string().optional().default("backspace,shift+backspace").describe("Backspace in input"),
+      input_delete: z.string().optional().default("delete,shift+delete").describe("Delete character in input"),
+      input_undo: z.string().optional().default("ctrl+-,cmd+z").describe("Undo in input"),
+      input_redo: z.string().optional().default("ctrl+.,cmd+shift+z").describe("Redo in input"),
+      input_word_forward: z.string().optional().default("alt+f,alt+right").describe("Move word forward in input"),
+      input_word_backward: z.string().optional().default("alt+b,alt+left").describe("Move word backward in input"),
+      input_select_word_forward: z
+        .string()
+        .optional()
+        .default("alt+shift+f,alt+shift+right")
+        .describe("Select word forward in input"),
+      input_select_word_backward: z
+        .string()
+        .optional()
+        .default("alt+shift+b,alt+shift+left")
+        .describe("Select word backward in input"),
+      input_delete_word_forward: z.string().optional().default("ctrl+d").describe("Delete word forward in input"),
+      input_delete_word_backward: z
+        .string()
+        .optional()
+        .default("ctrl+w,alt+backspace")
+        .describe("Delete word backward in input"),
       history_previous: z.string().optional().default("up").describe("Previous history item"),
       history_previous: z.string().optional().default("up").describe("Previous history item"),
       history_next: z.string().optional().default("down").describe("Next history item"),
       history_next: z.string().optional().default("down").describe("Next history item"),
       session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"),
       session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"),

+ 37 - 3
packages/web/src/content/docs/keybinds.mdx

@@ -46,10 +46,44 @@ OpenCode has a list of keybinds that you can customize through the OpenCode conf
     "agent_cycle_reverse": "shift+tab",
     "agent_cycle_reverse": "shift+tab",
     "input_clear": "ctrl+c",
     "input_clear": "ctrl+c",
     "input_paste": "ctrl+v",
     "input_paste": "ctrl+v",
-    "input_submit": "enter",
-    "input_newline": "shift+enter,ctrl+j",
+    "input_submit": "return",
+    "input_newline": "shift+return,ctrl+return,alt+return,ctrl+j",
+    "input_move_left": "left,ctrl+b",
+    "input_move_right": "right,ctrl+f",
+    "input_move_up": "up",
+    "input_move_down": "down",
+    "input_select_left": "shift+left",
+    "input_select_right": "shift+right",
+    "input_select_up": "shift+up",
+    "input_select_down": "shift+down",
+    "input_line_home": "ctrl+a",
+    "input_line_end": "ctrl+e",
+    "input_select_line_home": "none",
+    "input_select_line_end": "none",
+    "input_visual_line_home": "none",
+    "input_visual_line_end": "none",
+    "input_select_visual_line_home": "none",
+    "input_select_visual_line_end": "none",
+    "input_buffer_home": "home",
+    "input_buffer_end": "end",
+    "input_select_buffer_home": "shift+home",
+    "input_select_buffer_end": "shift+end",
+    "input_delete_line": "alt+d",
+    "input_delete_to_line_end": "ctrl+k",
+    "input_delete_to_line_start": "ctrl+u",
+    "input_backspace": "backspace,shift+backspace",
+    "input_delete": "delete,shift+delete",
+    "input_undo": "ctrl+-,cmd+z",
+    "input_redo": "ctrl+.,cmd+shift+z",
+    "input_word_forward": "alt+f,alt+right",
+    "input_word_backward": "alt+b,alt+left",
+    "input_select_word_forward": "alt+shift+f,alt+shift+right",
+    "input_select_word_backward": "alt+shift+b,alt+shift+left",
+    "input_delete_word_forward": "ctrl+d",
+    "input_delete_word_backward": "ctrl+w,alt+backspace",
     "history_previous": "up",
     "history_previous": "up",
-    "history_next": "down"
+    "history_next": "down",
+    "terminal_suspend": "ctrl+z"
   }
   }
 }
 }
 ```
 ```