ugly but works

actual-tui-plugins
Sebastian Herrlinger 2026-03-04 00:08:26 +01:00
parent 7e1d396427
commit 3b38e8dc24
9 changed files with 151 additions and 18 deletions

View File

@ -1,19 +1,16 @@
/** @jsxImportSource ../../packages/opencode/node_modules/@opentui/solid */
/** @jsxImportSource ../../packages/plugin/src */
import mytheme from "../themes/mytheme.json" with { type: "json" }
const slot = (label) => ({
id: "workspace-smoke",
slots: {
home_hint() {
console.error(`[workspace-smoke] render home_hint (${label})`)
return <text> [plugin:{label}]</text>
},
home_footer() {
console.error(`[workspace-smoke] render home_footer (${label})`)
return <text> theme:workspace-plugin-smoke</text>
},
session_footer(_ctx, props) {
console.error(`[workspace-smoke] render session_footer (${props.session_id.slice(0, 8)})`)
return <text> session:{props.session_id.slice(0, 8)}</text>
},
},
@ -27,7 +24,6 @@ const tui = async (input, options) => {
if (options?.enabled === false) return
const label = typeof options?.label === "string" ? options.label : "smoke"
input.slots.register(slot(label))
console.error(`[workspace-smoke] tui plugin initialized (${label})`)
}
export default {

View File

@ -335,8 +335,8 @@
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/util": "workspace:*",
"@openrouter/ai-sdk-provider": "1.5.4",
"@opentui/core": "0.1.86",
"@opentui/solid": "0.1.86",
"@opentui/core": "0.0.0-20260303-946d7494",
"@opentui/solid": "0.0.0-20260303-946d7494",
"@parcel/watcher": "2.5.1",
"@pierre/diffs": "catalog:",
"@solid-primitives/event-bus": "1.1.2",
@ -414,6 +414,7 @@
"version": "1.2.24",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@opentui/core": "0.1.86",
"zod": "catalog:",
},
"devDependencies": {
@ -1436,7 +1437,7 @@
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.86", "", { "os": "win32", "cpu": "x64" }, "sha512-HRfgAUlcu71/MrtgfX4Gj7PsDtfXZiuC506Pkn1OnRN1Xomcu10BVRDweUa0/g8ldU9i9kLjMGGnpw6/NjaBFg=="],
"@opentui/solid": ["@opentui/solid@0.1.86", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.86", "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-pOZC9dlZIH+bpstVVZ2AvYukBnslZTKSl/y5H8FWcMTHGv/BzpGxXBxstL65E/IQASqPFbvFcs7yMRzdLhynmA=="],
"@opentui/solid": ["@opentui/solid@0.0.0-20260303-946d7494", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20260303-946d7494", "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-q6md0VvqpWwxw9BlDeiGSuVbG2UU9qrRdcOBYs8+HH3/VRef87RtteaNn++GhYutODN+NNT8s+fgIN3I27YGkw=="],
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
@ -3796,7 +3797,7 @@
"pagefind": ["pagefind@1.4.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.4.0", "@pagefind/darwin-x64": "1.4.0", "@pagefind/freebsd-x64": "1.4.0", "@pagefind/linux-arm64": "1.4.0", "@pagefind/linux-x64": "1.4.0", "@pagefind/windows-x64": "1.4.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g=="],
"pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="],
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
"param-case": ["param-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A=="],
@ -5174,6 +5175,8 @@
"@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "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/solid/@opentui/core": ["@opentui/core@0.0.0-20260303-946d7494", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20260303-946d7494", "@opentui/core-darwin-x64": "0.0.0-20260303-946d7494", "@opentui/core-linux-arm64": "0.0.0-20260303-946d7494", "@opentui/core-linux-x64": "0.0.0-20260303-946d7494", "@opentui/core-win32-arm64": "0.0.0-20260303-946d7494", "@opentui/core-win32-x64": "0.0.0-20260303-946d7494", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-QXA4qUQT9ZV4lD7ckY3Zxh92WpYrO5OU0pNfSKQyokLRxSRT1IWhEk3Fy/4hl+4M9T8EiLQJ2exTCw6WyN1AXg=="],
"@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.9", "", { "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=="],
"@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
@ -5532,6 +5535,8 @@
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YspqqyJPzHjqWrjt4y/Wgc2aJgCcQj5uIJgZpq2Ar/lH30cEVhgE+keePDbjKpetD9UwNggCj7u6kO3unS23OQ=="],
"opencode/@opentui/core": ["@opentui/core@0.0.0-20260303-946d7494", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20260303-946d7494", "@opentui/core-darwin-x64": "0.0.0-20260303-946d7494", "@opentui/core-linux-arm64": "0.0.0-20260303-946d7494", "@opentui/core-linux-x64": "0.0.0-20260303-946d7494", "@opentui/core-win32-arm64": "0.0.0-20260303-946d7494", "@opentui/core-win32-x64": "0.0.0-20260303-946d7494", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-QXA4qUQT9ZV4lD7ckY3Zxh92WpYrO5OU0pNfSKQyokLRxSRT1IWhEk3Fy/4hl+4M9T8EiLQJ2exTCw6WyN1AXg=="],
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
"opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
@ -5546,6 +5551,8 @@
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"opentui-spinner/@opentui/solid": ["@opentui/solid@0.1.86", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.86", "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-pOZC9dlZIH+bpstVVZ2AvYukBnslZTKSl/y5H8FWcMTHGv/BzpGxXBxstL65E/IQASqPFbvFcs7yMRzdLhynmA=="],
"ora/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
"ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@ -5668,12 +5675,12 @@
"type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"unicode-trie/pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="],
"unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
"uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"utif2/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
"vite-plugin-icons-spritesheet/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
"vitest/@vitest/expect": ["@vitest/expect@4.0.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ=="],
@ -6022,6 +6029,18 @@
"@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"@opentui/solid/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20260303-946d7494", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JULjCOqUskZ/v2a7rpZb7kmlh9Z7Io2ajiqKWQ/DwZy+wwykQLydlOyIg4VzK+5z6cTD0KFTiadatEgg3b3o8Q=="],
"@opentui/solid/@opentui/core/@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20260303-946d7494", "", { "os": "darwin", "cpu": "x64" }, "sha512-ybwyEU+GlR/M2YfS9IHF9yzmLTixxUb2qw5UqIMZoEF1vCABq20O8vdypJRCz75rm4LqRXTajJ1w5LAsN3OQTA=="],
"@opentui/solid/@opentui/core/@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20260303-946d7494", "", { "os": "linux", "cpu": "arm64" }, "sha512-fzXnDfcoGE2OHWut6gjAtaTAXcKaNX0DS3RXOsJ4ZhwzurOaoULJRAXOWwVmdWqncftz2ao4cXeDK1dr8kqNqQ=="],
"@opentui/solid/@opentui/core/@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20260303-946d7494", "", { "os": "linux", "cpu": "x64" }, "sha512-YPJ4aEz1O/7P9FX78rlh++ClTn/LgVlyG5FLXXUDACWafnyuu0Vdoj2SddYJaNnnhk21UpmHzfBR0cuTZUVlSw=="],
"@opentui/solid/@opentui/core/@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20260303-946d7494", "", { "os": "win32", "cpu": "arm64" }, "sha512-mRoS5WJkMs40VzhWRAjA56ZPc8e7EwT91tNSpSvAYa15T0d5T55T7t2DuCWzn5Vor/JBPzPMlIHmTnX19raY5Q=="],
"@opentui/solid/@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20260303-946d7494", "", { "os": "win32", "cpu": "x64" }, "sha512-mqCjwP7k460oDIy6nj3FfAz+CzPlICyv1Ksl23VQ0uV3r55SqtiWL/g8LlxCV6gwG1a2tA+FO5YGRPtrzC0xaA=="],
"@pierre/diffs/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="],
"@pierre/diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
@ -6180,6 +6199,18 @@
"opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
"opencode/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20260303-946d7494", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JULjCOqUskZ/v2a7rpZb7kmlh9Z7Io2ajiqKWQ/DwZy+wwykQLydlOyIg4VzK+5z6cTD0KFTiadatEgg3b3o8Q=="],
"opencode/@opentui/core/@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20260303-946d7494", "", { "os": "darwin", "cpu": "x64" }, "sha512-ybwyEU+GlR/M2YfS9IHF9yzmLTixxUb2qw5UqIMZoEF1vCABq20O8vdypJRCz75rm4LqRXTajJ1w5LAsN3OQTA=="],
"opencode/@opentui/core/@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20260303-946d7494", "", { "os": "linux", "cpu": "arm64" }, "sha512-fzXnDfcoGE2OHWut6gjAtaTAXcKaNX0DS3RXOsJ4ZhwzurOaoULJRAXOWwVmdWqncftz2ao4cXeDK1dr8kqNqQ=="],
"opencode/@opentui/core/@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20260303-946d7494", "", { "os": "linux", "cpu": "x64" }, "sha512-YPJ4aEz1O/7P9FX78rlh++ClTn/LgVlyG5FLXXUDACWafnyuu0Vdoj2SddYJaNnnhk21UpmHzfBR0cuTZUVlSw=="],
"opencode/@opentui/core/@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20260303-946d7494", "", { "os": "win32", "cpu": "arm64" }, "sha512-mRoS5WJkMs40VzhWRAjA56ZPc8e7EwT91tNSpSvAYa15T0d5T55T7t2DuCWzn5Vor/JBPzPMlIHmTnX19raY5Q=="],
"opencode/@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20260303-946d7494", "", { "os": "win32", "cpu": "x64" }, "sha512-mqCjwP7k460oDIy6nj3FfAz+CzPlICyv1Ksl23VQ0uV3r55SqtiWL/g8LlxCV6gwG1a2tA+FO5YGRPtrzC0xaA=="],
"opencontrol/@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
@ -6190,6 +6221,10 @@
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
"opentui-spinner/@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "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": ["babel-preset-solid@1.9.9", "", { "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=="],
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
@ -6648,6 +6683,8 @@
"opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"opentui-spinner/@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],

View File

@ -91,8 +91,8 @@
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/util": "workspace:*",
"@openrouter/ai-sdk-provider": "1.5.4",
"@opentui/core": "0.1.86",
"@opentui/solid": "0.1.86",
"@opentui/core": "0.0.0-20260303-946d7494",
"@opentui/solid": "0.0.0-20260303-946d7494",
"@parcel/watcher": "2.5.1",
"@pierre/diffs": "catalog:",
"@solid-primitives/event-bus": "1.1.2",

View File

@ -1,5 +1,13 @@
import type { PluginModule, TuiPlugin as TuiPluginFn, TuiPluginInput, TuiSlotPlugin } from "@opencode-ai/plugin"
import {
setTuiJSXRuntime,
type PluginModule,
type TuiPlugin as TuiPluginFn,
type TuiPluginInput,
type TuiSlotPlugin,
} from "@opencode-ai/plugin"
import type { JSX } from "solid-js"
import { createComponent, createElement, spread } from "@opentui/solid"
import { Config } from "@/config/config"
import { TuiConfig } from "@/config/tui"
import { Log } from "@/util/log"
@ -7,6 +15,8 @@ import { BunProc } from "@/bun"
import { Instance } from "@/project/instance"
import { registerThemes } from "./context/theme"
import { existsSync } from "fs"
import { tmpdir } from "os"
import { fileURLToPath, pathToFileURL } from "url"
export namespace TuiPlugin {
const log = Log.create({ service: "tui.plugin" })
@ -26,6 +36,34 @@ export namespace TuiPlugin {
return BunProc.install(pkg, version)
}
async function module(path: string) {
if (!path.startsWith("file://")) {
return import(path)
}
const file = fileURLToPath(path)
if (!file.endsWith(".tsx") && !file.endsWith(".jsx")) {
return import(path)
}
const build = await Bun.build({
entrypoints: [file],
target: "bun",
format: "esm",
minify: false,
write: false,
})
if (!build.success || !build.outputs[0]) {
log.error("failed to build local tui plugin", {
path,
logs: build.logs,
})
return
}
const text = await build.outputs[0].text()
const out = `${tmpdir()}/opencode-tui-plugin-${Bun.hash(path)}-${Date.now()}.mjs`
await Bun.write(out, text)
return import(pathToFileURL(out).href)
}
function slot(entry: unknown) {
if (!entry || typeof entry !== "object") return
if ("id" in entry && typeof entry.id === "string" && "slots" in entry && typeof entry.slots === "object") {
@ -49,6 +87,25 @@ export namespace TuiPlugin {
})
}
const node = (type: unknown, props: unknown) => {
if (typeof type === "function") {
return createComponent(type as never, (props ?? {}) as never)
}
const out = createElement(String(type))
spread(out, (props ?? {}) as Record<string, unknown>)
return out
}
setTuiJSXRuntime({
Fragment(props: Record<string, unknown> | undefined) {
if (!props || !("children" in props)) return
return props.children
},
jsx: node,
jsxs: node,
jsxDEV: node,
})
await Instance.provide({
directory: dir,
fn: async () => {
@ -65,7 +122,7 @@ export namespace TuiPlugin {
})
if (!path) continue
const mod = await import(path).catch((error) => {
const mod = await module(path).catch((error) => {
log.error("failed to load tui plugin", { path: spec, error })
return
})
@ -87,7 +144,6 @@ export namespace TuiPlugin {
const plugin = slot(entry)
if (plugin) {
input.slots.register(plugin)
log.info("registered tui slot plugin", { id: plugin.id })
}
const tui = (() => {
@ -98,7 +154,6 @@ export namespace TuiPlugin {
})()
if (!tui) continue
await tui(input, Config.pluginOptions(item))
log.info("initialized tui plugin module", { path: spec })
}
}
},

View File

@ -10,7 +10,9 @@
},
"exports": {
".": "./src/index.ts",
"./tool": "./src/tool.ts"
"./tool": "./src/tool.ts",
"./jsx-runtime": "./src/jsx-runtime.ts",
"./jsx-dev-runtime": "./src/jsx-dev-runtime.ts"
},
"files": [
"dist"

View File

@ -18,6 +18,7 @@ import type { BunShell } from "./shell"
import { type ToolDefinition } from "./tool"
export * from "./tool"
export { getTuiJSXRuntime, setTuiJSXRuntime, type TuiJSXRuntime } from "./jsx"
export type { CliRenderer, SlotMode } from "@opentui/core"
export type ProviderContext = {

View File

@ -0,0 +1,8 @@
import { getTuiJSXRuntime } from "./jsx"
const runtime = getTuiJSXRuntime()
export const Fragment = runtime.Fragment
export const jsx = runtime.jsx
export const jsxs = runtime.jsxs
export const jsxDEV = runtime.jsxDEV ?? runtime.jsx

View File

@ -0,0 +1,7 @@
import { getTuiJSXRuntime } from "./jsx"
const runtime = getTuiJSXRuntime()
export const Fragment = runtime.Fragment
export const jsx = runtime.jsx
export const jsxs = runtime.jsxs

View File

@ -0,0 +1,27 @@
type TuiJSXFactory = (...args: unknown[]) => unknown
export type TuiJSXRuntime = {
Fragment: unknown
jsx: TuiJSXFactory
jsxs: TuiJSXFactory
jsxDEV?: TuiJSXFactory
}
const key = Symbol.for("opencode.tui.jsx-runtime")
export function setTuiJSXRuntime(runtime: TuiJSXRuntime) {
;(globalThis as Record<PropertyKey, unknown>)[key] = runtime
}
export function getTuiJSXRuntime() {
const runtime = (globalThis as Record<PropertyKey, unknown>)[key]
if (!runtime || typeof runtime !== "object") {
throw new Error("OpenCode TUI JSX runtime has not been initialized")
}
const jsx = (runtime as Record<string, unknown>).jsx
const jsxs = (runtime as Record<string, unknown>).jsxs
if (typeof jsx !== "function" || typeof jsxs !== "function") {
throw new Error("OpenCode TUI JSX runtime has invalid factories")
}
return runtime as TuiJSXRuntime
}