From 3b38e8dc24fcb176ea6632d1e9030ea2cd7d9f86 Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Wed, 4 Mar 2026 00:08:26 +0100 Subject: [PATCH] ugly but works --- .opencode/plugins/tui-smoke.tsx | 6 +- bun.lock | 49 ++++++++++++++-- packages/opencode/package.json | 4 +- packages/opencode/src/cli/cmd/tui/plugin.ts | 63 +++++++++++++++++++-- packages/plugin/package.json | 4 +- packages/plugin/src/index.ts | 1 + packages/plugin/src/jsx-dev-runtime.ts | 8 +++ packages/plugin/src/jsx-runtime.ts | 7 +++ packages/plugin/src/jsx.ts | 27 +++++++++ 9 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 packages/plugin/src/jsx-dev-runtime.ts create mode 100644 packages/plugin/src/jsx-runtime.ts create mode 100644 packages/plugin/src/jsx.ts diff --git a/.opencode/plugins/tui-smoke.tsx b/.opencode/plugins/tui-smoke.tsx index a8e446a215..ac3d5c07bf 100644 --- a/.opencode/plugins/tui-smoke.tsx +++ b/.opencode/plugins/tui-smoke.tsx @@ -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 [plugin:{label}] }, home_footer() { - console.error(`[workspace-smoke] render home_footer (${label})`) return theme:workspace-plugin-smoke }, session_footer(_ctx, props) { - console.error(`[workspace-smoke] render session_footer (${props.session_id.slice(0, 8)})`) return session:{props.session_id.slice(0, 8)} }, }, @@ -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 { diff --git a/bun.lock b/bun.lock index 4b7097232a..b0d71da6a7 100644 --- a/bun.lock +++ b/bun.lock @@ -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=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 4e4f46b0c6..2953a3e8c8 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -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", diff --git a/packages/opencode/src/cli/cmd/tui/plugin.ts b/packages/opencode/src/cli/cmd/tui/plugin.ts index 18a8174212..15f8851e43 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin.ts @@ -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) + return out + } + setTuiJSXRuntime({ + Fragment(props: Record | 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 }) } } }, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 7ab04901cb..9592d0e534 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -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" diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index ea07082000..33fbc7e02a 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -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 = { diff --git a/packages/plugin/src/jsx-dev-runtime.ts b/packages/plugin/src/jsx-dev-runtime.ts new file mode 100644 index 0000000000..d536cf326e --- /dev/null +++ b/packages/plugin/src/jsx-dev-runtime.ts @@ -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 diff --git a/packages/plugin/src/jsx-runtime.ts b/packages/plugin/src/jsx-runtime.ts new file mode 100644 index 0000000000..6f0f4d702c --- /dev/null +++ b/packages/plugin/src/jsx-runtime.ts @@ -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 diff --git a/packages/plugin/src/jsx.ts b/packages/plugin/src/jsx.ts new file mode 100644 index 0000000000..7fe7836469 --- /dev/null +++ b/packages/plugin/src/jsx.ts @@ -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)[key] = runtime +} + +export function getTuiJSXRuntime() { + const runtime = (globalThis as Record)[key] + if (!runtime || typeof runtime !== "object") { + throw new Error("OpenCode TUI JSX runtime has not been initialized") + } + const jsx = (runtime as Record).jsx + const jsxs = (runtime as Record).jsxs + if (typeof jsx !== "function" || typeof jsxs !== "function") { + throw new Error("OpenCode TUI JSX runtime has invalid factories") + } + return runtime as TuiJSXRuntime +}